前端工程师的画图日记

本文作者为 360 奇舞团前端开发工程师

作为一个前端,我先讲讲我是如何对前端产生兴趣的。当初接触 Web 页面开发时,发现网页这东西很神奇,在一个 Text 文本编辑器里敲上几行代码,改个扩展名,双击页面就展示出来了就是很有趣且很有成就感,一点点堆砌自己的代码,一个属性就可以改颜色改大小,可以做一个炫酷的特效等等。之前看到过一句话,说CSS就是网页版的亚洲四大邪术集合体。那么我今天的分享就从这里开始,介绍一下前端工程师如何画图。

在 Web 上,图形通常是通过浏览器绘制的。现代浏览器是一个复杂的系统,其中负责绘制图形的部分是渲染引擎。渲染引擎绘制图形的方式,我总结了一下,大体上有4种。

1.传统的 HTML+CSS

这种方式通常用来呈现普通的 Web 网页。实际上,现代浏览器的 HTML、CSS 表现能力很强大,完全可以实现常规的图表展现,和一些有趣的特效及动画。那css如何作画呢?

我们先来看两张图,猜一猜哪个是css画的?

51683941a23752a292ab4dcb23317a88.jpeg

如果你猜两个都是,恭喜你,猜对啦~ 这两幅画都是出自css的手笔。

第一位古典女性肖像(下文中简称为贵妇),作者Diana使用HTML和CSS创作了受Flemish和巴洛克风格启发的肖像。这张图片的实现需要数千行代码,她有一个严格的规则,所有的元素都必须手动绘制,创作过程中不用SVG,只用Atom文本编辑器和Chrome开发者工具。这意味着她不依赖库、快捷键或某种可视化编辑器。也就是说,画面上的每一条曲线和渐变、每一处高光和阴影、每一根头发和睫毛、每一片蕾丝和褶皱,都是一行行代码从头敲出来的。曲线、光影、渐变,每个元素都相当复杂。
如此精细程度和创造力,让学美术的网友感叹“学画画不如写代码”,让学计算机的同学觉得“别人写的这么艺术,一定是我的教科书打开方式不对”。
这个项目也一度登上了GitHub Trending排行榜第二名。

贵妇的睫毛部分代码实现:0b46010af55d1280fc5cfa18a8d5b860.png

第二张图片的水杯也有异曲同工之妙,整篇实现下来也是border-radius,box-shadow,transform等一点点画出来的。可以说是十分的瞳孔地震了。

从以上的例子我们可以看到html+css 可以非常方便地实现一些简单的图形,但是有利有弊:

优点:
  1. 一些简单的可视化图表,用 CSS 来实现很有好处,既能简化开发,又不需要引入额外的库,可以节省资源,提高网页打开的速度。

  2. 理解 CSS 的绘图思想对于可视化也是很有帮助的,比如,CSS 的很多理论就和视觉相关,可视化中都可以拿来借鉴。

缺点:
  1. HTML 和 CSS 主要还是为网页布局而创造的,在数据与图形的对应关系上,这种方式并不清晰,维护成本较高。

  2. 另外 css 是页面渲染的一部分,css 发生变化,可能对性能造成一定的影响。css 涉及的东西非常复杂,需要的性能开销较大,用来做绘图任务开销相对较大。

  3. dom对于非矩形元素支持复杂。举例,如果我们需要在图表上表现圆形、椭圆形、直线、不规则多边形等等,传统dom实现将变得比较复杂。像饼图、折线图这种,单用dom元素模拟就显得比较啰嗦。即便实现起来,也非常不直观。所以这种方式只适用于一些较为简单的图形。

2.svg

SVG 和传统的 HTML+CSS 的绘图方式差别不大。只不过,HTML 元素在绘制矢量图形方面的能力有些不足,而 SVG 恰好弥补了这方面的缺陷。一个简单的 SVG 文档由根元素和基本的形状元素构成,如用于定义圆形、矩形曲线的元素,可以绘制贝塞尔曲线。
先看一个简单的例子,使用标签:

