天天看點

Canvas學習:繪制線段

在上一篇介紹Canvas坐标系統的結尾處,我們使用了Canvavs繪制了一個網格。整個效果是由直線和文本構成。在這一節中,我們來看看如何使用Canvas繪制線段。

在Canvas中,線段也是路徑中的一種,被稱之為線性路徑。在Canvas中繪制線性路徑主要用到

moveTo(x,y)

lineTo(x,y)

stroke()

幾個方法。

先畫一條簡單的直線

Canvas畫一下直線非常的容易。衆所周之,兩點就能構成一條直線。使用兩個API就可以:

moveTo()

告訴你把畫筆移到Canvas畫布中的某個位置(直線的起點),然後通過

lineTo()

把畫筆移到另一個點。進而兩個點構成一條直線。

function drawScreen () {
    ctx.moveTo(50, 10);
    ctx.lineTo(350, 100);
}
                

但這樣在畫布看不到任何的線條。如果需要看到效果,還需要使用

stroke()

方法:

function drawScreen () {
    ctx.moveTo(50, 10);
    ctx.lineTo(350, 100);
    ctx.stroke();
}
                
Canvas學習:繪制線段

是不是很簡單,通過這三個方法就可以繪制出一條線段。

  • moveTo(x,y)

    :移動畫筆到指定的坐标點

    (x,y)

    ,該點就是新的子路徑的起始點。該方法并不會從目前路徑中清除任何子路徑
  • lineTo(x,y)

    :使用直線連接配接目前端點和指定的坐标點

    (x,y)

  • stroke()

    :沿着繪制路徑的坐标點順序繪制直線

改變線段寬度

我們在實際繪制線段時,總是有粗細的情況發生。那麼在Canvas中可以通過

lineWidth

來改變繪制線段的粗細。比如:

function drawScreen () {
    ctx.lineWidth = 10; // 改變線的粗細
    ctx.moveTo(50, 10); // 起始點
    ctx.lineTo(350, 100); // 第二點(如果是一條直線的話,就是終點)
    ctx.stroke();
}
                
Canvas學習:繪制線段

lineWidth

主要用來定義繪制線條的寬度。預設值是

1.0

,并且這個屬性必須大于

0.0

。較寬的線條在路徑上居中,每邊各有線條寬的一半。

改變線段的顔色

既然能改為線段的粗細,那必然能改變線段的顔色。在Canvas中可以通過

strokeStyle

來改變線段的顔色:

function drawScreen () {
    ctx.lineWidth = 10;
    ctx.strokeStyle = '#f36';
    ctx.moveTo(50, 10);
    ctx.lineTo(350, 100);
    ctx.stroke();
}
                
Canvas學習:繪制線段

strokeStyle

主要用于設定畫筆繪制路徑的顔色、漸變和模式。該屬性的值可以是一個表示CSS顔色值的字元串。如果你的繪制需求比較複雜,該屬性的值還可以是一個

CanvasGradient

對象或者

CanvasPattern

對象。

也就是說,我們也可以繪制漸變色的線段:

function drawScreen () {
    // 建立一個表示線性顔色漸變的CanvasGradient對象,
    // 并設定該對象的作用區域就是線段所在的區域
    var canvasGradient = ctx.createLinearGradient(50, 50, 250, 50);
    //在offset為0的位置(即起點位置)添加一個藍色的漸變
    canvasGradient.addColorStop(0, "blue");
    //在offset為0.2的位置(線段左起20%的位置)添加一個綠色的漸變
    canvasGradient.addColorStop(0.2, "green");
    //在offset為0的位置(即終點位置)添加一個紅色的漸變
    canvasGradient.addColorStop(1, "red");
    //将strokeStyle的屬性值設為該CanvasGradient對象
    ctx.strokeStyle = canvasGradient;
    ctx.lineWidth = 10;
    ctx.moveTo(50, 10);
    ctx.lineTo(350, 100);
    ctx.stroke();
}
                
Canvas學習:繪制線段

CanvasGradient

接口表示描述漸變的不透明對象。通過

CanvasRenderingContext2D.createLinearGradient()

CanvasRenderingContext2D.createRadialGradient()

