canvas游戲設計
1.canvas基礎知識
1.1canvas元素
- canvas元素我們稱之為畫布,之所以稱之為畫布就像我們的白板一樣,或者黑板一樣,我們它只是一個容器,我們需要繪制的圖形圖像都需要繪制在這個黑板上,那其實我們知道canvas不光可以用來去繪制基本的圖形或者圖圖案,同樣我們也可以拿它去實現游戲。
- 我們之所以不使用flash來完成游戲。是因為flash中。想要去運行,我們必須得要有對應的flash插件。沒有對應的flash插件,我們沒有辦法在瀏覽器當中去運行我們要的游戲。而canvas對于我們來講有個很方便的地方。就是他只是一個標簽,只要你的瀏覽器支持canvas標簽。我們就可以利用相關的代碼來去完成一個游戲,或者圖形回執。所以他是沒有平臺限制的并且也不需要我們去下載對應的插件。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<canvas id="can"></canvas>
</body>
</html>
- canvas 默認的寬高,300 X 150
- 給 canvas 元素設置寬高的時候,建議大家使用 canvas 標簽本身提供的 width 和 height 屬性。不建議大家使用 CSS 的樣式設置,會導致 canvas 原本繪制的圖片圖形拉伸,變形。
- 如果瀏覽器不支持 canvas 標簽,我們可以將提示的錯誤信息放在 canvas 標簽的中間內容中
// 1.獲取canvas元素
var can = document.getElementById('can');
// 通過canvas提供給我們的 繪圖環境(繪圖上線文) 繪制相關的 圖形或圖案
var ctx=can.getContext('2d');
// 3.獲取到用于繪制圖形圖案的 相關api
1.2canvas 的繪圖環境
- CanvasRenderingContext2D 對象提供的屬性
屬性 |
描述 |
canvas |
繪圖環境所屬的 canvas 對象。 |
fillStyle |
繪圖環境在后續的圖形填充操作中所使用的的顏色,漸變色或者圖案 |
font |
用來設置字形 |
lineCap |
繪制的線段的端點的樣式,butt,round,square |
lineWidth |
繪制的線條的寬度 |
shadowColor |
用什么顏色繪制陰影 |
shadowOffsetX |
水平方向的偏移 |
shadowOffsetY |
垂直方向的偏移 |
strokeStyle |
繪圖環境在后續的圖形描邊操作中所使用的的顏色,漸變色或者圖案 |
textAlign |
本文的對齊方式 |
textBaseline |
垂直的堆砌方式 |
1.3 直線
- 對畫直線的功能,我們可以使用beginPath(),closePath(),moveTo(),lineTo()和stroke()幾個方法組合起來。
1.3.1 畫直線
// 獲取對應的canvas 節點
var can=document.getElementById('canvas');
// 獲取2d繪圖環境
var ctx=can.getContext('2d');
// 開始一段新的路徑
ctx.beginPath();
// 將筆觸移動到某一點。
ctx.moveTo(100,100);
ctx.lineTo(200,200);
// 繪制直線
ctx.stroke();
- beginPath() 定義一個新的路徑繪制動作的開始
- moveTo() 為固定點創建一個新的子路徑,新的筆觸點。
- lineTo() 以上下文點為起點,到方法參數中指定的點之間畫一條直線
- stroke() 為所花的先賦予顏色等樣式,并使其可見。如果沒有特別指定顏色的話,則默認使用黑色。
1.3.2 直線的寬度
ctx.lineWidth=5; //默認的按像素單位。
1.3.3 線條顏色
直線的顏色用strokeStyle屬性
ctx.strokeStyle='red';
ctx.strokeStyle='rgba(0,0,0)';
ctx.strokeStyle='#f00';
1.3.4 直線端點的樣式
- canvas支持3中直線的端點樣式:butt,round,square. 使用lineCap屬性設定。
ctx.lineCap="butt";
ctx.lineCap="round";
ctx.lineCap="square";
var canvas=document.getElementById('canvas');
var ctx=canvas.getContext('2d');
// butt 黃色 10
ctx.beginPath();
ctx.strokeStyle='yellow';
ctx.lineWidth=10;
ctx.lineCap='butt';
ctx.moveTo(100,100);
ctx.lineTo(300,100);
ctx.stroke();
// round 藍色 20
ctx.beginPath();
ctx.strokeStyle='blue';
ctx.lineWidth=20;
ctx.lineCap='round';
ctx.moveTo(100,130);
ctx.lineTo(300,130);
ctx.stroke();
// square 紅色 13
ctx.beginPath();
ctx.strokeStyle='red';
ctx.lineWidth=15;
ctx.lineCap='square';
ctx.moveTo(100,160);
ctx.lineTo(300,160);
ctx.stroke();
1.4圓弧
1.4.1畫弧線
- 畫弧線的方法是arc().每一條弧線都要由中心點,半徑,起始角度(弧度制---弧度=角度值 * Math.PI/180),結束角度(弧度制---弧度=角度值 * Math.PI/180),后臺繪圖方向這幾個參數來去確定一條弧線
ctx.arc(x, y, radius, startAngle, endAngle, direction);
// direction 用來設置圓弧的繪制方向
// 默認 false 順時針繪制
// true 逆時針繪制
- 另外,也可以用 arcTo()方法來畫弧線,用來在路徑中繪制圓角。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<style>
canvas {
border: 3px solid red;
}
</style>
</head>
<body>
<canvas id="canvas" width="600" height="600"></canvas>
</body>
<script>
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
ctx.arc(300, 300, 150, (90 * Math.PI) / 180, (270 * Math.PI) / 180);
ctx.stroke();
</script>
</html>
1.4.2二次曲線(二次貝塞爾曲線)
- 通過 quadraticCurveTo()來完成二次曲線的繪制,每一條二次曲線要求要有上下文點,一個控制點和一個終止點來去定義。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<style>
canvas {
border: 3px solid red;
}
</style>
</head>
<body>
<canvas id="canvas" width="600" height="600"></canvas>
</body>
<script>
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
ctx.moveTo(100,100);
//二次曲線的起點
ctx.quadraticCurveTo(288,0,388,150);
// arg1 控制點的x坐標
// arg2 控制點的y坐標
// arg3 結束點的x坐標
// arg4 結束點的y坐標
ctx.stroke();
</script>
</html>
1.4.3貝塞爾曲線
- 使用bezierCurveTo(); 每一條貝塞爾曲線需要由起點,兩個控制點和一個終止點來確定。由于貝塞爾曲線的是有兩個控制點的,因此貝塞爾曲線可以比二次曲線更加復雜
ctx.bezierCurveTo(control1X,control1Y,control2X,control2Y,endx,endy);
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
canvas {
border: 3px solid red;
}
</style>
</head>
<body>
<canvas id="canvas" width="600" height="600"></canvas>
</body>
<script>
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');
ctx.beginPath();
ctx.fillStyle="red";
ctx.moveTo(100,300);
ctx.bezierCurveTo(0,0,600,0,400,300);
ctx.lineTo(400,300);
ctx.arc(400,400,150,0,1.3*Math.PI);
// ctx.stroke();
ctx.fill();
</script>
</html>
1.4.4線條的鏈接樣式
- HTML5 canvas 中支持3種線條的鏈接樣式;包括:miter,round和bevel,設定鏈接樣式使用lineJoin屬性,默認情況下使用miter樣式
ctx.lineJion='round';
- 注意:如果線條比較細他們之間鏈接并不形成很尖銳的角度的話,那么不同的樣式可能會比較難以區分
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
canvas {
border: 3px solid red;
}
</style>
</head>
<body>
<canvas id="canvas" width="600" height="600"></canvas>
</body>
<script>
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');
// 設置線條的寬度
ctx.lineWidth = 30;
// miter樣式
ctx.beginPath();
ctx.moveTo(100, 150);
ctx.lineTo(150, 50);
ctx.lineTo(200, 150);
ctx.lineJoin = "miter";
ctx.stroke();
// round樣式
ctx.beginPath();
ctx.moveTo(240, 150);
ctx.lineTo(290, 50);
ctx.lineTo(340, 150);
ctx.lineJoin = "round";
ctx.stroke();
// bevel樣式
ctx.beginPath();
ctx.moveTo(380, 150);
ctx.lineTo(430, 50);
ctx.lineTo(480, 150);
ctx.lineJoin = "bevel";
ctx.stroke();
</script>
</html>
1.4.5圓角
- 畫圓角使用arcTo()方法,這個方法需要一個控制點,一個終止點,半徑作為必要參數
ctx.arcTo(controlX, controlY, endX, endY, radius);
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
canvas {
border: 3px solid red;
}
</style>
</head>
<body>
<canvas id="canvas" width="600" height="600"></canvas>
</body>
<script>
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');
// 設置矩形相關的參數
var rectWidth=200;
var rectHeight=100;
var rectX=200;
var rectY=50;
var radius=50;
ctx.beginPath();
ctx.moveTo(rectX,rectY);
ctx.lineTo(rectX+rectWidth-radius,rectY);
ctx.arcTo(rectX+rectWidth,rectY,rectX+rectWidth,rectY+radius,radius);
ctx.lineTo(rectX+rectWidth,rectY+rectHeight);
ctx.stroke();
</script>
</html>
1.5圖形
1.5.1 自定義圖形
- 自定義圖形,需要創建一個路徑,然后closePath()方法閉合此路徑
1.5.2 圖形的顏色填充
- 要填充圖形,需要使用fillStyle屬性設置填充圖形的這個顏色,然后使用fillStyle方法完成圖形的填充。默認情況下,fillStyle屬性的這個顏色是黑色。
ctx.fillStyle = "color";
ctx.fill();
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
canvas {
border: 3px solid red;
}
</style>
</head>
<body>
<canvas id="canvas" width="600" height="600"></canvas>
</body>
<script>
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');
// 開始自定義圖形
ctx.beginPath();
ctx.moveTo(170, 80);
ctx.bezierCurveTo(130, 100, 130, 150, 230, 150);
ctx.bezierCurveTo(250, 180, 320, 180, 340, 150);
ctx.bezierCurveTo(420, 150, 420, 130, 390, 100);
ctx.bezierCurveTo(430, 40, 370, 30, 340, 50);
ctx.bezierCurveTo(320, 5, 250, 20, 250, 50);
ctx.bezierCurveTo(200, 5, 220, 20, 150, 50);
ctx.closePath();
ctx.strokeStyle = 'green';
ctx.stroke();
ctx.fillStyle = '#999';
ctx.fill();
</script>
</html>
1.5.3 矩形
- 繪制矩形使用rect()方法,每一個矩形需要有左上角(x,y)的這個和矩形的(w,h)寬高來確定。
ctx.rect(x,y,w,h);
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
canvas {
border: 3px solid red;
}
</style>
</head>
<body>
<canvas id="canvas" width="600" height="600"></canvas>
</body>
<script>
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');
// 開始自定義圖形
ctx.beginPath();
ctx.rect(200, 200, 200, 200)
ctx.strokeStyle = 'green';
ctx.lineWidth = 2;
ctx.stroke();
ctx.fillStyle = '#999';
ctx.fill();
</script>
</html>
1.5.4 圓
- 畫圓只需要調用arc()方法的時候,將起始角度設為零,終止角度設為2*Math.PI就可以
ctx.arc(x,y,radius,0,2*Math.PI,false)
1.5.5 半圓
- 畫半圓也是調用arc()方法,將起始角度設為零,終止角度設為1*Math.PI就可以
ctx.arc(x,y,radius,0,1*Math.PI,false)
1.6 填充類型
1.6.1線性漸變
- createLinearGradient() 方法從上下文對象中創建線性漸變對象。四個參數確定一條虛擬線段,漸變沿著這條線段的 方向。
- 然后用addColorStop方法為線性漸變對象設置漸變線上的關鍵點的顏色。offset表示關鍵點實在漸變虛擬線段的什么位置,offset的取值范圍是0-1之間,0表示起始點,1表示終止點。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
canvas {
border: 3px solid red;
}
</style>
</head>
<body>
<canvas id="canvas" width="600" height="600"></canvas>
</body>
<script>
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');
// 開始自定義圖形
ctx.beginPath();
ctx.moveTo(170,80);
ctx.bezierCurveTo(130,100,130,150,230,150);
ctx.bezierCurveTo(250,180,320,180,340,150);
ctx.bezierCurveTo(420,150,420,130,390,100);
ctx.bezierCurveTo(430,40,370,30,340,50);
ctx.bezierCurveTo(320,5,250,20,250,50);
ctx.bezierCurveTo(200,5,150,20,170,80);
ctx.closePath();
// 創建線性漸變對象
var grd=ctx.createLinearGradient(250,0,370,200);
// 添加漸變段 起點處顏色
grd.addColorStop(0,"#f00");
grd.addColorStop(0.2,"green");
grd.addColorStop(0.4,"orange");
// 添加漸變段 起點處顏色
grd.addColorStop(1,"yellow");
ctx.fillStyle=grd;
ctx.fill();
ctx.strokeStyle="green";
ctx.stroke();
</script>
</html>
1.6.2徑性漸變
- createRadialGradient() 方法創建徑向漸變對象,參數是漸變的起始圓和終止圓。
var grd = ctx.createRadialGradient(
startX,
startY,
startRadius,
endX,
endY,
endRadius
);
grd.addColorStop(offset, color);
ctx.fillStyle = grd;
ctx.fill();
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<style>
canvas {
border: 3px solid red;
}
</style>
</head>
<body>
<canvas id="canvas" width="600" height="600"></canvas>
</body>
<script>
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
// 開始自定義圖形
ctx.beginPath();
ctx.arc(300, 300, 200, 0, 2 * Math.PI);
ctx.closePath();
// 創建線性漸變對象
var grd = ctx.createRadialGradient(300, 300, 110, 300, 300, 200);
// 添加漸變段 起點處顏色
grd.addColorStop(0, "blue");
grd.addColorStop(0.5, "skyblue");
grd.addColorStop(0.8, "green");
ctx.fillStyle = grd;
ctx.fill();
ctx.strokeStyle = "green";
ctx.stroke();
</script>
</html>
1.6.3 圖案填充
- 圖案填充和圖案繪制不一樣,我們要使用 canvas 填充圖案,首先要使用 createParttern 的方法這個方法需要兩個參數,第一個參數是圖像對象,第二個參數是重復的這個模式。比如說 repeat,repeat-x repeat-y 以及 no-repeat。
var pattern = ctx.createPattern(imgObj, repeatOption);
// repeatOption 支持repeat,no-repeat,repeat-x,repeat-y
ctx.fillStyle = pattern;
ctx.fill();
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<style>
canvas {
border: 3px solid red;
}
</style>
</head>
<body>
<canvas id="canvas" width="1600" height="1600"></canvas>
</body>
<script>
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
// 圖案填充
var imgObj = new Image();
imgObj.src = "imgs/0.jpg";
imgObj.onload = function() {
var pattern = ctx.createPattern(imgObj, "no-repeat");
// ctx.rect(0,0,1500,1500);
ctx.arc(800, 800, 800, 0, 2 * Math.PI);
ctx.fillStyle = pattern;
ctx.fill();
};
</script>
</html>
1.7 圖像
1.7.1 繪制圖像
- 繪制圖像需要使用 drawImage()方法。這個方法需要一個圖片對象和一個起始點的坐標作為參數形式點的坐標是相對于 canvas 的左上角的位置
ctx.drawImage(imgObj, x, y);
1.7.2 圖像尺寸
// w,h 指定繪制的圖片的 寬高
ctx.drawImage(imgObj, x, y, w, h);
1.7.3 圖像裁剪
ctx.drawImage(
imgaeObj,
sourceX,
sourceY,
sourceW,
sourceH,
destX,
destY,
destW,
destH
);
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<style>
canvas {
border: 3px solid red;
}
</style>
</head>
<body>
<canvas id="canvas" width="1000" height="1000"></canvas>
</body>
<script>
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
// 圖像繪制
var imgObj = new Image();
imgObj.src = "imgs/1.jpeg";
imgObj.onload = function() {
ctx.drawImage(this, 0, 0);
// debugger
ctx.drawImage(this, 554, 0, 100, 100 / (this.width / this.height));
ctx.drawImage(this, 74, 312, 330, 480, 554, 200, 330, 480);
};
</script>
</html>
1.7.4 圖像加載器
- 使用多副圖像的時候,最好是在繪制圖像之前就把圖像全部加載進來。
- 我給大家提供一個簡單的方法。就是用一個圖像加載函數,一次性把所有的圖像全部加在今這個圖像對象當中去,然后再調用一個用戶自定義的函數。在圖像加載完成之后調用該函數。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<style>
canvas {
border: 3px solid red;
}
</style>
</head>
<body>
<canvas id="canvas" width="1000" height="1000"></canvas>
</body>
<script>
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
// 圖片資源加載器
function loadImages(sources, callback) {
var images = {};
var loadedImages = 0;
var numImages = 0;
// 獲取圖片的數量
for (var i in sources) {
numImages++;
}
for (var i in sources) {
images[i] = new Image();
images[i].src = sources[i];
images[i].onload = function() {
loadedImages++;
if (loadedImages == numImages) {
callback(images);
}
};
}
}
var sources = { p: "imgs/1.jpeg", p2: "imgs/2.jpeg", p3: "imgs/3.jpeg" };
loadImages(sources, function(images) {
ctx.drawImage(images.p2, 0, 0);
ctx.drawImage(images.p3, 100, 100);
});
</script>
</html>
1.8 文字
1.8.1 文本的字體,大小和樣式
- 要設置字體大小和樣式,需要用到上下文繪圖環境對象的font屬性。樣式可以通過normal,italic或者bold。默認情況下是bold。
ctx.font="italic 40px 微軟雅黑";
ctx.strokeStyle='red';
ctx.fillStyle='red';
1.8.2 填充文本
ctx.fillText(textContent,startX,startY);
ctx.fillText(textContent,startX,startY,textW);
1.8.3 描繪文本邊緣
ctx.strokeText(textContent,startX,startY);
ctx.strokeText(textContent,startX,startY,textW);
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
canvas {
border: 3px solid red;
}
</style>
</head>
<body>
<canvas id="canvas" width="1000" height="1000"></canvas>
</body>
<script>
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');
ctx.font="bold 40px 微軟雅黑";
ctx.fillStyle='blue';
ctx.strokeStyle="red";
ctx.fillText('你好,范俊!',100,100);
ctx.strokeText('你好,范俊!',100,100);
ctx.strokeText('你好,范??!',200,200);
ctx.fillText('你好,范俊!',200,200);
</script>
</html>
1.8.4 文本對齊
- 文本的對齊功能。我們使用textAlign屬性。他可以存在的屬性只有這么幾個:start,end,left,center,right.對齊的位置是相對于一條虛擬的垂直線,這條線是由fillText或者這個strokeText方法定義的文本的X位置來去決定的。
- 文本左對齊:
- 1.textAlign 屬性 left
- 2.textAlign 屬性 start,ltr(left toright)
- 3.textAlign 屬性 end ,rtl(right to left)
- 文本右對齊:
- 1.textAlign 屬性 right
- 2.textAlign 屬性 start rtl(right to left)
- 3.textAlign 屬性 end ltr(left to right)
1.8.5 文本基線
- 垂直對齊文本需要使用textBaseline屬性。這個屬性的可用值一共有這么幾個:Top,hanging,middle,alphabetic,ideographic ,bottom。默認是alphabetic。
值 |
描述 |
alphabetic |
默認。文本基線是普通的字母基線。 |
top |
文本基線是 em 方框的頂端。 |
hanging |
文本基線是懸掛基線。 |
middle |
文本基線是 em 方框的正中。 |
ideographic |
文本基線是表意基線。 |
bottom |
文本基線是 em 方框的底端。 |
2.canvas 高級進階
2.1 組合
2.1.1 陰影
- 要為圖形添加陰影,需要使用shadowColor,shadowBlur,shadowOffsetX和shadowOffsetY屬性。
ctx.shadowColor="black";
ctx.shadowBlur=20;
ctx.shadowOffsetX=10;
ctx.shadowOffsetY=10;
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
canvas {
border: 3px solid red;
}
</style>
</head>
<body>
<canvas id="canvas" width="1000" height="1000"></canvas>
</body>
<script>
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');
ctx.fillStyle="red";
ctx.shadowColor="black";
ctx.shadowBlur=20;
ctx.shadowOffsetX=-10;
ctx.shadowOffsetY=-10;
ctx.fillRect(50,50,200,100);
</script>
</html>
2.1.2 透明
- 設置圖形透明 globalAlpha屬性。 屬性值是介于0-1之間的 浮點數。0 表示完全透明,1表示完全不透明
ctx.globalAlpha = 0.5;
2.1.3 裁剪區
創建裁剪區的方法是先繪制一個路徑,然后使用clip()方法。
- save(); 的作用是在創建裁剪區之前將canvas的當前狀態保存起來,以便于在我們需要的時候,恢復當前狀態
- restore(); 恢復之前保存的狀態
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
canvas {
border: 3px solid red;
}
</style>
</head>
<body>
<canvas id="canvas" width="1000" height="1000"></canvas>
</body>
<script>
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');
var x=100,y=100,r=80;
var offset=60;
// save(); 的作用是在創建裁剪區之前將canvas的當前狀態保存起來,以便于在我們需要的時候,恢復當前狀態
// restore(); 恢復之前保存的狀態
ctx.save();
ctx.beginPath();
ctx.arc(x,y,r,0,2*Math.PI,false);
// ctx.fill();
ctx.clip();
ctx.beginPath();
ctx.fillStyle='blue';
ctx.arc(x-offset,y-offset,r,0,2*Math.PI,false);
ctx.fill();
ctx.beginPath();
ctx.fillStyle='yellow';
ctx.arc(x+offset,y-offset,r,0,2*Math.PI,false);
ctx.fill();
ctx.beginPath();
ctx.fillStyle='red';
ctx.arc(x,y+offset-30,r,0,2*Math.PI,false);
ctx.fill();
ctx.restore();
ctx.beginPath();
ctx.fillStyle='pink';
ctx.fillRect(100,100,200,100);
</script>
</html>
2.1.4全局組合操作
- 上下文對象的globalCompositeOperation屬性定義了組合操作的方式,
- 共有12種組合操作可供我們使用。包括: source-atop, source-in, sourceout,source-over, destination-atop, destination-in, destination-out,destination-over, lighter, xor, 和 copy 。默認狀態下是 source-over 。
值 |
描述 |
source-over |
默認。在目標圖像上顯示源圖像。 |
source-atop |
在目標圖像頂部顯示源圖像。源圖像位于目標圖像之外的部分是不可見的。 |
source-in |
在目標圖像中顯示源圖像。只有目標圖像內的源圖像部分會顯示,目標圖像是透明的。 |
source-out |
在目標圖像之外顯示源圖像。只會顯示目標圖像之外源圖像部分,目標圖像是透明的。 |
destination-over |
在源圖像上方顯示目標圖像。 |
destination-atop |
在源圖像頂部顯示目標圖像。源圖像之外的目標圖像部分不會被顯示。 |
destination-in |
在源圖像中顯示目標圖像。只有源圖像內的目標圖像部分會被顯示,源圖像是透明的。 |
destination-out |
在源圖像外顯示目標圖像。只有源圖像外的目標圖像部分會被顯示,源圖像是透明的。 |
lighter |
顯示源圖像 + 目標圖像。 |
copy |
顯示源圖像。忽略目標圖像。 |
xor |
使用異或操作對源圖像與目標圖像進行組合。 |
ctx.globalCompositeOperation='source-atop'
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
canvas {
border: 3px solid red;
}
</style>
</head>
<body>
<canvas id="canvas" width="1000" height="1000"></canvas>
<canvas id="tempCanvas" width="1000" height="1000"
style="position: fixed;left: 10px; top: 10px;border:3px solid green;display: none;"></canvas>
</body>
<script>
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');
// 創建一個臨時canvas 可以理解為內存中繪圖所用。
var tempCanvas = document.getElementById('tempCanvas');
var tempCtx = tempCanvas.getContext('2d');
var rectW = 50;
var circleRadius = 30;
var startx = 10;
var starty = 10;
var arr = ['source-atop', 'source-in', 'source-out', 'source-over', 'destination-atop', 'destination-in', 'destination-out', 'destination-over', 'lighter', 'xor', 'copy'];
for (var i in arr) {
var thisX = startx;
var thisY = starty;
if (i < 4) {
// 第一行
thisX = startx + i * 90;
thisY = starty;
} else if (i < 8) {
// 第二行
thisX = startx + (i - 4) * 90;
thisY = starty + 100;
} else {
// 第三行
thisX = startx + (i - 8) * 90;
thisY = starty + 200;
}
tempCtx.clearRect(0, 0, tempCanvas.width, canvas.height);
// 繪制矩形
tempCtx.beginPath();
tempCtx.rect(thisX, thisY, rectW, rectW);
tempCtx.fillStyle = 'blue';
tempCtx.fill();
// 源圖形 目標圖形
// 包括:source-atop,source-in,source-out,source-over,destination-atop,destination-in ,destination-out,destination-over,lighter,xor,copy 默認狀態 :source-over
// 設置全局組合模式
tempCtx.globalCompositeOperation = arr[i];
// 繪制圓
tempCtx.beginPath();
tempCtx.arc(thisX + circleRadius + 10, thisY + circleRadius + 10, circleRadius, 0, Math.PI * 2);
tempCtx.fillStyle = 'red';
tempCtx.fill();
// 繪制文字
tempCtx.beginPath();
tempCtx.globalCompositeOperation = 'source-over';
tempCtx.fillStyle = 'green';
tempCtx.font='10px bold';
tempCtx.fillText(arr[i], thisX, thisY + 90);
ctx.drawImage(tempCanvas, 0, 0);
}
</script>
</html>
2.2坐標轉換
2.2.1原點的位移
- 使用translate()方法可以將繪圖的原點橫向和縱向移動到指定的距離(x,y)。結果。一般表現是整張的一個移動。
ctx.translate(x,y);
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
canvas {
border: 3px solid red;
}
</style>
</head>
<body>
<canvas id="canvas" width="600" height="600"></canvas>
<script>
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');
ctx.beginPath();
ctx.fillStyle = "blue";
ctx.fillRect(0, 0, 200, 200)
ctx.translate(canvas.width / 2, canvas.height / 2);
ctx.beginPath();
ctx.fillStyle = "red";
ctx.fillRect(0, 0, 200, 100)
</script>
</html>
2.2.2縮放
- scale()縮放我們使用scale方法。參數分別代表橫向和縱向的縮放比例,;兩個參數都是浮點類型。1.0表示不縮放,小于1.0表示縮放,大于1.0表示放大。
ctx.scale(x,y);
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
canvas {
border: 3px solid red;
}
</style>
</head>
<body>
<canvas id="canvas" width="600" height="600"></canvas>
<script>
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');
ctx.beginPath();
ctx.fillStyle = "blue";
ctx.fillRect(0, 0, 200, 200)
ctx.scale(0.5, 0.5);
ctx.beginPath();
ctx.fillStyle = "red";
ctx.fillRect(0, 0, 200, 100)
</script>
</html>
2.2.3旋轉
- 旋轉,我們使用rotate方法。這個方法呢?接受一個以弧度為單位的旋轉的參數。整個二看worth將以坐標原點,也就是由translate所確定的原點為圓心進行旋轉。
ctx.rotate(x,y);
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
canvas {
border: 3px solid red;
}
</style>
</head>
<body>
<canvas id="canvas" width="600" height="600"></canvas>
<script>
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');
ctx.beginPath();
ctx.fillStyle = "blue";
ctx.fillRect(0, 0, 200, 200)
ctx.rotate(45 * Math.PI / 180);
ctx.beginPath();
ctx.fillStyle = "red";
ctx.fillRect(0, 0, 200, 100)
</script>
</html>
2.2.4自定義坐標轉換
- transform()方法以用戶自定義的變換矩陣對圖像坐標進行變換操作。這個方法有6個參數3x3的轉換矩陣 。
ctx.transform(a,b,c,d,e,f);
參數 |
描述 |
a |
水平縮放繪圖 |
b |
水平傾斜繪圖 |
c |
垂直傾斜繪圖 |
d |
垂直縮放繪圖 |
e |
水平移動繪圖 |
f |
垂直移動繪圖 |
2.2.5鏡面轉換
// 水平鏡面
ctx.scale(-1,1);
// 垂直鏡面
ctx.scale(1,-1);
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
canvas {
border: 3px solid yellow;
}
</style>
</head>
<body>
<canvas id="canvas" width="1000" height="1000"></canvas>
</body>
<script>
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');
ctx.font = '30px bold';
ctx.textBaseline = 'top';
// ctx.fillText('hello world', 0, 0);
// // 水平鏡面
// ctx.scale(-1, 1);
// ctx.fillText('hello world', -200, 100);
ctx.fillText('hello world', 0, 100);
// 垂直鏡面
ctx.scale(1, -1);
ctx.fillText('hello world', 0, -100);
</script>
</html>
2.2.6 變換狀態棧
- 使用save()和restore() 方法可以實現 對坐標變換狀態的保存或恢復。
- 先繪制一個傾斜45°的矩形藍色的。在繪制一個傾斜了70°大紅色矩形。再繪制一個不做傾斜,但是水平平移的,和垂直平是我的移的黃色矩形。
ctx.save(); //保存當前坐標系狀態
ctx.restore();//恢復之前保存的坐標系狀態
2.3 圖像數據與 URLS
2.3.1 圖像數據
- 我們通過 getImageData()方法和圖像對象的屬性獲取圖像中每一個像素點的數據。圖像數據中的每一個像素。都是由 red,green,blue 和 alpha 來表示。我們可以用 putImageData 方法來設置圖像的像素值,然后呢,重畫經過修改的這些圖像。
ctx.drawImage(imgObj, destX, destY);
var imageData = ctx.getImageData(startX, startY, canvas.width, canvas.height);
var data = imageData.data;
// ………………………………
// https://www.cnblogs.com/st-leslie/p/8317850.html?utm_source=debugrun&utm_medium=referral#removeColor
// 處理像素數據
ctx.putImageData(imageData, destX, destY);
- 注意:getImageData()方法要求圖像數據存儲在 web 服務器上,并且操作這個圖像的代碼必須是在同一臺服務器上,如果這兩條中的任意一條不滿足的話,將會拋出 SECURITY_ERR 異常。
2.3.2 獲取圖像數據 URL
- 通過 toDataURL()方法,可以得到一個指向當前圖像的 64 bit png 格式圖像文件的 URL;
canvas.toDataURL();
// 繪制已知的圖像的數據URL
// urls == 已知的圖像的數據URL 64bit 數據格式
var img = new Image();
img.src = urls;
img.onload = function() {
myctx.drawImage(this, 0, 0);
};
2.4 動畫
2.4.1 清除canvas上的內容
ctx.clearRect(startX||0,startY||0,canvas.width,canvas.height);
2.4.2 使用requestAnimationFrame 方法創建動畫
- 如果要用canvas創建動畫,推薦大家使用requestAnimationFrame方法。這個方法能夠使瀏覽器智能的去判斷它幀率,這個幀率一般我們叫什么FPS。而且對于動畫的每一幀我們都可以進行更新清除canvas,然后再申請下一幀的動畫。
- 接收一個由用戶自定義的回調函數對象作為參數。方法呢,會在當前幀完成后自定自動去調用這個回調函數,而我們要做的就是在這個回調函數。中實現或者進行我們下一步下一幀圖像繪制的一個操作,并且啊,在這個回調函數最后再次調用requestAnimationFrame方法使得這個動畫的執行可以一幀一幀的連續繪制下去。
- 由于不同的瀏覽器對該方法的支持程度不同。所以使用的時候一般要稍微做一些處理。
window.requestAnimFrame=(function(callback){
return window.requestAnimationFrame||window.webkitRequestAnimationFrame||window.mozRequestAnimationFrame||window.oRequestAnimationFrame||window.msRequestAnimationFrame||function(callback){
window.setTimeout(callback,1000/60);
}
})
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
canvas {
border: 3px solid red;
}
</style>
</head>
<body>
<canvas id="canvas" width="1000" height="1000"></canvas>
</body>
<script>
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');
function Rect(obj){
this.x=obj.x||0;
this.y=obj.y||0;
this.w=obj.w||50;
this.h=obj.h||50;
this.fcolor=obj.fcolor||'black';
this.scolor=obj.scolor||'red';
// this.speed=obj.speed||{sx:1,sy:1};
this.speedx=obj.sx;
this.speedy=obj.sy;
}
Rect.prototype.move=function(){
this.x+=this.speedx;
this.y+=this.speedy;
}
Rect.prototype.draw=function(ctx){
ctx.beginPath();
ctx.fillStyle=this.fcolor;
ctx.strokeStyle=this.scolor;
ctx.fillRect(this.x,this.y,this.w,this.h);
ctx.strokeRect(this.x,this.y,this.w,this.h);
}
var objArr=[];
// 創建指定的對象
function createRect(){
var rect1=new Rect({x:10,y:10,w:40,h:40,fcolor:'red',sx:5,sy:2});
objArr.push(rect1);
var rect2=new Rect({x:800,y:600,w:140,h:140,fcolor:'red',sx:-15,sy:-20});
objArr.push(rect2);
}
createRect();
var requestAniObj=null;
var flag=true;
function Animate(){
// 清屏
ctx.clearRect(0,0,ctx.canvas.width,ctx.canvas.height);
// 繪制下一幀動畫;
for(var i=0;i<objArr.length;i++){
objArr[i].move();
objArr[i].draw(ctx);
}
// 請求下一幀動畫的調用
// requestAnimationFrame(function(){
// Animate();
// })
requestAniObj=requestAnimationFrame(Animate);
}
Animate();
canvas.onclick=function(){
flag=!flag;
if(!flag){
cancelAnimationFrame(requestAniObj);
}else{
Animate();
}
}
</script>
</html>
<!DOCTYPE html>
<html>
<head>
<style>
body {
margin: 0px;
padding: 0px;
}
canvas {
border: 2px solid #f00;
}
.btn {
padding: 10px 25px;
background: #f00;
color: #fff;
display: inline-block;
border-radius: 4px;
}
</style>
</head>
<body>
<canvas id="myCanvas" width="1000" height="630"></canvas>
<div class="btn">創建矩形</div>
<script>
// CanvasRenderingContext2D.prototype.objArr=[];
CanvasRenderingContext2D.prototype.draw = function(obj) {
this.beginPath();
this.fillStyle = obj.fc;
this.strokeStyle = obj.sc;
if (obj.isfill) {
this.fillRect(obj.x, obj.y, obj.w, obj.h);
}
if (obj.isstroke) {
this.strokeRect(obj.x, obj.y, obj.w, obj.h);
}
// for(var i in objArr){
// objArr[i]
// }
};
CanvasRenderingContext2D.prototype.clear = function() {
this.clearRect(0, 0, this.canvas.width, this.canvas.height);
};
// 全局時間軸
var time = 0;
var allRectArr = [];
var myCanvas = document.getElementById("myCanvas");
var myctx = myCanvas.getContext("2d");
// 矩形對象 構造函數
function Rect(obj) {
this.x = obj.x || 0;
this.y = obj.y || 0;
this.w = obj.w || 50;
this.h = obj.h || 50;
this.vx = obj.vx || 2;
this.vy = obj.vy || 2;
this.isstroke = false;
this.isfill = true;
this.fc = obj.fc || "#999";
this.sc = obj.sc || "black";
}
Rect.prototype.move = function() {
// 線性運動
this.x += this.vx;
};
// 1000/60==fps
// 16 16.6
// 0----1----2----3----4----5----6
function Animate() {
time++;
// 清除畫布
myctx.clear();
// 間隔一段時間 創建一個新的元素 1600 毫秒
if (time % 100 == 0) {
var rData = {
x: 0,
y: getRandom(0, 600),
w: getRandom(50, 150),
h: getRandom(50, 150),
fc:
"rgb(" +
getRandom(0, 255) +
"," +
getRandom(0, 255) +
"," +
getRandom(0, 255) +
")",
vx: getRandom(1, 10),
vy: 0
};
// debugger
var r = new Rect(rData);
allRectArr.push(r);
}
// if(time%1==0){
// for(var i in allRectArr){
// allRectArr[i].move();
// }
// }
for (var i in allRectArr) {
myctx.draw(allRectArr[i]);
// 加速運動
allRectArr[i].vx += 1;
allRectArr[i].move();
}
// 并且移動 繪制 3200毫秒
requestAnimationFrame(Animate);
}
Animate();
function getRandom(min, max) {
return Math.round(Math.random() * (max - min) + min);
}
// Math.prototype.getRandom = function (min, max) {
// return Math.round(Math.random() * (max - min) + min);
// }
// //渲染 行為
// Rect.prototype.draw = function () {
// }
// // 移動 行為 線性運動
// Rect.prototype.move = function () {
// }
// requestAnimationFrame() 遞歸函數 有時間(間隔--系統自動調整)控制的
// 震蕩效果
// 計算像素的坐標位置:x(時間)+振幅*sin(時間*2PI/周期)+x0.(x0為像素點的原始位置)
</script>
</body>
</html>
<!DOCTYPE html>
<html>
<head>
<style>
body {
margin: 0px;
padding: 0px;
}
canvas {
border: 2px solid #f00;
}
.btn {
padding: 10px 25px;
background: #f00;
color: #fff;
display: inline-block;
border-radius: 4px;
}
</style>
</head>
<body>
<canvas id="myCanvas" width="1000" height="630"></canvas>
<div class="btn">創建矩形</div>
<script>
var myCanvas = document.getElementById("myCanvas");
var ctx = myCanvas.getContext("2d");
var myRect = {
x: 0,
y: 0,
w: 100,
h: 50,
bw: 5
};
// 震蕩效果
// 計算像素的坐標位置:振幅*sin(時間*2PI/周期)+x0.(x0為像素點的原始位置)
function animate() {
// 清除
ctx.clearRect(0, 0, myCanvas.width, myCanvas.height);
// 清除
// 更新圖像
var date = new Date();
var time = date.getTime(); //時間
var amplitude = 250; //振幅
var period = 600; //周期
var centerX = myCanvas.width / 2 - myRect.w / 2;
var nextX =
amplitude * Math.sin((time * 2 * Math.PI) / period) + centerX;
myRect.x = nextX;
// 更新圖像
// 繪圖
ctx.beginPath();
ctx.fillStyle = "orange";
ctx.strokeStyle = "#f00";
ctx.lineWidth = myRect.bw;
ctx.fillRect(myRect.x, myRect.y, myRect.w, myRect.h);
// 繪圖
// 請求下一幀
requestAnimationFrame(animate);
// 請求下一幀
}
animate();
</script>
</body>
</html>