<canvas> 標籤只是圖形的容器,
可以使用多種方法在Canvas 上繪製路徑、盒、圓、字符以及添加圖像。
下圖是用 <canvas> 創建的:
這是一個基本的空畫布示例:
<canvas id="emptyCanvas" width="200" height="100"></canvas>
注意:預設情況下<canvas>元素沒有邊框和內容。
使用 style 屬性來添加邊框:
<canvas id="myCanvas" width="200" height="100" style="border:1px solid #000000;"> </canvas>
var c = document.getElementById("myCanvas");
其次,需要一個畫布的繪圖物件。
getContext() 是一個內置的 HTML 物件,具有用於繪製的屬性和方法:
var ctx = c.getContext("2d");
在 Canvas上畫線,我們將使用以下兩種方法:
ctx.moveTo(0, 0); ctx.lineTo(200, 100); ctx.stroke();
var canvas = document.getElementById("myCanvas");
var ctx = canvas.getContext("2d");
ctx.fillStyle = "#FF0000";
ctx.fillRect(0,0,150,75);
var c = document.getElementById("myCanvas");
var ctx = c.getContext("2d");
ctx.beginPath();
ctx.arc(95, 50, 40, 0, 2 * Math.PI);
ctx.stroke();
beginPath() 產生一個新路徑,產生後再使用繪圖指令來設定路徑。
注意第4和5的參數使用弧度。
弧度與角度間換算: radians = (Math.PI/180) * degrees
var c = document.getElementById("myCanvas");
var ctx = c.getContext("2d");
ctx.lineWidth = 1;
ctx.fillStyle = "#FF0000"
ctx.beginPath();
ctx.moveTo(95,50);
ctx.lineTo(95-Math.sin(0.1666*Math.PI),50+Math.cos(0.1666*Math.PI) );
ctx.arc(95,50,40,1.1666*Math.PI,(2-0.1666)*Math.PI);
ctx.lineTo(95,50);
ctx.stroke();
ctx.fill();
quadraticCurveTo(cp1x, cp1y, x, y) (en-US)
從目前起始點畫一條二次貝茲曲線到x, y指定的終點,控制點由cp1x, cp1y指定。
bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y) (en-US)
從目前起始點畫一條三次貝茲曲線到x, y指定的終點,控制點由(cp1x, cp1y)和(cp2x, cp2y)指定。
ctx.beginPath();
ctx.moveTo(75,25);
ctx.quadraticCurveTo(25,25,25,62.5);
ctx.quadraticCurveTo(25,100,50,100);
ctx.quadraticCurveTo(50,120,30,125);
ctx.quadraticCurveTo(60,120,65,100);
ctx.quadraticCurveTo(125,100,125,62.5);
ctx.quadraticCurveTo(125,25,75,25);
ctx.stroke();
ctx.beginPath();
ctx.moveTo(75,40);
ctx.bezierCurveTo(75,37,70,25,50,25);
ctx.bezierCurveTo(20,25,20,62.5,20,62.5);
ctx.bezierCurveTo(20,80,40,102,75,120);
ctx.bezierCurveTo(110,102,130,80,130,62.5);
ctx.bezierCurveTo(130,62.5,130,25,100,25);
ctx.bezierCurveTo(85,25,75,37,75,40);
ctx.fill();
var c = document.getElementById("myCanvas");
var ctx = c.getContext("2d");
ctx.font = "30px Arial";
ctx.fillText("Hello World", 10, 50);
以下有兩種不同的方式來設置 Canvas 梯度:
addColorStop()方法指定色標,參數使用坐標來描述,可以是0至1。
使用梯度時,設定 fillStyle 或 strokeStyle 的值為梯度,然後繪製形狀,如矩形,文本,或一條線。
var c = document.getElementById("myCanvas");
var ctx = c.getContext("2d");
// 創建梯度物件
var grd = ctx.createLinearGradient(0, 0, 200, 0);
grd.addColorStop(0, "red");
grd.addColorStop(1, "white");
// 用梯度填充
ctx.fillStyle = grd;
ctx.fillRect(10, 10, 150, 80);
var c = document.getElementById("myCanvas");
var ctx = c.getContext("2d");
// Create gradient
var grd = ctx.createRadialGradient(75, 50, 5, 90, 60, 100);
grd.addColorStop(0, "red");
grd.addColorStop(1, "white");
// Fill with gradient
ctx.fillStyle = grd;
ctx.fillRect(10, 10, 150, 80);
drawImage()方法是一個多載 (overload)方法,最基本的型態:
drawImage(image,x,y)
window.onload = function() {
var canvas = document.getElementById("myCanvas");
var ctx = canvas.getContext("2d");
var img = document.getElementById("scream");
ctx.drawImage(img, 10, 10);
};
例題:canvas image
drawImage(image, x, y, width, height)
function draw() {
var ctx = document.getElementById('canvas').getContext('2d');
var img = new Image();
img.onload = function(){
for (var i=0;i<4;i++){
for (var j=0;j<3;j++){
ctx.drawImage(img,j*50,i*38,50,38);
}
}
};
img.src = 'https://mdn.mozillademos.org/files/5397/rhino.jpg';
}
drawImage(image, sx, sy, sWidth, sHeight, dx, dy, dWidth, dHeight)
function draw() {
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');
// Draw slice
ctx.drawImage(document.getElementById('source'),
33, 71, 104, 124, 21, 20, 87, 104);
// Draw frame
ctx.drawImage(document.getElementById('frame'),0,0);
}
save() : 儲存現階段畫布完整狀態。
restore() : 復原最近一次儲存的畫布狀態。
可以呼叫save()的次數不限,而每一次呼叫restore(),最近一次儲存的畫布狀態便會從堆疊中被取出,然後還原畫布到此畫布狀態。
var ctx = document.getElementById('canvas').getContext('2d');
ctx.fillRect(0,0,150,150); // 畫一預設設定的矩形
ctx.save(); // 儲存預設的狀態
ctx.fillStyle = '#09F' // 改變顏色的設定
ctx.fillRect(15,15,120,120); // 畫一新設定的矩形
ctx.save(); // 儲存目前的狀態
ctx.fillStyle = '#FFF' // 改變顏色的設定
ctx.globalAlpha = 0.5; //設定透明度
ctx.fillRect(30,30,90,90); // 畫一新設定的矩形
ctx.restore(); // 復原前一狀態
ctx.fillRect(45,45,60,60); // 畫一復原前設定的矩形
ctx.restore(); // 復原原有狀態
ctx.fillRect(60,60,30,30); // 畫一復原前設定的矩形
translate(x, y)移動網格上的畫布,其中x代表水平距離、y代表垂直距離。
另外一個draw()函數透過兩個 for 迴圈移動畫布原點、呼叫drawSpirograph()函數、復歸畫布圓點位置共九次。
例題:spirograph
我們執行了兩個迴圈來作圖,第一個迴圈決定的圓環個數和該圓環上圓環上圓點的個數的顏色。
第二個迴圈決定了圓環上圓點的個數,每一次作圖前我們都儲存了原始畫布狀態,以便結束時可以復原狀態。
畫布旋轉的弧度則以圓環上圓點的個數決定,像是最內圈的圓環共有六個圓點,所以每畫一個原點,畫布就旋轉60度(360度/6)。
第二的圓環有12個原點,所以畫布一次旋轉度數為30度(360度/12),以此類推。
function draw() {
var ctx = document.getElementById('canvas').getContext('2d');
ctx.translate(75,75);
for (var i=1;i<6;i++){ // 由內而外,六圈
ctx.save();
ctx.fillStyle = 'rgb('+(51*i)+','+(255-51*i)+',255)';
for (var j=0; j < i*6; j++){ // 畫各別圓點
ctx.rotate(Math.PI*2/(i*6));
ctx.beginPath();
ctx.arc(0,i*12.5,5,0,Math.PI*2,true);
ctx.fill();
}
ctx.restore();
}
}
transform(m11, m12, m21, m22, dx, dy)
呼叫Transform會拿目前的變形矩陣乘以下列矩陣:
m11 m21 dx m12 m22 dy 0 0 1其中m11代表水平縮放圖像,m12代表水平偏移圖像,m21代表垂直偏移圖像,m22代表垂直縮放圖像,dx代表水平移動圖像,dy代表垂直移動圖像。 如果輸入Infinity 值,不會引起例外錯誤,矩陣值會依照輸入設成無限。
setTransform(m11, m12, m21, m22, dx, dy)復原目前矩陣為恆等矩陣(Identiy matrix,也就是預設矩陣),然後再以輸入參數呼叫transform()。
例題:matrix
我們得靠每隔一段時間繪圖來產生動畫:
setInterval(function, delay)
setTimeout(function, delay)
requestAnimationFrame(callback)
例題:循環景色
例題:圓球彈跳
參考資料Basic_animations
例題:可撕布
參考資料tearable cloth
例題:blobsallad
參考資料blobsallad
例題:BigBuckBunny
參考資料9 Mind-Blowing Canvas Demos
<canvas id="canvas" width="400" height="400"
style="background-color:#333"></canvas>
// 在網頁創建畫布
<script>
var canvas = document.getElementById("canvas");
// 由畫布元件,造一畫布物件
var ctx = canvas.getContext("2d");
// 設定物件內容為平面圖形
var radius = canvas.height / 2; // 計算半徑
ctx.translate(radius, radius); // 平移到畫布中心
radius = radius * 0.90 // 留10% 鐘面邊緣
drawClock();
function drawClock() { // 畫白色鐘面
ctx.arc(0, 0, radius, 0 , 2 * Math.PI);
ctx.fillStyle = "white";
ctx.fill();
}
</script>
function drawClock() {
drawFace(ctx, radius);
}
function drawFace(ctx, radius) {
var grad;
// 繪製白色圓圈
ctx.beginPath();
ctx.arc(0, 0, radius, 0, 2 * Math.PI);
ctx.fillStyle = 'white';
ctx.fill();
// 創建徑向漸變(原始時鐘半徑的 95% 和 105%)
grad = ctx.createRadialGradient
(0, 0 ,radius * 0.95, 0, 0, radius * 1.05);
grad.addColorStop(0, '#333');
grad.addColorStop(0.5, 'white');
grad.addColorStop(1, '#333');
ctx.strokeStyle = grad;
ctx.lineWidth = radius*0.1;
ctx.stroke();
// 色標創建 3D 效果
ctx.beginPath();
ctx.arc(0, 0, radius * 0.1, 0, 2 * Math.PI);
ctx.fillStyle = '#333';
ctx.fill();
}
function drawClock() {
drawFace(ctx, radius);
drawNumbers(ctx, radius);
}
function drawNumbers(ctx, radius) {
var ang;
var num;
ctx.font = radius * 0.15 + "px arial";
ctx.textBaseline = "middle"; // 將文本對齊設置為打印位置的中間
ctx.textAlign = "center"; // 和中心:
for(num = 1; num < 13; num++){ // 將打印數字位置計算為半徑的 85%,
ang = num * Math.PI / 6; // 每個數字旋轉 30度:
ctx.rotate(ang);
ctx.translate(0, -radius * 0.85);
ctx.rotate(-ang);
ctx.fillText(num.toString(), 0, 0);
ctx.rotate(ang);
ctx.translate(0, radius * 0.85);
ctx.rotate(-ang);
}
}
function drawClock() {
drawFace(ctx, radius);
drawNumbers(ctx, radius);
drawTime(ctx, radius);
}
function drawTime(ctx, radius){
var now = new Date();
var hour = now.getHours();
var minute = now.getMinutes();
var second = now.getSeconds();
// 計算時針的角度、長度和寬度
hour = hour%12;
hour = (hour*Math.PI/6)+(minute*Math.PI/(6*60))
+(second*Math.PI/(360*60));
drawHand(ctx, hour, radius*0.5, radius*0.07);
// 計算分針的角度、長度和寬度
minute = (minute*Math.PI/30)+(second*Math.PI/(30*60));
drawHand(ctx, minute, radius*0.8, radius*0.07);
// 計算秒針的角度、長度和寬度
second = (second*Math.PI/30);
drawHand(ctx, second, radius*0.9, radius*0.02);
}
function drawHand(ctx, pos, length, width) {
ctx.beginPath();
ctx.lineWidth = width;
ctx.lineCap = "round";
ctx.moveTo(0,0);
ctx.rotate(pos);
ctx.lineTo(0, -length);
ctx.stroke();
ctx.rotate(-pos);
}
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
var radius = canvas.height / 2;
ctx.translate(radius, radius);
radius = radius * 0.90
//drawClock();
setInterval(drawClock, 1000);