环形进度条实现方案

474 阅读1分钟

方案1: border + clip-path

画圆

直接使用用border画圆

// html
<div class="bg"></div>

// css
.bg {
  position: absolute;
  top: 0;
  left: 0;
  width: 100px;
  height: 100px;
  border: 10px solid #000000;
  border-radius: 50%;
}

image.png

进度条

使用border + clip-path完成进度条逻辑。js辅助控制

// html
<div class="progress" id="progress"></div>

// css
.progress {
  position: absolute;
  top: 0;
  left: 0; 
  width: 100px;
  height: 100px;
  border: 10px solid red;
  border-radius: 50%;
}

// js
function getPathByDeg(deg) {
  const xLength = Math.sin(deg);
  const yLength = Math.cos(deg);
  const xPos = 1 + xLength;
  const yPos = 1 - yLength;
  if(deg <= Math.PI / 2) {
    return `(
      50% 0%,
      ${xPos / 2 * 100}% 0%,
      ${xPos / 2 * 100}% ${yPos / 2 * 100}%,
      50% 50%
    )`
  } else if(deg > Math.PI / 2 && deg <= Math.PI) {
    return `(
      50% 0%,
      100% 0%,
      100% ${yPos / 2 * 100}%,
      ${xPos / 2 * 100}% ${yPos / 2 * 100}%,
      50% 50%
    )`
  } else if(deg > Math.PI && deg < Math.PI / 2 * 3) {
    return `(
      ${xPos / 2 * 100}% ${yPos / 2 * 100}%,
      50% 50%,
      50% 0%,
      100% 0%,
      100% 100%,
      ${xPos / 2 * 100}% 100%,
      ${xPos / 2 * 100}% ${yPos / 2 * 100}%,
      50% 50%
    )`
  } else if(deg >= Math.PI / 2 * 3) {
    return `(
      ${xPos / 2 * 100}% ${yPos / 2 * 100}%,
      50% 50%,
      50% 0%,
      100% 0%,
      100% 100%,
      0% 100%,
      0% ${yPos / 2 * 100}%,
      ${xPos / 2 * 100}% ${yPos / 2 * 100}%,
      50% 50%
    )`
  }
}
function draw() {
  const path = getPathByDeg(currentDegree);
  progressDom.style.clipPath = `polygon${path}`;
  requestAnimationFrame(function () {
    currentDegree = (currentDegree  + Math.PI / 180) % (2 * Math.PI);
    draw();
  });
}

const progressDom = document.getElementById("progress");
let currentDegree = 0;
draw();

16.gif

clip-path裁剪方式创建元素的可显示区域。区域内的部分显示,区域外的隐藏。实际原理是这样的

17.gif

方案2: svg

画圆

使用circle + stroke属性

stroke: 定义了给定图形元素的外轮廓的颜色,用在svg的circle上,也就类似于边框

stroke-width 属性指定了当前对象的轮廓的宽度(注意这个宽度的变化是向图形内外同时扩大的,比如circle的半径是50px, stroke-width为10px,则会获得45px半径的园和10px的stroke边框)

// 部分代码
<circle
    :cx="ringOption?.ringCenterX"
    :cy="ringOption?.ringCenterY"
    :r="ringOption?.ringRadius"
    fill="none"
    :stroke="ringOption?.ringBgColor"
    :stroke-width="ringOption?.ringWidth"
/>

便可以获得一个圆形图

image.png

进度条

stroke-dasharray: 这个比较复杂,可以参考MDN上的属性说明,简单来说,就是控制stroke的长度。

于是,我们可以在上面的svg下再贴一个circle作为进度条

// 部分代码
<circle
    :cx="ringOption?.ringCenterX"
    :cy="ringOption?.ringCenterY"
    :r="ringOption?.ringRadius"
    fill="none"
    stroke="url(#gradientColor)"
    :stroke-width="ringOption?.ringWidth"
    :stroke-dasharray="`${ringPercent[index]} ${ringOption?.ringLength}`"
/>

18.gif

这个方案比使用border + clip-path更加灵活,毕竟svg的还有很多功能可以使用

方案3: canvas

这种就没有写demo了,原理应该和上面两种方式类似是两个园叠加。可以参照上面的实现思路 + canvas api实现