简单来说,可视化就是将数据信息组织起来后,以图形的形式展示出来。
在web上,图形通常是通过浏览器来绘制的。其中负责绘制图形的部分是渲染引擎。渲染引擎绘制图形的方式,大体上有以下4种
。
HTML+CSS
使用html + css 可以实现常规的图标展示。
柱状图
1 | /* dataset = { current: [15, 11, 17, 25, 37], total: [25, 26, 40, 45, 68], } */ |
效果如下图:
饼图
1 | .piegraph { |
效果如下图:
优缺点
- 简化开发,不需要引入额外的库,节省资源,提高网页打开的速度。
- html + css主要还是为了用于网页布局,虽然可以绘制可视化图表,但绘制的方法并不简洁。从css中很难看出图形与数据之间的关系,并且换算也需要developer自己来做,数据一旦发生变化,就需要重新计算生成。维护成本较高。
- 其次,开销较大。html+css 是浏览器渲染引擎的一部分,浏览器的渲染引擎在工作时,要先解析html、css绘制dom树,cssom树,render树等等,当用html绘图时,一旦图形发生变化,就要引发浏览器的重绘。
Canvas 2D
Canvas2D 是浏览器提供的简便快捷的指令式图形系统,它通过一些简单的指令就能快速绘制出复杂的图形。
MDN 使用教程链接:canvas 教程与指导
canvas元素和2d上下文
canvas元素本身的width和height,不等同于canvas元素css样式的宽高属性。
css宽高决定canvas页面呈现的大小,而canvas元素宽高决定了canvas的坐标系,决定可视区域的坐标范围。为了区分它们,我们称canvas元素属性宽高为画布宽高,css样式宽高为样式宽高
在实际绘制的过程中,如果不设置样式宽高,只设置了画布宽高,那么canvas的样式宽高就会等同于画布宽高。
如果不设置画布宽高,只设置了样式宽高,那么画布宽高将等同于样式宽高的二倍。
canvas操作步骤
此处不会赘述canvas的各个api,只是做于简单说明。
- 获取 Canvas 对象,通过 getContext(‘2d’) 得到 2D 上下文;
- 设置绘图状态,比如填充颜色 fillStyle,平移变换 translate 等等;
- 调用 beginPath 指令开始绘制图形;
- 调用绘图指令,比如 rect,表示绘制矩形;
- 调用 fill 指令,将绘制内容真正输出到画布上。
使用canvas绘制层次关系图
层次结构数据
用来表示能够体现层次结构的信息,例如城市与省与国家。一般来说,层次结构数据用层次关系图表来呈现。
城市层级示例图
json数据格式如下:
1 | { |
假设我们想要实现的层级关系图效果如下:
数据中只有”城市>省份>中国”这样的层级数据,我们需要把数据层级、位置和要绘制的半径、位置一一对应起来。
换句话说,就是需要数学计算。不过,我们可以直接使用 d3-hierarchy这个工具库转换数据。
1 | const regions = d3.hierarchy(cityData) |
使用d3.hierarchy进行数据转换。将数据映射到一个1000 * 1000的画布上,每个相邻圆之间间隔3px。拿到数据之后,只需要遍历数据并且根据数据内容绘制圆弧。
1 | const canvas = document.querySelector('canvas'); |
首先使用arc 指令(api)在当前节点绘制一个圆,arc 方法的五个参数分别是圆心的 x、y 坐标、半径 r、起始角度和结束角度,前三个参数就是数据中的 x、y 和 r。因为我们要绘制的是整圆,所以后面的两个参数中起始角是 0,结束角是 2π。
绘制成图后,如果当前数据有下一级的数据,则遍历它的下一级数据,递归的调用绘图过程。如果没有下一级,则说明当前数据为城市数据(最小单元数据),通过fillText指令直接给出当前城市的名字。
优缺点
- canvas能够直接操作绘图上下文,不需要html,css解析、渲染、布局等一系列操作。
- 不容易添加操作事件(如click事件)
SVG
svg,可缩放矢量图。是一种基于 XML 语法的图像格式,可以用图片(img 元素)的src属性加载。
svg MDN参考文档地址
实现柱状图
1 | <!-- dataset = { total: [25, 26, 40, 45, 68], current: [15, 11, 17, 25, 37], } --> |
绘制层次关系图
以canvas绘制的城市分级为例。数据同样需要经过d3.hierarchy
进行转换。
转换完成之后获取当前的svg节点。同样实现draw方法从root开始遍历数据。不同于canvas的调用绘图指令(api)来绘图,svg是通过创建svg元素,将元素添加到DOM中,来使得图像显现出来。
1 | const svgroot = document.querySelector('svg'); |
使用Document.createElementNS来创建一个具有指定命名空间(arg1)和限定名称(arg2)的元素。
因为要绘制圆形,所以创建一个circle元素,指定x,y,r 分别为圆的cx(中心点x),cy(中心点y),cr(半径);fillStyle赋值给fill属性。然后将circle元素添加到他的parent里。
1 | if(children) { |
svg的g代表一个分组,可以考虑用它(g)来建立一个层级结构,且g元素的属性,其子元素也可继承。
如果有子节点,则接着遍历下一层数据,直到数据最小单元(没有下一级的数据了)
1 | else { |
当没有下一级数据时,就需要为其添加text文字元素了,然后设置元素的属性,添加到父节点。
优缺点
- 相较于html + css ,弥补了html绘制不规则图形的能力,使用svg实现不规则形状图形要简单的多。
- 适用于元素较少的简单场景。同样需要经过浏览器渲染引擎的一系列操作。如果数据很复杂,同样会开销很多的内存空间。
svg与canvas
- 写法不同
svg是以创建图形元素绘图的”声明式”的绘图系统,Canvas 是执行绘图指令绘图的“指令式”绘图系统。 - 交互实现不同
svg的交互方式与dom操作大体相同。如(addEventListner)而canvas,则需要用到复杂的数学计算。 - svg绘制大量几何图形会极大的增大浏览器的重绘和重排。
WebGL
webGL比上述三种方式要复杂一些,因为 WebGL 是基于 OpenGL ES 规范的浏览器实现的,API 相对更底层,使用起来不如前三种那么简单直接。要使用webGL绘图,我们必须要深入细节里。换句话说就是,我们必须要和内存、GPU 打交道,真正控制图形输出的每一个细节。
图形是如何绘制的
首先说一下计算机图形系统的主要组成部分,以及他们在绘图过程中的作用。