实现效果

image.png
图1 **实现效果

实现步骤

下面我们分步实现

拆解问题

  1. 实现一个扇形
    1. 把一个圆分成100份儿
    2. 用1个 div 代表一份儿
      1. div的宽高
      2. 位置怎么定
    3. div 一定的旋转角度
      1. 多少角度
      2. 角度怎么变
  2. 在扇形中间覆盖一个小圆,从而形成环

静态实现

直接贴初始代码吧

  1. <div class="out-circle">
  2. <div class="inner-circle">4</div>
  3. <div class="item item1"></div>
  4. <div class="item item2"></div>
  5. <div class="item item3"></div>
  6. <div class="item item4"></div>
  7. </div>
.out-circle {
  position: relative;
  width: 500px;
  height: 500px;
  border-radius: 50%;
  border: 1px solid red;
  overflow: hidden;
 }
.inner-circle {
  z-index: 10;
  position: absolute;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%);
  width: 480px;
  height: 480px;
  text-align: center;
  line-height: 480px;
  font-size: 30px;
  border-radius: 50%;
  border: 1px solid red;
  background-color: #fff;
}

.item {
  position: absolute;
  left: 50%;
  height: 250.1234208092861px;
  width: 15.705379539064147px;
  transform-origin: 50% 100%;
  background-color: blue;
}

.item1 {
  transform: translateX(-50%) rotate(0deg);
}

.item2 {
  transform: translateX(-50%) rotate(3.6deg);
}

.item3 {
  transform: translateX(-50%) rotate(7.2deg);
}

.item4 {
  transform: translateX(-50%) rotate(10.8deg);
}
                                            ![image.png](https://cdn.nlark.com/yuque/0/2019/png/116146/1562429017046-091ac4cb-a6d3-40da-a748-893cf1efc765.png#align=left&display=inline&height=259&name=image.png&originHeight=1036&originWidth=1040&size=89816&status=done&width=260)**图2** 静态效果图

要点:

  • 蓝条的位置和旋转角度
    1. transfrom-origin 属性
    2. transform 的rotate和translate需同时设置,不然后面的会把前面的覆盖
  • 蓝条的宽高

image.png
图3 计算规则

动态实现

.progress 内的所有元素清空,用js生成进度条相关的dom,从而达到解偶的目的。甚至可以将css也写到js中,这样使用者只需要引入js即可。
代码如下:

class Progress {
  constructor({ dom, ratio }) {
    this.dom = dom || document.body;
    this.ratio = ratio || 0;
    this.create();
  }

  create() {
    this.outCircleDom = document.createElement('div');
    this.outCircleDom.classList.add('out-circle');
    this.innerCircleDom = document.createElement('div');
    this.innerCircleDom.classList.add('inner-circle');
    this.outCircleDom.appendChild(this.innerCircleDom);
    this.dom.appendChild(this.outCircleDom);
    this.render(this.ratio)
  }

  onChange(ratio) {
    this.render(ratio)
  }

  renderRatio(ratio) {
    if (ratio > 1 || ratio < 0) {
      throw Error('ratio may be great than 0 and less than 1')
    }
    this.innerCircleDom.textContent = parseInt(100 * ratio) + '%';
  }

  renderItems(ratio) {
    this.itemCount = parseInt(100 * ratio);
    var domFrag = document.createDocumentFragment();
    for (let i = 0; i < this.itemCount; i++) {
      var item = document.createElement('div');
      item.classList.add('item')
      item.style.cssText = `transform: translateX(-50%) rotate(${3.6 * i}deg);`
      domFrag.appendChild(item)
    }
    this.outCircleDom.appendChild(domFrag)
  }

  render(ratio) {
    this.renderItems(ratio)
    this.renderRatio(ratio)
  }
}

到此,我们就可以以如下的方式调用我们的组件了:

// 实例化我们的进度条
let progress = new Progress({
  dom: document.querySelector('.progress'),
  ratio: 0
});

// 模拟:假设r是某事件的进度:
let r = 0
document.body.onclick = e => {
  progress.onChange(r+=0.01)
}

便可实现如下效果:

Jietu20190707-002213-HD.mp4 (474.84KB)

总结

虽然完成了基础功能,但还有很多东西需要改进。这篇文章只提供思路,生产环境不建议使用。