示例代码托管于:http://www.github.com/dashnowords/blogs博客园地址:《大史住在大前端》原博文目录华为云社区地址:【你要的前端打怪升级指南】【TOC】1.任务描述使用原生canvasAPI绘制水球地图,这将是一个非常有趣的挑战任务。水球图是一种常见的加载动画,属于扩展图形。在echarts中使用时需要下载扩展库(同样的扩展库还包括词云插件和地图插件,项目地址为https://github.com/ecomfe/echarts-liquidfill)。2.重要提示水球图的绘制有以下难点:水波的绘制水波的绘制实际上是用简谐振动公式来模拟的,即x=A*(wt+φ),其中振幅A决定了水波波纹的高度,角频率w决定了水波的速度,相位φ决定了初始位移差,再加上y轴方向的一些位移偏差和颜色的差异,可以模拟不同的水波,然后只需要在帧动画中不断改变φ重新绘制曲线,就可以模拟出水波效果。球形裁剪区域内的水波范围不能流出球形的外轮廓。这里的方法是在绘制水波之前使用context.clip()方法控制水球内部水波的可见绘制区域。如果还有水球外面的图形需要绘制。记得在每一帧画完水波后调用context.restore()取消之前的裁剪。如果只是绘制漂浮在水球地图上的文字,绘制文字是比较容易的,但是要达到一些更细致的效果就不是那么容易了。我们期望达到的效果是,当文字没有浸入水波中时,显示水波纹的蓝色,浸入水中的部分显示为白色,看起来更加生动。但是画起来并不容易。如果文字用蓝色绘制,淹没的部分将消失在水线中。如果画成白色,水线高度小的时候根本看不到文字。那么如何实现这样的渲染文字呢?3.示例代码letoptions={value:0,a:20,//amplitudepos:[300,300],//水球图位置r:160,//水球图半径color:['#2E5199','#1567c8','#1593E7','#42B8F9']//水纹颜色};start(options);/***绘制水球图*/functionstart(options){//移动绘图水球地图左边界点的坐标context.translate(options.pos[0],options.pos[1]);context.font='粗体60pxArial';context.textAlign='中心';context.textBaseLine='基线';//计算水球地图绘制数据createParams(options);//开始帧动画requestAnimationFrame(startAnim);}//生成水波动画参数,位置坐标公式为y=A*(wt+φ)functioncreateParams(options){options.w=[];//存储水波的角速度options.theta=[];//存储每个水波的位移for(leti=0;i<4;i++){options.w.push(Math.PI/(100+20*数学.random()));options.theta.push(20*Math.random());}}//绘制水线functiondrawWaterLines(options){letoffset;letA=options.a;//正弦曲线振幅lety,x,w,theta;让r=options.r;//遍历每个水纹理for(letline=0;line<4;line++){context.save();//水波每次偏移的距离theta=Math.random();offset=r+A/2-(r*19/8+A)*(options.value/100)+line*r/12;//获取正弦曲线计算参数w=options.w[line];theta=options.theta[线];context.fillStyle=options.color[line];context.moveTo(0,0);context.beginPath();//绘制步长为0.1的正弦曲线for(x=0;x<=2*r;x+=0.1){y=A*Math.sin(w*x+theta)+offset;//绘制点context.lineTo(x,y);}//画成超出水球范围的闭合图形context.lineTo(x,r);context.lineTo(x-2*r,r);context.lineTo(0,A*Math.sin(theta)-options.height);context.closePath();//填充封闭图形得到水波context.fill();//截取水波的范围并绘制文字(这个后面会解释)context.clip();context.fillStyle='白色';context.fillText(parseInt(options.value,10)+'%',options.r+10,10);上下文.restore();}}//绘制底部文字functiondrawText1(options){context.fillStyle=options.color[0];context.fillText(parseInt(选项s.value,10)+'%',options.r+10,10);}//帧动画循环函数startAnim(){//模拟位移变化的水波options.theta=options.theta.map(item=>项目-0.03);//用百分比进度计算水波高度options.value+=options.value>100?0:0.1;上下文.保存();resetClip(options);//裁剪绘图区域drawText1(options);//绘制蓝色文字drawWaterLines(options);//绘制水线context.restore();requestAnimationFrame(startAnim);}/**设置水球的范围为裁剪区域*(本例中除了水球外没有其他部分需要绘制,其实不需要添加到帧动画循环,只需要在开始时设置一次)*/functionresetClip(options){letr=options.r;context.strokeStyle='#2E5199';context.fillStyle='白色';context.lineWidth=10;context.beginPath();context.arc(r,0,r+10,0,2*Math.PI,false);context.closePath();context.fill();context.stroke();context.beginPath();context.arc(r,0,r,0,2*Math.PI,true);context.clip();}在浏览器中可以看到效果:4。文字淹没效果的实现文字淹没效果的绘制其实是按照以下思路进行的:首先绘制与最上面的水纹颜色相同的文字,让文字在被淹没前显示出可见的颜色水。在绘制水波过程中,连接完成后,调用context.clip()方法将绘制区域裁剪到所有被水浸过的部分。此时将填充颜色设置为白色,然后在同一位置渲染文字,这样渲染出来的白色文字就不会超出水印的范围,所以水印之外的蓝色部分文字会被保存下来在画布上。为了防止绘制下一层水印时,文字的白色部分被剪掉,我们需要在绘制完每一层水印后,重复步骤2,设置从这一层水印到底部的所有范围把水球的部分作为裁剪区域,然后在这一层的水纹里面画出白色的文字部分,这样在画好几层水纹之后,文字被淹的部分就会被染成白色。在这样的绘制方式中,文字的最终效果相当于将绘制的片段逐层拼接,每张图最后能保存的部分只是与当前图层水纹相交的部分。如果我们改变每层文字的绘制颜色,绘制过程会更容易理解:5.关于画布抗锯齿。参差不齐。网上查到的方法大多是调整画布大小(canvas.height,canvas.width)为元素大小(CSS中设置的canvas元素大小)的3-4倍,希望利用缩放来实现反-别名。但是,测量结果并没有明显改善。使用画布大小缩放在解决图像和填充模糊方面效果较好,但抗锯齿效果似乎与线条本身的大小有关,并不是绝对有效的方法。计划。另一种更有效的解决方法是在画外圆的时候加上2px-4px的深色阴影,可以在视觉上很好的弱化锯齿感。//在绘制外圈之前添加如下代码context.shadowColor='#2E5199';context.shadowBlur=2;context.shadowOffsetX=0;context.shadowOffsetY=2;基础图表的原生API绘制,一些比较高级的图表,绘制过程不一定很复杂,比如长方形树图,其实是画成长方形的正方形,但有助于我们更直观、更直观地观察数据强大的方式,比如可视化呈现webpack的打包结果。数据可视化的基本任务是让数据可视化,这就需要我们为要观察的数据选择合适的表示方式。这不能单纯靠技术来实现,还需要一定的艺术细胞和想象力。但无论如何,这是一个值得研究的有趣方向。
