在圆周上设置线条

3
我正在尝试使用CSS和Javascript创建下面的图片。

enter image description here

我已经成功地创建了半圆,但是我不知道如何将不同的线条根据圆周对齐。我祈求CSS神灵能够在我的探索中帮助我。
这是我到目前为止的代码:

.roll-degrees{
  position: absolute;
  top: 0px;
  z-index: 1;
}

.roll-curve{
  width:400px;
  height:120px;  
  border:solid 3px #000;
  border-color: black transparent transparent transparent;
  border-radius: 50%/120px 120px 0px 0;
}
<div class="roll-degrees">
  <div class="roll-curve"></div>
</div>

根据答案更新:现在我正在尝试在圆形底部显示箭头。下面是代码。我是否需要添加/减去箭头的尺寸?

.roll-degrees-arrow{
  --width: 3px;
  --height: 16px;
  position: absolute;
  width: 0; 
  height: 0; 
  border-left: 15px solid transparent;
  border-right: 15px solid transparent;
  border-bottom: 25px solid white;
  z-index: 1;
  --arrow-angle-direction: 1; /* default towards right*/
  top: calc(50% - var(--height));
  left: calc(50% - var(--width) / 2);
  transform:
        rotate(calc(var(--arrow-angle-direction) * var(--arrow-angle)))
        translateY(calc(-1 * var(--circle-radius)))
        ;
}

<div class="roll-degrees-wrapper">
     <div class="roll-degrees">
          <div class="roll-degrees-arrow"></div>
          ...
     </div>
</div>
1个回答

2

初始化设置

首先,使用一个合适的圆形进行数学计算比使用椭圆更容易,因此让roll-curve元素成为一个圆形(然后我们可以使用容器裁剪其余部分)。我还在整个结构周围添加了一个额外的包装器,用于裁剪大小,而原始包装器(roll-degrees)将是适当的大小以便更容易定位刻度。

刻度

接下来的方法是通过应用绝对定位(从圆心开始)并使用 transform: translateY() 移动刻度,从而控制每个刻度的确切位置。将所有刻度都居中放置在 roll-curve 圆的中间,旋转每个刻度,并从那里将每个刻度向外平移至圆的周长。先旋转一下,就可以将每个刻度向上移动圆的半径,“向上”就会是恰当的方向。

标记

关于动态设置标记的位置,您可以使用与刻度基本相同的方法(使用相同的CSS样式)。你只需要一种方式将值转换为角度(可以相当简单地完成——有关详细信息,请参见下面的示例),以及一个角度方向。

下面的示例包括一个范围输入,但是您可以以任何喜欢的方式为标记提供值。

示例

const minValue = 0;
const maxValue = 100;

function getAngleForValue(value) {
    const angleRange = 90;
    const angleCenter = angleRange / 2;
    return value / maxValue * angleRange - angleCenter;
}

const marker = document.querySelector("#marker");

function setMarkerPosition(value) {
    const angle = getAngleForValue(value);
    const absAngle = Math.abs(angle);
    const angleDirection = Math.sign(angle);
    marker.style.setProperty("--tick-angle", `${absAngle}deg`);
    marker.style.setProperty("--tick-angle-direction", angleDirection);
}

const markerRangeInput = document.querySelector("#marker-range-input");
markerRangeInput.min = minValue;
markerRangeInput.max = maxValue;
markerRangeInput.value = (maxValue - minValue) / 2;

markerRangeInput.addEventListener("change", (event) => {
    setMarkerPosition(event.target.value)
})
* {
    box-sizing: border-box;
}

body {
    background-color: blue;
}

.roll-degrees-wrapper {
    --circle-radius: 200px;
    height: calc(var(--circle-radius) / 2);
    position: relative;
    overflow: hidden;

    --tick-1-angle: 45deg;
    --tick-2-angle: 30deg;
    --tick-3-angle: 15deg;
    --tick-4-angle: 0deg; /* middle */
}

.roll-degrees {
    box-sizing: content-box;
    --size: calc(var(--circle-radius) * 2);
    width: var(--size);
    height: var(--size);
    padding: 16px;
    position: relative;
}

.roll-curve {
    --size: calc(var(--circle-radius) * 2);
    width: var(--size);
    height: var(--size);
    border: solid 3px #000;
    border-color: transparent;
    border-top-color: white;
    border-radius: 50%;
}

.tick {
    --width: 3px;
    --height: 16px;
    width: var(--width);
    height: var(--height);
    background-color: white;
    position: absolute;
    --v-offset: var(--height);
    --h-offset: var(--width);
    top: calc(50% - var(--v-offset));
    left: calc(50% - var(--h-offset) / 2);
    --tick-angle-direction: 1;
    transform:
        rotate(calc(var(--tick-angle-direction) * var(--tick-angle)))
        translateY(calc(-1 * var(--circle-radius)))
    ;
    transform-origin: bottom;
}

.tick--tall {
    --height: 24px;
}

.tick:nth-child(1),
.tick:nth-child(2),
.tick:nth-child(3) {
    --tick-angle-direction: -1;
}

.tick:nth-child(1),
.tick:nth-child(7) {
    --tick-angle: var(--tick-1-angle);
}

.tick:nth-child(2),
.tick:nth-child(6) {
    --tick-angle: var(--tick-2-angle);
}

.tick:nth-child(3),
.tick:nth-child(5) {
    --tick-angle: var(--tick-3-angle);
}

.tick:nth-child(4) {
    --tick-angle: var(--tick-4-angle);
}

.tick.marker {
    --marker-half-size: 14px;
    --marker-full-size: calc(var(--marker-half-size) * 2);
    --v-offset: var(--marker-half-size);
    --h-offset: var(--marker-full-size);
    --tick-angle: 0deg;
    background-color: transparent;
    width: 0;
    height: 0;
    border: var(--marker-half-size) solid transparent;
    --marker-color: red;
    border-top-color: var(--marker-color);
    border-bottom-color: var(--marker-color);
    transform-origin: center;
}
<div class="roll-degrees-wrapper">
    <div class="roll-degrees">
        <div class="tick tick--tall"></div>
        <div class="tick"></div>
        <div class="tick tick--tall"></div>
        <div class="tick"></div>
        <div class="tick tick--tall"></div>
        <div class="tick"></div>
        <div class="tick tick--tall"></div>
        <div id="marker" class="marker tick"></div>
        <div class="roll-curve"></div>
    </div>
</div>

<input id="marker-range-input" type="range">

注意:您可能希望在标记上进行更好的样式设置或使用某种图标,但这只是一个简单的示例。

2
你不需要像那样硬编码cos和sin,transform可以为你完成所有这些操作:从圆心开始,旋转(rotate)你的刻度,然后仅在y轴上进行平移,这将使其移动到圆上的正确位置:https://jsfiddle.net/b4e7oLah/ (在fiddle中仍有一些工作要处理边框宽度,但你已经有了想法)。 - Kaiido
非常感谢大家,我从中学到了很多! - Jonathan Sandler
1
@JonathanSandler更新了答案,包括一个动态标记,请告诉我它的效果如何!我所包含的方法使用单个元素作为顶部和底部箭头(这可能更容易处理),但如果您只想在底部有一个箭头,您可以调整元素上的--v-offset属性(使其更小)。 - Henry Woody
2
你可能希望将 .markertransform-origin 设置为其中心点:https://jsfiddle.net/50xbnh79/ - Kaiido
非常感谢大家的帮助,我真的很感激。 - Jonathan Sandler
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接