关于canvas在retina屏下绘制文字或图像模糊的解决方案(转)
原文地址:http://blog.csdn.net/laijingyao881201/article/details/39505043
一、问题描述
最近在鼓捣canvas的时候,发现绘制在canvas上的文字(或图片)在retina屏幕上会出现显示模糊的问题,感觉很不爽,于是就Google了一番,还真发现了一个解决方案。有兴趣的同学可以去读一下原文,我在这里简单的记录一下。
先看一下Deom页面,对比一下前后两个效果,请务必在配有retina屏幕的设备上浏览(iOS6下的safari除外)。否则是看不到效果的。
二、问题分析
熟悉retina屏的同学应该都知道,在浏览器的window变量中有一个devicePixelRatio的属性,该属性决定了浏览器会用几个(通常是2个)像素点来渲染1个像素,举例来说,假设devicePixelRatio的值为2,一张100x100像素大小的图片,在retina屏幕下,会用2个像素点的宽度去渲染图片的1个像素点,因此该图片在retina屏幕上实际会占据200x200像素的空间,相当于图片被放大了一倍,因此图片会变得模糊。
知道了这一点,关于canvas的问题也就容易理解了。我们可以把canvas当成是一张图片,当浏览器去渲染canvas的时候,他会用2个像素点的宽度去渲染canvas,因此在大多数retina设备的浏览器中会出现绘制的图片或文字变模糊的情况。
请注意,我说的是大多数,难道还有例外?
没错,在iOS6下的safari会正常显示。
看来,我们需要再往深了刨。
在window中有一个devicePixelRatio的属性,类似的,在canvas context中也存在一个webkitBackingStorePixelRatio的属性(仅safari和chrome),该属性的值决定了浏览器在渲染canvas之前会用几个像素来来存储画布信息。在iOS6下的safari中的值是2,因此,如果将一张100x100像素的图片绘制到safari中,该图片首先会在内存中生成一张200x200的图片,然后浏览器渲染的时候,会按100x100的图片来渲染,因此就变成了200x200,正好和内存中的图片大小一致,因此在iOS的safari中不会出现失真的问题。但是在chrome和iOS7的safari中却出现了失真,其原因是,chrome和iOS7中的safari的webkitBackingStorePixelRatio值都是1。值得一提的是在iOS7中,苹果把webkitBackingStorePixelRatio的值置为了1,目的是处于性能的考虑,这一点在WWDC 2013中找到,感兴趣的同学可以异步What’s New in Safari and WebKit for Web Developers,搜索关键字‘backing’。
三、如何解决
扯了半天,还没有说怎么解决呢。其实方案很简单,也很容易明白。我们可以创建一个两倍于实际大小的canvas,然后用css样式把canvas限定在实际的大小。具体实现方法可以参看我Demo中的代码,或者直接使用github上的这个polyfill。
解决办法,我们只需要得到 devicePixelRatio 和 webkitBackingStorePixelRatio(不同浏览器叫法不一,见代码),相除,得到一个比例,然后画图的时候,宽和高都乘以这个比例就好了。 var devicePixelRatio = window.devicePixelRatio || 1; var backingStorePixelRatio = ctx.webkitBackingStorePixelRatio || ctx.mozBackingStorePixelRatio || ctx.msBackingStorePixelRatio || ctx.oBackingStorePixelRatio || ctx.backingStorePixelRatio || 1; var ratio = devicePixelRatio / backingStorePixelRatio; 但是在cocos2d js中,我们是不会直接用ctx 直接调用drawImage这个方法的。cocos都帮我们封装好了。所以就要修改下源码了。 在cocos2d-html5/cocos2d/下找到CCDrawingPrimitivesCanvas.js。打开这个文件,309行找打drawImage方法。加入以下代码 var ctx = this._renderContext; (function (canvas, ctx) { var devicePixelRatio = window.devicePixelRatio || 1; var backingStorePixelRatio = ctx.webkitBackingStorePixelRatio || ctx.mozBackingStorePixelRatio || ctx.msBackingStorePixelRatio || ctx.oBackingStorePixelRatio || ctx.backingStorePixelRatio || 1; var ratio = devicePixelRatio / backingStorePixelRatio; if (devicePixelRatio !== backingStorePixelRatio) { sourceSize.width *= ratio; sourceSize.height *= ratio; ctx.scale(ratio, ratio); } })(canvas, ctx);