【摘要】 用原生canvasAPI实现百度Echarts图表

示例代码托管在:http://www.github.com/dashnowords/blogs

一. 任务说明

使用原生canvasAPI绘制散点图。(截图以及数据来自于百度Echarts官方示例库【查看示例链接】)。

二. 重点提示

学习过折线图的绘制后,如果数据点只有坐标数据,则通过基本的坐标转换在对应的点上绘制出散点并不难实现。而在气泡图中,当我们直接将百度Echarts示例中的数据拿来经过一定的线性缩小后作为半径直接绘制散点时,就会出现一些问题,数据集的范围跨度较大,导致大部分点呈现后都非常小,这个时候就需要使用某种方法从真实数据值映射到散点圆半径进行映射,来缩小它们之间的差异,否则一旦数据集中有一个偏离度较大的点,就会造成其他点所对应的散点半径都很大或者都很小,对数据呈现来说是不可取的。例如在下面的示例中,当使用几种不同的映射方法来处理数据后,可以看到绘制的散点图是不一样的。

//求散点半径时所使用的公式
//1.直接数值
r = value * 5 / 100000000;
//2.求对数
r = Math.log(value);
//3.求指数
r = Math.pow(value,0.4) / 100;

所绘制出的散点图如下所示:

坐标映射的实现思路其实并不算复杂,它的概念可以参考算法的时间复杂度来进行理解,挑选一个增长更快的映射函数来区分相近的点,或者挑选一个增长更慢的映射函数来减小大跨度数据之间的差异,在数据可视化中是非常实用的技巧。本文示例中的效果是笔者自己手动调的,如果要实现根据数据集自动挑选适当的映射函数,还需要设计一些计算方法,感兴趣的读者可以自行研究。

三. 示例代码

气泡散点图绘制示例代码(坐标轴的绘制过程在前述博文中已经出现过很多次,故不再赘述,有需要的小伙伴可以直接翻看这个系列之前的博文或者查看本篇的demo):

/*数据点来自于百度Echarts官方示例库,每个数值分别表示[横坐标,纵坐标,数值,国家,年份]
*[28604,77,17096869,'Australia',1990]
*/ /**
* 绘制数据
*/
function drawData(options) {
let data = options.data;//获取数据集
let xLength = (options.chartZone[2] - options.chartZone[0]);
let yLength = (options.chartZone[3] - options.chartZone[1]);
let gap = xLength / options.xAxisLabel.length; //遍历两个年份
for(let i = 0; i < data.length ;i++){
let x,y,r,c;
context.fillStyle = options.colorPool[i];//从颜色池中选取颜色
context.globalAlpha = 0.8;//为避免点覆盖,采取半透明绘制
//遍历各个数据点
for(let j = 0; j < data[i].length ; j++){
//计算坐标
x = options.chartZone[0] + xLength * data[i][j][0] / 70000;
y = options.chartZone[3] - yLength * (data[i][j][1] - 55) / (85 - 55); //直接数值
r = data[i][j][2] * 5 / 100000000;
//求对数
r = Math.log(data[i][j][2]);
//开根号
r = Math.pow(data[i][j][2],0.4) / 100;
//绘制散点
context.beginPath();
context.arc(x, y , r , 0 , 2*Math.PI,false);
context.fill();
context.closePath();
}
}
}

浏览器中可查看效果:

四.散点hover交互效果的实现

4.1 基本算法

在散点图上实现hover交互效果的基本算法如下:

  1. canvas元素上监听鼠标移动事件,将鼠标坐标转换为canvas坐标系的坐标值。

  2. 遍历数据点查看是否存在当前鼠标点距离某个数据中心点的距离小于其散点的绘制半径,如果有则认为鼠标在该点之上。

  3. 利用之前缓存的该点绘图数据,调整绘图样式,增大数据点的绘图半径覆盖式绘图即可。

  4. 当鼠标距离任何数据点的距离都大于该点的绘图半径,或鼠标从一个hover数据点移动到另一个hover点时,均需要调用一次resetHover( )方法清除之前的hover状态。

  5. 为了恢复hover前的状态,可以使用【离屏canvas技术】缓存首次绘图后的结果,然后使用drawImage( )方法将对应区域恢复到hover前的状态。

4.2 参考代码

hover效果的关键代码如下,完整示例代码请在demo中获取,或访问【我的github仓库】

4.3 Demo中的小问题

  1. 为了简化代码,demo中的一些绘图数据并没有参数化,而是采取直接写死的形式放在代码里,尤其是逐帧绘图的代码,一般开发中此处都会配合动画来进行实现。

  2. 为了重置某个数据点的hover状态,笔者最初的实现思路是在每一帧中,使用context.clip( )方法裁切出绘图区域,先用全局背景绘制出背景图,缩小数据点半径,然后再绘制数据点,直到半径缩小至hover前的值。但在实现后发现这种方式存在一个问题,那就是数据点之间出现重叠时,如果只是简单地背景重绘,就会将部分重叠区域清除掉,造成其他数据点无法复原,如下图所示:

    所以最终采用离屏canvas的方法,将初次绘制后的数据点先暂存下来,然后在清除hover状态时,使用context.drawImage( )方法将有关区域的数据复制粘贴过来,以替代原来的使用背景图填充该区域的做法,这样就可以在数据点之间有重叠时重现hover前的状态。

