You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

206 lines
16 KiB
Markdown

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

# 01 | 浏览器中实现可视化的四种方式
你好,我是月影。
上一节课我们了解了什么是可视化。可视化用一句话来说本质上就是将数据信息组织起来后以图形的方式呈现出来。在Web上图形通常是通过浏览器绘制的。现代浏览器是一个复杂的系统其中负责绘制图形的部分是渲染引擎。渲染引擎绘制图形的方式我总结了一下大体上有4种。
第1种是传统的**HTML+CSS**。这种方式通常用来呈现普通的Web网页。
第2种是使用**SVG**。SVG和传统的**HTML+CSS的绘图方式差别不大**。只不过HTML元素在绘制矢量图形方面的能力有些不足我们后面会讲到而SVG恰好弥补了这方面的缺陷。
第3种是使用**Canvas2D**。这是浏览器提供的Canvas API中的其中一种上下文使用它可以非常方便地绘制出基础的几何图形。在可视化中Canvas比较常用下一节课我们会学习它的基本用法。
第4种是使用**WebGL**。这是浏览器提供的Canvas API中的另一种上下文它是OpenGL ES规范在Web端的实现。我们可以通过它用GPU渲染各种复杂的2D和3D图形。值得一提的是WebGL利用了GPU并行处理的特性这让它在处理大量数据展现的时候性能大大优于前3种绘图方式。因此在可视化的应用中一些数据量大、视觉效果要求高的特殊场景使用WebGL渲染是一种比较合适的选择。
这4种方式各有利弊今天我就从宏观层面带你了解这些图形系统为我们后面更深入的学习打好基础。
## 方式一HTML+CSS
与传统的Web应用相比可视化项目尤其是PC端的可视化大屏展现不只是使用HTML与CSS相对较少而且使用方式也不太一样。于是有些同学就会认为可视化只能使用SVG、Canvas这些方式不能使用HTML与CSS。当然了这个想法是不对。具体的原因是什么呢我一起来看看。
实际上现代浏览器的HTML、CSS表现能力很强大完全可以实现常规的图表展现比如我们常见的柱状图、饼图和折线图。
虽然我们后面的课程会主要使用Canvas和WebGL绘图少数会涉及部分CSS。但是你可不要觉得它不重要。为啥呢理由有两个
* 一些简单的可视化图表用CSS来实现很有好处既能简化开发又不需要引入额外的库可以节省资源提高网页打开的速度。
* 理解CSS的绘图思想对于可视化也是很有帮助的比如CSS的很多理论就和视觉相关可视化中都可以拿来借鉴。
所以呢这一节里我们多讲一点你一定要好好听。接下来我们就来说一说CSS是如何实现常规图表的。
### 1\. HTML与CSS是如何实现可视化的
用CSS实现柱状图其实很简单原理就是使用网格布局Grid Layout加上线性渐变Linear-gradient我就不多说了你可以直接看我这里给出的CSS代码。
![](https://static001.geekbang.org/resource/image/68/31/68d43be360923664f2a3d8c2c65fbc31.jpg "用HTML+CSS绘制的柱状图")
```
/**
dataset = {
current: [15, 11, 17, 25, 37],
total: [25, 26, 40, 45, 68],
}
*/
.bargraph {
display: grid;
width: 150px;
height: 100px;
padding: 10px;
transform: scaleY(3);
grid-template-columns: repeat(5, 20%);
}
.bargraph div {
margin: 0 2px;
}
.bargraph div:nth-child(1) {
background: linear-gradient(to bottom, transparent 75%, #37c 0, #37c 85%, #3c7 0);
}
.bargraph div:nth-child(2) {
background: linear-gradient(to bottom, transparent 74%, #37c 0, #37c 89%, #3c7 0);
}
.bargraph div:nth-child(3) {
background: linear-gradient(to bottom, transparent 60%, #37c 0, #37c 83%, #3c7 0);
}
.bargraph div:nth-child(4) {
background: linear-gradient(to bottom, transparent 55%, #37c 0, #37c 75%, #3c7 0);
}
.bargraph div:nth-child(5) {
background: linear-gradient(to bottom, transparent 32%, #37c 0, #37c 63%, #3c7 0);
}
```
而要实现饼图,我们可以使用圆锥渐变,方法也很简单,你直接看代码就可以理解。
![](https://static001.geekbang.org/resource/image/58/6d/58c6ea6ffce4e47446c0c9636d47226d.jpg "使用圆锥渐变绘制的饼图")
```
.piegraph {
display: inline-block;
width: 250px;
height: 250px;
border-radius: 50%;
background-image: conic-gradient(#37c 30deg, #3c7 30deg, #3c7 65deg, orange 65deg, orange 110deg, #f73 110deg, #f73 200deg, #ccc 200deg);
}
```
柱状图和饼图都比较简单所以我带你快速过了一下。除此之外我们用HTML和CSS也可以实现折线图。
我们可以用高度很小的Div元素来模拟线段然后用transform改变角度和位置这样就能拼成折线图了。 另外如果使用clip-path这样的高级属性我们还能实现更复杂的图表比如用不同的颜色表示两个不同折线的面积。
![](https://static001.geekbang.org/resource/image/cc/c9/cc4d0f6d9260d508758c8043a14ea1c9.jpg "折线图和面积图")
实际上很多常见的可视化图表我们都可以用HTML和CSS来实现不需要用其他的绘图方式。但是为什么在可视化领域很少有人直接用HTML和CSS来绘制图表呢这主要是因为使用HTML和CSS绘图有2个缺点。
### 2.用HTML+CSS实现可视化的缺点
首先HTML和CSS主要还是为网页布局而创造的使用它们虽然能绘制可视化图表但是绘制的方式并不简洁。这是因为从CSS代码里我们很难看出数据与图形的对应关系有很多换算也需要开发人员自己来做。这样一来一旦图表或数据发生改动就需要我们重新计算维护起来会很麻烦。
其次HTML和CSS作为浏览器渲染引擎的一部分为了完成页面渲染的工作除了绘制图形外还要做很多额外的工作。比如说浏览器的渲染引擎在工作时要先解析HTML、SVG、CSS构建DOM树、RenderObject树和RenderLayer树然后用HTML或SVG绘图。当图形发生变化时我们很可能要重新执行全部的工作这样的性能开销是非常大的。
而且传统的Web开发因为涉及UI构建和内容组织所以这些额外的解析和构建工作都是必须做的。而可视化与传统网页不同它不太需要复杂的布局更多的工作是在绘图和数据计算。所以对于可视化来说这些额外的工作反而相当于白白消耗了性能。
因此相比于HTML和CSSCanvas2D和WebGL更适合去做可视化这一领域的绘图工作。它们的绘图API能够直接操作绘图上下文一般不涉及引擎的其他部分在重绘图像时也不会发生重新解析文档和构建结构的过程开销要小很多。
![](https://static001.geekbang.org/resource/image/d4/9d/d49d2fb673a7fb9f8de329c12fab009d.jpg "图形系统与浏览器渲染引擎工作对比")
## 方式二SVG
在介绍Canvas2D和WebGL之前我们先来说一说SVG。现代浏览器支持SVGScalable Vector Graphics可缩放矢量图SVG是一种基于 XML 语法的图像格式可以用图片img元素的src属性加载。而且浏览器更强大的是它还可以内嵌SVG标签并且像操作普通的HTML元素一样利用DOM API操作SVG元素。甚至CSS也可以作用于内嵌的SVG元素。
比如上面的柱状图如果用SVG实现的话我们可以用如下所示的代码来实现
```
<!--
dataset = {
total: [25, 26, 40, 45, 68],
current: [15, 11, 17, 25, 37],
}
-->
<svg xmlns="http://www.w3.org/2000/svg" width="120px" height="240px" viewBox="0 0 60 100">
<g transform="translate(0, 100) scale(1, -1)">
<g>
<rect x="1" y="0" width="10" height="25" fill="#37c"/>
<rect x="13" y="0" width="10" height="26" fill="#37c"/>
<rect x="25" y="0" width="10" height="40" fill="#37c"/>
<rect x="37" y="0" width="10" height="45" fill="#37c"/>
<rect x="49" y="0" width="10" height="68" fill="#37c"/>
</g>
<g>
<rect x="1" y="0" width="10" height="15" fill="#3c7"/>
<rect x="13" y="0" width="10" height="11" fill="#3c7"/>
<rect x="25" y="0" width="10" height="17" fill="#3c7"/>
<rect x="37" y="0" width="10" height="25" fill="#3c7"/>
<rect x="49" y="0" width="10" height="37" fill="#3c7"/>
</g>
</g>
</svg>
```
从上面的SVG代码中我们可以一目了然地看出数据total和current分别对应SVG中两个g元素下的rect元素的高度。也就是说元素的属性和数值可以直接对应起来。而CSS代码并不能直观体现出数据的数值需要进行CSS规则转换。具体如下图所示
![](https://static001.geekbang.org/resource/image/c9/62/c9716f8c3768a2384c3baf5f8ec87362.jpg)
在上面这段SVG代码中g表示分组rect表示绘制一个矩形元素。除了rect外SVG还提供了丰富的图形元素可以绘制矩形、圆弧、椭圆、多边形和贝塞尔曲线等等。由于SVG比较复杂我们会在第4节课专门介绍如何用SVG绘制可视化图表。在那之前你也可以通过[MDN官方文档](https://developer.mozilla.org/zh-CN/docs/Web/SVG/Tutorial)来学习更多的SVG的API。
SVG绘制图表与HTML和CSS绘制图表的方式差别不大只不过是将HTML标签替换成SVG标签运用了一些SVG支持的特殊属性。
HTML的不足之处在于HTML元素的形状一般是矩形虽然用CSS辅助也能够绘制出各种其它形状的图形甚至不规则图形但是总体而言还是非常麻烦的。而SVG则弥补了这方面的不足让不规则图形的绘制变得更简单了。因此用SVG绘图比用HTML和CSS要便利得多。
但是SVG图表也有缺点。在渲染引擎中SVG元素和HTML元素一样在输出图形前都需要经过引擎的解析、布局计算和渲染树生成。而且一个SVG元素只表示一种基本图形如果展示的数据很复杂生成图形的SVG元素就会很多。这样一来大量的SVG元素不仅会占用很多内存空间还会增加引擎、布局计算和渲染树生成的开销降低性能减慢渲染速度。这也就注定了SVG只适合应用于元素较少的简单可视化场景。
## 方式三Canvas2D
除了SVG使用Canvas2D上下文来绘制可视化图表也很方便但是在绘制方式上Canvas2D和HTML/CSS、SVG又有些不同。
无论是使用HTML/CSS还是SVG它们都属于**声明式**绘图系统也就是我们根据数据创建各种不同的图形元素或者CSS规则然后利用浏览器渲染引擎解析它们并渲染出来。但是Canvas2D不同它是浏览器提供的一种可以直接用代码在一块平面的“画布”上绘制图形的API使用它来绘图更像是传统的“编写代码”简单来说就是调用绘图指令然后引擎直接在页面上绘制图形。这是一种**指令式**的绘图系统。
那Canvas到底是怎么绘制可视化图表的呢我们一起来看。
首先Canvas元素在浏览器上创造一个空白的画布通过提供渲染上下文赋予我们绘制内容的能力。然后我们只需要调用渲染上下文设置各种属性然后调用绘图指令完成输出就能在画布上呈现各种各样的图形了。
为了实现更加复杂的效果Canvas还提供了非常丰富的设置和绘图API我们可以通过操作上下文来改变填充和描边颜色对画布进行几何变换调用各种绘图指令然后将绘制的图形输出到画布上。
总结来说Canvas能够直接操作绘图上下文不需要经过HTML、CSS解析、构建渲染树、布局等一系列操作。因此单纯绘图的话Canvas比HTML/CSS和SVG要快得多。
但是因为HTML和SVG一个元素对应一个基本图形所以我们可以很方便地操作它们比如在柱状图的某个柱子上注册点击事件。而同样的功能在Canvas上就比较难实现了因为对于Canvas来说绘制整个柱状图的过程就是一系列指令的执行过程其中并没有区分“A柱子”、“B柱子”这让我们很难单独对Canvas绘图的局部进行控制。不过这并不代表我们就不能控制Canvas的局部了。实际上通过数学计算我们是可以通过定位的方式来获取局部图形的在后续的课程中我们会解决这个问题。
这里有一点需要你注意Canvas和SVG的使用也不是非此即彼的它们可以结合使用。因为SVG作为一种图形格式也可以作为image元素绘制到Canvas中。举个例子我们可以先使用SVG生成某些图形然后用Canvas来渲染。这样我们就既可以享受SVG的便利性又可以享受Canvas的高性能了。
## 方式四WebGL
WebGL绘制比前三种方式要复杂一些因为WebGL是基于OpenGL ES规范的浏览器实现的API相对更底层使用起来不如前三种那么简单直接。关于WebGL的使用内容我会在3D篇详细来说。
一般情况下Canvas2D绘制图形的性能已经足够高了但是在三种情况下我们有必要直接操作更强大的GPU来实现绘图。
第一种情况,如果我们**要绘制的图形数量非常多**比如有多达数万个几何图形需要绘制而且它们的位置和方向都在不停地变化那我们即使用Canvas2D绘制了性能还是会达到瓶颈。这个时候我们就需要使用GPU能力直接用WebGL来绘制。
第二种情况,如果我们要**对较大图像的细节做像素处理**比如实现物体的光影、流体效果和一些复杂的像素滤镜。由于这些效果往往要精准地改变一个图像全局或局部区域的所有像素点要计算的像素点数量非常的多一般是数十万甚至上百万数量级的。这时即使采用Canvas2D操作也会达到性能瓶颈所以我们也要用WebGL来绘制。
第三种情况是**绘制3D物体**。因为WebGL内置了对3D物体的投影、深度检测等特性所以用它来渲染3D物体就不需要我们自己对坐标做底层的处理了。那在这种情况下WebGL无论是在使用上还是性能上都有很大优势。
## 要点总结
今天,我们介绍了四种可视化实现方式和它们的优缺点。
HTML+CSS的优点是方便不需要第三方依赖甚至不需要JavaScript代码。如果我们要绘制少量常见的图表可以直接采用HTML和CSS。它的缺点是CSS属性不能直观体现数据绘制起来也相对麻烦图形复杂会导致HTML元素多而消耗性能。
SVG 是对HTML/CSS的增强弥补了HTML绘制不规则图形的能力。它通过属性设置图形可以直观地体现数据使用起来非常方便。但是SVG也有和HTML/CSS同样的问题图形复杂时需要的SVG元素太多也非常消耗性能。
Canvas2D 是浏览器提供的简便快捷的指令式图形系统它通过一些简单的指令就能快速绘制出复杂的图形。由于它直接操作绘图上下文因此没有HTML/CSS和SVG绘图因为元素多导致消耗性能的问题性能要比前两者快得多。但是如果要绘制的图形太多或者处理大量的像素计算时Canvas2D依然会遇到性能瓶颈。
WebGL 是浏览器提供的功能强大的绘图系统它使用比较复杂但是功能强大能够充分利用GPU并行计算的能力来快速、精准地操作图像的像素在同一时间完成数十万或数百万次计算。另外它还内置了对3D物体的投影、深度检测等处理这让它更适合绘制3D场景。
知道了这些优缺点,在实际面对可视化需求的时候,我们就可以根据具体项目的特点来选择合适的方案实现可视化了。那具体来说,我们应该怎么选择呢?我这里梳理了一张技术方案的选择图,你可以看一看。
![](https://static001.geekbang.org/resource/image/3b/6f/3bf11fcf520504a4e342dd335698c76f.jpg)
## 小试牛刀
我们在文中实现了SVG版本的柱状图你可以尝试用SVG实现同HTML/CSS版本一样的饼图、折线图和面积图体会一下使用SVG实现和HTML/CSS实现的不同点。
另外下一节课我们会介绍Canvas2D绘制可视化图表你可以提前预习一下试一试能否用Canvas2D来绘制文中的柱状图。
欢迎在留言区和我讨论,分享你的答案和思考,也欢迎你把这节课分享给你的朋友,我们下节课见!