<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);