<svg width="300" height="300" style="border: 1px solid red;">
  <circle
    cx="60"
    cy="80"
    r="50"
  >
  </circle>
</svg>

我们将会得到一个圆形:
8be31ac887df655a978667ce5153bf91.png

从以上例子可以看出,SVG 绘制图表与 HTML 和 CSS 绘制图表的方式差别不大,只不过是将 HTML 标签替换成SVG 标签,运用了一些 SVG 支持的特殊属性。HTML 的不足之处在于 HTML 元素的形状一般是矩形,虽然用 CSS 辅助,也能够绘制出各种其它形状的图形,甚至不规则图形,但是总体而言还是非常麻烦的。而 SVG 则弥补了这方面的不足,让不规则图形的绘制变得更简单了。因此,用 SVG 绘图比用 HTML 和 CSS 要便利得多。

优点:

svg 是一种基于 xml 语法的图像格式,与 html 有点像。

  1. 图片是矢量图(内连位图除外),可缩放不失真。

  2. 作为专业绘图格式,它提供了很多图形元素,如圆、矩形、曲线等。

  3. 可以用img 元素的 src 属性加载。

  4. 可内连在html中,可以通过 js 进行操作,非常方便。

  5. 部分CSS可对SVG元素起作用。很多绘图的库也使用SVG作为默认的绘图渲染引擎。比如@antv/x6,d3等等。

缺点:
  1. svg 和 html 元素一样,在输出图形前都需要经过引擎的解析、布局计算和渲染器生成,一个 SVG 元素只表示一种基本图形,如果展示的数据很复杂,生成图形的 SVG 元素就会很多svg 元素很多,性能开销较大。大量的 SVG 元素不仅会占用很多内存空间,还会增加引擎、布局计算和渲染树生成的开销,降低性能,减慢渲染速度。这也就注定了 SVG 只适合应用于元素较少的简单可视化场景。

  2. 像 svg 这种声明式的图像表现形式,不利于编程及进行细节控制,精细图形及动画的开发成本较高。

3.canvas 画布

与 svg 声明式的绘制方式不同,canvas 它是浏览器提供的一种可以直接用代码在一块平面的“画布”上绘制图形的API,使用它来绘图更像是传统的“编写代码”,简单来说就是调用绘图指令,然后引擎直接在页面上绘制图形。
也就是说需要你告诉 canvas 怎么画,是一种指令式的绘制系统。标签只是图形容器,相当于一个画布,canvas 元素本身是没有绘图能力的。所有的绘制工作必须在 JavaScript 内部完成,相当于使用画笔在画布上画画。

那 Canvas 到底是怎么绘制可视化图表的呢?
  1. Canvas 在浏览器上创造一个空白的画布,通过提供渲染上下文,赋予我们绘制内容的能力。

getContext("2d") 对象是内建的 HTML5 对象,拥有多种绘制路径、矩形、圆形、字符以及添加图像的方法。 
getContext() 获取 canvas 的上下文环境
  1. 为了实现更加复杂的效果,Canvas 还提供了非常丰富的设置和绘图 API,我们可以通过操作上下文,来改变填充和描边颜色,对画布进行几何变换,调用各种绘图指令,然后将绘制的图形输出到画布。

canvas 创建图形有两种方式:
  1. context.fill()
    fill() 方法填充当前的图像(路径)。默认颜色是黑色。在填充前要先使用 fillStyle 设置填充的颜色或者渐变,并且如果路径未关闭,那么 fill() 方法会从路径结束点到开始点之间添加一条线,以关闭该路径(正如 closePath() 一样),然后填充该路径。

  2. context.stroke()
    stroke() 方法会实际地绘制出通过 moveTo() 和 lineTo() 方法定义的路径。默认颜色是黑色。在进行图形绘制前,要设置好绘图的样式

