Canvas 在高清屏下繪製圖片變模糊的解決方法

以前我在 SF 上回答過「html5 canvas繪製圖片模糊的問題」,可是多是因爲我給出的答案過於簡略,加上答案中的 demo 連接已經失效,不少人反映這種辦法並很差使。可是我在給出答案以前是在小米2 和 iPhone 上測試過的,沒有任何問題。下面我會一步一步地描述具體的步驟。html

前提條件

假設咱們要在 canvas 中繪製一張 300 x 90 的圖片,而且要保證它在高清屏中不模糊。那麼咱們首先要準備一張 600 x 180 的圖片,處理太高清屏的同窗應該會有這方面的經驗。html5

問題重現

OK,咱們先把問題重現一下,以便有一個更直觀的瞭解。下面是相關的代碼(爲了簡單起見,我把代碼都寫在了 HTML 文檔裏面):git

<!-- 經過 img 標籤引入圖片,以便繪製到 canvas 中 -->
<img src="html5rocks.png" alt="" width="300" height="90">

<!-- canvas -->
<canvas width="300" height="90"></canvas>

<script>
    function init() {
        var canvas = document.querySelector('canvas');
        var ctx = canvas.getContext('2d');
        ctx.drawImage(document.querySelector('img'), 0, 0, 300, 90);
    }
    window.onload = init;
</script>

代碼很簡單,沒有作任何處理,具體的效果和完整的代碼能夠查看這個 DEMO,這個 demo 在高清屏的手機中會出現的問題:canvas 中的圖片變模糊了!。github

至於爲何會變模糊,這和瀏覽器處理 canvas 的方式有關,相關的文章能夠參考這篇 High DPI Canvas,這裏不做深刻介紹。web

解決問題

首先,引入 hidpi-canvas-polyfill

其實,不僅是繪製圖片時會出現模糊的問題,正常狀況下,在高清屏的設備中,任何繪製在 canvas 中的圖形(包括文字)都會出現模糊的問題。上面這個 polyfill 就是爲了解決這個問題,可是它沒有處理圖片。canvas

接下來,修改繪製圖片的代碼

init 函數修改爲下面這樣:segmentfault

function init() {
    var canvas = document.querySelector('canvas');
    var ctx = canvas.getContext('2d');

    // polyfill 提供了這個方法用來獲取設備的 pixel ratio
    var getPixelRatio = function(context) {
        var backingStore = context.backingStorePixelRatio ||
            context.webkitBackingStorePixelRatio ||
            context.mozBackingStorePixelRatio ||
            context.msBackingStorePixelRatio ||
            context.oBackingStorePixelRatio ||
            context.backingStorePixelRatio || 1;
    
        return (window.devicePixelRatio || 1) / backingStore;
    };

    var ratio = getPixelRatio(ctx);
    
    // 注意,這裏的 width 和 height 變成了 width * ratio 和 height * ratio
    ctx.drawImage(document.querySelector('img'), 0, 0, 300 * ratio, 90 * ratio);
}

能夠點擊這裏查看完整的代碼和效果,在高清屏的設備中打開,看看 cavans 中的圖片是否完美顯示。瀏覽器

說明

這個解決方案本質上和 @白一梓 的答案是同樣的:先放大 canvas,再用 CSS 將其限制回原始大小。app

若是你看了 polyfill 的代碼就會明白其中的原理了,這個 polyfill 的代碼十分簡短明瞭,它作了兩件事:一是將 canvas 的高和寬分別乘以 ratio 將其放大,而後又用 CSS 將高和寬限制成初始的大小;二是 hack canvas 中經常使用的函數,如:fillRect, clearRect, lineTo, arc 等,將它們的參數都乘以 ratio,以方便咱們能夠像之前那樣使用這些方法,而不用在傳參的時候手動乘以 ratio。函數

相關文章
相關標籤/搜索