的傳回值得到。

如此一來,是不是可以畫具有紋理的線段呢?思考一下,你就會有答案。

beginPath()和closePath()

前面也說過了,線段也是線性路徑中的一種。有開始也會有結束。其實在Canvas中具有兩個方法:

beginPath()

closePath()

  • beginPath()

    :開始一個新的繪制路徑。每次繪制新的路徑之前記得調用該方法。它将重置記憶體中現有的路徑
  • closePath()

    :如果目前的繪制路徑是打開的,則關閉掉該繪制路徑。此外,調用該方法時,它會嘗試用直線邊接目前端點與起始端點來關閉路徑,但如果圖形已經關閉(比如先調用

    stroke()

    )或者隻有一個點,它會什麼都不做。

在Canvas中繪制路徑,最好加上

beginPath()

closePath()

。配合

lineTo()

不同點,我們可以繪制不同的路徑。

function drawScreen () {

    ctx.strokeStyle = '#f36';
    ctx.lineWidth = 4;

    ctx.beginPath();
    ctx.moveTo(50, 10);
    ctx.lineTo(150, 10);
    ctx.lineTo(150,200);
    ctx.lineTo(200,200);
    ctx.lineTo(200,150);
    ctx.stroke();
    ctx.closePath();
}
                
Canvas學習:繪制線段

把上面的代碼稍做修改:

function drawScreen () {

    ctx.strokeStyle = '#f36';
    ctx.lineWidth = 4;

    ctx.beginPath();
    ctx.moveTo(50, 10);
    ctx.lineTo(150, 10);
    ctx.lineTo(150,200);
    ctx.stroke();
    ctx.closePath();

    ctx.beginPath();
    ctx.moveTo(200,200);
    ctx.lineTo(200,150);   
    ctx.stroke();
    ctx.closePath();    
}
                
Canvas學習:繪制線段

你在效果中可以看到,這個示例,我們是繪制了兩個路徑。

特别提醒:在繪制圖形路徑時,一定要先調用

beginPath()

beginPath()

方法将會清空記憶體中之前的繪制路徑資訊。如果不這樣做,對于繪制單個圖形可能沒什麼影響,但是在繪制多個圖形時(例如上面示例的兩條直線),将會導緻路徑繪制或者顔色填充等操作出現任何意料之外的結果。

此外,對于

closePath()

方法,初學者一定要稍加注意。在上面繪制折線的代碼示例中,我們先調用了

stroke()

,再調用了

closePath()

。其實在調用

stroke()

方法時,折線就已經繪制好了,目前的繪制路徑也就被關閉掉了,是以再調用

closePath()

方法時,它就不會使用直線連接配接目前端點和起始端點(也就是說,這裡的

closePath()

是可有可無的,不過為了保持良好的習慣,還是建議寫上)。如果我們交換一下

stroke()

closePath()

的調用順序,則情況完全不一樣了。由于

closePath()

先調用,此時繪制路徑并沒有關閉,那麼

closePath()

将會用直線連接配接目前端點和起始端點。

來看下面這段代碼,一條路徑是

stroke()

closePath()

前面(紅色折線);另一條路徑是

stroke()

closePath()

後面(藍色折線):

function drawScreen () {

    ctx.strokeStyle = 'red';
    ctx.lineWidth = 4;

    ctx.beginPath();
    ctx.moveTo(50, 10);
    ctx.lineTo(150, 10);
    ctx.lineTo(150,200);
    ctx.lineTo(200,200);
    ctx.lineTo(200,150);   
    ctx.stroke();
    ctx.closePath();    

    ctx.strokeStyle = 'blue';
    ctx.beginPath();
    ctx.moveTo(250, 10);
    ctx.lineTo(350,10);
    ctx.lineTo(350,200);
    ctx.lineTo(400,200);
    ctx.lineTo(400,150);
    ctx.closePath();
    ctx.stroke();

}
                
Canvas學習:繪制線段

很明顯,紅色的終點和起點沒連在一起,而藍色的則連起來了。

對于上面的這種多條線段(路徑),如果我們在代碼中添加一個

fill()

,這個時候效果就不是線條效果了,而是線條起點和終點連起來的一個圖形:

