示例代码托管于:http://www.github.com/dashnowords/blogs博客园地址:《大史住在大前端》原博文目录华为云社区地址:【你想要的前端打怪升级指南】[TOC]1.任务描述使用原生canvasAPI绘制散点图。(截图及数据来自百度Echarts官方示例库【查看示例链接】)。2.KeyTips在学会了折线图的绘制之后,如果数据点只有坐标数据,通过基本的坐标转换,在相应的点上绘制散点并不困难。在气泡图中,当我们直接使用百度Echarts例子中的数据,以一定的线性缩减为半径后直接绘制散点时,会出现一些问题。数据集的范围跨度大,导致大部分点呈现后,都非常小。这时候就需要使用某种方法,将真实数据值映射到散点圆的半径上,以减小它们之间的差异。否则,一旦数据集中出现一个偏差较大的点,则其他点对应的散点的半径都很大或很小,这对于数据呈现来说是不可取的。例如,在下面的例子中,当你使用几种不同的映射方法来处理数据时,你可以看到绘制的散点图是不同的。//求散点半径时使用的公式//1.直接值r=值*5/100000000;//2.求对数r=Math.log(value);//3.求指数r=Math.pow(value,0.4)/100;绘制的散点图如下:坐标映射的实现思路其实并不复杂。其概念可以参考算法的时间复杂度来理解。使用快速映射函数来区分相似点,或者选择增长较慢的映射函数来减少大跨度数据之间的差异,是数据可视化中非常实用的技术。本文示例中的效果是作者手动调整的。如果要根据数据集自动选择合适的映射函数,就需要设计一些计算方法。感兴趣的读者可以自行研究。三、示例代码绘制气泡散点图的示例代码(坐标轴的绘制过程在之前的博文中多次出现,这里不再赘述,需要的可以直接阅读之前的博文ofthisseriesorviewthisarticledemo):/*数据点来自百度Echarts官方样本库,每个值代表[横坐标,纵坐标,数值,国家,年份]*[28604,77,17096869,'澳大利亚',1990]*//***绘制数据*/functiondrawData(options){letdata=options.data;//获取数据集letxLength=(options.chartZone[2]-options.chartZone[0]);让yLength=(options.chartZone[3]-options.chartZone[1]);让gap=xLength/options.xAxisLabel.length;//遍历两年for(leti=0;i0;i--){context.save();//绘制外圈context.beginPath();context.arc(x,y,r+30*step,0,2*Math.PI,false);context.closePath();//设置剪切裁剪区域context.clip();//用全局背景色绘制裁剪区域背景context.globalAlpha=1;context.fillStyle=options.globalGradient;context.fill();//绘制内圈context.beginPath();context.arc(x,y,r+i*step,0,2*Math.PI,false);context.closePath();context.fillStyle=c;context.globalAlpha=0.8;//填充内圈context.fill();上下文.restore();}options.hoverData=null;console.log('清除悬停效果');}//使用离屏画布恢复悬停前的状态functionresetHoverWithOffScreen(){if(!options.hoverData)return;让{x,y,r,c}=options.hoverData;让步骤=0.5;context.globalAlpha=1;for(leti=29;i>0;i--){context.save();//恢复悬停状态下数据点圆的方形范围为悬停前的状态context.drawImage(canvas2,x-r-30*step,y-r-30*step,2*(r+30*step),2*(r+30*step),x-r-30*step,y-r-30*step,2*(r+30*step),2*(r+30*step));//绘制内圆context.beginPath();context.arc(x,y,r+i*step,0,2*Math.PI,false);context.closePath();context.fillStyle=c;context.globalAlpha=0.8;//填充内圆??context.fill();上下文.restore();}options.hoverData=null;console.log('clearhovereffect');}4.3Demo中的小问题为了简化代码,demo中的一些绘制数据并没有参数化,而是直接写在代码中,尤其是frame的代码-逐帧绘图。一般在开发中动画都是在这里实现的。为了重置某个数据点的悬停状态,作者最初的实现思路是在每一帧中使用context.clip()方法裁剪出绘图区域,先使用全局背景绘制背景图,减少数据点的半径,然后绘制数据点,直到半径减小到hover之前的值。但是在实现之后发现这个方法有一个问题,就是当数据点之间存在重叠时,如果单纯重绘背景,会清除部分重叠区域,导致其他数据点是不可恢复的,如下图所示:所以最后使用离屏canvas的方法,在初始绘制后暂存数据点,然后在hover状态清除时,使用context.drawImage()方法复制粘贴相关区域的数据,替换原来使用背景图片填充区域,这样当数据点之间有重叠时,可以重现悬停前的状态。