详解双缓冲绘图机制 HTML5-canvas绘图黑屏白屏解决方案

HTML5-绘图黑屏白屏解决方案(详解双缓冲绘图机制)
使用绘图的前端工程师可能都会遇到过绘图黑屏、白屏的问题 。以前写过一篇文章描述过下黑屏的一种可能性:
那么如果我们面对的场景就是移动端各大浏览器呢?当出现黑屏、白屏的时候就只能认栽了吗?不是的 , 还有很多优化工作是我们可以做的 。在这里本文就将详细分析绘图策略中最伟大的方法:双缓冲绘图机制 。
百度百科搜索双缓冲技术绘图 , 解释如下:双缓冲即在内存中创建一个与屏幕绘图区域一致的对象 , 先将图形绘制到内存中的这个对象上 , 再一次性将这个对象上的图形拷贝到屏幕上 , 这样能大大加快绘图的速度 。拿html5-场景来举例:就是我们创建两个相同的画布 , 一张画来打草稿(在内存中的虚拟节点上绘制) , 一张真正画给观众看(真正的页面dom元素绘制) 。
【详解双缓冲绘图机制HTML5-canvas绘图黑屏白屏解决方案】介绍完了背景知识 , 那这种技术到底能做什么?说了那么多和本文的主题解决黑屏白屏问题又有什么关联?
·黑屏
先来讨论黑屏的情况 。我们知道 , 移动设备上呈现出黑屏很大一部分原因就是绘图的计算量太大 , 导致设备显示出现问题 。那么最直接的解决方案就是减少计算量(或者称为优化) 。
正常情况下 , 我们会为页面添加一个元素 , 并且一边算各种图形坐标 , 一边一点点的绘制上去 。在这个过程当中我们就损耗了相当大的性能 , 每执行一次.fill()或者.() , 图形都会被渲染一次 。而利用双缓冲机制 , 我们这样处理:为页面添加一个元素 , 同时创建另一个一个一模一样的元素 , 但是不添加到页面dom树(也就是存在内存中) , 这时我们在内存中的元素上一边算图形坐标一边绘制 , 当绘制完成时直接调用的复制方法(.)一次性将“草稿画布”上的图形绘制到真正的页面元素上 , 通过这样的技巧 , 我们将会节省大量页面绘制的性能损耗 , 不但能解决黑屏的问题 , 还能缩短图形绘制的时间 。这里有一篇可以说明虚拟画布和普通画布差异的文章:
下面我们来描述一下如何实现上文所述的双缓冲机制 。
先来看一段很普通的绘制一个正方形的代码:
//获取页面中的canvas画布容器 , 通常为一个divvar container=document.getElementById("container");//创建一个真实的画布 , 他将呈现给用户var realCanvas=document.createElement("canvas");//设置realCanvas的width/height属性 , 乘以二以防止像素点模糊问题realCanvas.width=container.clientWidth*2;realCanvas.height=container.clientHeight*2;//设置realCanvas的样式width/height属性 , 填充满父元素realCanvas.style.width=container.clientWidth+"px";realCanvas.style.height=container.clientHeight+"px";container.appendChild(realCanvas);//开始绘制var realContext=realCanvas.getContext("2d");realContext.fillRect(0,0,100,100);
下面一段代码与前面的代码功能完全相同 , 但是实现时使用了双缓冲绘图机制:
//获取页面中的canvas画布容器 , 通常为一个divvar container=document.getElementById("container");//创建一个真实的画布 , 他将呈现给用户var realCanvas=document.createElement("canvas");//设置realCanvas的width/height属性 , 乘以二以防止像素点模糊问题realCanvas.width=container.clientWidth*2;realCanvas.height=container.clientHeight*2;//设置realCanvas的样式width/height属性 , 填充满父元素realCanvas.style.width=container.clientWidth+"px";realCanvas.style.height=container.clientHeight+"px";container.appendChild(realCanvas);//创建一个虚拟节点cacheCanvas , 我们不会将他添加到页面上var cacheCanvas=document.createElement("canvas");//设置cacheCanvas的width/height属性 , 和realCanvas的完全相同cacheCanvas.width=container.clientWidth*2;cacheCanvas.height=container.clientHeight*2;//开始绘制 , 获取真实节点和虚拟节点的上下文contextvar realContext=realCanvas.getContext("2d");var cacheContext=cacheCanvas.getContext("2d");/** 这次试用双缓冲技术 , 我们在cacheCanvas中绘制 , * 但此时什么都看不到 , 因为cacheCanvas没有添加到页面上*/cacheContext.fillRect(0,0,100,100);/** 在缓冲区完成绘制之后 , 我们将缓冲区的内容一次性绘制到页面上*/realContext.drawImage(cacheCanvas,0,0);