function drawScreen () {

    ctx.strokeStyle = 'red';
    ctx.lineWidth = 4;

    ctx.beginPath();
    ctx.moveTo(50, 10);
    ctx.lineTo(150, 10);
    ctx.lineTo(150,200);
    ctx.lineTo(200,200);
    ctx.lineTo(200,150);   
    ctx.stroke();
    ctx.fill();
    ctx.closePath();    

    ctx.strokeStyle = 'blue';
    ctx.beginPath();
    ctx.moveTo(250, 10);
    ctx.lineTo(350,10);
    ctx.lineTo(350,200);
    ctx.lineTo(400,200);
    ctx.lineTo(400,150);
    ctx.closePath();
    ctx.stroke();
    ctx.fill();

}
                
Canvas學習:繪制線段

同時,在上例的基礎上,如果把

strokeStyle

換成

fillStyle

,同時删除代碼中的

stroke()

。效果又不一樣:

function drawScreen () {

    ctx.fillStyle = '#ddaae2';
    ctx.lineWidth = 4;

    ctx.beginPath();
    ctx.moveTo(50, 10);
    ctx.lineTo(150, 10);
    ctx.lineTo(150,200);
    ctx.lineTo(200,200);
    ctx.lineTo(200,150);   
    ctx.fill();
    ctx.closePath();    

    ctx.beginPath();
    ctx.moveTo(250, 10);
    ctx.lineTo(350,10);
    ctx.lineTo(350,200);
    ctx.lineTo(400,200);
    ctx.lineTo(400,150);
    ctx.closePath();
    ctx.fill();

}
                
Canvas學習:繪制線段

這個時候,不管是

fill()

closePath()

前後,最終看到的效果都是一樣的。也就是說

fill()

會把路徑填充成一個圖形。

簡單小結一下

在HTML5 Canvas中繪制直線隻需要使用

