解決canvas合成圖片大小錯誤、模糊以及跨域的問題

最近要作一個生成海報的h5, 原理就是用canvasdrawImageAPI把圖片畫出來,想着應該很簡單,卻發現裏面有大坑。在填完坑後分享下解決方案,文章主要圍繞如下兩個問題來展開。css

  • 繪製圖片會有跨域的問題
  • 生成的圖片、文字大小不正確,還會模糊,不清晰

1. demo初試

<style>
.J_ret_poster {
    position: fixed;
    z-index: 999;
    top: 0;
    right: 0;
    bottom: 0;
    left: 0;
}
</style>

 <canvas hidden id="J_poster">你的瀏覽器版本較低,請換個瀏覽器試試</canvas>
 
 <script>
    const $ = q => document.querySelector(q),
        $JPoster = $('#J_poster'),
        $btn = $('J_btn'),
        ctx = $canvas.getContext('2d'),
        $bg = new Image(), // 須要的背景圖片(海報底圖)
        $retImg = new Image(), // 最終生成的圖片
        winW = window.innerWidth,
        winH = window.innerHeight

    // 設置canvas的大小爲全屏
    $JPoster.setAttribute('width', winW); 
    $JPoster.setAttribute('height', winH);
    
    // 設置生成的圖片相關信息
    $retImg.setAttribute('width', winW);
    $retImg.setAttribute('height', winH);
    $retImg.setAttribute('alt', '海報');
    $retImg.setAttribute('class', 'J_ret_poster');
    
    $bg.src = 'http://canvas-img.oss-cn-shenzhen.aliyuncs.com/normal.jpg';
    $bg.onload = () => {
        ctx.drawImage($bg, 0, 0, winW, winH); // 從窗口的左上角開始畫起,鋪滿整個屏幕
        ctx.font = '14px Arial';
        ctx.fillStyle = '#fff';
        ctx.fillText('這是test測試文字123', 100, 100);
        
        const retUrl = $JPoster.toDataURL('image/png'); // 生成的圖片url
        $retImg.setAttribute('src', retUrl);
        $retImg.onload = () => {
            $('body').appendChild($retImg); // 添加到body下
        }
    }
 </script>
複製代碼

運行以後出現錯誤node

1.1 出現圖片跨域問題

cors-error
報了一個跨域的錯誤

1.2 爲圖片添加跨域請求頭 access-control-allow-origin:*

因爲我這裏使用的是阿里雲的oss,這裏就用圖說明下該如何操做(沒有阿里雲oss的也能夠本身用node進行轉發) web

ali-set-cors
而後又出現了問題!
toDataURL-error
這裏是相關說明

1.3 爲圖片添加crossOrigin屬性

$bg.crossOrigin = ''; // 跨域設置,這裏不用設置爲`Anonymous`也是能夠的
複製代碼

而後圖片就能夠出來了
chrome

size-big

這時又出現了問題,圖片尺寸不對

2. canvas大小錯誤以及模糊

2.1 大小錯誤

緣由:須要繪製的圖片尺寸(1242*2208)遠大於咱們的屏幕尺寸iphone 6sp414*736,所以猜測把canvascss大小設置爲咱們屏幕的大小,這樣繪製應該就是整個屏幕的區域了。canvas

// css定寬高爲全屏
$JPoster.style.width = winW + 'px';
$JPoster.style.height = winH + 'px';
複製代碼

而後在chrome的模擬器下圖片大小顯示一切正常
跨域

pc-display


可是,在手機上又出現了新的問題,圖片和文字都是 模糊的!!(看不出模糊的能夠和後面的一張圖對比看看)

mobile-display

2.2 繪製模糊

由於 canvas 不是矢量圖,而是像圖片同樣是位圖模式的。高dpi 顯示設備意味着每平方英寸有更多的像素。也就是說倍屏,瀏覽器就會以2個像素點的寬度來渲染一個像素,該 canvasRetina 屏幕下至關於佔據了2倍的空間,至關於圖片被放大了一倍,所以繪製出來的圖片文字等會變模糊。 所以,要作 Retina 屏適配,關鍵是知道當前屏幕的設備像素比,而後將 canvas 放大到該設備像素比來繪製,而後將 canvascss設置爲屏幕的大小來展現。瀏覽器

解決思路: 在瀏覽器的 window 對象中有一個 devicePixelRatio 的屬性,該屬性表示了屏幕的設備像素比,即用幾個像素點寬度來渲染1個像素。app

相似的,在 canvas context 中也存在一個 backingStorePixelRatio 的屬性,該屬性的值決定了瀏覽器在渲染canvas以前會用幾個像素來來存儲畫布信息。 backingStorePixelRatio 屬性在各瀏覽器廠商的獲取方式不同,因此須要加上瀏覽器前綴來實現兼容。cors

這裏是引用來源iphone

代碼實現:

let devRatio = window.devicePixelRatio || 1, // 獲取設備像素比
        // ctx的像素比
        backingStore = ctx.backingStorePixelRatio ||
        ctx.webkitBackingStorePixelRatio ||
        ctx.mozBackingStorePixelRatio ||
        ctx.msBackingStorePixelRatio ||
        ctx.oBackingStorePixelRatio ||
        ctx.backingStorePixelRatio || 1;
  
    const ratio = devRatio / backingStore;
    // canvas放大像素比倍
    $JPoster.setAttribute('width', winW * ratio);  
    $JPoster.setAttribute('height', winH * ratio);
    // canvas 放大後,相應的繪製圖片也要放大
    ctx.scale(ratio, ratio);
複製代碼

而後咱們的圖片終於正常繪製出來了,手機上也顯示清晰。

draw-right

3. 其餘須要注意的點

一是咱們繪製的圖片大小要控制好,太大就去壓縮一下,否則生成的base64太大,繪製時間長,還可能會出錯。二是瀏覽器上標題欄會佔據高度,致使窗口大小比例是不對的,生成的圖片會發生變形,須要注意處理一下,就不展開說了。

相關文章
相關標籤/搜索