HTML5 Canvas繪制圖形從入門到精通
目錄
- 一、canvas 簡介
- 二、Canvas基本使用
- 2.1 <canvas> 元素
- 2.2 渲染上下文(Thre Rending Context)
- 2.3 檢測支持性
- 2.4 一個簡單的例子
- 三、繪制形狀
- 3.1 柵格 (grid) 和坐標空間
- 3.2 繪制矩形
- 四、繪制路徑 (path)
- 4.1 繪制線段
- 4.2 繪制三角形邊框
- 4.3 填充三角形
- 4.4 繪制圓弧
- 4.5 繪制貝塞爾曲線
- 五、添加樣式和顏色
- fillStyle
- strokeStyle
- Transparency(透明度)
- 1、line style
- 2. lineCap = type
- 3. lineJoin = type
- 4. 虛線
- 六、繪制文本
- 繪制文本的兩個方法
- 給文本添加樣式
- 七、繪制圖片
- 7.1 由零開始創建圖片
- 7.2 繪制 img 標簽元素中的圖片
- 7.3 縮放圖片
- 7.4 切片(slice)
- 八、狀態的保存和恢復
- 九、變形
- 9.1 translate
- 9.2 rotate
- 9.3 scale
- 9.4 transform (變形矩陣)
- 十、合成
- 十一、裁剪路徑
- 十二、動畫
- 動畫的基本步驟
- 控制動畫
Canvas API(畫布)是在HTML5中新增的標簽用于在網頁實時生成圖像,并且可以操作圖像內容,基本上它是一個可以用JavaScript操作的位圖(bitmap)。Canvas 對象表示一個 HTML 畫布元素 -<canvas>。它沒有自己的行為,但是定義了一個 API 支持腳本化客戶端繪圖操作。它可以用來制作照片集或者制作簡單(也不是那么簡單)的動畫,甚至可以進行實時視頻處理和渲染。
一、canvas 簡介
<canvas> 標記由 Apple 在 Safari 1.3 Web 瀏覽器中引入。<canvas> 標記和 SVG以及 VML 之間的一個重要的不同是,<canvas> 有一個基于 JavaScript 的繪圖 API,而 SVG 和 VML 使用一個 XML 文檔來描述繪圖。
Mozilla 程序從 Gecko 1.8 (Firefox 1.5) 開始支持 <canvas>
, Internet Explorer 從 IE9 開始 <canvas>
。Chrome 和 Opera 9+ 也支持 <canvas>
。
二、Canvas基本使用
<canvas id="tutorial" width="300" height="300"></canvas>
2.1 <canvas>
元素
?<canvas>
看起來和 <img>
標簽一樣,只是 <canvas>
只有兩個可選的屬性 width、heigth
屬性,而沒有 src、alt
屬性。
?如果不給 <canvas>
設置 widht、height
屬性時,則默認 width
為300、height
為 150,單位都是 px
。也可以使用 css
屬性來設置寬高,但是如寬高屬性和初始比例不一致,他會出現扭曲。所以,建議永遠不要使用 css
屬性來設置 <canvas>
的寬高。
替換內容
?由于某些較老的瀏覽器(尤其是 IE9 之前的 IE 瀏覽器)或者瀏覽器不支持 HTML 元素 <canvas>
,在這些瀏覽器上你應該總是能展示替代內容。
?支持 <canvas>
的瀏覽器會只渲染 <canvas>
標簽,而忽略其中的替代內容。不支持 <canvas>
的瀏覽器則 會直接渲染替代內容。
用文本替換:
<canvas> 你的瀏覽器不支持 canvas,請升級你的瀏覽器。 </canvas>
用 <img>
替換:
<canvas> <img decoding="async" src="./美女.jpg"> </canvas>
結束標簽 </canvas>
不可省略。
與 <img>
元素不同,<canvas>
元素需要結束標簽(</canvas>
)。如果結束標簽不存在,則文檔的其余部分會被認為是替代內容,將不會顯示出來。
2.2 渲染上下文(Thre Rending Context)
?<canvas>
會創建一個固定大小的畫布,會公開一個或多個渲染上下文(畫筆),使用渲染上下文來繪制和處理要展示的內容。
? 我們重點研究 2D 渲染上下文。 其他的上下文我們暫不研究,比如, WebGL 使用了基于 OpenGL ES的3D 上下文 ("experimental-webgl") 。
var canvas = document.getElementById("tutorial"); //獲得 2d 上下文對象 var ctx = canvas.getContext("2d");
2.3 檢測支持性
var canvas = document.getElementById("tutorial"); if (canvas.getContext){ var ctx = canvas.getContext("2d"); // drawing code here } else { // canvas-unsupported code here }
2.4 一個簡單的例子
以下實例繪制兩個長方形:
<canvas id="tutorial" width="300" height="300"></canvas> <script type="text/javascript"> function draw(){ var canvas = document.getElementById("tutorial"); if(!canvas.getContext) return; var ctx = canvas.getContext("2d"); ctx.fillStyle = "rgb(200,0,0)"; //繪制矩形 ctx.fillRect (10, 10, 55, 50); ctx.fillStyle = "rgba(0, 0, 200, 0.5)"; ctx.fillRect (30, 30, 55, 50); } draw(); </script>
三、繪制形狀
3.1 柵格 (grid)
和坐標空間
?如下圖所示,canvas
元素默認被網格所覆蓋。通常來說網格中的一個單元相當于 canvas
元素中的一像素。柵格的起點為左上角,坐標為 (0,0) 。所有元素的位置都相對于原點來定位。所以圖中藍色方形左上角的坐標為距離左邊(X 軸)x 像素,距離上邊(Y 軸)y 像素,坐標為 (x,y)。
?后面我們會涉及到坐標原點的平移、網格的旋轉以及縮放等。
3.2 繪制矩形
?<canvas>
只支持一種原生的圖形繪制:矩形。所有其他圖形都至少需要生成一種路徑 (path
)。不過,我們擁有眾多路徑生成的方法讓復雜圖形的繪制成為了可能。
canvast 提供了三種方法繪制矩形:
- 1、fillRect(x, y, width, height):繪制一個填充的矩形。
- 2、strokeRect(x, y, width, height):繪制一個矩形的邊框。
- 3、clearRect(x, y, widh, height):清除指定的矩形區域,然后這塊區域會變的完全透明。
說明:這 3 個方法具有相同的參數。
- x, y:指的是矩形的左上角的坐標。(相對于canvas的坐標原點)
- width, height:指的是繪制的矩形的寬和高。
function draw(){ var canvas = document.getElementById('tutorial'); if(!canvas.getContext) return; var ctx = canvas.getContext("2d"); ctx.fillRect(10, 10, 100, 50); // 繪制矩形,填充的默認顏色為黑色 ctx.strokeRect(10, 70, 100, 50); // 繪制矩形邊框 } draw();
ctx.clearRect(15, 15, 50, 25);
四、繪制路徑 (path
)
圖形的基本元素是路徑。
路徑是通過不同顏色和寬度的線段或曲線相連形成的不同形狀的點的集合。
一個路徑,甚至一個子路徑,都是閉合的。
使用路徑繪制圖形需要一些額外的步驟:
- 創建路徑起始點
- 調用繪制方法去繪制出路徑
- 把路徑封閉
- 一旦路徑生成,通過描邊或填充路徑區域來渲染圖形。
下面是需要用到的方法:
beginPath()
新建一條路徑,路徑一旦創建成功,圖形繪制命令被指向到路徑上生成路徑
moveTo(x, y)
把畫筆移動到指定的坐標
(x, y)
。相當于設置路徑的起始點坐標。closePath()
閉合路徑之后,圖形繪制命令又重新指向到上下文中
stroke()
通過線條來繪制圖形輪廓
fill()
通過填充路徑的內容區域生成實心的圖形
4.1 繪制線段
function draw(){ var canvas = document.getElementById("tutorial"); if (!canvas.getContext) return; var ctx = canvas.getContext("2d"); ctx.beginPath(); //新建一條path ctx.moveTo(50, 50); //把畫筆移動到指定的坐標 ctx.lineTo(200, 50); //繪制一條從當前位置到指定坐標(200, 50)的直線. //閉合路徑。會拉一條從當前點到path起始點的直線。如果當前點與起始點重合,則什么都不做 ctx.closePath(); ctx.stroke(); //繪制路徑。 } draw();
4.2 繪制三角形邊框
function draw(){ var canvas = document.getElementById("tutorial"); if (!canvas.getContext) return; var ctx = canvas.getContext("2d"); ctx.beginPath(); ctx.moveTo(50, 50); ctx.lineTo(200, 50); ctx.lineTo(200, 200); ctx.closePath(); //雖然我們只繪制了兩條線段,但是closePath會closePath,仍然是一個3角形 ctx.stroke(); //描邊。stroke不會自動closePath() } draw();
4.3 填充三角形
function draw(){ var canvas = document.getElementById("tutorial"); if (!canvas.getContext) return; var ctx = canvas.getContext("2d"); ctx.beginPath(); ctx.moveTo(50, 50); ctx.lineTo(200, 50); ctx.lineTo(200, 200); ctx.fill(); //填充閉合區域。如果path沒有閉合,則fill()會自動閉合路徑。 } draw();
4.4 繪制圓弧
有兩個方法可以繪制圓弧:
1、arc(x, y, r, startAngle, endAngle, anticlockwise): 以(x, y)
為圓心,以r
為半徑,從 startAngle
弧度開始到endAngle
弧度結束。anticlosewise
是布爾值,true
表示逆時針,false
表示順時針(默認是順時針)。
注意:
這里的度數都是弧度。
0
弧度是指的x
軸正方向。radians=(Math.PI/180)*degrees //角度轉換成弧度
2、arcTo(x1, y1, x2, y2, radius): 根據給定的控制點和半徑畫一段圓弧,最后再以直線連接兩個控制點。
圓弧案例
function draw(){ var canvas = document.getElementById("tutorial"); if (!canvas.getContext) return; var ctx = canvas.getContext("2d"); ctx.beginPath(); ctx.moveTo(50, 50); ctx.lineTo(200, 50); ctx.lineTo(200, 200); ctx.fill(); //填充閉合區域。如果path沒有閉合,則fill()會自動閉合路徑。 } draw();
arcTo
方法的說明:
這個方法可以這樣理解。繪制的弧形是由兩條切線所決定。
第 1 條切線:起始點和控制點1決定的直線。
第 2 條切線:控制點1 和控制點2決定的直線。
?其實繪制的圓弧就是與這兩條直線相切的圓弧。
4.5 繪制貝塞爾曲線
4.5.1 什么是貝塞爾曲線
貝塞爾曲線(Bézier curve),又稱貝茲曲線或貝濟埃曲線,是應用于二維圖形應用程序的數學曲線。一般的矢量圖形軟件通過它來精確畫出曲線,貝茲曲線由線段與節點組成,節點是可拖動的支點,線段像可伸縮的皮筋,我們在繪圖工具上看到的鋼筆工具就是來做這種矢量曲線的。
貝塞爾曲線是計算機圖形學中相當重要的參數曲線,在一些比較成熟的位圖軟件中也有貝塞爾曲線工具如 PhotoShop 等。在 Flash4 中還沒有完整的曲線工具,而在 Flash5 里面已經提供出貝塞爾曲線工具。
4.5.2 繪制貝塞爾曲線
一次貝塞爾曲線其實是一條直線
繪制二次貝塞爾曲線:
quadraticCurveTo(cp1x, cp1y, x, y)
說明:
- ? 參數 1 和 2:控制點坐標
- ? 參數 3 和 4:結束點坐標
function draw(){ var canvas = document.getElementById("tutorial"); if (!canvas.getContext) return; var ctx = canvas.getContext("2d"); ctx.beginPath(); ctx.moveTo(10, 200); //起始點 var cp1x = 40, cp1y = 100; //控制點 var x = 200, y = 200; // 結束點 //繪制二次貝塞爾曲線 ctx.quadraticCurveTo(cp1x, cp1y, x, y); ctx.stroke(); ctx.beginPath(); ctx.rect(10, 200, 10, 10); ctx.rect(cp1x, cp1y, 10, 10); ctx.rect(x, y, 10, 10); ctx.fill(); } draw();
繪制三次貝塞爾曲線:
bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y)
說明:
- ? 參數 1 和 2:控制點 1 的坐標
- ? 參數 3 和 4:控制點 2 的坐標
- ? 參數 5 和 6:結束點的坐標
function draw(){ var canvas = document.getElementById("tutorial"); if (!canvas.getContext) return; var ctx = canvas.getContext("2d"); ctx.beginPath(); ctx.moveTo(40, 200); //起始點 var cp1x = 20, cp1y = 100; //控制點1 var cp2x = 100, cp2y = 120; //控制點2 var x = 200, y = 200; // 結束點 //繪制二次貝塞爾曲線 ctx.bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y); ctx.stroke(); ctx.beginPath(); ctx.rect(40, 200, 10, 10); ctx.rect(cp1x, cp1y, 10, 10); ctx.rect(cp2x, cp2y, 10, 10); ctx.rect(x, y, 10, 10); ctx.fill(); } draw();
五、添加樣式和顏色
? 在前面的繪制矩形章節中,只用到了默認的線條和顏色。
? 如果想要給圖形上色,有兩個重要的屬性可以做到。
fillStyle = color
設置圖形的填充顏色strokeStyle = color
設置圖形輪廓的顏色
備注:
- 1. color 可以是表示 css 顏色值的字符串、漸變對象或者圖案對象。
- 2. 默認情況下,線條和填充顏色都是黑色。
- 3. 一旦您設置了 strokeStyle 或者 fillStyle 的值,那么這個新值就會成為新繪制的圖形的默認值。如果你要給每個圖形上不同的顏色,你需要重新設置 fillStyle 或 strokeStyle 的值。
fillStyle
function draw(){ var canvas = document.getElementById("tutorial"); if (!canvas.getContext) return; var ctx = canvas.getContext("2d"); for (var i = 0; i < 6; i++){ for (var j = 0; j < 6; j++){ ctx.fillStyle = "rgb(" + Math.floor(255 - 42.5 * i) + "," + Math.floor(255 - 42.5 * j) + ",0)"; ctx.fillRect(j * 50, i * 50, 50, 50); } } } draw();
strokeStyle
function draw(){ var canvas = document.getElementById("tutorial"); if (!canvas.getContext) return; var ctx = canvas.getContext("2d"); for (var i = 0; i < 6; i++){ for (var j = 0; j < 6; j++){ ctx.strokeStyle = `rgb(${randomInt(0, 255)},${randomInt(0, 255)},${randomInt(0, 255)})`; ctx.strokeRect(j * 50, i * 50, 40, 40); } } } draw(); /** 返回隨機的 [from, to] 之間的整數(包括from,也包括to) */ function randomInt(from, to){ return parseInt(Math.random() * (to - from + 1) + from); }
Transparency(透明度)
globalAlpha = transparencyValue: 這個屬性影響到 canvas 里所有圖形的透明度,有效的值范圍是 0.0 (完全透明)到 1.0(完全不透明),默認是 1.0。
? globalAlpha 屬性在需要繪制大量擁有相同透明度的圖形時候相當高效。不過,我認為使用rgba()設置透明度更加好一些。
1、line style
線寬。只能是正值。默認是 1.0。
起始點和終點的連線為中心,上下各占線寬的一半。
ctx.beginPath(); ctx.moveTo(10, 10); ctx.lineTo(100, 10); ctx.lineWidth = 10; ctx.stroke(); ctx.beginPath(); ctx.moveTo(110, 10); ctx.lineTo(160, 10) ctx.lineWidth = 20; ctx.stroke()
2. lineCap = type
線條末端樣式。
共有 3 個值:
butt
:線段末端以方形結束round
:線段末端以圓形結束square
:線段末端以方形結束,但是增加了一個寬度和線段相同,高度是線段厚度一半的矩形區域。var lineCaps = ["butt", "round", "square"]; for (var i = 0; i < 3; i++){ ctx.beginPath(); ctx.moveTo(20 + 30 * i, 30); ctx.lineTo(20 + 30 * i, 100); ctx.lineWidth = 20; ctx.lineCap = lineCaps[i]; ctx.stroke(); } ctx.beginPath(); ctx.moveTo(0, 30); ctx.lineTo(300, 30); ctx.moveTo(0, 100); ctx.lineTo(300, 100) ctx.strokeStyle = "red"; ctx.lineWidth = 1; ctx.stroke();
3. lineJoin = type
同一個 path 內,設定線條與線條間接合處的樣式。
共有 3 個值
round
,bevel
和miter
:round
通過填充一個額外的,圓心在相連部分末端的扇形,繪制拐角的形狀。 圓角的半徑是線段的寬度。bevel
在相連部分的末端填充一個額外的以三角形為底的區域, 每個部分都有各自獨立的矩形拐角。miter
(默認) 通過延伸相連部分的外邊緣,使其相交于一點,形成一個額外的菱形區域。function draw(){ var canvas = document.getElementById("tutorial"); if (!canvas.getContext) return; var ctx = canvas.getContext("2d"); var lineJoin = ["round", "bevel", "miter"]; ctx.lineWidth = 20; for (var i = 0; i < lineJoin.length; i++){ ctx.lineJoin = lineJoin[i]; ctx.beginPath(); ctx.moveTo(50, 50 + i * 50); ctx.lineTo(100, 100 + i * 50); ctx.lineTo(150, 50 + i * 50); ctx.lineTo(200, 100 + i * 50); ctx.lineTo(250, 50 + i * 50); ctx.stroke(); } } draw();
4. 虛線
用
setLineDash
方法和lineDashOffset
屬性來制定虛線樣式。setLineDash
方法接受一個數組,來指定線段與間隙的交替;lineDashOffset
屬性設置起始偏移量。function draw(){ var canvas = document.getElementById("tutorial"); if (!canvas.getContext) return; var ctx = canvas.getContext("2d"); ctx.setLineDash([20, 5]); // [實線長度, 間隙長度] ctx.lineDashOffset = -0; ctx.strokeRect(50, 50, 210, 210); } draw();
備注: getLineDash() 返回一個包含當前虛線樣式,長度為非負偶數的數組。
六、繪制文本
繪制文本的兩個方法
canvas 提供了兩種方法來渲染文本:
round
通過填充一個額外的,圓心在相連部分末端的扇形,繪制拐角的形狀。 圓角的半徑是線段的寬度。bevel
在相連部分的末端填充一個額外的以三角形為底的區域, 每個部分都有各自獨立的矩形拐角。miter
(默認) 通過延伸相連部分的外邊緣,使其相交于一點,形成一個額外的菱形區域。var ctx; function draw(){ var canvas = document.getElementById("tutorial"); if (!canvas.getContext) return; ctx = canvas.getContext("2d"); ctx.font = "100px sans-serif" ctx.fillText("天若有情", 10, 100); ctx.strokeText("天若有情", 10, 200) } draw();
給文本添加樣式
font = value
當前我們用來繪制文本的樣式。這個字符串使用和CSS font
屬性相同的語法。 默認的字體是10px sans-serif
。textAlign = value
文本對齊選項。 可選的值包括:start
,end
,left
,right
orcenter
。 默認值是start
。textBaseline = value
基線對齊選項,可選的值包括:top
,hanging
,middle
,alphabetic
,ideographic
,bottom
。默認值是alphabetic。
。direction = value
文本方向。可能的值包括:ltr
,rtl
,inherit
。默認值是inherit
。
七、繪制圖片
? 我們也可以在
canvas
上直接繪制圖片。7.1 由零開始創建圖片
var img = new Image(); // 創建一個<img>元素 img.src = "myImage.png"; // 設置圖片源地址
腳本執行后圖片開始裝載。
繪制
img
// 參數 1:要繪制的 img // 參數 2、3:繪制的 img 在 canvas 中的坐標 ctx.drawImage(img,0,0);
注意:考慮到圖片是從網絡加載,如果
drawImage
的時候圖片還沒有完全加載完成,則什么都不做,個別瀏覽器會拋異常。所以我們應該保證在img
繪制完成之后再drawImage
。var img = new Image(); // 創建img元素 img.onload = function(){ ctx.drawImage(img, 0, 0) } img.src = "myImage.png"; // 設置圖片源地址
7.2 繪制
img
標簽元素中的圖片?
img
可以new
也可以來源于我們頁面的<img>
標簽。<img decoding="async" src="./美女.jpg" width="300"><br> <canvas id="tutorial" width="600" height="400"></canvas> function draw(){ var canvas = document.getElementById("tutorial"); if (!canvas.getContext) return; var ctx = canvas.getContext("2d"); var img = document.querySelector("img"); ctx.drawImage(img, 0, 0); } document.querySelector("img").onclick = function (){ draw(); }
7.3 縮放圖片
drawImage()
也可以再添加兩個參數:drawImage(image, x, y, width, height)
?這個方法多了 2 個參數:
width
和height
,這兩個參數用來控制 當像 canvas 畫入時應該縮放的大小。ctx.drawImage(img, 0, 0, 400, 200)
7.4 切片(slice)
drawImage(image, sx, sy, sWidth, sHeight, dx, dy, dWidth, dHeight)
第一個參數和其它的是相同的,都是一個圖像或者另一個 canvas 的引用。
其他 8 個參數:
前 4 個是定義圖像源的切片位置和大小,后 4 個則是定義切片的目標顯示位置和大小。
八、狀態的保存和恢復
Saving and restoring state
是繪制復雜圖形時必不可少的操作。save()
和restore()
save
和restore
方法是用來保存和恢復canvas
狀態的,都沒有參數。?
Canvas
的狀態就是當前畫面應用的所有樣式和變形的一個快照。1、關于 save() :Canvas狀態存儲在棧中,每當save()方法被調用后,當前的狀態就被推送到棧中保存。
一個繪畫狀態包括:
當前應用的變形(即移動,旋轉和縮放)
strokeStyle
,fillStyle
,globalAlpha
,lineWidth
,lineCap
,lineJoin
,miterLimit
,shadowOffsetX
,shadowOffsetY
,shadowBlur
,shadowColor
,globalCompositeOperation 的值
當前的裁切路徑(
clipping path
)可以調用任意多次
save
方法(類似數組的push()
)。可以調用任意多次
save
方法(類似數組的push()
)。2、關于restore():每一次調用 restore 方法,上一個保存的狀態就從棧中彈出,所有設定都恢復(類似數組的
pop()
)。var ctx; function draw(){ var canvas = document.getElementById("tutorial"); if (!canvas.getContext) return; var ctx = canvas.getContext("2d"); ctx.fillRect(0, 0, 150, 150); // 使用默認設置繪制一個矩形 ctx.save(); // 保存默認狀態 ctx.fillStyle = "red" // 在原有配置基礎上對顏色做改變 ctx.fillRect(15, 15, 120, 120); // 使用新的設置繪制一個矩形 ctx.save(); // 保存當前狀態 ctx.fillStyle = "#FFF" // 再次改變顏色配置 ctx.fillRect(30, 30, 90, 90); // 使用新的配置繪制一個矩形 ctx.restore(); // 重新加載之前的顏色狀態 ctx.fillRect(45, 45, 60, 60); // 使用上一次的配置繪制一個矩形 ctx.restore(); // 加載默認顏色配置 ctx.fillRect(60, 60, 30, 30); // 使用加載的配置繪制一個矩形 } draw();
九、變形
9.1 translate
translate(x, y)
用來移動
canvas
的原點到指定的位置?
translate
方法接受兩個參數。x
是左右偏移量,y
是上下偏移量,如右圖所示。在做變形之前先保存狀態是一個良好的習慣。大多數情況下,調用
restore
方法比手動恢復原先的狀態要簡單得多。又如果你是在一個循環中做位移但沒有保存和恢復canvas
的狀態,很可能到最后會發現怎么有些東西不見了,那是因為它很可能已經超出canvas
范圍以外了。? 注意:
translate
移動的是canvas
的坐標原點(坐標變換)。var ctx; function draw(){ var canvas = document.getElementById("tutorial1"); if (!canvas.getContext) return; var ctx = canvas.getContext("2d"); ctx.save(); //保存坐原點平移之前的狀態 ctx.translate(100, 100); ctx.strokeRect(0, 0, 100, 100) ctx.restore(); //恢復到最初狀態 ctx.translate(220, 220); ctx.fillRect(0, 0, 100, 100) } draw();
9.2 rotate
rotate(angle)
旋轉坐標軸。這個方法只接受一個參數:旋轉的角度(angle),它是順時針方向的,以弧度為單位的值。旋轉的中心是坐標原點。
var ctx; function draw(){ var canvas = document.getElementById("tutorial1"); if (!canvas.getContext) return; var ctx = canvas.getContext("2d"); ctx.fillStyle = "red"; ctx.save(); ctx.translate(100, 100); ctx.rotate(Math.PI / 180 * 45); ctx.fillStyle = "blue"; ctx.fillRect(0, 0, 100, 100); ctx.restore(); ctx.save(); ctx.translate(0, 0); ctx.fillRect(0, 0, 50, 50) ctx.restore(); } draw();
9.3 scale
scale(x, y)
我們用它來增減圖形在
canvas
中的像素數目,對形狀,位圖進行縮小或者放大。scale
方法接受兩個參數。x,y
分別是橫軸和縱軸的縮放因子,它們都必須是正值。值比 1.0 小表示縮 小,比 1.0 大則表示放大,值為 1.0 時什么效果都沒有。? 默認情況下,
canvas
的 1 單位就是 1 個像素。舉例說,如果我們設置縮放因子是 0.5,1 個單位就變成對應 0.5 個像素,這樣繪制出來的形狀就會是原先的一半。同理,設置為 2.0 時,1 個單位就對應變成了 2 像素,繪制的結果就是圖形放大了 2 倍。9.4 transform (變形矩陣)
transform(a, b, c, d, e, f)
- a (m11): Horizontal scaling.
- b (m12): Horizontal skewing.
- c (m21):? Vertical skewing.
- d (m22):? Vertical scaling.
- e (dx):? Horizontal moving.
- f (dy):? Vertical moving.
var ctx; function draw(){ var canvas = document.getElementById("tutorial1"); if (!canvas.getContext) return; var ctx = canvas.getContext("2d"); ctx.transform(1, 1, 0, 1, 0, 0); ctx.fillRect(0, 0, 100, 100); } draw();
十、合成
?在前面的所有例子中、,我們總是將一個圖形畫在另一個之上,對于其他更多的情況,僅僅這樣是遠遠不夠的。比如,對合成的圖形來說,繪制順序會有限制。不過,我們可以利用 globalCompositeOperation 屬性來改變這種狀況。
globalCompositeOperation = type
var ctx; function draw(){ var canvas = document.getElementById("tutorial1"); if (!canvas.getContext) return; var ctx = canvas.getContext("2d"); ctx.fillStyle = "blue"; ctx.fillRect(0, 0, 200, 200); ctx.globalCompositeOperation = "source-over"; //全局合成操作 ctx.fillStyle = "red"; ctx.fillRect(100, 100, 200, 200); } draw();
注:下面的展示中,藍色是原有的,紅色是新的。
type 是下面 13 種字符串值之一:
1、這是默認設置,新圖像會覆蓋在原有圖像。
2. source-in
僅僅會出現新圖像與原來圖像重疊的部分,其他區域都變成透明的。(包括其他的老圖像區域也會透明)3. source-out
僅僅顯示新圖像與老圖像沒有重疊的部分,其余部分全部透明。(老圖像也不顯示)
4. source-atop
新圖像僅僅顯示與老圖像重疊區域。老圖像仍然可以顯示。
5. destination-over
新圖像會在老圖像的下面。
6. destination-in
僅僅新老圖像重疊部分的老圖像被顯示,其他區域全部透明。
7. destination-out
僅僅老圖像與新圖像沒有重疊的部分。 注意顯示的是老圖像的部分區域。
8. destination-atop
老圖像僅僅僅僅顯示重疊部分,新圖像會顯示在老圖像的下面。
9. lighter
新老圖像都顯示,但是重疊區域的顏色做加處理。
10. darken
保留重疊部分最黑的像素。(每個顏色位進行比較,得到最小的)
blue: #0000ff red: #ff0000
所以重疊部分的顏色:#000000。
11. lighten
保證重疊部分最量的像素。(每個顏色位進行比較,得到最大的)
blue: #0000ff red: #ff0000
所以重疊部分的顏色:#ff00ff。
12. xor
重疊部分會變成透明。
13. copy
只有新圖像會被保留,其余的全部被清除(邊透明)。
十一、裁剪路徑
clip()
?把已經創建的路徑轉換成裁剪路徑。
?裁剪路徑的作用是遮罩。只顯示裁剪路徑內的區域,裁剪路徑外的區域會被隱藏。
?注意:clip() 只能遮罩在這個方法調用之后繪制的圖像,如果是 clip() 方法調用之前繪制的圖像,則無法實現遮罩。
var ctx; function draw(){ var canvas = document.getElementById("tutorial1"); if (!canvas.getContext) return; var ctx = canvas.getContext("2d"); ctx.beginPath(); ctx.arc(20,20, 100, 0, Math.PI * 2); ctx.clip(); ctx.fillStyle = "pink"; ctx.fillRect(20, 20, 100,100); } draw();
十二、動畫
動畫的基本步驟
清空
canvas
再繪制每一幀動畫之前,需要清空所有。清空所有最簡單的做法就是clearRect()
方法。保存
canvas
狀態 如果在繪制的過程中會更改canvas
的狀態(顏色、移動了坐標原點等),又在繪制每一幀時都是原始狀態的話,則最好保存下canvas
的狀態繪制動畫圖形這一步才是真正的繪制動畫幀
恢復
canvas
狀態如果你前面保存了canvas
狀態,則應該在繪制完成一幀之后恢復canvas
狀態。控制動畫
我們可用通過
canvas
的方法或者自定義的方法把圖像會知道到canvas
上。正常情況,我們能看到繪制的結果是在腳本執行結束之后。例如,我們不可能在一個for
循環內部完成動畫。也就是,為了執行動畫,我們需要一些可以定時執行重繪的方法。
一般用到下面三個方法:
setInterval()
setTimeout()
requestAnimationFrame()
案例:太陽系
let sun; let earth; let moon; let ctx; function init(){ sun = new Image(); earth = new Image(); moon = new Image(); sun.src = "sun.png"; earth.src = "earth.png"; moon.src = "moon.png"; let canvas = document.querySelector("#solar"); ctx = canvas.getContext("2d"); sun.onload = function (){ draw() } } init(); function draw(){ ctx.clearRect(0, 0, 300, 300); //清空所有的內容 /*繪制 太陽*/ ctx.drawImage(sun, 0, 0, 300, 300); ctx.save(); ctx.translate(150, 150); //繪制earth軌道 ctx.beginPath(); ctx.strokeStyle = "rgba(255,255,0,0.5)"; ctx.arc(0, 0, 100, 0, 2 * Math.PI) ctx.stroke() let time = new Date(); //繪制地球 ctx.rotate(2 * Math.PI / 60 * time.getSeconds() + 2 * Math.PI / 60000 * time.getMilliseconds()) ctx.translate(100, 0); ctx.drawImage(earth, -12, -12) //繪制月球軌道 ctx.beginPath(); ctx.strokeStyle = "rgba(255,255,255,.3)"; ctx.arc(0, 0, 40, 0, 2 * Math.PI); ctx.stroke(); //繪制月球 ctx.rotate(2 * Math.PI / 6 * time.getSeconds() + 2 * Math.PI / 6000 * time.getMilliseconds()); ctx.translate(40, 0); ctx.drawImage(moon, -3.5, -3.5); ctx.restore(); requestAnimationFrame(draw); }
相關文章: