当前位置: 首页 > 知识库问答 >
问题:

前端 - 如何优雅地用CSS实现一个罗盘指示器?

仰雅昶
2024-05-23

如何优雅地用CSS实现一个罗盘指示器?

实现效果大体类似于图片中这样:

  • 一个圆盘里面包含四个箭头,四个箭头的尾部位于圆盘的中心,头部分别指向东、西、 南、北四个方面。
  • 当前物体所朝向方面的箭头颜色与其他箭头颜色不同。

扩展:

  • 在箭头头部用文字("N","S","E","W")标识方向。
  • js获取设备的方向,在罗盘上实时显示当前设备的方向。
  • ....

image.png

我的实现思路:

  • 首先实现一个箭头,一个箭头是由上下两个三角形组成的。

    • CSS制作一个三角形常用的方法就是借助border
    • 使用css变量来设置箭头的大小、颜色,方便我们修改以制作不同大小、比例、颜色的箭头。
<div class="arrow">  <div class="arrow-top"></div>  <div class="arrow-bottom"></div></div>
.arrow {  --width: 50px;  --height: 200px;  --color: blue;   width: var(--width);  height: var(--height);}.arrow-top {  width: 0;  border-width: 0 calc(var(--width) / 2) var(--width) calc(var(--width) / 2);  border-color: transparent;  border-bottom-color: var(--color);  border-style: solid;}.arrow-bottom {  margin-left: calc(var(--width) / 4);  width: 0;  --bottom-width: calc(var(--width) / 2);  --bottom-height: calc(var(--height) - var(--width));  border-width: var(--bottom-height) calc(var(--bottom-width) / 2) 0 calc(var(--bottom-width) / 2);  border-color: transparent;  border-top-color: var(--color);  border-style: solid;}

image.png

  • 使用transform实现不同方向的箭头
.arrow.top {}.arrow.right {  transform: rotate(90deg);}.arrow.left {  transform: rotate(-90deg);}.arrow.bottom {  transform: rotate(180deg);}
  • 通过添加类名light给其中一个箭头设置不同的颜色

    .arrow.light {--color: yellow;}
  • 最后用一个容器包含四个箭头,采用绝对定位+transform将箭头定位到指定的位置。

    • 定位到指定位置的具体方法:

      • 先定位到上方箭头的位置

        .arrow {...top: 0;left: 50%;transform: translate(-50%, 0);}
      • 然后再旋转就定位到达其他指定的位置。

        • 需要注意的几点是

          • 为了让箭头的尾部位于圆盘的中心,需要调整变换原点transform-origin: 50% 100%
          • 在好几个规则(.arrow, .arrow.left..)中都出现了transform变换,但是当施加到同一个元素上时只有一个transform..声明有效,所以将.arrow中出现的transform变换整合到arrow.left..中。

            .arrow.top {transform: translate(-50%, 0);}.arrow.right {transform: translate(-50%, 0) rotate(90deg);}.arrow.left {transform: translate(-50%, 0) rotate(-90deg);}.arrow.bottom {transform: translate(-50%, 0) rotate(180deg);}
    • relative容器的大小怎么确定?也是借助于css变量。箭头的大小在这个容器中确定了,然后箭头继承了这个大小。容器的大小就是calc(max((--arrow-height), var(--arrow-weight)) * 2)

      .arrow {--width: var(--arrow-width, 50px);--height: var(--arrow-height, 200px);--color: var(--arrow-color, blue);......compass-indicator {--arrow-width: 50px;--arrow-height: 100px;--arrow-color: blue;--arrow-light-color: yellow;--size: calc(max(var(--arrow-height), var(--arrow-width)) * 2);  position: relative;width: var(--size);height: var(--size);background: red;border-radius: 50%;border: 4px dashed orange;}

在做项目的时候,需要一个这样的罗盘指示器,觉得可以作为一个比较好的CSS挑战题。
欢迎给出其他好的实现思路。

附:

  • codepen

共有1个答案

欧阳斌
2024-05-23

https://jsrun.net/uR5Kp/edit

<div class="compass-indicator">  <div class="arrow" style="--rotate:0deg;"></div>  <div class="arrow" style="--rotate:90deg;"></div>  <div class="arrow" style="--rotate:180deg;"></div>  <div class="arrow light" style="--rotate:270deg;"></div>  </div>.arrow {  --width: var(--arrow-width, 50px);  --height: var(--arrow-height, 200px);  --color: var(--arrow-color, blue);  width: var(--width);  height: var(--height);  transform-origin: 50% 100%;  background-color: var(--color);  position: absolute;  top: 0;  left: 50%;  clip-path: polygon(50% 0, 100% 50%, 70% 50%, 50% 100%, 30% 50%, 0 50%);  transform: translate(-50%, 0) rotate(var(--rotate));}.arrow.light {  --color: var(--arrow-light-color, blue);}.compass-indicator {  --arrow-width: 50px;  --arrow-height: 100px;  --arrow-color: blue;  --arrow-light-color: yellow;  --size: calc(max(var(--arrow-height), var(--arrow-width)) * 2);  position: relative;  width: var(--size);  height: var(--size);  background: red;  border-radius: 50%;  border: 4px dashed orange;}
 类似资料:
  • 如何优雅地使用react做一个wordle小游戏? wordle游戏wiki 在线wordle游戏 我用reat + tailwindcss实现了该游戏,但是比较纠结什么什么时候该使用React.useCallback,自定义的hooks是否合理? 我写了一个自定义的hook,用来实现数据持久化(刷新之后仍可以保持刷新之前的游戏状态) 这里的name,initial永远都不会变化。 初始状态先从l

  • 如何优雅地实现图片局部预览组件? 下面是bilibili用户头像上传的界面,左侧我们可以通过移动选择框和缩放选择框来选择图片的某一部分,右侧是头像预览。 我自己尝试使用react+tailwindcss来实现了一下,但是实现的并不太理想。 通过四个角来缩放的效果并不好。有时候没有反应,有时候又突然之间变换很大。 应该是当我按下鼠标之后,抬起鼠标之前,才能移动和缩放选择框。但是有时候,抬起了依然可以

  • 如何优雅地覆盖antdesign的样式? 我想要把将这个button的border去掉(只是这个button,不影响其他button的样式)。 className or style都不行,Button组件没有提供这两个prop。倒是有一个classNames不知道是干什么的。 在index.css中添加以下规则,倒是可以去除Button上的border但是会影响所有的Button样式。 通过Con

  • 情景 Vue 模板中我们经常会用到一些中间量,比如 中的 item.a.b.c 中的 message.split(' ') 问题 由于 v-for 或 <slot> 等的包裹,我们难以利用 computed 简单地提出这些中间量(当然我知道都有普通的解决方案) 我想到 Vue 既然有 v-if / v-for 等和 JavsScript 关键字风格类似的内建指令,能不能实现一个 v-let 来简化

  • 想要的效果 如何使用以下 html 只修改 css 不增加元素的前提下实现上边的效果

  • 人生太短,不能写没人会读的废话,如果你写了废话,没人会去读。所以好一点的文档是最好的。经理不会去理解这些东西,因为不好的文档会给他们错误的安全感以至于他们不敢依赖他们的程序员。如果一些人绝对坚持你真的在写没用的文档,就告诉他们“是的”,然后安静的找一份更好的工作。 没有其他事情比精确估计 把好的文档转为放松文档要求的估计 更为有效率。真相是冷酷而艰难的:文档,就像测试,会花比开发代码多几倍的时间。