进阶版一步一步去实现一个环形进度条

曾经我写过一个简单的环形进度条
本篇文章对进度条再进行一下优化,效果如下

进阶版一步一步去实现一个环形进度条_第1张图片
QQ20180228-204416-HD.gif

由图可见,进度加了渐变,文字和进度都加了动画
首先,我们来一步步分解
一个进度条由三个部分组成
1.底部背景线
2.中间文字
3.当前进度

注意事项1

先讲一下大概的思路吧,这个的难点是画渐变,首先,这里面的渐变是由一条条圆弧组成的,每一条圆弧都有自己的颜色,那么我们要平分圆弧和颜色,只要解决了这个,基本上渐变就出来了,这里要注意,不能用canvas自己的渐变,因为线性渐变要么是上下,要么是左右,没有沿着圆弧的,径向渐变就更加不行了,只能由内而外

注意事项2

一般情况下,假如我们用css来定义canvas时,还要给canvas定义宽度和高度,其值为css的两倍,如下所示


那么此时canvas要定义如下


但是,现在我们主要做手机端,要兼容各种分辨率,一般我们都是用rem来定义大小,此时就不能直接给canvas赋值width和height了,但是也很简单,假如我们的css这样定义


那么可以用js来定义canvas的大小,如下

  canvas.width = canvas.offsetWidth * 2
  canvas.height = canvas.offsetHeight * 2

1.底部背景线

这个比较简单,就是画一个圆即可,大概的代码如下

    ctx.lineWidth = percentageWidth / 2
    ctx.arc(canvas.width / 2, canvas.height / 2, canvas.height / 2 - percentageWidth / 2, 0, Math.PI * 2)
    ctx.strokeStyle = '#00FFFF'
    ctx.stroke()

其中的percentageWidth是指进度的宽度,这里把底部背景线设为了它的一半,另外注意arc中的半径,是canvas宽度的一般减去了进度条宽度的一半,如果不减的话,就超出了canvas了

2.画进度条

从上面我们知道,进度条是由一段一段的圆弧组成的,所以这里的核心就是怎么去分割圆弧,并且拼起来的圆弧的颜色要是渐变的,首先,需要定义一个变量来设置每一个段圆弧的大小,那么就能根据圆弧的起始点和结束点,算出需要画多少条圆弧才能拼起来
首先把一些全局变量列出来,注释就写清楚了

  var canvas = document.getElementById("canvas")
  var ctx = canvas.getContext('2d')
  // 要画的圆弧的百分比,总共为100
  var percentage = 77
  // 每一次加的圆弧大小
  var step = .05
  // 进度条的宽度
  var percentageWidth = 20
  // 结束点的弧度
  var endArc = Math.PI * 2 * percentage / 100 - Math.PI / 2
  // 当前进度的弧度,设置默认值,把当前进度放到12点位置(默认是3点位置)
  var currentArc = -Math.PI / 2
  // 用来做循环的对象
  var intervalObj = null
  // 文字大小,当canvas大小设置之后会再重新设置
  var fontSize = 20
  // 要画的弧度条数,结束弧度减去开始弧度,再除以每一次加的弧度,即可得到
  var times = (endArc - currentArc) / step
  // 调用的第三方算法,正好根据要画的弧度条数来分割
  var gradient = new gradientColor('#008000', '#ff0000', times)
  // 当前在画的那条弧的索引,用来取颜色值
  var index = -1

接下来我们来看怎么一步步去画圆弧的

    // 开启一段新路径
    ctx.beginPath()
    // 设置当前的圆弧的颜色
    ctx.strokeStyle = gradient[++index]
    // 设置圆弧的宽度
    ctx.lineWidth = percentageWidth
    // 记住圆弧开始的弧度
    var startArc = currentArc
    // 算出圆弧结束的角度
    currentArc += step
    // 0.05的偏移量,不加的话没有完全盖住上一个弧,看起来有间隔
    // 画文字
    drawText((currentArc + Math.PI / 2 - 0.05) / (Math.PI * 2))
    // 如果当前结束的弧度大于最后结束的弧度,那么取消动画,并且画出最后的这段弧度
    // 否则继续画下一条弧度
    if (currentArc >= endArc) {
      cancelAnimationFrame(intervalObj)
      ctx.arc(canvas.width / 2, canvas.height / 2, canvas.height / 2 - percentageWidth / 2, startArc, endArc)
      ctx.stroke()
      return
    } else {
      ctx.arc(canvas.width / 2, canvas.height / 2, canvas.height / 2 - percentageWidth / 2, startArc.toFixed(2) - 0.05, currentArc.toFixed(2))
      ctx.stroke()
    }
    // 做动画
    intervalObj = requestAnimationFrame(drawCurrentProgress)

3.画文字

其实,在上一步中,我们已经调用了画文字的方法,但是方法体没有给出,这里主要的思路是,每一次画出当前的百分比,下一次画时,把上一次的文字擦除即可,用到的clearRect和fillText,代码如下

  function drawText(text) {
    // 把文字转换为整数
    text = (text * 100).toFixed(0)
    // 设置字体,其实可以全局设置,没拿出去
    ctx.font = `${fontSize}px serif`
    // 获取文字的宽度,这里加上了百分号
    var textWidth = ctx.measureText(text + '%').width
    // 清除文字,注意这里是根据文字的宽度和文字的大小来计算的,
    // 宽度乘上1.1是为了将擦除区域变得比文字区域大一点
    ctx.clearRect(canvas.width / 2 - textWidth / 2, canvas.height / 2 - fontSize, textWidth * 1.1, fontSize * 2)
    //设置文本的垂直对齐方式
    ctx.textBaseline = 'middle'
    //设置文本的水平对齐方式
    ctx.textAlign = 'center'
    // 填充文字
    ctx.fillText(text + '%', canvas.width / 2, canvas.height / 2)
  }

ok,基本上差不多了
完整代码

你可能感兴趣的:(进阶版一步一步去实现一个环形进度条)