canvas实现仪表盘

根据项目要求,要实现这样一个仪表盘的效果:


canvas实现仪表盘


拿到图之后首先做一个拆分,分成几个小模块。从里往外看,首先需要一个内环的刻度条,这个内环刻度条由若干个点构成,所以我的实现方式为:先画一根线,通过循环,旋转得到一个圆形的刻度条

//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);

好了,此时内环的效果已经有了,可以看到如下效果:

canvas实现仪表盘

再来看外环的刻度条,外环刻度条有一个缺口,目测估算一下,算缺口为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根。好了,知道角度之后还是按照之前的方法,画一根线,然后循环,在旋转,跳跃画出灰色的外环刻度线:

// 细分内环刻度线
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();

这个时候效果是这样的:

canvas实现仪表盘

还差外环的渐变线,以及线上面的那个点,那根线是根据分数动态画的,所以长度需要计算,我打算用一个圆弧来实现,上面说过每个线之间的旋转弧度为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();

此时效果是这样的:

canvas实现仪表盘
还有圆外环彩线上面的圆点,可以用一个小圆实现,然后改变他的x,y坐标即可实现:

ctx.save();
ctx.beginPath();
ctx.fillStyle = 'rgba(255, 255, 255, 1)';
ctx.arc(this.x, this.y, 5, 0, Math.PI * 2, false);
ctx.fill();
ctx.lineWidth = 1;
ctx.strokeStyle = "rgba(246, 5, 51, 1)";
ctx.stroke();
ctx.restore();

ctx.save();
ctx.beginPath();
ctx.fillStyle = 'rgba(246, 5, 51, 1)';

ctx.arc(this.x, this.y, 3, 0, Math.PI * 2, false);
ctx.fill();

ctx.restore();

此时,还差个彩色的线,效果如下,额,这个彩色的线如何实现呢,刚开始也是不知如何下手,经过高人指点后,得出结局方案:把渐变色切割,多少根彩线就分割成多少份,然后感谢 嗑瓜子儿gf-颜色渐变的JS代码 写的博客,提供了算法
canvas实现仪表盘
最终效果出来了:

canvas实现仪表盘
预览组件

第一次写canvas组件,此代码仅供参考,最后感谢[masqli-canvas仿芝麻信用分仪表盘
][3],参考了前面两位博主的代码才能实现我项目的需求,所谓前人栽树,后人乘凉,今天把我写的也分享给大家,能帮到大家就更好,对我自己也算做个笔记了,在此再次感谢两位博主

相关推荐