canvas基础2--绘制图形
柵格
繪制矩形
不同于SVG,HTML中的元素canvas只支持一種原生的圖形繪制:矩形。所有其他的圖形的繪制都至少需要生成一條路徑。不過,我們擁有眾多路徑生成的方法讓復雜圖形的繪制成為了可能。
首先,我們回到矩形的繪制中。canvas提供了三種方法繪制矩形:
fillRect(x, y, width, height)繪制一個填充的矩形strokeRect(x, y, width, height)繪制一個矩形的邊框clearRect(x, y, width, height)清除指定矩形區域,讓清除部分完全透明。
上面提供的方法之中每一個都包含了相同的參數。x與y指定了在canvas畫布上所繪制的矩形的左上角(相對于原點)的坐標。width和height設置矩形的尺寸。
下面的draw() 函數是前一頁中取得的,現在就來使用上面的三個函數。
矩形(Rectangular)例子
function draw() {
var canvas = document.getElementById('canvas');
if (canvas.getContext) {
var ctx = canvas.getContext('2d');
ctx.fillRect(25,25,100,100);
ctx.clearRect(45,45,60,60);
ctx.strokeRect(50,50,50,50);
}
}
繪制路徑
圖形的基本元素是路徑。路徑是通過不同顏色和寬度的線段或曲線相連形成的不同形狀的點的集合。一個路徑,甚至一個子路徑,都是閉合的。使用路徑繪制圖形需要一些額外的步驟。
首先,你需要創建路徑起始點。
然后你使用畫圖命令去畫出路徑。
之后你把路徑封閉。
一旦路徑生成,你就能通過描邊或填充路徑區域來渲染圖形。
以下是所要用到的函數:
beginPath()新建一條路徑,生成之后,圖形繪制命令被指向到路徑上生成路徑。closePath()閉合路徑之后圖形繪制命令又重新指向到上下文中。stroke()通過線條來繪制圖形輪廓。fill()通過填充路徑的內容區域生成實心的圖形。
生成路徑的第一步叫做beginPath()。本質上,路徑是由很多子路徑構成,這些子路徑都是在一個列表中,所有的子路徑(線、弧形、等等)構成圖形。而每次這個方法調用之后,列表清空重置,然后我們就可以重新繪制新的圖形。
注意:當前路徑為空,即調用beginPath()之后,或者canvas剛建的時候,第一條路徑構造命令通常被視為是moveTo(),無論最后的是什么。出于這個原因,你幾乎總是要在設置路徑之后專門指定你的起始位置。
第二步就是調用函數指定繪制路徑,本文稍后我們就能看到了。
第三,就是閉合路徑closePath(),不是必需的。這個方法會通過繪制一條從當前點到開始點的直線來閉合圖形。如果圖形是已經閉合了的,即當前點為開始點,該函數什么也不做。
注意:當你調用fill()函數時,所有沒有閉合的形狀都會自動閉合,所以你不需要調用closePath()函數。但是調用stroke()時不會自動閉合。
繪制一個三角形
function draw(){
var canvas = document.getElementById('canvas');
if(canvas.getContext){
var ctx = canvas.get Context('2d');
ctx.beginPath();
ctx.moveTo(75,50);
ctx.lineTo(100,75);
ctx.fillTo(100,25);
ctx.fill();
}
}
移動筆觸
moveTo(x,y)
將筆觸移動到指定的坐標x以及y上。
當canvas初始化或者beginPath()調用后,你通常會使用moveTo()函數設置起點。我們也能夠使用moveTo()繪制一些不連續的路徑。看一下下面的笑臉例子。我將用到moveTo()方法(紅線處)的地方標記了。
function draw() {
var canvas = document.getElementById('canvas');
if (canvas.getContext){
var ctx = canvas.getContext('2d');
ctx.beginPath();
ctx.arc(75,75,50,0,Math.PI*2,true); // 繪制
ctx.moveTo(110,75);
ctx.arc(75,75,35,0,Math.PI,false); // 口(順時針)
ctx.moveTo(65,65);
ctx.arc(60,65,5,0,Math.PI*2,true); // 左眼
ctx.moveTo(95,65);
ctx.arc(90,65,5,0,Math.PI*2,true); // 右眼
ctx.stroke();
}
}
線
lineTo(x, y)
繪制一條從當前位置到指定x以及y位置的直線。
該方法有兩個參數:x以及y ,代表坐標系中直線結束的點。開始點和之前的繪制路徑有關,之前路徑的結束點就是接下來的開始點,等等。。。開始點也可以通過moveTo()函數改變。
下面的例子繪制兩個三角形,一個是填充的,另一個是描邊的。
function draw() {
var canvas = document.getElementById('canvas');
if (canvas.getContext){
var ctx = canvas.getContext('2d');
// 填充三角形
ctx.beginPath();
ctx.moveTo(25,25);
ctx.lineTo(105,25);
ctx.lineTo(25,105);
ctx.fill();
// 描邊三角形
ctx.beginPath();
ctx.moveTo(125,125);
ctx.lineTo(125,45);
ctx.lineTo(45,125);
ctx.closePath();
ctx.stroke();
}
}
圓弧
繪制圓弧或者圓,我們使用arc()方法。當然可以使用arcTo(),不過這個的實現并不是那么的可靠,所以我們這里不作介紹。
arc(x, y, radius, startAngle, endAngle, anticlockwise)畫一個以(x,y)為圓心的以radius為半徑的圓弧(圓),從startAngle開始到endAngle結束,按照anticlockwise給定的方向(默認為順時針)來生成。arcTo(x1, y1, x2, y2, radius)根據給定的控制點和半徑畫一段圓弧,再以直線連接兩個控制點。--繪制與當前端點、端點1、端點2三個點所形成的夾角的兩邊相切并且半徑為radius的圓的一段弧線
該方法有五個參數:x,y為繪制圓弧所在圓上的圓心坐標。radius為半徑。startAngle以及endAngle參數用弧度定義了開始以及結束的弧度。這些都是以x軸為基準。參數anticlockwise為一個布爾值。為true時,是逆時針方向,否則順時針方向。
注意:arc()函數中的角度單位是弧度,不是度數。角度與弧度的js表達式:radians=(Math.PI/180)*degrees。
function draw() {
var canvas = document.getElementById('canvas');
if (canvas.getContext){
var ctx = canvas.getContext('2d');
for(var i=0;i<4;i++){
for(var j=0;j<3;j++){
ctx.beginPath();
var x = 25+j*50; // x 坐標值
var y = 25+i*50; // y 坐標值
var radius = 20; // 圓弧半徑
var startAngle = 0; // 開始點
var endAngle = Math.PI+(Math.PI*j)/2; // 結束點
var anticlockwise = i%2==0 ? false : true; // 順時針或逆時針
ctx.arc(x, y, radius, startAngle, endAngle, anticlockwise);
if (i>1){
ctx.fill();
} else {
ctx.stroke();
}
}
}
}
}
貝塞爾(bezier)以及二次貝塞爾
下一個十分有用的路徑類型就是貝塞爾曲線。一次以及二次貝塞爾曲線都十分有用,一般用來繪制復雜有規律的圖形。
quadraticCurveTo(cp1x, cp1y, x, y)繪制貝塞爾曲線,cp1x,cp1y為控制點,x,y為結束點。bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y)繪制二次貝塞爾曲線,cp1x,cp1y為控制點一,cp2x,cp2y為控制點二,x,y為結束點。
右邊的圖能夠很好的描述兩者的關系,貝塞爾曲線有一個開始、結束點(藍色)以及一個控制點(紅色),而二次貝塞爾曲線使用兩個控制點。
參數x、y在這兩個方法中都是結束點坐標。cp1x,cp1y為坐標中的第一個控制點,cp2x,cp2y為坐標中的第二個控制點。
使用一次以及二次貝塞爾曲線是有一定的難度的,因為不同于像Adobe Illustrators這樣的矢量軟件,我們所繪制的曲線沒有直接的視覺反饋給我們。這讓繪制復雜的圖形十分的困難。在下面的例子中,我們會繪制一些簡單有規律的圖形,如果你有時間,以及更多的耐心很多復雜的圖形你都可以繪制出來。
下面的這些例子沒有多少是困難的。這兩個例子中我們會連續繪制貝塞爾曲線,最后會形成復雜的圖形。
貝塞爾曲線
這個例子使用多個貝塞爾曲線來渲染對話氣泡。
function draw() {
var canvas = document.getElementById('canvas');
if (canvas.getContext) {
var ctx = canvas.getContext('2d');
// 貝爾賽曲線
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();
}
}
二次貝塞爾曲線
這個例子使用貝塞爾曲線繪制心形。
function draw() {
var canvas = document.getElementById('canvas');
if (canvas.getContext){
var ctx = canvas.getContext('2d');
//二次曲線
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();
}
}
矩形
直接在畫布上繪制矩形的三個額外方法,正如我們開始所見的Drawing rectangles,同樣,也有rect()方法,將一個矩形路徑增加到當前路徑上。
rect(x,y,width,height)繪制一個左上角坐標為(x,y),寬高為width以及height的矩形。
當該方法執行的時候,moveTo()方法自動設置坐標參數(0,0)。也就是說,當前筆觸自動重置會默認坐標。
組合使用
目前為止,每一個例子中的每個圖形都只用到一種類型的路徑。然而,繪制一個圖形并沒有限制使用數量以及類型。所以在最后的一個例子里,讓我們組合使用所有的路徑函數來重現一組著名的游戲人物。
function draw() {
var canvas = document.getElementById('canvas');
if (canvas.getContext){
var ctx = canvas.getContext('2d');
roundedRect(ctx,12,12,150,150,15);
roundedRect(ctx,19,19,150,150,9);
roundedRect(ctx,53,53,49,33,10);
roundedRect(ctx,53,119,49,16,6);
roundedRect(ctx,135,53,49,33,10);
roundedRect(ctx,135,119,25,49,10);
ctx.beginPath();
ctx.arc(37,37,13,Math.PI/7,-Math.PI/7,false);
ctx.lineTo(31,37);
ctx.fill();
for(var i=0;i<8;i++){
ctx.fillRect(51+i*16,35,4,4);
}
for(i=0;i<6;i++){
ctx.fillRect(115,51+i*16,4,4);
}
for(i=0;i<8;i++){
ctx.fillRect(51+i*16,99,4,4);
}
ctx.beginPath();
ctx.moveTo(83,116);
ctx.lineTo(83,102);
ctx.bezierCurveTo(83,94,89,88,97,88);
ctx.bezierCurveTo(105,88,111,94,111,102);
ctx.lineTo(111,116);
ctx.lineTo(106.333,111.333);
ctx.lineTo(101.666,116);
ctx.lineTo(97,111.333);
ctx.lineTo(92.333,116);
ctx.lineTo(87.666,111.333);
ctx.lineTo(83,116);
ctx.fill();
ctx.fillStyle = "white";
ctx.beginPath();
ctx.moveTo(91,96);
ctx.bezierCurveTo(88,96,87,99,87,101);
ctx.bezierCurveTo(87,103,88,106,91,106);
ctx.bezierCurveTo(94,106,95,103,95,101);
ctx.bezierCurveTo(95,99,94,96,91,96);
ctx.moveTo(103,96);
ctx.bezierCurveTo(100,96,99,99,99,101);
ctx.bezierCurveTo(99,103,100,106,103,106);
ctx.bezierCurveTo(106,106,107,103,107,101);
ctx.bezierCurveTo(107,99,106,96,103,96);
ctx.fill();
ctx.fillStyle = "black";
ctx.beginPath();
ctx.arc(101,102,2,0,Math.PI*2,true);
ctx.fill();
ctx.beginPath();
ctx.arc(89,102,2,0,Math.PI*2,true);
ctx.fill();
}
}
// 封裝的一個用于繪制圓角矩形的函數.
function roundedRect(ctx,x,y,width,height,radius){
ctx.beginPath();
ctx.moveTo(x,y+radius);
ctx.lineTo(x,y+height-radius);
ctx.quadraticCurveTo(x,y+height,x+radius,y+height);
ctx.lineTo(x+width-radius,y+height);
ctx.quadraticCurveTo(x+width,y+height,x+width,y+height-radius);
ctx.lineTo(x+width,y+radius);
ctx.quadraticCurveTo(x+width,y,x+width-radius,y);
ctx.lineTo(x+radius,y);
ctx.quadraticCurveTo(x,y,x,y+radius);
ctx.stroke();
}
我們不會很詳細地講解上面的代碼,因為事實上這很容易理解。重要的一點是繪制上下文中使用到了fillStyle屬性,以及封裝函數(例子中的roundedRect())。使用封裝函數對于減少代碼量以及復雜度十分有用。
在稍后的課程里,我們會回頭再看看fillStyle樣式的更多細節。這章節中,我們所做的關于fillStyle樣式僅是改變填充顏色,由默認的黑色到白色,然后又是黑色。
Path2D 對象
正如我們在前面例子中看到的,你可以使用一系列的路徑和繪畫命令來把對象“畫”在畫布上。為了簡化代碼和提高性能,Path2D對象已可以在較新版本的瀏覽器中使用,用來緩存或記錄繪畫命令,這樣你將能快速地回顧路徑。
怎樣產生一個Path2D對象呢?
Path2D()Path2D()會返回一個新初始化的Path2D對象(可能將某一個路徑作為變量——創建一個它的副本,或者將一個包含SVG path數據的字符串作為變量)。
new Path2D(); // 空的Path對象 new Path2D(path); // 克隆Path對象 new Path2D(d); // 從SVG建立Path對象
所有的路徑方法比如moveTo,rect,arc或quadraticCurveTo等,如我們前面見過的,都可以在Path2D中使用。
Path2D API 添加了addPath作為將path結合起來的方法。當你想要從幾個元素中來創建對象時,這將會很實用。比如:
Path2D.addPath(path [, transform])?添加了一條路徑到當前路徑(可能添加了一個變換矩陣)。
Path2D 示例
在這個例子中,我們創造了一個矩形和一個圓。它們都被存為Path2D對象,后面再派上用場。隨著新的Path2D API產生,幾種方法也相應地被更新來使用Path2D對象而不是當前路徑。在這里,帶路徑參數的stroke和fill可以把對象畫在畫布上。
function draw() {
var canvas = document.getElementById('canvas');
if (canvas.getContext){
var ctx = canvas.getContext('2d');
var rectangle = new Path2D();
rectangle.rect(10, 10, 50, 50);
var circle = new Path2D();
circle.moveTo(125, 35);
circle.arc(100, 35, 25, 0, 2 * Math.PI);
ctx.stroke(rectangle);
ctx.fill(circle);
}
}
使用 SVG paths
新的Path2D API有另一個強大的特點,就是使用SVG path data來初始化canvas上的路徑。這將使你獲取路徑時可以以SVG或canvas的方式來重用它們。
這條路徑將先移動到點(M10 10)然后再水平移動80個單位(h 80),然后下移80個單位(v 80),接著左移80個單位(h -80),再回到起點處 (z)。你可以在Path2D constructor查看這個例子。
var p = new Path2D("M10 10 h 80 v 80 h -80 Z");
總結
以上是生活随笔為你收集整理的canvas基础2--绘制图形的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: [转载]神经网络偏置项(bias)的设置
- 下一篇: 开独立网店商城18个理由