Progress circle - draw a small arc at the end tip of the circle + more

a 夏天 提交于 2019-12-01 18:40:29

CSS is definitely not the right tool for doing this and I would urge you to give up on that idea. Canvas is definitely a good option but for a responsive website, I would recommend you to use SVG. With SVG, it is easy to draw the progress circle based on user input and also add the circle/dot at the tip of it.

The following are the steps that would have to be performed to first create the progress circle:

  • Get the user input for the progress circle (0 to 100) and then calculate the arc's angle based on it. The formula would be (input * 360/100) because a circle has 360 degrees.
  • SVG arcs generally start from the 3's position in a clock and so the calculated angle of arc has to be offset by 90 degree in the negative. That is, the arc has to be from -90 degree to 270 degree.
  • Convert the calculated angle into radians using the formula - (Angle in Degrees * PI / 180).
  • Once the angle in radians is calculated, find a point on the circle based on the angle using simple trigonometric functions:

    • X coordinate = Cos(Angle in Radians) * Radius + X coordinate of center point.
    • Y coordinate = Sin(Angle in Radians) * Radius + Y coordinate of center point.
  • Once the point is found, we need to create the path such that it starts at the point that was found in the previous step, goes to the center of circle, then to the starting point of the arc and from that point draw the arc again to the original point. The path is created this way because we need the path to end at the required point (because we will be attaching the marker to the end point).

  • One thing to note with arc creation is that any angle > 180 degree would need two arc commands to create. The first arc would be 12's position in clock to 6's position and the next arc would be for the rest. So, we make use of an if/else loop.

For adding the dot/circle at its tip, the following need to be done:

  • A marker element is added to the SVG and a marker in the form of a circle is created using the circle element. The circle element takes 3 attributes - cx, cy are the center points of the dot and r is the radius of the dot.
  • Using the marker-end attribute, this marker is then added to the path. Setting this attribute will mean that any path that is created would have this dot automatically appended at its ending point. No other coding needs to be done for this because SVG automatically takes care of positioning.

Text can also be added using the text element and then its position can be set using the x and y attributes. (This bit still needs tuning in the below snippet.)

Demo:

Below is a very rough demo of the implementation.

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;
  }
}
svg {
  width: 200px;
  height: 200px;
}
.output {
  min-height: 20px;
}
h4 {
  border-bottom: 1px solid;
}
<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>

Further Reading:

You can read more about SVG, its elements and attributes in the following links:

SVG Paths | Marker element | Text element | Circle element

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!