根据项目要求,要实现这样一个仪表盘的效果:
拿到图之后首先做一个拆分,分成几个小模块。从里往外看,首先需要一个内环的刻度条,这个内环刻度条由若干个点构成,所以我的实现方式为:先画一根线,通过循环,旋转得到一个圆形的刻度条
//innerLineNums 为刻度数量
ctx.save();
for (var i = 0; i <= $this.innerLineNums; i++) {
ctx.beginPath();
ctx.lineWidth = 2;
ctx.strokeStyle = 'rgba(155,157,183,1)';
ctx.moveTo(82, 0);
ctx.lineTo(80, 0);
ctx.stroke();
//每个点的弧度,360°弧度为2π,即旋转弧度为 2π / 75
ctx.rotate(2*Math.PI / $this.innerLineNums);
}
ctx.restore();
其次再是里面的长刻度线,以及数字标志,还有中间的文字
// 内环刻度线
ctx.save();
for (var i = 0; i < 6; i++) {
ctx.beginPath();
ctx.lineWidth = 2;
ctx.strokeStyle = 'rgba(155,157,183,1)';
ctx.moveTo(82, 0);
ctx.lineTo(78, 0);
ctx.stroke();
//每10个点分一个刻度,共5个刻度,旋转角度为deg1 * 10
ctx.rotate(deg1 * 10);
}
ctx.restore();
//内环刻度上面数字
ctx.save();
ctx.rotate(Math.PI / 2);
for (i = 0; i < 6; i++) {
ctx.fillStyle = 'rgba(165,180,198, .4)';
ctx.font = '10px Microsoft yahei';
ctx.textAlign = 'center';
ctx.fillText(20 * i, 0, -65);
ctx.rotate(deg1 * 10);
}
ctx.restore();
//内环文字
ctx.save();
ctx.rotate(210 * Math.PI / 180);
ctx.fillStyle = '#000';
ctx.font = '44px Microsoft yahei';
ctx.textAlign = 'center';
ctx.textBaseLine = 'top';
ctx.fillText(process, 0, 10);
var width = ctx.measureText(process).width;
ctx.fillStyle = '#000';
ctx.font = '20px Microsoft yahei';
ctx.fillText('分', width / 2 + 10, 10);
好了,此时内环的效果已经有了,可以看到如下效果:
再来看外环的刻度条,外环刻度条有一个缺口,目测估算一下,算缺口为1/3,即外环的刻度线需要画120-360°,我们这里分数满分为100分,从0开始,为了方便计算就给他画50根刻度线,那么每根刻度线的角度deg1的算法为:
//弧长计算公式是一个数学公式,为L=n(圆心角度数)× π(1)× r(半径)/180(角度制),L=α(弧度)× r(半径) (弧度制)。其中n是圆心角度数,r是半径,L是圆心角弧长。
//整个运动的角度是(360-120)度,转换成弧度就是12π/9,一共分成了50个分数段,那么每一个分数段就是12π/450 = 2π / 75
//如需旋转 5 度,可规定下面的公式:5*Math.PI/180。
deg1() {
return (Math.PI * 12) / (9 * this.lineNums)
}
从这里我们知道了外环2/3个圆的刻度线为50根,那么内环整个圆的刻度线innerLineNums = 50 * 3 / 2 = 75根。好了,知道角度之后还是按照之前的方法,画一根线,然后循环,在旋转,跳跃画出灰色的外环刻度线:
//lineNums 灰色长刻度条数量
ctx.save();
for (var i = 0; i <= lineNums; i++) {
var is_on = (((i - 1) / lineNums) * 100 < activeProcess - 1);
var color = getTickColor(is_on, i);
ctx.beginPath();
ctx.lineWidth = 2;
ctx.strokeStyle = color;
ctx.moveTo(radius + 8, 0);
ctx.lineTo(radius + 35, 0);
ctx.stroke();
//每个点的弧度,360°弧度为2π,即旋转弧度为 2π / 75
ctx.rotate(2*Math.PI / innerLineNums);
}
ctx.restore();
这个时候效果是这样的:
还差外环的渐变线,以及线上面的那个点,那根线是根据分数动态画的,所以长度需要计算,我打算用一个圆弧来实现,上面说过每个线之间的旋转弧度为deg1,那圆弧的长度就等于score * deg1 / 2,根据公式画出线如下:
//色彩段数与彩色刻度条保持一致,线条无间隔,所以段数 * 2
var gradient = ctx.createLinearGradient(0, 0, $this.colorLineNums * 2, 0);
gradient.addColorStop("0", "rgba(252,3,44,.6)");
gradient.addColorStop("0.5", "rgba(134,37,168,.6)");
gradient.addColorStop("1.0", "rgba(54,63,255,.6)");
//外环渐变线
ctx.save();
ctx.beginPath();
ctx.lineWidth = 2;
ctx.strokeStyle = gradient;
ctx.arc(0, 0, radius, 0, angle, false);
ctx.stroke();
ctx.restore();
此时效果是这样的:
还有圆外环彩线上面的圆点,可以用一个小圆实现,然后使用rotate方法,以渐变线的半径,旋转的角度为当前的分数对应的角度即可:
// 终点
ctx.save();
ctx.rotate(activeProcess / 2 * deg1);
ctx.beginPath();
ctx.fillStyle = 'rgba(255, 255, 255, 1)';
ctx.arc(radius + 45, 0, 5, 0, Math.PI * 2, false);
ctx.fill();
ctx.lineWidth = 1;
ctx.strokeStyle = "rgba(246, 5, 51, 1)";
ctx.stroke();
ctx.save();
ctx.beginPath();
ctx.fillStyle = 'rgba(246, 5, 51, 1)';
ctx.arc(radius + 45, 0, 3, 0, Math.PI * 2, false);
ctx.fill();
ctx.restore();
此时,还差个彩色的线,效果如下,额,这个彩色的线如何实现呢,刚开始也是不知如何下手,经过同事大佬指点后,得出解决方案:把渐变色切割,多少根彩线就分割成多少份,然后感谢 嗑瓜子儿gf-颜色渐变的JS代码 写的博客,提供了算法
最终效果出来了:
第一次写canvas组件,此代码仅供参考,最后感谢masqli-canvas仿芝麻信用分仪表盘,参考了前面两位博主的代码才能实现我项目的需求,所谓前人栽树,后人乘凉,今天把我写的也分享给大家,能帮到大家就更好,对我自己也算做个笔记了,在此再次感谢两位博主
完整demo 戳这里
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。