一、canvs基礎
1、繪圖環境
canvas 的能力是通過 context 對象表現出來的,context一般稱為繪圖環境。
const context = canvas.getContext(DOMString)
DOMString 為 "2d" 時,context 是CanvasRenderingContext2D
對象;(本文重點)
DOMString 為 "webgl" 時,context 是WebGLRenderingContext
對象;
DOMString 為 "webgl2" 時,context 是WebGL2RenderingContext
對象;
DOMString 為 "bitmaprenderer" 時,context 是ImageBitmap
對象;
2、canvas 尺寸問題
- 默認大小 300*150
- canvas 尺寸
注意 canva 元素實際上有兩套尺寸:元素大小E與繪圖表面大小S。
當使用 <canvas width='600' height='300' id='canvas'> 這種方式設置大小時,是同時設置了E和S為600*300;
當使用CSS設定canvas大小時,僅僅是設置了E,不會影響S(仍是300*150);
當S和E的大小不一致時,瀏覽器就會將S進行縮放,使之與E大小相同,這也會導致繪制在S上的內容跟著被縮放。這往往不是期待的結果,所以應該避免用CSS設置canvas大小 - 當CSS設置的寬高小于canvas直接設置的寬高,則繪制結果會被縮小;反之放大
3、canvas 自身的API
- 兩屬性
width: 繪圖表面寬度,height: 繪圖表面高度 - 三方法
getContext
toDataURL(type,quality),canvas 轉 data URL, 可賦值給 img 的 src
toBlob(callback,type),canvas 轉文件對象,可以以圖片文件格式保存
4、CanvasRenderingContext2D 屬性
CanvasRenderingContext2D 對象實例 instance 共有16個屬性,只要設置了這些屬性,就會影響 instance 調用繪制方法時的表現,常用的有:
- fillStyle & strokeStyle
設置填充和描邊 - font & textAlign & textBaseline
設置字體及水平和垂直對其方式 - lineCap & lineWidth & lineJoin & miterLimit
設置線段端點、線段屏幕像素寬度、線段交匯等繪制方式 - shadowBlur & shadowColor & shadowOffsetX & shadowOffsetY
設置陰影參數 - globalAlpha & globalCompsiteOperation
設置全局透明度,圖像合成方式
注意:可使用 instance 的 save() 和 restore() 方法來臨時修改 instance 的屬性
5、事件處理
- 鼠標事件中,需要注意坐標轉換
onmousedonwn,onmousemove,onmouseup,onmouseout etc
function windowToCanvas(canvas, x, y) {
var bbox = canvas.getBoundingClientRect();
console.log("canvas", canvas.width, canvas.height); // 繪圖表面大小S
console.log("bbox", bbox.width, bbox.height); // 元素大小E,受CSS設置影響
return {
x: (x - bbox.left)* (canvas.width / bbox.width), // 如果被縮放,則坐標也跟著縮放
y: (y - bbox.top)* (canvas.height / bbox.height)
};
}
canvas.onmousemove = function(e) {
var loc = windowToCanvas(canvas, e.clientX, e.clientY);
// ...
};
- 鍵盤事件
由于 canvas 是不可獲焦元素,所以無法直接在 canvas 上監聽鍵盤事件。不過可以在 documen或window上監聽,通常需要處理 keydown,keypress,keyup 三種事件,一般在游戲處理中會遇到。
6、離屏 canvas
1、一般用來保存數據,不展示在瀏覽器頁面上,創建的兩種方式:a、css 方式設置為 display:none;b、JS創建 document.createElement('canvas');
2、與HTML結合使用:可以采用 CSS 定位的方式,將 HTML 元素置于 canvas 元素之上,比如:在 canva 上疊加一個 div panel 作為某個開關控制界面;選景橡皮筋;時鐘等
3、也可使用兩個 canvas,一個用來顯示,另一個用來做數據準備和處理,這種方式通常效率高,但比較耗費內存
7、Canvas 繪圖基本套路
1、準備一個繪制背景的函數,用于每次擦除上一次繪制的結果
2、繪制輔助線
3、監聽事件,做坐標轉換 windowToCanvas
4、繪制內容的保存于恢復
function saveDrawingSurface() {
drawingSurfaceImageData = context.getImageData(0, 0,
canvas.width,
canvas.height);
}
function restoreDrawingSurface() {
context.putImageData(drawingSurfaceImageData, 0, 0);
}
5、三事件
onmousedown:保存初始 canvas 繪制狀態
onmousemove: 更新位置信息,并不斷調用初始 canvas 繪制狀態來擦除上一個繪制
onmouseup: 調用初始 canvas 繪制狀態來擦除上一個繪制,并根據onmousemove保存的信息做最終繪制,將結果繪制在 canvas 上
二、canvas 圖形文本繪制
1、矩形繪制
- clearReact(x,y,width,height)
- strokeReact(x,y,width,height)
相關屬性:strokeStyle,lineJoin,lineWidth - fillReact(x,y,width,height)
相關屬性:fillStyle - rect(x,y,width,height)
逆時針繪制
2、漸變色與圖案和陰影
fillStyle 和 strokeStyle 可以是任意有效的css顏色值或者漸變色以及圖像Pattern
- 線性漸變
gradient = context.createLinearGradient(0, 0, 0, canvas.height / 2);
gradient.addColorStop(0, "blue");
gradient.addColorStop(0.25, "white");
gradient.addColorStop(0.5, "purple");
gradient.addColorStop(0.75, "red");
gradient.addColorStop(1, "yellow");
context.fillStyle = gradient;
- 徑向漸變
gradient = context.createRadialGradient(
canvas.width / 2,
canvas.height,
10,
canvas.width / 2,
0,
100
);
gradient.addColorStop(0, "blue");
gradient.addColorStop(0.25, "white");
gradient.addColorStop(0.5, "purple");
gradient.addColorStop(0.75, "red");
gradient.addColorStop(1, "yellow");
context.fillStyle = gradient;
- 圖像 Pattern
var image = new Image();
var pattern = context.createPattern(image, repeatString);
context.fillStyle = pattern;
- 陰影
1、繪制陰影的條件:a、shadowColor 不是全透明;b、shadowBlur,shadowOffsetX,shadowOffsetY 有一個不為0
2、shadowOffsetX,shadowOffsetY 為負值時,可繪制內嵌陰影
3、路徑
canvas 某一時刻只能有一條路徑存在,這條路徑可以包含多條子路徑。用 beginPath 來開始一條新路徑或清除上一次子路徑
- 子路徑
當畫完一個圖形時,如果不調用 beginPath 就開始畫另一個圖形,那么相當于當前路徑里擁有兩條子路徑(這種情況會導致第一個圖形再被繪制一遍);如果調用了 beginPath,則當前路徑里只有一條子路徑。 - 路徑方向
繪制圖形時,以逆時針順時針方向作為路徑方向,如 rect 永遠是逆時針,arc 最后一個參數為true則順時針 - 非零環繞規則
如果當前路徑是循環的或者子路徑有交叉,則在調用 fill 方法填充時,會進行非零環繞判斷,這個跟路徑方向有關:從填充區任意一點向外畫一條線,這條線會與路徑相交,順時針加1,逆時針減一,最后值如果不為0,則 fill 會生效。
4、線段
- 1px 線段
調用 lineTo,moveTo 方法時坐標要加個0.5,否則繪制1px會變成2px。因為當在兩個像素中間開始繪制1px時,繪制系統會各占用兩個像素中的0.5px,而0.5px不會單獨繪制,被占據0.5px的那個1px像素會被完整繪制,從而變成了2px - 線段
1、lineCap
默認butt,無任何效果;round 增加一個半圓;square 增加一個半正方形
2、lineJoin
控制兩條線段接合處,默認bevel,直線連接;miter 變為矩形;round 圓弧連接 - moveTo(x,y)
- lineTo(x,y)
5、圓弧
- arc
arc(radius,x,y,start,end,ccw) - arcTo
arcTo(x1,y1,x2,y2,radius),適合畫圓角那種弧
6、貝塞爾曲線
- 二階貝塞爾
quadraticCurveTo(cx,cy,x,y) 一個控制點和一個錨點,另一個錨點是當前路徑中最后一個點 - 三階貝塞爾
bezierCurveTo(c1x,c1y,c2x,c2y,x,y) 二個控制點和一個錨點,另一個錨點是當前路徑中最后一個點
7、文本
textAlign: left,center,right
textBaseline: top,middle,bottom
1、三屬性 font,textAlign,textBaseline
2、三方法 strokeText, fillText, measureText
strokeText(text,x,y,maxWidth) 指定文本超過maxWidth會被縮放
measureText(text).width 返回指定文本寬度
3、水平垂直居中
context.textAlign = 'center'
context.textBaseline = 'middle'
function drawText(text){
context.fillStyle = 'blue'
context.fillText(text,canvas.width/2,canvas.height/2)
}
8、坐標變換
注意每次變換前,用 save 和 restore 來保存原來繪制上下文
- 平移
context.translate(x,y) - 旋轉
context.rotate(angle) - 縮放
context.scale(x,y) - transform & setTransform
結合了以上三種變換
x1=ax+cy+e
y1=bx+dy+f
9、剪輯區域
1、由路徑定義的一片區域,如一個三角形,矩形,圓形,然后調用 clip 即可得到剪輯區域
2、默認和 canvas 大小一致
3、設置剪輯區域后,瀏覽器將只對該區域進行繪制
4、調用clip會把剪輯區域設為當前剪輯區域與當前路徑定義的區域的交集,故clip 的調用經常在 save 和 restore 之間,這是為了防止剪輯區域越來越小
三、canvas 圖像與視頻繪制
主要是 drawImage,getImageData,putImageData,createImageData 四個 API
- drawImage,受全局設置影響(globalAlpha,globalCompositeOperation)
將一幅圖片,一個canvas內容,視頻某一幀繪制到當前 canvas 中
1、drawImage(imageOrCanvas,dx,dy) 不縮放,從 (dx,dy) 處開始繪制
2、drawImage(imageOrCanvas,dx,dy,dw,dh) 縮放,從 (dx,dy) 處開始,繪制大小(dw,dh)
3、drawImage(imageOrCanvas,sx,sy,sw,sh,dx,dy,dw,dh),指定部分繪制到指定位置 - getImageData(left,top,width,height)
獲取圖像像素數據,返回值{width,height,data},注意是設備像素,不是CSS像素 - putImageData(imageData,x,y,dx,dy,dw,dh)
以給定的圖像數據重新繪制到(x,y)處,大小為(dx,dy,dw,dh),注意: x,y 是 CSS 像素,dx,dy,dw,dh 是設備像素。此外 putImageData 不受全局設置的影響 - createImageData()
創建一個空 ImageData 對象