曾经我写过一个简单的环形进度条
本篇文章对进度条再进行一下优化,效果如下
由图可见,进度加了渐变,文字和进度都加了动画
首先,我们来一步步分解
一个进度条由三个部分组成
1.底部背景线
2.中间文字
3.当前进度
注意事项1
先讲一下大概的思路吧,这个的难点是画渐变,首先,这里面的渐变是由一条条圆弧组成的,每一条圆弧都有自己的颜色,那么我们要平分圆弧和颜色,只要解决了这个,基本上渐变就出来了,这里要注意,不能用canvas自己的渐变,因为线性渐变要么是上下,要么是左右,没有沿着圆弧的,径向渐变就更加不行了,只能由内而外
注意事项2
一般情况下,假如我们用css来定义canvas时,还要给canvas定义宽度和高度,其值为css的两倍,如下所示
<style>
#canvas1{
width:200px;
height:100px
}
</style>
那么此时canvas要定义如下
<canvas width=400 height=200 />
但是,现在我们主要做手机端,要兼容各种分辨率,一般我们都是用rem来定义大小,此时就不能直接给canvas赋值width和height了,但是也很简单,假如我们的css这样定义
<style>
#canvas1{
width:10rem;
height:5rem
}
</style>
那么可以用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,基本上差不多了
完整代码
网友评论