demo.rar

作者:大史不说话

带着canvas去流浪系列之四 绘制散点图的更多相关文章

  1. 带着canvas去流浪系列之五 绘制K线图

    [摘要] 用canvas原生API实现百度Echarts 示例代码托管在:http://www.github.com/dashnowords/blogs 一. 任务说明 使用原生canvasAPI绘制 ...

  2. 带着canvas去流浪系列之七 绘制水球图

    [摘要] 用原生canvasAPI实现百度echarts 示例代码托管在:http://www.github.com/dashnowords/blogs 一. 任务说明 使用原生canvasAPI绘制 ...

  3. 带着canvas去流浪系列之三 绘制饼图

    [摘要] 用canvas原生API绘制Echarts图表 示例代码托管在:http://www.github.com/dashnowords/blogs 一. 任务说明 使用原生canvasAPI绘制 ...

  4. 带着canvas去流浪系列之六 绘制雷达图

    [摘要] 用canvas原生API实现百度Echarts基本图表. 示例代码托管在:http://www.github.com/dashnowords/blogs 一. 任务说明 使用原生canvas ...

  5. 带着canvas去流浪系列之二 绘制折线图

    [摘要] 用canvasAPI实现echarts简易图表 示例代码托管在:http://www.github.com/dashnowords/blogs 一. 任务说明 使用原生canvasAPI绘制 ...

  6. 带着canvas去流浪系列之八 碰撞

    [摘要] canvas动画-碰撞仿真 示例代码托管在:http://www.github.com/dashnowords/blogs 经过前面章节相对枯燥的练习,相信你已经能够上手canvas的原生A ...

  7. 带着canvas去流浪系列之八 碰撞【华为云技术分享】

    [摘要] canvas动画-碰撞仿真 示例代码托管在:http://www.github.com/dashnowords/blogs 经过前面章节相对枯燥的练习,相信你已经能够上手canvas的原生A ...

  8. 带着canvas去流浪系列之一:绘制柱状图

    [摘要] 学习使用canvasAPI来实现数据可视化. 示例代码托管在:http://www.github.com/dashnowords/blogs 一. 任务说明 使用原生canvasAPI绘制柱 ...

  9. 带着canvas去流浪系列之九 粒子动画【华为云技术分享】

    版权声明:本文为博主原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明. 本文链接:https://blog.csdn.net/devcloud/article/detai ...

随机推荐

  1. 7.25 NOIP模拟8

    这次考试前面状态还行,后两个小时真是一言难尽,打了个T3的n^2暴力就懵逼了,不知道怎么优化. T1.匹配 看了一边题发现不太懂(这不是考试的难度啊),然后水完T2后回来5分钟水过,非常愉快的一道题. ...

  2. CSPS模拟 42

    T3数位$dp$还没改完啊 哭了 T1 对$DAG$里所有点求他能到达的点的数量 考试时算了$bitset$内存扛不住,yy了个线段树合并上去 没有A不是因为被卡了,而是数组又开小了.. 我以为不能卡 ...

  3. Js对象继承小结

    1.继承 对象的定义好用一些的一般是把实例对象的属性定义在类里面,通过this指针指向具体实例属性.定义对象的public方法时将其绑定到prototype中.子类在继承父类时可以通过对象冒充来继承父 ...

  4. elasticsearch操作命令

    总结几个es的curl操作命令: 查询某条数据:curl -XGET http://elasticsearch:9200/company/company/123?pretty 查询索引下数据总量:cu ...

  5. python中字符串的常见操作(一)

    如有字符串: str1 = '192.168.1.1' str2 = 'asdfghjk' str3 = 'Asd fg hj ki' str4 = ' ' str5 = '' 以下是常见操作:# i ...

  6. VsCode Python配置安装教程

    1.软件下载地址 Python官网: https://www.python.org/downloads/windows/ Python下载地址: https://www.python.org/ftp/ ...

  7. Docker学习-Kubernetes - 集群部署

    Docker学习 Docker学习-VMware Workstation 本地多台虚拟机互通,主机网络互通搭建 Docker学习-Docker搭建Consul集群 Docker学习-简单的私有Dock ...

  8. 替换"marquee",实现无缝滚动

    js的marquee标签,可以实现元素循环滚动,但是不能无缝连接,要实现“无缝滚动”的效果必须使用js(借鉴百度),思路是使要滚动元素相对位置不断改变,上下滚动就相对top或者bottom,左右滚动就 ...

  9. hdu 1533 Going Home (KM)

    Going HomeTime Limit: 10000/5000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)Total ...

  10. Mybatis动态SQL(where元素、set元素、if元素)

    Mybatis动态SQL(where元素.set元素.if元素) - where 元素只会在至少有一个子元素的条件返回 SQL 子句的情况下才去插入“WHERE”子句.而且,若语句的开头为“AND”或 ...