在具體項目開發時,咱們常常會使用canvas繪製圖表、豐富的圖形、動畫交互等應用場景。
本篇文章總結概括一下本身在canvas開發的認知和可能遇到的常見問題。
至於基礎知識和常見API使用,你們自行學習,這裏不予複述。javascript
HTML5 <canvas>
元素用於圖形的繪製,生成圖形容器,必須經過JS腳原本完成。
canvas繪圖時相對於一根畫筆,每一步驟的繪製是基於當前狀態的(位置、顏色等)
canvas api文檔canvas.width/height
:用來控制Canvas畫布繪製區域的寬高。需根據實際屏幕環境設置,不然繪製的圖形會失真。canvas.getContext()
:返回canvas的繪製上下文, 實際繪製依賴於該對象。canvas.toBlob()
:能夠對Canvas圖像生成對應的Blob對象。canvas.toDataURL()
:返回base64 data圖片數據。css
<canvas>
標籤HTML5 中的<canvas>
標籤只會生成圖形容器,圖形繪製必須經過JS腳原本完成。html
<canvas width="600" height="300" style="width: 300px; height: 150px"></canvas>
複製代碼
當使用Canvas API繪製圖形時使用的座標、尺寸大小是基於Canvas寬高屬性的,而與CSS樣式寬高無關。
而CSS寬高則決定canvas圖形的視覺顯示大小,canvas畫布的寬高會等比例縮放成CSS寬高顯示。java
實際使用時,儘可能避免這種因尺寸不一致比例縮放渲染,致使的圖形模糊、鋸齒化等問題。canvas
canvas.getContext()
:返回canvas的繪製上下文, 實際繪製依賴於該對象。
本文主要討論二維平面繪製:const context =``canvas.getContext('2d');
api
實際畫布繪圖時每一路徑的繪製過程是基於繪製上下文的當前狀態的。
繪製過程至關於只使用一根畫筆做畫,每一次路徑的繪製點、顏色等樣式是基於繪製上下文的當前狀態(位置、顏色、粗細等)。
對於非連續的路徑繪製,繪製過程要用context.beginPath()
聲明,這樣可使繪製樣式不會被覆蓋影響。跨域
好比:bash
context.strokeStyle = 'blue';
context.moveTo(10, 10);
context.lineTo(100, 10);
context.stroke();
context.strokeStyle = 'red';
context.moveTo(100, 10);
context.lineTo(10, 50);
context.stroke();
複製代碼
樣式被覆蓋,繪製效果以下:
iphone
context.beginPath()
聲明路徑,代碼:
context.beginPath();
context.strokeStyle = 'blue';
context.moveTo(10, 10);
context.lineTo(100, 10);
context.stroke();
context.beginPath();
context.strokeStyle = 'red';
context.moveTo(100, 10);
context.lineTo(10, 50);
context.stroke();
複製代碼
則兩條線的樣式不受影響,繪製效果以下:
函數
這個在不少時候須要在繪製時獲取當前畫布信息時很是有用。CanvasRenderingContext2D.canvas
是一個只讀屬性,能夠返回當前上下文源自哪一個<canvas>
元素,並獲取畫布的width/height等屬性信息。
在實際開發時常常會使用到的api,清除制定的繪製區域,不然在同一區域重複繪製會發生圖形疊加。
Canvas和SVG是當前HTML5中主要使用的圖形繪製技術,前者提供畫布標籤和繪製API,後者是一整套獨立的矢量圖形語言,使用 XML 格式定義圖像。
<canvas>
標籤上,但經過事件委託,能夠細化到像素點(x,y)的交互;SVG則能夠爲某個元素附加 單獨的JavaScript 事件處理器,但也只能控制細化在圖形元素上。Canvas適用場景:適合像素處理,動態渲染和大數據量繪製; 適合圖像密集型的遊戲;
SVG適用場景:適合靜態圖片展現,高保真文檔查看和打印的應用場景。
以下圖:
關於移動端高清屏DPR、圖片模糊、移動端適配等問題,不清楚的童鞋能夠參考「關於移動端適配,你必需要知道的」這篇文章,講的比較詳細。這裏再也不贅述,本文章只處理移動端Canvas模糊問題。
在移動端高清屏幕上,常常會遇到Canvas圖形模糊的問題。本質上跟移動端圖片模糊問題是同樣的。canvas繪製成的圖像跟也是位圖,在dpr > 1
的屏幕上,位圖的一個像素可能由多個物理像素來渲染,然而這些物理像素點並不能被準確的分配上對應位圖像素的顏色,只能取近似值,因此在dpr > 1
的屏幕上就會模糊。
在PC端繪製canvas圖形,咱們都直接把1個canvas像素直接等於1px的css像素處理,這沒有問題,應該目前PC端屏幕dpr都是1。而在dpr > 1
的移動端屏幕上就不能直接這樣處理。
解決方案固然仍是從dpr入手。
window.devicePixelRatio
獲取當前設備屏幕的dpr;dpr = 2
時至關於擴大畫布2倍;context.scale(dpr, dpr)
縮放Canvas畫布的座標系。在dpr = 2
時至關於把canvas座標系也擴大了兩倍,這樣繪製比例放大了2倍,以後canvas的實際繪製像素就能夠按原先的像素值處理。在渲染到屏幕時,擴大的畫布圖形又等比例縮放渲染到canvas容器中。從而保證canvas圖形的質量。
// 獲取dpr
const dpr = window.devicePixelRatio;
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
// 獲取Canvas容器的寬高
const { width: cssWidth, height: cssHeight } = canvas.getBoundingClientRect();
// 根據dpr,設置Canvas的寬高,使1個canvas像素和1個物理像素相等
canvas.width = dpr * cssWidth;
canvas.height = dpr * cssHeight;
// 根據dpr,設置canvas元素的寬高屬性
ctx.scale(dpr,dpr);
複製代碼
canvas的drawImage() 函數有個特別容易混淆搞錯的地方。它的5參數和9參數用法的參數位置是不一樣的。實際開發中沒注意到這一點,會讓本身特別困惑問題出在哪!汗!
drawImage()方法有一個很是怪異的地方,你們必定要注意,那就是5參數和9參數用法的參數位置是不同的,這個和通常的API有所不一樣。通常API可選參數是放在後面。可是,這裏的drawImage()使用9個參數時候,可選參數sx,sy,sWidth和sHeight是在前面的。若是不注意這一點,有些表現會讓你沒法理解。
且drawImage()函數插入的圖形在移動端dpr >1
屏幕一樣會有圖片模糊的問題。
在移動端經過drawImage()載入另外一個已繪製的Canvas元素時,也要注意對另外一個canvas元素作兼容處理,還須要注意二者座標系的不一樣。
// 設置canvas_bg寬高
canvas_bg.width = (config.unit * (scale_len - 1) + config.width) * dpr;
canvas_bg.height = config.height * dpr;
ctx_bg.scale(dpr, dpr);
...
// 初始化開始位置
point_x = (config.def - config.start) / config.capacity * config.unit;
//在主畫布ctx上,經過drawImage()插入另外一個canvas_bg畫布;
ctx.drawImage(canvas_bg, point_x * dpr, 0, config.width * dpr, config.height * dpr, 0, 0, config.width, config.height);
複製代碼
上面的代碼中, canvas_bg畫布一樣須要處理上面提到的canvas模糊問題;在主畫布ctx上,經過drawImage()插入另外一個canvas_bg畫布圖形時,須要注意此時二者座標系比例的不一樣,此時canvas_bg的座標系是根據dpr縮放後的。
在實際開發中遇到,canvas繪製尺寸或drawImage插入圖像、getImageDate獲取圖形資源等尺寸大於某個閾值時,渲染出來的圖片整個都是空白。這個具體的閾值不肯定,跟運行環境有關。但這應該也是drawImage繪製的一個不知什麼時候爆發的隱患。
好比下圖,繪製的刻度尺畫布尺寸過大,截取後渲染到主畫布上,整個刻度空白,但不影響交互。
只要可以在網頁中正常顯示出來的跨域圖片,就可使用canvas的drawImage()
API繪製出來。可是若是想經過getImageData()
方法獲取圖片的完整的像素信息,轉換成本地輸出時,則多半會出現跨域問題。
解決:
1. 是頁面與服務端開啓容許跨域;
2. 給圖片設置容許跨域,`img.setAttribute('crossOrigin', 'anonymous');`
複製代碼