优点:
  1. Canvas直接把绘图API提供出来,给开发者的能力更多,能够直接操作绘图上下文。

  2. 同时,对于图形本身的封装以及事件等处理没有做更多的封装,不需要经过 HTML、CSS 解析、构建渲染树、布局等一系列操作。因此单纯绘图的话,Canvas 比 HTML/CSS 和 SVG 要快得多,更加高效。

缺点:
  1. canvas 渲染非常高效,但是由于它在页面中是一个独立的画布元素,所以我们很难像操作 dom 一样单独对局部进行精确的控制。通过数学计算我们是可以通过定位的方式来获取局部图形的,但是比较麻烦。

  2. 但是如果要绘制的图形太多,或者处理大量的像素计算时,Canvas2D 依然会遇到性能瓶颈。

4.webgl

webgl 是最复杂的一种绘图方式,是浏览器提供的功能强大的绘图系统,它使用比较复杂,但是功能强大,能够充分利用 GPU 并行计算的能力,来快速、精准地操作图像的像素,在同一时间完成数十万或数百万次计算。WebGL迄今发展不过10多年。但是已经经历了两代,基于OpenGL图形编程语言实现,可直接与 cpu GPU通信,可以控制到图形输出的每个细节,基于此,编写纯 WebGL 代码与常规的 JavaScript 不尽相同,api 相对来说更加底层,更加复杂,但无比强大。
理论上,Canvas2D已经足够快了。不过当以下几种情况,我们就需要更底层的技术了,适用于以下的场景:

  1. 绘制的图形数量非常多,

  2. 需要对图像的细节进行大量处理,如光影处理、流体效果等需要计算非常多的像素点,由于这些效果往往要精准地改变一个图像全局或局部区域的所有像素点,要计算的像素点数量非常的多(一般是数十万甚至上百万数量级的)。这时,即使采用 Canvas2D 操作,也会达到性能瓶颈。

  3. 它还内置了对 3D 物体的投影、深度检测等处理,这让它更适合绘制 3D 场景。

对于 HTML/CSS、SVG 和 Canvas,是对GPU程序的各种封装,我们不需要关心它们具体的底层机制,可直接使用其提供的标签和 API 来绘制图形,比如我们只要理解创建 SVG 元素的绘图声明,学会执行 Canvas 对应的绘图指令,就能够将图形输出。但是 WebGL 让我们直面底层的接口,只能够绘制点、线段、三角形等基本图元,想要利用 WebGL 完成更复杂任务,需要你提供合适的代码,组合使用点,线和三角形等代替实现。因此要使用 WebGL 绘图,我们必须要深入细节里。换句话说就是,我们必须要和内存、GPU 打交道,真正控制图形输出的每一个细节。当然,对于WebGL是有一定的库做封装的。如Three.JS、Babylon.js等等。我们可以用它们创建各种三维场景,包括了摄影机、光影、材质等各种对象,轻松地实现各种想要的几何体。

相关文章:
贵妇:https://diana-adrianne.com/purecss-francine/
水杯:https://codepen.io/ivorjetski/pen/xMJoYO
webgl基础:https://juejin.cn/post/6844903478456745997

参考文章:https://zhuanlan.zhihu.com/p/201424448

- END -

关于奇舞团

奇舞团是 360 集团最大的大前端团队,代表集团参与 W3C 和 ECMA 会员(TC39)工作。奇舞团非常重视人才培养,有工程师、讲师、翻译官、业务接口人、团队 Leader 等多种发展方向供员工选择,并辅以提供相应的技术力、专业力、通用力、领导力等培训课程。奇舞团以开放和求贤的心态欢迎各种优秀人才关注和加入奇舞团。

7cc01d8e8c85b8a002066e8fa31819d8.png

猜你喜欢

转载自blog.csdn.net/qiwoo_weekly/article/details/131798998