【摘要】 用原生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. 硬件内存模型到 Java 内存模型,这些硬核知识你知多少?

    Java 内存模型跟上一篇 JVM 内存结构很像,我经常会把他们搞混,但其实它们不是一回事,而且相差还很大的,希望你没它们搞混,特别是在面试的时候,搞混了的话就会答非所问,影响你的面试成绩,当然也许你 ...

  2. Python基本数据结构之二进制

    二进制---->ASCII :只能存英文和拉丁字符.一个字符占一个字节,8位----->gb2312:只能6700多个中文,1980------->gbk1.0:村落2万多字符,19 ...

  3. PHP防止客户端多次点击

    PHP防止客户端多次点击 第一种用ip判断 第二种就是用 用户名第三种就是cookie仅限 H5第四种 用swoole 用swoole id

  4. 网站搭建 - 虚拟机的安装 - Linux 本地网站搭建第一步

    搭建网站-1-域名申请参见公众号 生物信息系统(swxxxt) 搭建网站-域名绑定见稍后的一章,就是直接点解析,然后就完事了,可以不看的. 首先准备材料: 先装虚拟机,会要求重启,那就重启吧,安装界面 ...

  5. Python连接SqlServer+GUI嵌入式——学生管理系统1.0

    学生管理系统1.0 1.建学生数据库 2.数据库嵌入高级语言(Python) 3.界面设计 简化思路: 1.先通过SqlServer2012建立学生数据库,包括账号.密码,姓名.选课等信息 2.运用P ...

  6. 【Linux】Debian 下安装 Apache,MySQL,PHP

    首先,对你的源进行更新: $ sudo apt-get update 第一步--安装 Apache Apache 是一个开源软件,它目前运行在全球超过 50% 的服务器上,是 LAMP(Linux,A ...

  7. PHP Laravel 中使用简单的方法跟踪用户是否在线

    今天,我的任务是,在 Laravel 应用程序用户个人资料页面上,用户名旁边添加一个绿点,表示他们是否在线.我首先想到的是,我们将需要启动一个 node.js 服务器并跟踪每个用户的活动套接字连接.然 ...

  8. 开始逆向objc基础准备(二)我的平台是simulator-x86_64

    建项目运行中断调试,lldb中显示寄存器看到有rax-r15, stm0-stm7, xmm0-xmm15, ymm0-ymm15,即为x64体系支持sse4. 再在lldb中查看寄存器别名得到以下对 ...

  9. HDFS之NameNode

    NameNode&Secondary NameNode工作机制 1)第一阶段:namenode启动 (1)第一次启动namenode格式化后,创建fsimage和edits文件.如果不是第一次 ...

  10. SQL查询优化实践

    为什么要优化 系统的吞吐量瓶颈往往出现在数据库的访问速度上,即随着应用程序的运行,数据库的中的数据会越来越多,处理时间会相应变慢,且数据是存放在磁盘上的,读写速度无法和内存相比 如何优化 设计数据库时 ...