Canvas在图像处理、绘制渲染上有一些得天独厚的优势。但是当我们当前展示的内容中在主题内容变化不大的情况下,会有一些小部分内容的变化,在页面刷新或者滚动的时候,一帧中会有很多复杂内容元素的图画运算,重新对页面元素绘制会导致CPU使用率飙升。
而重新绘制的过程,实质上是一个不断刮白-重画的过程。但在屏幕上完成这一系列操作是需要一定时间的,而且屏幕上的图形越复杂,所花的时间就越长,我们肉眼可见的刮白-重画操作,在使用过程中就会让就会直接感觉到屏幕的闪烁。
重绘带来的性能负担和闪烁的问题,会给使用者带来较差的使用体验。为了更好的优化这个两个问题,出现了双缓存画布和油画分层的绘制方法。而本节内容我们也将从电子表格技术出发,为大家揭秘在电子表格技术中双缓存与优化技术的具体应用。
双缓存画布现在我们有一幅图需要放在Canvas中,使用drawImage()方法,有三种写法:
// 将image放到目标canvas指定位置 void ctx.drawImage(image, dx, dy); // 将image放到目标canvas指定位置,指定宽高渲染 void ctx.drawImage(image, dx, dy, dWidth, dHeight); // 将image裁剪之后放到目标canvas指定位置,指定宽高渲染 void ctx.drawImage(image, sx, sy, sWidth, sHeight, dx, dy, dWidth, dHeight);第一种方法只是把图片原样放到Canvas中,第二种方法指定宽高就意味着放大或者缩小图片后再放进去,第三种是将图片裁剪后再放大或者缩小放到canvas中,这三种写法操复杂度作依次增加,性能开销也随之增大。
而如果使用离屏渲染(即我们所说的双缓存画布),我们可以预先把图片裁剪成想要的尺寸,然后将该内容保存起来,绘制的时候直接使用第一种写法直接将图片放入Canvas中。
// 在离屏 canvas 上绘制 var offscreencanvas = document.createElement('canvas'); // 宽高赋值为想要的图片尺寸 offscreencanvas.width = dWidth; offscreencanvas.height = dHeight; // 裁剪 offscreencanvas.getContext('2d').drawImage(image, sx, sy, sWidth, sHeight, dx, dy, dWidth, dHeight); // 在视图canvas中绘制 viewcontext.drawImage(canvas, x, y);双缓存画布技术的核心在于系统需要在内存中开辟一块与当前画面等大的“逻辑屏幕“。我们的画图和动画操作都会先作用于这块”逻辑屏幕“中,当一个操作在这块”逻辑屏幕“上完成之后,再把整块”逻辑屏幕“投放到我们的屏幕上。
(离屏渲染原理示意图)
在这样的过程之下,我们是无法看到整个图形在屏幕上的重绘过程,从而解决了闪烁问题。就好像看动漫一样,不用双缓存技术,就是画一帧看一帧,肯定会卡顿。而用了双缓存技术,会事先把每一帧画好,不断翻动展示出来。
(逐帧动画)
Canvas为此提供了OffscreenCanvas方法,用来构建一个可以脱离屏幕渲染的canvas对象,它在窗口环境和web worker环境均有效。对于一些渲染,如果创建 Image 再进行渲染,会消耗大量 CPU,但用离屏渲染,实测在高频事件中 CPU 使用率减少了一倍之多。
分层渲染来处理画面动画的思路并不是现在才有的,从非物质文化遗产皮影戏、套色印刷技术,到现在的音影工业等众多领域都有频繁出现, 而这种思想在Canvas中也处在基石的地位。
(分层渲染原理示意图)
Canvas分层的思想是,动画中每种元素,对渲染和动画的要求是不一样的。
用下图举个例子,在这张图片中除了猫本身在运动外,背景以及下方的文字都是静止重复的。
(油画分层机制示意图)
按照分层的逻辑,我们需要频繁更新绘制的只有最上方的猫咪。这个方法类似油画的绘制,所以也被称为油画分层机制。使用这个方法结合双缓冲技术可以有效的将重复绘制的内容分流到屏幕外的画布上,然后再根据我们的需求将屏幕外图像渲染到主画布上,省去了频繁生成重复部分的步骤。
技术应用落地