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);
好了,此时内环的效果已经有了,可以看到如下效果:
再来看外环的刻度条,外环刻度条有一个缺口,目测估算一下,算缺口为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();
这个时候效果是这样的:
还差外环的渐变线,以及线上面的那个点,那根线是根据分数动态画的,所以长度需要计算,我打算用一个圆弧来实现,上面说过每个线之间的旋转弧度为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();
此时效果是这样的:
还有圆外环彩线上面的圆点,可以用一个小圆实现,然后改变他的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组件,此代码仅供参考,最后感谢[masqli-canvas仿芝麻信用分仪表盘
][3],参考了前面两位博主的代码才能实现我项目的需求,所谓前人栽树,后人乘凉,今天把我写的也分享给大家,能帮到大家就更好,对我自己也算做个笔记了,在此再次感谢两位博主