探究canvas环形进度条及其背后的原理

在银行做前端开发,最痛苦的莫过于内网开发,很多第三方库和组件无法使用,只能自己造轮子,真可谓是举步维艰!

上周写过一篇《纯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,逆时针绘制圆弧,反之,顺时针绘制。

这里有两句话:

  1. x 轴方向开始计算
  2. 单位以弧度表示

这是什么意思呢?我们试着来实现上面说的两个希望实现的效果,顺便理解这两句话。

先实现仪表盘形进度条

画一个草图理解一下:

一个圆一共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
);

效果是一样的

不过我个人觉得仪表盘那个效果,还是先拿到角度再转换为弧度,比较好理解一点。

相关推荐
漠河愁21 天前
pdf文件渲染到canvas
canvas·pdf.js·fabirc.js
xachary1 个月前
前端使用 Konva 实现可视化设计器(21)- 绘制图形(椭圆)
canvas·konva
x007xyz1 个月前
前端纯手工绘制音频波形图
前端·音视频开发·canvas
甄齐才2 个月前
canvas绘制文本时,该如何处理首行缩进、自动换行、多内容以省略号结束、竖排的呢?
canvas·html2canvas·海报·html转图片·文章分享·dom-to-image·html转image
万水千山走遍TML2 个月前
canvas绘制表格
前端·javascript·vue.js·canvas·canvas绘图·在vue中使用canvas·canvas绘制表格
xachary2 个月前
前端使用 Konva 实现可视化设计器(19)- 连接线 - 直线、折线
javascript·vue·canvas·konva
梦想身高1米82 个月前
canvas.toDataURL后图片背景变成黑色
前端·canvas
x007xyz2 个月前
Fabric.js实时播放视频并扣除绿幕
前端·javascript·canvas
xachary2 个月前
前端使用 Konva 实现可视化设计器(18)- 素材嵌套 - 加载阶段
算法·canvas·konva
LeaferJS2 个月前
LeaferJS 1.0 重磅发布:强悍的前端 Canvas 渲染引擎
前端·canvas