在银行做前端开发,最痛苦的莫过于内网开发,很多第三方库和组件无法使用,只能自己造轮子,真可谓是举步维艰!
上周写过一篇《纯css轻松实现环形进度条》,就像标题所说,实现起来确实"轻松",不过也因此受到一些限制,例如无法让进度条两头呈现圆形,也无法做出动画加载进度的效果。
那么今天就用canvas
来写一个环形进度条来解决这些限制。
实现
使用canvas写一个环形进度条非常简单,只需要调用两次CanvasRenderingContext2D.arc()
方法,画两段不同颜色的圆弧即可。
html
<canvas id="canvas" width="200" height="200"></canvas>
js
const canvas = document.getElementById("canvas");
const ctx = canvas.getContext("2d");
// 定义弧线的宽度
ctx.lineWidth = 8;
// 定义两端为圆形
ctx.lineCap = "round";
// 先画下面的底色,一个整圆
ctx.beginPath();
ctx.arc(100, 100, 50, 0, 2 * Math.PI);
// 底色用灰色
ctx.strokeStyle = "#ebeef5";
ctx.stroke();
ctx.closePath();
// 再画有颜色的部分,根据进度动态计算
ctx.beginPath();
ctx.arc(100, 100, 50, 0, 2 * Math.PI * 0.8);
ctx.strokeStyle = "#00a0fb";
ctx.stroke();
ctx.closePath();
// 在中间写上百分比
ctx.textAlign = "center";
ctx.font = "14px sans-serif";
ctx.fillText(0.8 * 100 + "%", 100, 100);
就这么简单,但你可能会说:
- 你这跟element-ui的也不一样啊,如果我想让起点在12点钟方向呢?
看,element-ui 环形进度条的起点在正上方
- 如果要画一个仪表盘形进度条呢?
element-ui 仪表盘形进度条
别急,下面才是重中之重!
原理
MDN中说道:CanvasRenderingContext2D.arc()
是 Canvas 2D API 绘制圆弧路径的方法。圆弧路径的圆心在 (x, y) 位置,半径为 r,根据anticlockwise (默认为顺时针)指定的方向从 startAngle 开始绘制,到 endAngle 结束。
语法:
js
ctx.arc(x, y, radius, startAngle, endAngle, anticlockwise);
参数:
x
:圆弧中心(圆心)的 x 轴坐标。
y
:圆弧中心(圆心)的 y 轴坐标。
radius
:圆弧的半径。
startAngle
:圆弧的起始点,x 轴方向开始计算,单位以弧度表示。
endAngle
:圆弧的终点,单位以弧度表示。
anticlockwise
(可选):可选的Boolean值,默认为 false,如果为 true,逆时针绘制圆弧,反之,顺时针绘制。
这里有两句话:
- x 轴方向开始计算
- 单位以弧度表示
这是什么意思呢?我们试着来实现上面说的两个希望实现的效果,顺便理解这两句话。
先实现仪表盘形进度条
画一个草图理解一下:
一个圆一共360度,分成四份,12点钟、3点钟、6点钟、9点钟方向分别是0°、90°、180°、270°,这个很好理解,那么很显然,仪表盘形进度条需要的就是顺时针从225°(180至270的中间)到135°(90至180的中间)这总共270°这部分。
但是这里拿到的是度数,arc
函数需要的是弧度,这是什么意思呢?高中时期你一定学过,忘了?没关系,一起来回顾一下:
角度定义:两条射线从圆心向圆周射出,形成一个夹角和夹角正对的一段圆弧。当这段弧长正好等于圆周长的360分之一时,两条射线的夹角的大小为1度。
假设AB这段圆弧的长度为圆周长的360分之一,那么∠AOB的度数为1°
弧度定义:两条射线从圆心向圆周射出,形成一个夹角和夹角正对的一段圆弧。当这段孤长正好等于圆的半径时,两条射线的夹角大小为1弧度。
假设AB这段圆弧的长度刚好等于圆的半径,我们就说∠AOB的大小为1弧度
另外我们都知道,圆的周长=2πr。
那么如果我用周长除以半径,是不是就知道了这个圆周上有几个半径的长度,也就得到了这个圆有多少弧度。
即: 2πr / r = 2π,也就是说,一个整圆的弧度为2π,即360° = 2π弧度。
所以我们知道了,如果要用canvas画出一个完整的圆,api就是:
js
ctx.arc(x, y, r, 0弧度, 2 * Math.PI 弧度)
回到正题
画一个仪表盘形进度条,也就不是一个完整的圆,从前面的分析可知,我们要画的是顺时针方向从225°到135°这部分,那么这个角度如何转换为弧度呢?
从上面的分析知道了,360° = 2π弧度,那么180° = π弧度,那么1° = π / 180弧度。
所以就得到了角度转弧度公式:
弧度 = 角度 * π / 180
来画一段圆弧看看效果:
js
ctx.arc(
100,
100,
50,
225 * Math.PI / 180,
135 * Math.PI / 180
);
咦?说好的仪表盘形,怎么是这个样子的?别急,还记得前面留下的问题吗,x轴方向开始计算这句话。
也就是说,起点是在x那个位置,三点钟方向,而不在y的位置,也就是说我们计算的225~135,实际上多了90度,那就减去90度呗。
js
ctx.arc(
100,
100,
50,
(225 - 90) * Math.PI / 180,
(135 - 90) * Math.PI / 180
);
诶!这回就对味儿了嘛。
但现在画出来的进度值貌似是100%的,再在同一个位置画一个不同颜色,不同长度的圆弧就是了:
圆环一共270度,要画一个80%进度的,用270 * (1 - 0.8)
js
// 先画底色
ctx.beginPath();
ctx.arc(
100,
100,
50,
(225 - 90) * (Math.PI / 180),
(135 - 90) * (Math.PI / 180)
);
// 底色用灰色
ctx.strokeStyle = "#ebeef5";
ctx.stroke();
ctx.closePath();
// 再画体现进度的部分
ctx.beginPath();
ctx.arc(
100,
100,
50,
(225 - 90) * (Math.PI / 180),
(135 - 270 * (1 - 0.8) - 90) * (Math.PI / 180)
);
// 进度用蓝色
ctx.strokeStyle = "#00a0fb";
ctx.stroke();
现在是不是有那味儿了?
现在我们已经知道了,canvas画圆弧的起点在x轴方向,那么要让环形进度条的起点在12点钟方向,就好办了。
起点在12点钟方向的环形进度条
js
ctx.arc(
100,
100,
50,
((0 - 90) * Math.PI) / 180,
((360 * 0.8 - 90) * Math.PI) / 180
);
总结
其实,如果你非常熟悉弧度的相关知识,大可不必这么复杂,无需用角度去换算弧度。一个圆的弧度是2π,半圆的弧度是π,前面多算的那90度,就是2 * Math.PI * 0.25,直接减去这个值就好了。
js
ctx.arc(
100,
100,
50,
0 - 2 * Math.PI * 0.25,
2 * Math.PI * 0.8 - 2 * Math.PI * 0.25
);
效果是一样的
不过我个人觉得仪表盘那个效果,还是先拿到角度再转换为弧度,比较好理解一点。