最近幫學校官方微信做了一個頭像合成的小頁面,用戶可以自己選擇本地照片,上傳并裁剪后,與學校logo合成最終圖片,用戶可以長按保存到本地,同樣,我也把他放到了GitHub,效果大概下面這個樣子。因為有官方微信的推廣,點擊量分分鐘過5千,雖然是個很一般的東西,自己寫的代碼也很一般,都是看著朋友圈很多人用著通過自己寫的制作器做的頭像,感覺很棒哦。
這個頁面全部在前端完成,因為HTML5的強大,我沒寫一句后臺代碼,此外非常感謝這么優秀的開源項目Cropper.js,為我們提供了這么好的裁剪插件。但是在做的過程中,還是遇到了不少問題,特別是canvas繪的圖在手機等高分辨率設備上,會變模糊的問題,所以還是有必要總結一下。
Canvas繪圖API使用
關于canvas的其他知識自行查閱API,今天直接上手canvas繪制圖像。你需要知道
1.canvas的width和height 和css 的width和height不是一回事
可以看出canvas的css width,height分別為400px,畫布width,height為200,200是把css像素分成了200份,所以填充寬度為100,卻占了csswidth的1/2.
結論(假設cx指畫布長度單位)
- canvas畫布的默認寬高300*150
- css width和height決定你在屏幕上占多大空間,在你不設置的時候,默認和畫布大小一致
- 當你的css寬高和畫布寬高不一樣時,會有一個兌換關系,這會導致一些奇怪的問題。比如說css width:450px;height:300px; 畫布沒有設置的話。默認300cx150cx,所以水平方向1cx=450/300=1.5px 垂直方向1cx=300/150=2px 。所以你這個時候畫一個100100的正方形,畫出來的是1.5100,2100的長方形,人家本來是我們數學里學的標準坐標軸,你強行讓人家單位長度不同了。所以如果沒有必要,不要修改canvas的css width和height,這時候1cx=1px,而且水平方向和垂直方向是一樣的,如果你非要改,起碼讓他們比例一樣,即水平方向1cx等于幾個px,在垂直方向保持一致
- 在canvas里繪圖,用到的所有長度單位都是指cx.默認情況下,1cx=1px.
2.看繪圖上下文提供的相關API
context.drawImage(image,x,y)
x,y為畫布中畫圖的起始坐標,用此方法繪制出的圖像與原圖大小一致,即如果圖片尺寸為640320,那么即640cx320cx,默認cx=pxcontext.drawImage(image,x,y,w,h)
w,h指繪制出來的圖像的寬度與高度,比例與原圖寬高最好一致,圖像不會變形。-
context.drawImage(image,sx,sy,sw,sh,dx,dy,dw,dh);
此方法可用于繪制圖像的局部區域,sx,sy為局部區域的起始點坐標,sw,sh為局部區域的寬高。(sx,sy,sw,sh) 與(dx,dy,dw,dh)沒有關系,前者表示從哪里截取,取多大,后者表示你要在哪里放,在多大的地方放,可以放大或者縮小。如可以用于局部放大,實現特寫效果
上述方法中的image 均指image對象var img=new Image(); img.src="" //指定url img.onload=function(){ //繪制圖像 }
3.實現圖像組合
用canvas實現圖像組合也很簡單,可以通過設置圖形上下文的globalCompositeOperation 屬性來決定圖像組合模式
如我實現這個圖像合成的效果,我想要的logo不動,然后用戶圖像繪制在其下方。
其他模式還有很多,請自行查閱API
4.如何將canvas繪制的圖形保存成圖片
其實canvas繪制的任何東西都可以保存為圖片,代碼也很簡單
var oCanvas=document.getElementById("myCanvas");
var src=oCanvas.toDataURL("image/png") ;
//然后你就可以隨便怎么利用,你可以利用HTML5 a標簽 download屬性,也可以將其通過img標簽展示到
//頁面,然后長按/右鍵保存到本地
利用canvas在高清屏繪制圖像的大坑
一開始沒有發現這個問題,但是發給別人頭像制作器的鏈接,別人跟我說生成的頭像太模糊了,我自己試了下,的確很模糊,于是就百度了下,還好這是一個有目共睹的坑,所以一百度就找到了解決辦法。
出現這個問題的原因就是,在手機等高清屏設備上,存在一個devicePixelRatio,這個東西對于搞移動端web開發還是比較重要的,你在開發移動端網頁是,開頭常常會寫一個name為viewport的標簽也與這個東西有關。詳情請看我的移動端開發必須知道的viewport
解決辦法也很簡單,先通過hidpi-canvas-polyfill提供的函數獲得PixelRatio
var getPixelRatio = function(context) {
var backingStore = context.backingStorePixelRatio ||
context.webkitBackingStorePixelRatio ||
context.mozBackingStorePixelRatio ||
context.msBackingStorePixelRatio ||
context.oBackingStorePixelRatio ||
context.backingStorePixelRatio || 1;
return (window.devicePixelRatio || 1) / backingStore;
};
然后,將canvas畫布寬高先擴大PixelRatio 倍,然后再用css將寬高縮小到原來的寬高。經測試,當原圖比畫布大小大時,此方法比不用此方法清晰的多,當原圖大小與canvas大小一樣時,這個問題不是很明顯。