不写网页的前端工程师,还能做什么
作为前端工程师,很多人的主要工作就是和网页打交道。那扪心自问一下,写了这么多网页之后,你是不是也想要做些尝试或者突破呢?如果是的话,我建议大家试试可视化。
为什么要学可视化
“双十一”可视化大屏
echarts 绘制地图
学习可视化对我们的帮助
学习相关的图形、视觉呈现的原理和方法,能够最大化地丰富我们的知识面,拓宽你的技术成长路线,让你的技术天花板变得更高。更详细点来说,像视觉呈现技术中也有涉及高级 CSS 原理的部分,所以如果你学会了可视化,也会对你的 CSS 技能有很大的启发和提升。
如何学习可视化
与传统的 Web 应用相比,可视化项目,尤其是 PC 端的可视化大屏展现,使用 HTML 与 CSS 相对较少,而且使用方式也不太一样。我们可能就会认为,可视化只能使用 SVG、Canvas 这些方式,不能使用 HTML 与 CSS。
实际上,浏览器的 HTML、CSS 表现能力很强大,完全可以实现常规的图表展现,比如,我们常见的柱状图、饼图和折线图。
例如:柱状图和饼状图实现
我们可以用高度很小的 div 元素来模拟线段,然后用 transform 改变角度和位置,这样就能拼成折线图了。 如果使用 clip-path 这样的高级属性,我们还能实现更复杂的图表,比如,用不同的颜色表示两个不同折线的面积。
优点
缺点
因此,相比于 HTML 和 CSS,Canvas2D 和 WebGL 更适合去做可视化这一领域的绘图工作。它们的绘图 API 能够直接操作绘图上下文,一般不涉及引擎的其他部分,在重绘图像时,也不会发生重新解析文档和构建结构的过程,开销要小很多。
SVG(Scalable Vector Graphics,可缩放矢量图),SVG 是一种基于 XML 语法的图像格式,可以用图片(img 元素)的 src 属性加载。而且,浏览器更强大的是,它还可以内嵌 SVG 标签,并且像操作普通的 HTML 元素一样,利用 DOM API 操作 SVG 元素。甚至,CSS 也可以作用于内嵌的 SVG 元素。
例如:柱状图实现
<svg xmlns="http://www.w3.org/2000/svg" width="200px" 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 规则转换。
在上面这段 SVG 代码中,g 表示分组,rect 表示绘制一个矩形元素。除了 rect 外,SVG 还提供了丰富的图形元素,可以绘制矩形、圆弧、椭圆、多边形和贝塞尔曲线等等。SVG 绘制图表与 HTML 和 CSS 绘制图表的方式差别不大,只不过是将 HTML 标签替换成 SVG 标签,运用了一些 SVG 支持的特殊属性。
优点
HTML 的不足之处在于 HTML 元素的形状一般是矩形,虽然用 CSS 辅助,也能够绘制出各种其它形状的图形,甚至不规则图形,但是总体而言还是非常麻烦的。而 SVG 则弥补了这方面的不足,让不规则图形的绘制变得更简单了。因此,用 SVG 绘图比用 HTML 和 CSS 要便利得多。
缺点
在渲染引擎中,SVG 元素和 HTML 元素一样,在输出图形前都需要经过引擎的解析、布局计算和渲染树生成。而且,一个 SVG 元素只表示一种基本图形,如果展示的数据很复杂,生成图形的 SVG 元素就会很多。这样一来,大量的 SVG 元素不仅会占用很多内存空间,还会增加引擎、布局计算和渲染树生成的开销,降低性能,减慢渲染速度。这也就注定了 SVG 只适合应用于元素较少的简单可视化场景。
无论是使用 HTML/CSS 还是 SVG,它们都属于声明式
绘图系统,也就是我们根据数据创建各种不同的图形元素(或者 CSS 规则),然后利用浏览器渲染引擎解析它们并渲染出来。但是 Canvas2D 不同,它是浏览器提供的一种可以直接用代码在一块平面的“画布”上绘制图形的 API,使用它来绘图更像是传统的“编写代码”,简单来说就是调用绘图指令,然后引擎直接在页面上绘制图形。这是一种指令式
的绘图系统。(微信小程序支持:2.9.0)
使用
首先,Canvas 元素在浏览器上创造一个空白的画布,通过提供渲染上下文,赋予我们绘制内容的能力。然后,我们只需要调用渲染上下文,设置各种属性,然后调用绘图指令完成输出,就能在画布上呈现各种各样的图形了。
为了实现更加复杂的效果,Canvas 还提供了非常丰富的设置和绘图 API,我们可以通过操作上下文,来改变填充和描边颜色,对画布进行几何变换,调用各种绘图指令,然后将绘制的图形输出到画布上。
例如:绘制正方形
优点
总结来说,Canvas 能够直接操作绘图上下文,不需要经过 HTML、CSS 解析、构建渲染树、布局等一系列操作。因此单纯绘图的话,Canvas 比 HTML/CSS 和 SVG 要快得多。
缺点
因为 HTML 和 SVG 一个元素对应一个基本图形,所以我们可以很方便地操作它们,比如在柱状图的某个柱子上注册点击事件。而同样的功能在 Canvas 上就比较难实现了,因为对于 Canvas 来说,绘制整个柱状图的过程就是一系列指令的执行过程,其中并没有区分“A 柱子”、“B 柱子”,这让我们很难单独对 Canvas 绘图的局部进行控制。
WebGL 绘制比前三种方式要复杂一些,因为 WebGL 是基于 OpenGL ES 规范的浏览器实现的,API 相对更底层,使用起来不如前三种那么简单直接。(微信小程序支持:2.7.0)
一般情况下,Canvas2D 绘制图形的性能已经足够高了,但是在三种情况下我们有必要直接操作更强大的 GPU 来实现绘图。
Canvas 的坐标系和浏览器窗口的坐标系类似,它们都默认左上角为坐标原点,x 轴水平向右,y 轴垂直向下。那在我们设置好的画布宽高为 512 * 512 的 Canvas 画布中,它的左上角坐标值为(0,0),右下角坐标值为(512,512) 。这意味着,坐标(0,0)到(512,512)之间的所有图形,都会被浏览器渲染到画布上。
获取 Canvas 上下文
首先是获取 Canvas 元素。因为 Canvas 元素就是 HTML 文档中的 canvas 标签,所以,我们可以通过 DOM API 获取它,代码如下:
const canvas = document.querySelector('canvas');
获取了 canvas 元素后,我们就可以通过 getContext 方法拿到它的上下文对象。具体的操作就是,我们调用 canvas.getContext 传入参数 2d。
const context = canvas.getContext('2d');
用 Canvas 上下文绘制图形
我们拿到的 context 对象上会有许多 API,它们大体上可以分为两类:一类是设置状态的 API,可以设置或改变当前的绘图状态,比如,改变要绘制图形的颜色、线宽、坐标变换等等;另一类是绘制指令 API,用来绘制不同形状的几何图形。
假设我们要在画布的中心位置绘制一个 100 * 100 的红色正方形。
总结
利用 Canvas 给一组城市数据绘制一个层次关系图了。也就是在一组给出的层次结构数据中,体现出同属于一个省的城市。
数据源:
结果:
canvas arc()
参数 | 描述 |
---|---|
x | 圆的中心的 x 坐标 |
y | 圆的中心的 y 坐标 |
r | 圆的半径 |
sAngle | 起始角,以弧度计。(弧的圆形的三点钟位置是 0 度) |
eAngle | 结束角,以弧度计 |
counterclockwise | 可选。规定应该逆时针还是顺时针绘图。False = 顺时针,true = 逆时针 |
SVG 的全称是 Scalable Vector Graphics,可缩放矢量图,它是浏览器支持的一种基于 XML 语法的图像格式。因为描述 SVG 的 XML 语言本身和 HTML 非常接近,都是由标签 + 属性构成的,而且浏览器的 CSS、JavaScript 都能够正常作用于 SVG 元素。我们可以认为,SVG 是 HTML 的增强版。
SVG 属于声明式绘图系统,它的绘制方式和 Canvas 不同,它不需要用 JavaScript 操作绘图指令,只需要和 HTML 一样,声明一些标签就可以实现绘图了。
svg 元素是 SVG 的根元素,属性 xmlns 是 xml 的名字空间。那第一行 代码就表示,svg 元素的 xmlns 属性值是"http://www.w3.org/2000/svg",浏览器根 据这个属性值就能够识别出这是一段 SVG 的内容了。
我们是使用 document.createElementNS 方法来创建 SVG 元素的。这里你要注意,与使用 document.createElement 方法创建普通的 HTML 元素不 同,SVG 元素要使用 document.createElementNS 方法来创建。
SVG 的 g 元素表示一个分组,我们可以用它来对 SVG 元素建立起层级结构。而且,如果 我们给 g 元素设置属性,那么它的子元素会继承这些属性。
SVG 是以创建图形元素绘图的“声明式”绘图系统,Canvas 是执行绘图指令绘图的“指令式”绘图系统。
在绘制层次关系图的过程中,SVG 首先通过创建标签来表示图形元素,circle 表示圆,g 表示分组,text 表示文字。接着,SVG 通过元素的 setAttribute 给图形元素赋属性值,这 个和操作 HTML 元素是一样的。
而 Canvas 先是通过上下文执行绘图指令来绘制图形,画圆是调用 context.arc 指令,然后再调用 context.fill 绘制,画文字是调用 context.fillText 指令。另外,Canvas 还通过上下文设置状态属性,context.fillStyle 设置填充颜色,conext.font 设置元素的字体。
给这个 SVG 版本的层次关系图添加一个功能,也就是当鼠标移动到某个区域时,这个区域会高亮,并且显示出对应的省 - 市信息。
利用 SVG 的 一个图形对应一个 svg 元素的机制,我们就可以像操作普通的 HTML 元素那样,给 svg 元 素添加事件实现用户交互了。所以,SVG 有一个非常大的优点,那就是可以让图形的用户交互非常简单。
和 SVG 相比,利用 Canvas 对图形元素进行用户交互就没有那么容易了。对于圆形的层次关系图来说,在 Canvas 图形上定位鼠标处于哪个圆中并不难,我们只需要计算一下鼠标到每个圆的圆心距离,如果这个距离小于圆的半径,我们就可以确定鼠标在某个圆内部了。如果我们要绘制的图形不是圆、矩形这样的规则图形,而是一个复杂得多的多边形,我们又该怎样确定鼠标在哪个图形元素的内部呢?这对于 Canvas 来说,就是一个 比较复杂的问题了。
虽然使用 SVG 绘图能够很方便地实现用户交互,但是有得必有失,SVG 这个设计给用户交互带来便利性的同时,也带来了局限性。为什么这么说呢?因为它和 DOM 元素一样,以节点的形式呈现在 HTML 文本内容中,依靠浏览器的 DOM 树渲染。如果我们要绘制的图形非常复杂,这些元素节点的数量就会非常多。而节点数量多,就会大大增加 DOM 树渲染和重绘所需要的时间。
就比如说,在绘制如上的层次关系图时,我们只需要绘制数十个节点。但是如果是更复杂的应用,比如我们要绘制数百上千甚至上万个节点,这个时候,DOM 树渲染就会成为性能瓶颈。事实上,在一般情况下,当 SVG 节点超过一千个的时候,你就能很明显感觉到性能问题了。
幸运的是,对于 SVG 的性能问题,我们也是有解决方案的。比如说,我们可以使用虚拟 DOM 方案来尽可能地减少重绘,这样就可以优化 SVG 的渲染。但是这些方案只能解决一 部分问题,当节点数太多时,这些方案也无能为力。这个时候,我们还是得依靠 Canvas 和 WebGL 来绘图,才能彻底解决问题。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。