进度圆 - 在圆的末端绘制一个小弧+更多

Den*_*los 4 css geometry canvas responsive-design css-shapes

如何在进度圆的末端绘制一个小圆圈,并在其下方/上方添加一个小文本块?

示例img:

在此输入图像描述

<div class="radial-progress" data-progress="0">
    <div class="circle">
    <div class="img"></div>
        <div class="mask full">
            <div class="fill"></div>
        </div>
        <div class="mask half">
            <div class="fill"></div>
            <div class="fill fix"></div>
        </div>
        <div class="shadow"></div>
    </div>
    <div class="inset">
        <div class="percentage">
            <div class="numbers"><span>-</span><span>0%</span><span>1%</span> <!--- lots of spans --->
        </div>
    </div>
</div>
Run Code Online (Sandbox Code Playgroud)

感谢Andre的medium.com文章,我已经编辑了他的版本 - 一个UPDATED版本说明了我想要为给定的%值动态实现的内容:http://codepen.io/Inlesco/pen/pgKXeG

但是,编译的CSS太多了.对于每百分之一的百分比,定位的东西会有太多的CSS.当前的CSS(@codepen示例)已经加权~50 KB.不是最好的做法.

我已经在基于JS Canvas的变体上做了一些进展,将vert&horz居中img定位在画布上方.但对于一个好看的响应式网站来说,它真的是唯一的方法和最佳实践吗?

Har*_*rry 6

CSS绝对不是这样做的正确工具,我会敦促你放弃这个想法.Canvas绝对是一个不错的选择,但对于响应式网站,我建议你使用SVG.使用SVG,可以根据用户输入轻松绘制进度圆,并在其顶部添加圆/点.

以下是首次创建进度循环时必须执行的步骤:

  • 获取进度圆的用户输入(0到100),然后根据它计算圆弧的角度.公式将是(输入*360/100)因为圆圈具有360度.
  • SVG弧通常从时钟中的3位开始,因此计算出的弧角必须在负值处偏移90度.也就是说,电弧必须在-90度到270度之间.
  • 使用公式将计算的角度转换为弧度 - (角度度数*PI​​/180).
  • 计算出以弧度表示的角度后,使用简单的三角函数在角度上找到圆上的点:

    • X坐标= Cos(以弧度表示的角度)*半径+中心点的X坐标.
    • Y坐标= Sin(弧度角)*半径+中心点的Y坐标.
  • 一旦找到该点,我们需要创建路径,使其从上一步中找到的点开始,到达圆心,然后到弧的起点,然后再从该点开始绘制圆弧.到了原点.路径是以这种方式创建的,因为我们需要路径在所需的点结束(因为我们将标记附加到终点).

  • 弧创建需要注意的一点是,任何角度> 180度都需要两个弧命令来创建.第一个弧线是12点钟位置到6点的位置,下一个弧线表示其余部分.因此,我们使用if/else循环.

要在其尖端添加点/圆,需要执行以下操作:

  • 将一个marker元素添加到SVG中,并使用该circle元素创建一个圆形标记.该circle元素有3个属性--cx,cy是点的中心点,r是点的半径.
  • 然后使用该marker-end属性将此标记添加到path.设置此属性意味着创建的任何路径都会在其结束点自动附加此点.不需要为此进行其他编码,因为SVG会自动处理定位.

也可以使用text元素添加文本,然后可以使用xy属性设置其位置.(这一点仍然需要在下面的片段中进行调整.)

演示:

下面是一个非常粗略的实现演示.

window.onload = function() {
  var btn = document.querySelector('button'),
    inp = document.querySelector('#progress'),
    path = document.querySelector('#p'),
    text = document.querySelector('#val'),
    rect = document.querySelector('rect'),
    output = document.querySelector('#path-output');
  var x = 0,
    y = 0,
    r = 30,
    cx = 50,
    cy = 50,
    d = '',
    fill = 'yellowgreen',
    stroke = 'transparent';
  btn.addEventListener('click', function() {
    progress = (inp.value == '') ? 0 : inp.value;
    arcRad = ((progress * 360 / 100) - 90) * Math.PI / 180;

    x = Math.cos(arcRad) * r + cx;
    y = Math.sin(arcRad) * r + cy;

    if (progress > 0 && progress <= 50) {
      d = 'M' + x + ',' + y + ' L' + cx + ',' + cy + ' L' + cx + ',' + (cy - r) + ' A' + r + ',' + r + ' 0 0,1' + x + ',' + y;
      setColors(fill, stroke);
      setOutput();
    } else if (progress > 50 && progress <= 100) {
      d = 'M' + x + ',' + y + ' L' + cx + ',' + cy + ' L' + cx + ',' + (cy - r) + ' A' + r + ',' + r + ' 0 0,1' + cx + ',' + (cy + r) + ' A' + r + ',' + r + ' 0 0,1' + x + ',' + y;
      setColors(fill, stroke);
      setOutput();
    } else {
      text.innerHTML = '';
      path.setAttribute('d', '');
      output.innerHTML = 'Enter a value between 0 and 100';
      setColors('transparent', 'transparent');
    }

    if (progress > 0 && progress <= 10) {
      rect.setAttribute('x', x);
      rect.setAttribute('y', y + 7.5);
      text.setAttribute('x', x + 2);
      text.setAttribute('y', y + 15);
    } else if (progress > 10 && progress <= 62) {
      rect.setAttribute('x', x - 5);
      rect.setAttribute('y', y + 7.5);
      text.setAttribute('x', x - 3.5);
      text.setAttribute('y', y + 15);
    } else if (progress > 62 && progress <= 100) {
      rect.setAttribute('x', x - 5);
      rect.setAttribute('y', y - 15);
      text.setAttribute('x', x - 3.5);
      text.setAttribute('y', y - 7.5);
    }

  });

  function setColors(fill, stroke) {
    rect.setAttribute('fill', fill);
    rect.setAttribute('stroke', stroke);
    path.setAttribute('fill', fill);
    path.setAttribute('stroke', stroke);
  }

  function setOutput() {
    path.setAttribute('d', d);
    text.innerHTML = progress;
    output.innerHTML = 'Angle in Radians: ' + arcRad + '<br/>';
    output.innerHTML += 'Point in Circle: ' + x + ',' + y + '<br/>';
    output.innerHTML += 'Path d attribute: ' + d;
  }
}
Run Code Online (Sandbox Code Playgroud)
svg {
  width: 200px;
  height: 200px;
}
.output {
  min-height: 20px;
}
h4 {
  border-bottom: 1px solid;
}
Run Code Online (Sandbox Code Playgroud)
<input id='progress' type='number' />
<button>Generate</button>
<br/>
<svg viewBox='0 0 100 100'>
  <defs>
    <marker id='dot' viewBox='0 0 10 10' markerHeight='10' markerWidth='10' refX='5' refY='5'>
      <circle cx='5' cy='5' r='2' />
    </marker>
  </defs>
  <path d='' marker-end='url(#dot)' id='p' stroke='transparent' fill='transparent' />
  <rect height='10' width='10' x='10' y='10' stroke='transparent' fill='transparent' />
  <text x='10' y='10' id='val' font-family='Arial' font-size='6'></text>
</svg>
<div class='output'>
  <h4>Output:</h4>
  <output id='path-output'></output>
</div>
Run Code Online (Sandbox Code Playgroud)

进一步阅读:

您可以在以下链接中阅读有关SVG,其元素和属性的更多信息:

SVG路径 | 标记元素 | 文字元素 | 圆元素

  • 很棒的例子和解释!非常感谢!我会在晚上带着解决方案回来,现在我正走在正确的道路上. (2认同)