目錄
0x00 繪製線條的一些細節
ctx.moveTo() 表示將筆尖移動到某個位置,並不會將兩點連接
ctx.lineTo() 表示從上一個座標點 連接 到當前座標點
ctx.beginPath() + ctx.lineTo() 相當於 ctx.moveTo 因爲上一個座標點被beginPath清空了,清空並不代表上一個座標點爲(0,0),而是代表上一個座標點沒有。
繪製多條不連續的線段就需要應用moveTo
效果:
代碼:
<script>
var canvas = document.getElementById('canvas');
canvas.width = 1024;
canvas.height = 689;
var ctx = canvas.getContext('2d');
ctx.moveTo(100,200);
ctx.lineTo(300,400);
ctx.lineTo(100,600);
ctx.moveTo(300,200);
ctx.lineTo(500,400);
ctx.lineTo(300,600);
ctx.moveTo(500,200);
ctx.lineTo(700,400);
ctx.lineTo(500,600);
ctx.lineWidth = 10;
ctx.strokeStyle ='#058';
ctx.stroke();
</script>
0x01 繪製多條不同顏色的折線
ctx.beginPath() 表示開始一段全新的路徑,清空原有狀態。
但是如果一個狀態沒有被改變,則該狀態不會被清空。
例如下文中ctx.lineWidth = 10
var canvas = document.getElementById('canvas');
canvas.width = 1024;
canvas.height = 689;
var ctx = canvas.getContext('2d');
ctx.lineWidth = 10;
ctx.beginPath();
ctx.moveTo(100,200);
ctx.lineTo(300,400);
ctx.lineTo(100,600);
ctx.strokeStyle ='green';
ctx.stroke();
ctx.beginPath();
ctx.moveTo(300,200);
ctx.lineTo(500,400);
ctx.lineTo(300,600);
ctx.strokeStyle ='red';
ctx.stroke();
ctx.beginPath();
ctx.moveTo(500,200);
ctx.lineTo(700,400);
ctx.lineTo(500,600);
ctx.strokeStyle ='blue';
ctx.stroke();
效果:
0x02 繪製封閉圖形
ctx.closePath() 表示當前路徑要被封閉,並且結束路徑。
如果路徑沒有被封閉,那麼closePath會自動封閉
封閉圖形建議放在ctx.beginPath() 和 ctx.closePath() 之間,否則可能會有缺口
修正:
var canvas = document.getElementById('canvas');
canvas.width = 1024;
canvas.height = 689;
var ctx = canvas.getContext('2d');
ctx.lineWidth = 10;
ctx.beginPath();
ctx.moveTo(100,200);
ctx.lineTo(300,400);
ctx.lineTo(100,600);
ctx.lineTo(100,200);
ctx.closePath();
ctx.strokeStyle ='green';
ctx.stroke();
0x03 填充圖形
如果先描邊再填充顏色的話,邊框的內側5px將會被填充色覆蓋
如果先填充後描邊的話,邊框的內側的5px將會覆蓋填充色
<script>
var canvas = document.getElementById('canvas');
canvas.width = 1024;
canvas.height = 689;
var ctx = canvas.getContext('2d');
ctx.lineWidth = 10;
ctx.beginPath();
ctx.moveTo(100,200);
ctx.lineTo(300,400);
ctx.lineTo(100,600);
ctx.lineTo(100,200);
ctx.closePath();
ctx.strokeStyle ='green';
ctx.stroke();
ctx.fillStyle = 'yellow';
ctx.fill();
</script>
0x04 繪製矩形:
rect(x,y,width,height) 只是規劃出了矩形的路徑
var canvas = document.getElementById('canvas');
canvas.width = 1024;
canvas.height = 689;
var ctx = canvas.getContext('2d');
ctx.beginPath();
ctx.rect(100,100,200,200);
ctx.closePath();
ctx.lineWidth = 10;
ctx.stroke();
ctx.fillStyle='red';
ctx.fill();
fillRect(x,y,width,height) 不但規劃出了路徑,而且將直接使用當前的fillStyle,繪製出一個填充的矩形
strokeRect(x,y,width,height) 不但規劃出了路徑,而且將直接使用當前的strokeStyle,繪製出一個矩形邊框
/**
* 修改填充色爲紅色
*/
ctx.fillStyle = 'red';
/**
*繪製矩形
*@ 起點的x軸座標,以canvas畫布的左上角爲原點,向右爲x軸的正方向,向下爲y軸的正方向建立座標系
*@ 起點的y軸座標
*@ 寬度
*@ 高度
*/
ctx.fillRect(0,0,50,50);
0x05 線條的屬性:
#lineCap 線條的帽子(線條兩端):只能用於線段的開始和結尾處,不能用於線段的連接處
butt
round 多出一個圓形的頭
stroke 多出一個方形的頭
代碼:
var canvas = document.getElementById('canvas');
canvas.width = 1024;
canvas.height = 689;
var ctx = canvas.getContext('2d');
ctx.lineWidth = 10;
ctx.strokeStyle = "#005588";
ctx.beginPath();
ctx.moveTo(100,200);
ctx.lineTo(700,200);
ctx.lineCap = "butt";
ctx.stroke();
ctx.beginPath();
ctx.moveTo(100,400);
ctx.lineTo(700,400);
ctx.lineCap = "round";
ctx.stroke();
ctx.beginPath();
ctx.moveTo(100,600);
ctx.lineTo(700,600);
ctx.lineCap = "square";
ctx.stroke();
//baseline
ctx.lineWidth = 1;
ctx.strokeStyle = '#27a';
ctx.moveTo(100,100);
ctx.lineTo(100,700);
ctx.moveTo(700,100);
ctx.lineTo(700,700);
ctx.stroke();
#lineJoin 設置線條與線條相接時的樣式
ctx.lineJoin = 'miter';
miter(默認) 尖角,當設置爲miter時,還可以設置miterLimit屬性
ctx.miterLimit = 20
miterLimit是什麼呢?是內角尖端 與 外角尖端之間的距離的最小值,當小於該值時,lineJoin將會被修改爲bevel
產生miterLimit的前提是線條有寬度
bevel 斜接的形式,類似於長條紙帶的摺疊
round 圓角的形式
0x06 繪製五角星
思路:
先假設座標原點爲 五角星的中心
那麼就可以通過三角函數算出五角星各個角的位置座標了
因爲不論 五角星大圓的點還是小圓上的點,相鄰的點之間都相差 360/5 = 72°,所以可以 寫一個循環:畫大圓上的點 >> 畫小圓上的點。
然後將畫好的五角星平移一下,就可以了。
代碼:
<script>
var canvas = document.getElementById('canvas');
canvas.width = 1024;
canvas.height = 689;
var ctx = canvas.getContext('2d');
/**
* 繪製五角星
* ctx 上下文
* r 小圓半徑
* R 大圓半徑
* x x軸方向上的平移
* y y軸方向上的平移
* rot 順時針旋轉角度
*/
function drawStar( ctx,r,R,x,y,rot){
ctx.beginPath();
for(var i=0;i<5;i++){
// 大圓半徑爲300 ,平移400px
ctx.lineTo(
Math.cos((18 +i*72 - rot)*Math.PI/180)*R + x,
-Math.sin((18 +i*72 - rot )*Math.PI/180)*R + y
);
//小圓半徑爲150,平移400px;
ctx.lineTo(
Math.cos((54 +i*72 - rot)*Math.PI/180)*r +x,
-Math.sin((54 +i*72 - rot)*Math.PI/180)*r +y
);
}
ctx.closePath();
ctx.lineWidth = 10;
ctx.stroke();
0}
drawStar(ctx,3,7,500,400,12);
</script>
0x07 優化:五角星的繪製 (圖形變換) 繪製一片星空
圖形學中繪製圖形的套路:
先繪製一個標準圖形,然後通過圖形變換得到我們想要的形狀。
在圖形學中,圖形變換一般有三種:
位移:translate(x,y)將圖像原點位移到x,y的位置
旋轉:rotate(deg)
縮放:scale(sx,sy)在橫向進行sx倍的縮放,在縱向進行sy倍的縮放
因爲ctx.translate是可以疊加的,所以當我們繪製第二個矩形的時候,矩形的原點座標已經移動到400,400的位置
var canvas = document.getElementById('canvas');
canvas.width = 1024;
canvas.height = 689;
var ctx = canvas.getContext('2d');
ctx.fillStyle = "red";
ctx.translate(100,100);
ctx.fillRect(0,0,400,400);
ctx.fillStyle ='green';
ctx.translate(300,300);
ctx.fillRect(0,0,400,400);
解決方法:
方法1:反向translate抵消(過於麻煩)
var canvas = document.getElementById('canvas');
canvas.width = 1024;
canvas.height = 689;
var ctx = canvas.getContext('2d');
ctx.fillStyle = "red";
ctx.translate(100,100);
ctx.fillRect(0,0,400,400);
ctx.translate(-100,-100);
ctx.fillStyle ='green';
ctx.translate(300,300);
ctx.fillRect(0,0,400,400);
方法2:ctx.save() 保存當前圖形的狀態
ctx.restore() 恢復在ctx.save() 時保存的所有狀態
ctx.save();
ctx.fillStyle = "red";
ctx.translate(100,100);
ctx.fillRect(0,0,400,400);
ctx.restore();
ctx.save();
ctx.fillStyle ='green';
ctx.translate(300,300);
ctx.fillRect(0,0,400,400);
ctx.restore();
#scale 縮放
scale縮放不僅僅縮放 寬度 和 高度,而且會縮放 圖像的原點座標,已經線條的寬度
案例:
var canvas = document.getElementById('canvas');
canvas.width = 1024;
canvas.height = 689;
var ctx = canvas.getContext('2d');
ctx.lineWidth = 5;
ctx.save();
ctx.scale(1,1);
ctx.strokeRect(50,50,200,200);
ctx.restore();
ctx.save();
ctx.scale(2,2);
ctx.strokeRect(50,50,200,200);
ctx.restore();
ctx.save();
ctx.scale(3,3);
ctx.strokeRect(50,50,200,200);
ctx.restore();
效果:
#繪製一片星空
效果:
代碼:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<style>
#canvas{
border:1px solid black;
position:absolute;
left:50%;
top:50%;
transform: translate(-50%,-50%);
}
</style>
</head>
<body>
<canvas id='canvas'></canvas>
</body>
<script>
window.onload=function(){
var canvas = document.getElementById('canvas');
canvas.width = 1024;
canvas.height = 569;
var ctx = canvas.getContext('2d');
//黑夜
ctx.fillStyle = 'black';
ctx.fillRect(0,0,canvas.width,canvas.height);
//200個星星
for(var i=0;i<40;i++){
var r = Math.random() * 5 + 2; //2 - 7之間的隨機值
var x = Math.random() * canvas.width;
var y = Math.random() * canvas.height / 3;
var rot = Math.random() * 360;
drawStar(ctx,r,x,y,rot);
}
}
/**
* 產生一個標準星星的路徑
* 一個位於(0,0)點 外圓半徑爲1,內圓半徑爲0.5的星星
*
*/
function starPath(ctx){
var R = 1;
var r = 0.5 * R;
var rot =0;
var x = 0;
var y =0;
ctx.beginPath();
for(var i=0;i<5;i++){
// 大圓半徑爲300 ,平移400px
ctx.lineTo(
Math.cos((18 +i*72 - rot)*Math.PI/180)*R + x,
-Math.sin((18 +i*72 - rot )*Math.PI/180)*R + y
);
//小圓半徑爲150,平移400px;
ctx.lineTo(
Math.cos((54 +i*72 - rot)*Math.PI/180)*r +x,
-Math.sin((54 +i*72 - rot)*Math.PI/180)*r +y
);
}
ctx.closePath();
}
/**
* 繪製一個五角星
* ctx 上下文
* r 小圓半徑
* x x軸方向上的平移
* y y軸方向上的平移
* rot 順時針旋轉角度
*/
function drawStar( ctx,r,x,y,rot){
ctx.save();
//因爲 我們左上角座標是0,0,所以scale不會改變左上角的座標
ctx.translate(x,y);
ctx.rotate(rot * Math.PI/180);
ctx.scale(r,r);
starPath(ctx);
ctx.lineJoin = 'round';
ctx.fillStyle = '#fb3';
ctx.strokeStyle = '#fd5';
ctx.fill();
ctx.restore();
}
</script>
</html>
0x08 圖形變換的本質
圖形變換的本質是對一個圖形的所有頂點座標的一次再計算
這個計算是由一個叫變換矩陣的 矩陣來完成的
對於二維繪圖系統來說,變換矩陣是一個3x3的矩陣
如果是三維的繪圖系統來說,變換矩陣則是一個4x4的矩陣
a 控制 水平縮放的值, 默認值爲1
圖形原有的頂點座標 乘以 該矩陣 即可得到 新的頂點座標
canvas中便提供了一個函數讓我們可以改變這個矩陣
transform(a,b,c,d,e,f) 設置變換矩陣
注意:transform 是可以疊加的,爲了解決這個問題,canvas提供了一個函數 setTransfrom(a,b,c,d,e,f) 即忽略之前設置的所有transfrom,設置一個全新的transform
<script>
var canvas = document.getElementById('canvas');
canvas.width = 1024;
canvas.height = 689;
var ctx = canvas.getContext('2d');
ctx.fillStyle = "red";
ctx.strokeStyle = "#058";
ctx.lineWidth = 5;
/**
* a,d 水平,垂直縮放
* b,c 水平,垂直傾斜
* e,f 水平,垂直位移
*
*/
ctx.save();
ctx.transform(1,0,0,1,50,100)
ctx.fillRect(50,50,300,300);
ctx.strokeRect(50,50,300,300);
ctx.restore();
</script>