最近要作一個生成海報的h5, 原理就是用canvas
的drawImage
API把圖片畫出來,想着應該很簡單,卻發現裏面有大坑。在填完坑後分享下解決方案,文章主要圍繞如下兩個問題來展開。css
<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
access-control-allow-origin:*
因爲我這裏使用的是阿里雲的oss,這裏就用圖說明下該如何操做(沒有阿里雲oss的也能夠本身用node進行轉發) web
而後又出現了問題!$bg.crossOrigin = ''; // 跨域設置,這裏不用設置爲`Anonymous`也是能夠的
複製代碼
而後圖片就能夠出來了
chrome
緣由:須要繪製的圖片尺寸(
1242*2208
)遠大於咱們的屏幕尺寸iphone 6sp414*736
,所以猜測把canvas
的css
大小設置爲咱們屏幕的大小,這樣繪製應該就是整個屏幕的區域了。canvas
// css定寬高爲全屏
$JPoster.style.width = winW + 'px';
$JPoster.style.height = winH + 'px';
複製代碼
而後在chrome的模擬器下圖片大小顯示一切正常
跨域
由於
canvas
不是矢量圖,而是像圖片同樣是位圖模式的。高dpi
顯示設備意味着每平方英寸有更多的像素。也就是說二
倍屏,瀏覽器就會以2
個像素點的寬度來渲染一個像素,該canvas
在Retina
屏幕下至關於佔據了2
倍的空間,至關於圖片被放大了一倍,所以繪製出來的圖片文字等會變模糊。 所以,要作Retina
屏適配,關鍵是知道當前屏幕的設備像素比,而後將canvas
放大到該設備像素比來繪製,而後將canvas
用css
設置爲屏幕的大小來展現。瀏覽器
解決思路: 在瀏覽器的 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);
複製代碼
而後咱們的圖片終於正常繪製出來了,手機上也顯示清晰。
一是咱們繪製的圖片大小要控制好,太大就去壓縮一下,否則生成的base64太大,繪製時間長,還可能會出錯。二是瀏覽器上標題欄會佔據高度,致使窗口大小比例是不對的,生成的圖片會發生變形,須要注意處理一下,就不展開說了。