canvas繪圖詳解筆記之線條及線條屬性


創建 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();//繪制

運行結果如下圖:

360截圖20150729201030921

接下來我們繪制一個連續折線:

context.moveTo(100,100);
context.lineTo(500,100);
context.lineTo(500,500);
context.lineTo(100,500);

運行結果如下:

360截圖20150729205421318

如果我們想要讓這個折線閉合形成一個矩形的話,可以再設置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);

運行結果如下:

360截圖20150729210752556

此時,如果我們想要繪制的這三條折線是不同顏色的,那該怎么辦?一種常見的錯誤用法就是:

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()方法下面的狀態。運行結果如下:

360截圖20150729221419394

從上面的代碼我們可以看到: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();

運行結果如下:

360截圖20150729224657298

接下來我們介紹一下填充屬性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(); //填充

最后兩行代碼就是設置了填充的顏色(紅色)和進行閉合圖形的顏色填充,運行結果如下:

360截圖20150729225257658

可以看到圖形的邊框寬度比沒有填充顏色的圖形的邊框寬度小,這是因為我們先繪制邊框,再填充顏色。而實際上應該是先填充顏色,再繪制邊框。而且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(); //再繪制邊框

這次的運行結果:

360截圖20150729230210867

可以看到,這次的結果就是正確的了。

對於繪制矩形這個很常用,那么我們就可以封裝成函數,方便以后調用:

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

同樣的,運行結果也是正常的:

360截圖20150729231344357

但是呢,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");

運行結果是這樣的:

360截圖20150729232938883

可以看到,后繪制的矩形會蓋在前繪制的矩形之上,當然得顏色不同才體現出來。

此時我們將第二個矩形的填充顏色改一下:

drawRect(context,300,300,400,400,8,"#333","rgba(255,0,0,0.5)");

結果如下:

360截圖20150729233705525

可以看到第二個矩形的填充顏色是半透明的紅色。那么就需要說明一下: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屬性值,看看有什么不同:

360截圖20150730102937532

從結果可以看出來,使用round屬性值會比默認值butt多出一個半圓的形狀包在線段的開始和結尾處,而是用square屬性值會在開始和結尾處多處半個方形,這就是它們的不同之處。其中,round對於我們繪制一些圓角效果的圖形比較有用,但是它只對線條的開頭和結尾處有效果,對於線段之間的連接處沒有作用,這點需要注意。

接下來我們來繪制一個五角星:

先看一下效果圖:

360截圖20150730125849637

我們要繪制一個這樣的五角星,其實就是要繪制出十個頂點,而且這十個頂點是有規律可循的:

360截圖20150730114520220

從這個圖可以看到,五角星的十個頂點可以分成兩組,分別在外圓和內圓上的五個頂點,通過數學公式可以得知: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";

360截圖20150730132137351

context.lineJoin = "round";
360截圖20150730132215582

這些細微的差別還是大家自己試一下才有比較直觀的感受,更能理解其中的用法。

還有一個miterLimit屬性,這個屬性只有當lineJoin屬性值是miter時才有用,默認值是10 。那么它到底是什么呢?

我們通過代碼來演示:

context.lineWidth = 10;
context.strokeStyle = "#005588";
context.lineJoin = "miter";
drawStar(context,30,300,400,400,0);
context.stroke();

我們將小圓的半徑設置得比較小,此時我們的lineJoin是miter,即尖角。那么結果會是怎么樣的呢?

360截圖20150730132839722

可以看到它畢竟變成bevel連接方法了,這就是miterLimit屬性的影響,那么它究竟怎么計算的呢?看下圖:

360截圖20150730131716474

可以看到,它的計算還是比較復雜的,所以canvas給我們一個經驗值,也就是10,當然自己也可以修改miterLimit的值,得到自己想要的結果。

好的,以上就是線條的全部內容!未完待續….


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM