創建 canvas
首先創建一個canvas元素,我們只需要在html文件中加入這么一句代碼:
<canvas id="canvas">當前瀏覽器不支持canvas,請更換瀏覽器使用!</canvas>
同時我們也可以通過canvas的標簽屬性width和height設置canvas畫布的大小:
<canvas id="canvas" width="800" height="800">當前瀏覽器不支持canvas,請更換瀏覽器使用!</canvas>
當然,我們也可以通過js來設置canvas的寬高,下文會提到如何設置。
接下來我們就在js中獲取到該canvas元素,然后設置它的寬高,並獲取到上下文的環境:
var canvas = document.getElementById("canvas");//獲取到canvas元素 //設置寬高 canvas.width = 800; canvas.height = 800; var context = canvas.getContext("2d");//獲取上下文的環境
接下來我們的所有操作都是基於這個context的上下文環境。
現在畫一條簡單的直線:
context.moveTo(100,100);
context.lineTo(500,500);
moveTo()方法表示一次繪制的起點坐標,lineTo()表示基於上一個坐標點到這個坐標點之間的直線連接。
注意的是,canvas是基於狀態的繪制,而不是基於對象的繪制。所以,上面代碼都是狀態的設置,我們還需要使用stroke()方法進行繪制:
context.stroke();//繪制
除此之外,我們還可以設置線條的一些基本屬性:
context.lineWidth = 8;//線條的寬度 context.strokeStyle = "#333";//線條的顏色
一個簡單的繪制一條直線的完整例子:
var canvas = document.getElementById("canvas");//獲取到canvas元素 //設置寬高 canvas.width = 800; canvas.height = 800; var context = canvas.getContext("2d");//獲取上下文的環境 //canvas 是基於狀態的繪制,而不是對象 context.moveTo(100,100); context.lineTo(500,500); context.lineWidth = 8;//線條的寬度 context.strokeStyle = "#333";//線條的顏色 context.stroke();//繪制
運行結果如下圖:
接下來我們繪制一個連續折線:
context.moveTo(100,100); context.lineTo(500,100); context.lineTo(500,500); context.lineTo(100,500);
運行結果如下:
如果我們想要讓這個折線閉合形成一個矩形的話,可以再設置context.lineTo(100,100);然而如果線條的寬度比較大的時候,就會出現一些瑕疵,這個的話大家自己試試看。所以標准的話應該使用context.closePath();這個知識點后面會講到,這里大家可以先試試,看看運行結果是怎么樣的。
上面這個簡單的例子我們是連續繪制的折線,也就是說可以一筆連續畫完的折線。如果是多條間斷的折線,那么我們就需要使用context.moveTo()來重新繪制一條折線的起點坐標:
context.moveTo(100,200); context.lineTo(300,400); context.lineTo(100,600); context.moveTo(300,200); context.lineTo(500,400); context.lineTo(300,600); context.moveTo(500,200); context.lineTo(700,400); context.lineTo(500,600);
運行結果如下:
此時,如果我們想要繪制的這三條折線是不同顏色的,那該怎么辦?一種常見的錯誤用法就是:
context.moveTo(100,200); context.lineTo(300,400); context.lineTo(100,600); context.lineWidth = 8; context.strokeStyle = "red"; context.stroke();//繪制 context.moveTo(300,200); context.lineTo(500,400); context.lineTo(300,600); context.lineWidth = 8; context.strokeStyle = "green"; context.stroke();//繪制 context.moveTo(500,200); context.lineTo(700,400); context.lineTo(500,600); context.lineWidth = 8; context.strokeStyle = "blue"; context.stroke();//繪制
運行結果會是三條折線都是藍色的。因為這里的第二條折線的strokeStyle屬性會覆蓋第一條折線的strokeStyle屬性,而stroke()方法的執行會繪制當前所有的狀態,所以第一條折線就變成綠色。同理,最終三條折線就都是藍色。因此呢,這里就需要使用beginPath()方法來重新進行一次全新的繪制。
context.lineWidth = 8;//線條的寬度 context.beginPath(); context.moveTo(100,200); context.lineTo(300,400); context.lineTo(100,600); context.strokeStyle = "red"; context.stroke();//繪制 context.beginPath(); context.moveTo(300,200); context.lineTo(500,400); context.lineTo(300,600); context.strokeStyle = "green"; context.stroke();//繪制 context.beginPath(); context.moveTo(500,200); context.lineTo(700,400); context.lineTo(500,600); context.strokeStyle = "blue"; context.stroke();//繪制
這里在每繪制一條折線的時候,都使用beginPath()方法來進行一次全新的路徑繪制。此時再使用stroke()方法進行繪制的時候,就只會繪制beginPath()方法下面的狀態。運行結果如下:
從上面的代碼我們可以看到:context.lineWidth = 8;放在了最前面,這是因為beginPath()方法不會將沒有修改的屬性變成默認值。比如這里的lineWidth,三條折線都是8,那么我們放在前面的話,在進行stroke()方法進行繪制的時候,都是會使用lineWidth = 8;來進行繪制。而需要修改的屬性則在具體的路徑繪制里進行修改,比如這里的線條顏色屬性,每條折線的顏色都不一樣,所以就需要各自設置。
還有一點就是:此段代碼中的moveTo()方法可以改成lineTo()方法:
context.lineWidth = 8;//線條的寬度 context.beginPath(); context.lineTo(100,200); context.lineTo(300,400); context.lineTo(100,600); context.strokeStyle = "red"; context.stroke();//繪制
因為使用了beginPath()方法,就會對之前繪制的路徑進行清空,但不會回到(0,0)原點。所以這里我們直接使用lineTo()方法同樣能起到繪制起點的作用,但必須和beginPath()方法一起使用才能替換moveTo()方法。
上面曾簡單講過context.closePath()方法,這里將具體展開介紹:context.closePath()方法和context.beginPath()方法一起使用,以繪制閉合的路徑圖形,這是繪制封閉多邊形的標准做法。而且使用context.closePath()方法,最后一條線的繪制可以省略,它會自動幫我們連接到繪制起點以形成封閉的多邊形。
context.beginPath(); context.moveTo(100,100); context.lineTo(500,100); context.lineTo(500,500); context.lineTo(100,500); context.closePath(); context.lineWidth = 8; context.strokeStyle = "#333"; context.stroke();
運行結果如下:
接下來我們介紹一下填充屬性fillStyle和填充繪制方法fill():
context.beginPath(); context.moveTo(100,100); context.lineTo(500,100); context.lineTo(500,500); context.lineTo(100,500); context.closePath(); context.lineWidth = 8; context.strokeStyle = "#333"; context.stroke(); context.fillStyle = "red"; //填充顏色 context.fill(); //填充
最后兩行代碼就是設置了填充的顏色(紅色)和進行閉合圖形的顏色填充,運行結果如下:
可以看到圖形的邊框寬度比沒有填充顏色的圖形的邊框寬度小,這是因為我們先繪制邊框,再填充顏色。而實際上應該是先填充顏色,再繪制邊框。而且fill()方法和stroke()方法的原理是一樣的,都是基於當前狀態進行繪制。所以,為了可讀性,我們可以將屬性設置放在一起,最后在使用fill()和stroke()方法:
context.beginPath(); context.moveTo(100,100); context.lineTo(500,100); context.lineTo(500,500); context.lineTo(100,500); context.closePath(); context.lineWidth = 8; context.strokeStyle = "#333"; context.fillStyle = "red"; context.fill(); //先填充 context.stroke(); //再繪制邊框
這次的運行結果:
可以看到,這次的結果就是正確的了。
對於繪制矩形這個很常用,那么我們就可以封裝成函數,方便以后調用:
drawRect(context,100,100,400,400,8,"#333","blue"); function drawRect(cxt,x,y,width,height,borderWidth,borderColor,fillColor){ cxt.beginPath(); cxt.moveTo(x,y); cxt.lineTo(x+width,y); cxt.lineTo(x+width,y+height); cxt.lineTo(x,y+height); cxt.closePath(); cxt.lineWidth = borderWidth; cxt.strokeStyle = borderColor; cxt.fillStyle = fillColor; cxt.fill(); cxt.stroke(); }
同樣的,運行結果也是正常的:
但是呢,canvas API 提供了繪制矩形更加方便的方法:rect()方法用於規划矩形的路徑,fillRect()方法在規划了矩形的路徑之后還填充了矩形的顏色,而strokeRect()方法則繪制了矩形的邊框。
所以上面的函數若使用context.rect()方法可以簡化為:
function drawRect(cxt,x,y,width,height,borderWidth,borderColor,fillColor){ cxt.beginPath(); cxt.rect(x,y,width,height); cxt.closePath(); cxt.lineWidth = borderWidth; cxt.strokeStyle = borderColor; cxt.fillStyle = fillColor; cxt.fill(); //先填充 cxt.stroke(); //再繪制邊框 }
而使用fillRect()和strokeRect()這兩個方法則可以更加簡單就實現:
function drawRect(cxt,x,y,width,height,borderWidth,borderColor,fillColor){ cxt.lineWidth = borderWidth; cxt.strokeStyle = borderColor; cxt.fillStyle = fillColor; cxt.fillRect(x,y,width,height); cxt.strokeRect(x,y,width,height); }
此時如果我們繪制了兩個矩形:
drawRect(context,100,100,400,400,8,"#333","blue");
drawRect(context,300,300,400,400,8,"#333","red");
運行結果是這樣的:
可以看到,后繪制的矩形會蓋在前繪制的矩形之上,當然得顏色不同才體現出來。
此時我們將第二個矩形的填充顏色改一下:
drawRect(context,300,300,400,400,8,"#333","rgba(255,0,0,0.5)");
結果如下:
可以看到第二個矩形的填充顏色是半透明的紅色。那么就需要說明一下:fillStyle 和 strokeStyle 屬性的顏色值可以是CSS3支持的任何一種形式:
#ffffff
#333
red
rgb(0,0,0)
rgba(255,0,0,0.5)
hsl(20,50%,50%)
hsla(20,50%,60%,0.6)
線段還有其他一些屬性,接下來分別講解:
lineCap屬性就是定義線段開頭和結尾處的形狀,此屬性有三個屬性值:butt(默認值),round,square 。
通過代碼來演示這三種效果:
context.lineWidth = 50; context.strokeStyle = "#005588"; context.beginPath(); context.moveTo(100,200); context.lineTo(700,200); context.lineCap = "butt"; context.stroke(); context.beginPath(); context.moveTo(100,400); context.lineTo(700,400); context.lineCap = "round"; context.stroke(); context.beginPath(); context.moveTo(100,600); context.lineTo(700,600); context.lineCap = "square"; context.stroke();
這里畫了三條線段,分別設置了不同的lineCap屬性值,看看有什么不同:
從結果可以看出來,使用round屬性值會比默認值butt多出一個半圓的形狀包在線段的開始和結尾處,而是用square屬性值會在開始和結尾處多處半個方形,這就是它們的不同之處。其中,round對於我們繪制一些圓角效果的圖形比較有用,但是它只對線條的開頭和結尾處有效果,對於線段之間的連接處沒有作用,這點需要注意。
接下來我們來繪制一個五角星:
先看一下效果圖:
我們要繪制一個這樣的五角星,其實就是要繪制出十個頂點,而且這十個頂點是有規律可循的:
從這個圖可以看到,五角星的十個頂點可以分成兩組,分別在外圓和內圓上的五個頂點,通過數學公式可以得知:a是18度,b是36度,故a+b=54度,有了一個角的度數,還有大小圓的半徑,就可以根據三角函數公式求出頂點的坐標了,而且大小圓上的每個頂點之間的度數都是相差72度,那么我們就可以使用函數來繪制這個五角星:
context.lineWidth = 10; context.strokeStyle = "#005588"; drawStar(context,150,300,400,400,0); context.stroke(); function drawStar(cxt,r,R,x,y,rotate){ cxt.beginPath(); for(var i = 0; i < 5; i++){ cxt.lineTo(Math.cos((18 + i*72 - rotate)/180*Math.PI)*R + x, -Math.sin((18 + i*72 - rotate)/180 * Math.PI)*R + y); cxt.lineTo(Math.cos((54 + i*72 - rotate)/180*Math.PI)*r + x, -Math.sin((54 + i*72 - rotate)/180 * Math.PI)*r + y); } cxt.closePath(); }
至於這里面的公式,大家就自己想一下吧,其實就是正弦和余弦函數。drawStar()方法的參數分別是上下文環境,小圓半徑,大圓半徑,偏移量x,偏移量y,旋轉角度。
最后還有兩個線條的屬性:lineJoin 和 miterLimit
lineJoin顧名思義就是線條與線條之間的連接方式,該屬性有三個屬性值:miter(默認值,尖角),bevel(銜接),round(圓角)。
我們基於上面的五角星的例子,分別設置不同的lineJoin屬性值,看看有什么不同:
context.lineJoin = "bevel";
context.lineJoin = "round";

這些細微的差別還是大家自己試一下才有比較直觀的感受,更能理解其中的用法。
還有一個miterLimit屬性,這個屬性只有當lineJoin屬性值是miter時才有用,默認值是10 。那么它到底是什么呢?
我們通過代碼來演示:
context.lineWidth = 10; context.strokeStyle = "#005588"; context.lineJoin = "miter"; drawStar(context,30,300,400,400,0); context.stroke();
我們將小圓的半徑設置得比較小,此時我們的lineJoin是miter,即尖角。那么結果會是怎么樣的呢?
可以看到它畢竟變成bevel連接方法了,這就是miterLimit屬性的影響,那么它究竟怎么計算的呢?看下圖:
可以看到,它的計算還是比較復雜的,所以canvas給我們一個經驗值,也就是10,當然自己也可以修改miterLimit的值,得到自己想要的結果。
好的,以上就是線條的全部內容!未完待續….