[CanvasRenderingContext2D](//developer.mozilla.org/zh-CN/docs/Web/API/CanvasRenderingContext2D)

對象的幾個屬性和方法即可輕松實作。

屬性或方法 基本描述

strokeStyle

用于設定畫筆繪制路徑的顔色、漸變和模式

lineWidth

定義繪制線條的寬度

beginPath()

開始一個新的繪制路徑

moveTo(x,y)

移動畫筆到指定的坐标點

(x,y)

,該點就是新的子路徑的起始點

lineTo(x,y)

使用直線邊接目前端點和指定的坐标點

(x,y)

stroke()

沿着繪制路徑的坐标點順序繪制直線

closePath()

如果目前的繪制路徑是打開的,則關閉掉該繪制路徑

在Canvas的圖形繪制過程中,幾乎都是先按照一定順序先定下幾個坐标點,也就是所謂的繪制路徑,然後再根據我們的需要将這些坐标點用指定的方式連接配接起來,就形成了我們所需要的圖形。當我們了解了

CanvasRenderingContext2D

對象的上述API後,那麼繪制線條就顯得非常簡單了。

線段與像素邊界

這是繪制線段的一個小細節。在說這個細節之前,咱們先來看一個小示例,就是繪制兩條簡單的直線。

function drawScreen () {

    ctx.strokeStyle = 'red';
    ctx.lineWidth = 1;

    ctx.beginPath();
    ctx.moveTo(50, 50);
    ctx.lineTo(350, 50);
    ctx.stroke();

    ctx.beginPath();
    ctx.moveTo(50.5, 100.5);
    ctx.lineTo(350.5,100.5);
    ctx.closePath();
    ctx.stroke();
}
                
Canvas學習:繪制線段

明顯第二條比第一條線。借助制圖軟體放大功能,來看一下:

Canvas學習:繪制線段

雖然我們在代碼中設定了

lineWidth

的值為

1

,同樣的值,但繪制出來的結果卻不一樣,第一條的寬度變成了

2

,而第二條的寬度是

1

。這就是我們接下來要說的線段與像素邊界。

如果你在某

2

個像素的邊界處繪制一條

1

像素寬的線段,那麼該線段實際上會占據

2

個像素的寬度,如下圖所示:

Canvas學習:繪制線段

如果在像素邊界處繪制一條

1

像素寬的垂直線段,那麼Canvas的繪圖環境對象會試着将半個像素畫在邊界中線的右邊,将另外半個像素畫在邊界中線的左邊。

然而,在一個整像素的範圍内繪制半個像素寬的線段是不可能的,是以左右兩個方向上的半像素都被擴充為

1

個像素。正如上圖中左圖,本來我們想要将線段繪制在深灰色的區域内,但實際上浏覽器卻将其延伸繪制到整個灰色的範圍内。

另一方面,我們來看看如果将線段繪制在某

2

個像素之間的那個像素中,效果就如上圖中右圖。這條垂直線段繪制在兩個像素之間,這樣的話,中線左右兩端的那半個像素就不會再延伸了,它們合半起來恰好占據

1

像素的寬度。是以說,如果繪制一條真正

1

像素寬的線段,你必須将該線段繪制在某兩個像素之間的那個像素中,而不能将它繪制在兩個像素的交界處。

所有浏覽器的Canvas實作都使用了抗鋸齒技術,以便建立出亞像素線段的繪制效果來。

再回過頭來看上一節中,繪制的網格線,我們可以看到它的線條寬度其實不是真正的

1px

。也就是上面說的原因造成的。既然明白原因了,那我們就可以輕松修改上節中繪制的網格。

var x = 0.5;
var y = 0.5;
var w = myCanvas.width + .5;
var h = myCanvas.height + .5;
           

效果如下:

将上面示例,繪制網格的代碼,可以用JavaScript将其封裝成為一個

drawGrid()

函數。

function drawGrid(color, stepX, stepY) {
    ctx.strokeStyle = color;
    ctx.lineWidth = 0.5;

    for (var i = stepX + 0.5; i < myCanvas.width; i += stepX) {
        ctx.beginPath();
        ctx.moveTo(i, 0);
        ctx.lineTo(i, myCanvas.height);
        ctx.stroke();
    }

    for (var i = stepY + 0.5; i < myCanvas.height; i += stepY) {
        ctx.beginPath();
        ctx.moveTo(0, i);
        ctx.lineTo(myCanvas.width, i);
        ctx.stroke(); 
    }
  }
                

其中

color

是網格線顔色,

stepX

x

軸的網格間距,

stepY

y

軸的網格間距。隻需要在

drawScree()

中繪制網格線刻度,調用

drawGrid()

函數:

function drawScreen() {
    var dx = 50,
        dy = 50,

        // 初始坐标原點
        x = 0,
        y = 0,
        w = myCanvas.width,
        h = myCanvas.height,
        xy = 10;

    while (y < h) {
        y = y + dy;
        //橫坐标的數字
        ctx.font = "1px Calibri";
        ctx.fillText(xy, x, y);
        xy += 10;
    }
    // 畫豎線
    y =0;
    xy =10;
    while (x < w) {
        x = x + dx;
        //縱坐标的數字
        ctx.font = "1px Calibri";
        ctx.fillText(xy,x,10);
        xy+=10;
    }

    drawGrid('#000', 50,50);
  }

           

最後的效果如下:

總結

這篇文章主要記錄了如何在Canvas中繪制線段(路徑)。簡單的說,使用

moveTo(x,y)

lineTo(x,y)

stroke()

就可以繪制出一條線段,或者說多線段。另外使用

lineWidth

可以改變線段的寬度,而

strokeStyle

可以改變線段的顔色。不過,在Canvas中繪制路徑時記得在開始時先調用

beginPath()

是不是很簡單呀。不知道大家發現沒有,在這篇文章中,看到繪制的線都是實線,那麼在Canavs中有沒有直接的API可以繪制虛線或者點劃線呢?先思考一下,我們後續來回答。

著作權歸作者所有。

商業轉載請聯系作者獲得授權,非商業轉載請注明出處。

原文: http://www.w3cplus.com/canvas/draw-lines.html © w3cplus.com著作權歸作者所有。

個人建了前端學習群,旨在一起學習前端。純淨、純粹技術讨論,非前端人員勿擾!入群加我微信:iamaixiaoxiao。

Canvas學習:繪制線段

繼續閱讀