canvas和普通的html標籤用法差很少,canvas擁有width(默認300)和height(默認150)兩個屬性,有時出現模糊扭曲的狀況極可能是忘記設置width和height了,並非元素的style中的width和height。javascript
<canvas id="tutorial" width="300" height="150"></canvas>
複製代碼
經過元素已經建立好一個固定大小的繪畫區域,經過元素的getContext('2d')得到一個CanvasRenderingContext2D的繪畫上下文。也能夠經過元素是否有getContext方法來判斷瀏覽器是否支持cnavas(都2021年了!)。畫布的方向符合瀏覽器的滾動條屬性,左上爲 (0,0),向下是無窮大的Y軸,向右是無窮大的X軸。設置元素對應的width和height後坐標中的一格表明畫布的一個像素。css
const canvas = document.getElementById('tutorial');
if (canvas.getContext) {
const ctx = canvas.getContext('2d');
// drawing code here
} else {
// canvas-unsupported code here
}
複製代碼
canvas只支持兩個基本形狀: 矩形和路徑(由直線鏈接的點列表)。全部其餘形狀都必須經過組合一個或多個路徑來建立。html
x 和 y 指定矩形左上角在畫布上的位置(相對於原點)。width和height提供了矩形的大小。java
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>canvas</title>
<style> body { background: #f5f5f5; } canvas { border: 1px solid #1890ff; } </style>
</head>
<body>
<canvas id="tutorial" width="520" height="200"></canvas>
</body>
<script> const draw = ctx => { ctx.fillRect(10, 10, 300 - 20, 150 - 20); ctx.clearRect(20, 20, 300 - 40, (150 - 40) / 2); ctx.strokeRect(30, 30, 30, 30); } const render = () => { const canvas = document.getElementById('tutorial'); if (canvas.getContext) { const ctx = canvas.getContext('2d'); draw(ctx); } else { // canvas-unsupported code here } } window.setTimeout(render, 0); </script>
</html>
複製代碼
const draw = (ctx) => {
ctx.beginPath();
ctx.moveTo(10, 20);
ctx.lineTo(10, 80);
ctx.lineTo(40, 50);
ctx.fill();
ctx.lineTo(150, 50);
ctx.stroke();
ctx.closePath();
}
複製代碼
const draw = (ctx) => {
ctx.beginPath();
ctx.moveTo(10, 20);
ctx.lineTo(10, 80);
ctx.lineTo(40, 50);
ctx.stroke();
ctx.lineTo(150, 50);
ctx.stroke();
ctx.closePath();
}
複製代碼
const draw = (ctx) => {
ctx.beginPath();
ctx.moveTo(10, 20);
ctx.lineTo(10, 80);
ctx.lineTo(40, 50);
ctx.stroke();
ctx.closePath();
ctx.lineTo(150, 50);
ctx.stroke();
ctx.closePath();
}
複製代碼
const draw = (ctx) => {
ctx.beginPath();
ctx.moveTo(10, 20);
ctx.lineTo(10, 80);
ctx.lineTo(40, 50);
ctx.stroke();
ctx.beginPath();
ctx.moveTo(10, 20);
ctx.lineTo(150, 50);
ctx.stroke();
ctx.closePath();
}
複製代碼
經過quadraticCurveTo、bezierCurveTo、arc、arcTo、ellipse能夠繪製一些奇奇怪怪的圖形chrome
樣式屬性在設置以後會應用到後續的全部圖形繪製上,對於不一樣樣式的每一個形狀,都須要從新分配canvas
const draw = ctx => {
ctx.beginPath()
ctx.lineWidth = 10
ctx.strokeStyle = '#1890ff'
ctx.moveTo(0, 20);
ctx.lineTo(200, 20);
ctx.setLineDash([20, 10])
ctx.stroke();
ctx.beginPath()
ctx.moveTo(0, 40);
ctx.lineWidth = 10
ctx.setLineDash([])
ctx.lineCap = 'round' // 兩端爲圓角
ctx.lineJoin = 'round' // 線段交叉處爲圓角
ctx.globalAlpha = 0.5
ctx.lineTo(200, 40);
ctx.lineTo(100, 80);
ctx.stroke();
ctx.closePath()
}
複製代碼
漸變色api
上述函數調用後會生成CanvasGradient對象,經過對象的addColorStop(offset, color)方法生成過渡色,offset取值爲0~1,表示漸變的起始位置數組
const draw = ctx => {
const gradient = ctx.createLinearGradient(0, 0, 200, 0);
gradient.addColorStop(0, 'green');
gradient.addColorStop(.5, 'white');
gradient.addColorStop(1, 'pink');
ctx.fillStyle = gradient;
ctx.fillRect(10, 10, 200, 100);
}
複製代碼
const draw = ctx => {
const gradient = ctx.createRadialGradient(100, 100, 10, 100, 100, 70);
gradient.addColorStop(0, 'green');
gradient.addColorStop(.5, 'white');
gradient.addColorStop(1, 'pink');
ctx.fillStyle = gradient;
ctx.fillRect(0, 0, 200, 200);
}
複製代碼
const draw = ctx => {
const gradient = ctx.createConicGradient(0, 100, 100);
gradient.addColorStop(0, 'green');
gradient.addColorStop(.5, 'white');
gradient.addColorStop(1, 'pink');
ctx.fillStyle = gradient;
ctx.fillRect(0, 0, 200, 200);
}
複製代碼
const draw = ctx => {
ctx.strokeStyle = "#1890ff";
ctx.beginPath();
ctx.moveTo(0,48);
ctx.lineTo(500,48);
ctx.stroke();
ctx.closePath();
ctx.font = "48px serif";
ctx.textBaseline = "middle";
ctx.strokeText("Hello world", 0, 48);
}
複製代碼
const draw = ctx => {
ctx.fillStyle = "#1890ff";
ctx.shadowOffsetX = 8;
ctx.shadowOffsetY = 8;
ctx.shadowBlur = 2;
ctx.shadowColor = "rgba(0, 0, 0, 0.5)";
ctx.font = "32px Times New Roman";
ctx.fillText("Hello World", 0, 32);
ctx.fillRect(0, 64, 50, 50)
}
複製代碼
若調用 drawImage 時,圖片沒裝載完,那什麼都不會發生(在一些舊的瀏覽器中可能會拋出異常)。所以應該用load事件來保證不會在加載完畢以前使用這個圖片。瀏覽器
const draw = ctx => {
const img = new Image();
img.onload = function () {
ctx.drawImage(img, 0, 0);
}
img.src = 'https://sf3-ttcdn-tos.pstatp.com/img/mosaic-legacy/3795/3033762272~300x300.image';
}
複製代碼
const draw = ctx => {
const img = new Image();
img.onload = function () {
ctx.drawImage(img, 0, 0);
ctx.drawImage(img, 200, 0, 90, 180);
}
img.src = 'https://sf3-ttcdn-tos.pstatp.com/img/mosaic-legacy/3795/3033762272~300x300.image';
}
複製代碼
const draw = ctx => {
const img = new Image();
img.onload = function () {
ctx.drawImage(img, 0, 0);
ctx.drawImage(img, 0, 0, 90, 90, 200, 0, 180, 180);
}
img.src = 'https://sf3-ttcdn-tos.pstatp.com/img/mosaic-legacy/3795/3033762272~300x300.image';
}
複製代碼
在繪製複雜圖形時必不可少的兩個方法:markdown
canvas 的狀態就是當前畫面應用的全部樣式和變形的一個快照。canvas狀態存儲在棧中,每當 save 方法被調用後,當前的狀態就被推送到棧中保存;每一次調用 restore 方法,上一個保存的狀態就從棧中彈出,全部設定都恢復。一個繪畫狀態包括:
僅單獨調用方法時不能體現這兩個方法的用處,僅僅一個壓棧和出棧的過程:
// 僅調用save
const draw = ctx => {
ctx.fillStyle = "#1890ff";
ctx.fillRect(0, 0, 150, 150);
ctx.save();
}
// 僅調用restore
const draw = ctx => {
ctx.fillStyle = "#1890ff";
ctx.fillRect(0, 0, 150, 150);
ctx.restore();
}
// 僅調用save和restore
const draw = ctx => {
ctx.fillStyle = "#1890ff";
ctx.fillRect(0, 0, 150, 150);
ctx.save();
ctx.restore();
}
/* 三種方式都是相同的結果:填充一個藍色的矩形 */
複製代碼
當調用save方法後當前畫筆的狀態不會被清除,仍然和save前的同樣,後續改了狀態後,調用restore後會將棧中的狀態取出並自動改變當前畫筆的狀態
const draw = ctx => {
ctx.fillStyle = "#1890ff"; // 設置畫筆顏色爲藍色
ctx.fillRect(0, 0, 30, 30); // 第一個矩形繪製
ctx.save(); // 把藍色(#1890ff)壓入棧中
ctx.fillRect(40, 0, 30, 30); // 第二個矩形繪製
ctx.fillStyle = "#389E0D"; // 設置畫筆顏色爲綠色
ctx.fillRect(80, 0, 30, 30); // 第三個矩形繪製
ctx.save(); // 把綠色(#389E0D)壓入棧中
ctx.fillStyle = "#F5222D"; // 設置畫筆顏色爲紅色
ctx.restore(); // 彈出第二次壓棧的狀態
ctx.fillRect(120, 0, 30, 30); // 第四個矩形繪製
ctx.restore(); // 彈出第一次壓棧的狀態
ctx.fillRect(160, 0, 30, 30); // 第五個矩形繪製
}
複製代碼
scale(x, y) 方法能夠縮放畫布的水平和垂直的單位。兩個參數都是實數,能夠爲負數,x 爲水平縮放因子,y 爲垂直縮放因子,若是比1小,會縮小圖形,若是比1大會放大圖形。默認值爲1,爲實際大小。
const draw = (ctx) => {
ctx.fillRect(0, 0, 50, 50);
ctx.scale(2,2);
ctx.fillRect(50, 0, 50, 50);
}
複製代碼
能夠看到不只是目標的長寬發生了縮放,距離原點(0,0)的偏離也出現了相應的縮放。
注意縮放的數值若是爲負數,至關因而對X、Y軸作鏡像的翻轉,且繪製的窗寬的方向也變成了負數
const draw = (ctx) => {
ctx.fillRect(0, 0, 50, 50); // 寬度爲正數,從左往右計算
ctx.fillRect(100, 50, -50, 50); // 寬度爲負數,從右往左計算
ctx.scale(-1,1); // 原點左側爲0到正無窮,右側爲0到負無窮,Y軸沒作縮放
ctx.fillRect(-50, 100, 50, 50); // 寬度爲正是數,從右往左計算
ctx.fillRect(-50, 150, -50, 50); // 寬度爲負數,從左往右計算
}
複製代碼
translate(x, y)方法接受兩個參數。x 是左右偏移量,y 是上下偏移量。和其餘屬性同樣,一旦設置之後對後續操做都是疊加效應,每次移動是基於上一次移動的基礎之上的,並不會一直相對於原點。
const draw = (ctx) => {
ctx.fillRect(0, 0, 20, 20);
ctx.translate(30, 0)
ctx.fillRect(0, 0, 20, 20);
ctx.translate(0, 30); // 只平移了Y值,而X值仍然是以前的30
ctx.fillRect(0, 0, 20, 20);
ctx.translate(30, 0); // 是在以前(30,0)+(0,30)的基礎上再加(30,0),至關於相對於原點的位移是(60,30)
ctx.fillStyle = '#1890ff';
ctx.fillRect(0, 0, 20, 20);
}
複製代碼
能夠配合使用save和restore方法來使位移一直相對於原點,更符合咱們的計算
const draw = (ctx) => {
ctx.save(); // 繪製前保存一個原始的狀態
ctx.fillRect(0, 0, 20, 20);
ctx.restore(); // 繪製後還原到原始的狀態
ctx.save();
ctx.translate(20, 20)
ctx.fillRect(0, 0, 20, 20);
ctx.restore();
ctx.save();
ctx.translate(40, 40)
ctx.fillRect(0, 0, 20, 20);
ctx.restore();
ctx.save();
ctx.translate(60, 60)
ctx.fillRect(0, 0, 20, 20);
ctx.restore();
}
複製代碼
rotate(angle)方法只接受一個參數:旋轉的角度(angle),它是以原點爲中心順時針方向的角度,以 弧度 爲單位的值。
const draw = (ctx) => {
ctx.lineWidth = 5;
ctx.beginPath();
ctx.moveTo(0, 0);
ctx.lineTo(100, 0);
ctx.stroke();
ctx.save();
ctx.rotate(2 * Math.PI / 360 * 30)
ctx.beginPath();
ctx.moveTo(0, 0);
ctx.lineTo(100, 0);
ctx.stroke();
ctx.restore();
ctx.save();
ctx.rotate(2 * Math.PI / 360 * 60)
ctx.beginPath();
ctx.moveTo(0, 0);
ctx.lineTo(100, 0);
ctx.stroke();
ctx.restore();
ctx.save();
ctx.rotate(2 * Math.PI / 360 * 90)
ctx.beginPath();
ctx.moveTo(0, 0);
ctx.lineTo(100, 0);
ctx.stroke();
ctx.restore();
}
複製代碼
若是要繞圖形的中心旋轉,須要經過旋轉角度反推計算平移的距離
const draw = (ctx) => {
ctx.fillRect(0, 0, 40, 40);
ctx.save();
ctx.translate(80, 40)
ctx.globalAlpha = 0.5
ctx.fillRect(0, 0, 40, 40); // 圖形2, 位移以後的參考座標
ctx.rotate(2 * Math.PI / 360 * 45)
ctx.fillStyle = "#1890ff"; // 藍色
ctx.fillRect(0, 0, 40, 40);
ctx.restore();
// 若是想在圖形2的位置中心旋轉,須要動態計算水平偏移座標
ctx.save();
ctx.globalAlpha = 0.5
ctx.fillStyle = "#FFEC3D"; // 黃色
const R = 20; // 2分之一的邊長
const r = 60; // 旋轉角度
const diagonalR = Math.sqrt(Math.pow(R, 2) + Math.pow(R, 2)) // 2分之一的對角線長
const translateX = 80 + R - diagonalR * Math.sin(2 * Math.PI / 360 * (45 - r));
const translateY = 40 + R - diagonalR * Math.cos(2 * Math.PI / 360 * (45 - r));
ctx.translate(translateX, translateY)
ctx.rotate(2 * Math.PI / 360 * r)
ctx.fillRect(0, 0, 2 * R, 2 * R);
ctx.restore();
}
複製代碼