文章出處

周老虎落網的時候,網易跟騰訊都推出了牛逼轟轟的HTML5頁面來展示其關系網(網易http://news.163.com/special/data_zyk/  ,騰訊http://news.qq.com/zt2014/zykgxw/index.htm),查看這倆頁面,都是通過H5中canvas強大的繪圖功能來實現的。如果你未曾學習過H5,看完這倆屌炸天的頁面,興許會勾起你學習的欲望。

canvas其實沒有那么玄乎,它不外乎是一個H5的標簽,跟其它HTML標簽如出一轍:

<canvas></canvas>


canvas本身沒有任何的繪圖能力,所有的繪圖工作都是通過js來實現的。通常我們在js通過getElementById來獲取要操作的canvas(這意味著咱得給canvas設個id):

<canvas id="myCanvas"></canvas>

<script>
var c = document.getElementById("myCanvas"); //獲取要操作的canvas
//操作canvas的代碼...
</script>

注意最好在一開始的時候就給canvas設置好其寬高(若不設定寬高,瀏覽器會默認設置canvas大小為寬300、高100像素),而且不能使用css來設置(會被拉伸),建議直接寫于canvas標簽內部:

<canvas id="myCanvas" width="200" height="200"></canvas>

也可以在js腳本中設置:

<canvas id="myCanvas"></canvas>

<script>
var c = document.getElementById("myCanvas");
c.width=200;
c.height=200;
</script>

關于canvas大小需要知道的一點是,后續咱們對canvas所做的全部繪圖操作,超出此大小范圍的部分是不可見的。顧名思義,可以把canvas看成一塊畫布,其大小是咱設定好的寬高,那么無論你怎么畫,畫布外的地方自然是畫不到的。

對于有些瀏覽器是不支持canvas功能的,我們可以直接在canvas標簽中寫一些替換內容,在瀏覽器不支持canvas時顯示:

<canvas id="myCanvas" width="200" height="200" style="border:solid 1px #CCC;">
您的瀏覽器不支持canvas,建議使用最新版的Chrome
</canvas>

接著在聊如何在canvas上繪圖前,咱得先說說.getContext("2d")這東西。
.getContext() 是canvas的繪圖對象/方法,要讓canvas執行繪圖工作必須先獲取canvas的.getContext()對象來執行。

.getContext()只接受一個參數,該參數用于獲取canvas的繪圖環境,例如.getContext("2d")表示該canvas的繪圖環境為2D平面(可以繪制文本、直線、弧線、矩形、圓形等)。當前H5只支持2D環境,在不久的將來會開放3D繪圖功能。(故咱可將“getContext”翻譯為“獲取繪圖環境”)

理論不多說,我們先來個小例子,從最簡單的繪制直線開始:

<canvas id="myCanvas" width="200" height="200" style="border:solid 1px #CCC;">
您的瀏覽器不支持canvas,建議使用最新版的Chrome
</canvas>

<script>
var c = document.getElementById("myCanvas");
var ctx = c.getContext("2d"); //獲取該canvas的2D繪圖環境對象
ctx.moveTo(10,10);   //定義繪畫開始的位置
ctx.lineTo(150,50);  //畫一條直線,結束點坐標是x=150,y=50
ctx.stroke();  //描邊
</script>

效果如下:

在這里我們使用了3個getContext("2d")對象的繪圖方法:

.moveTo(x坐標 , y坐標)      可以理解為定位畫筆在畫布上的位置(注意所有繪圖方法所定義的坐標是相對canvas而言的而不是瀏覽器窗口,對canvas來說,最左上角的點的坐標是(0,0))

.lineTo(x坐標 , y坐標)      顧名思義,就是畫一條直線到某個點,很好理解。需要知道的是此方法僅僅做路徑運動,而不存在任何視覺上的繪圖效果(上色、描邊)

.stroke()     描邊方法,有玩過AfterEffect的朋友會很清楚,不給運動路徑加stroke特效的畫是不存在描邊效果的,canvas也一樣,想要運動路徑軌跡能有視覺效果,需要使用相應的上色/描邊方法

自此我們很輕松地繪制了一條黑色的直線,但如果我們想要繪制一條紅色的或者其它顏色的線段,該怎么做呢?

答案很簡單,使用ctx.strokeStyle來設定描邊的顏色即可。我們畫三條紅色的線段吧:

<canvas id="myCanvas" width="200" height="200" style="border:solid 1px #CCC; margin:30px;">
您的瀏覽器不支持canvas,建議使用最新版的Chrome
</canvas>

<script>
var c = document.getElementById("myCanvas");
var ctx = c.getContext("2d"); //獲取該canvas的2D繪圖環境對象
ctx.moveTo(0,0);   //咱把“畫筆”移到坐標(0,0)
ctx.lineTo(150,50);  //從上個點(0,0)畫一條直線,結束點坐標是(150,50)
ctx.lineTo(20,100);  //從上個點(150,50)繼續畫一條直線,結束點坐標是(20,100)
ctx.moveTo(90,90);   //咱把“畫筆”移到坐標(90,90)
ctx.lineTo(80,150);  //從上個點(60,60)繼續畫一條直線,結束點坐標是(80,150)
ctx.strokeStyle = "red";    //設定描邊顏色為紅色,只要寫在.stroke()方法前面即可
ctx.stroke();  //描邊
</script>

注釋都說的很清楚了,故不再贅述實現原理,其效果如下:

注意在開始繪制路徑的時候,一定要加上moveTo(x,y),否則第一個lineTo()的運動軌跡將不計入繪圖中(瀏覽器會認為沒獲取到該運動軌跡的起始點,故忽略此線段)。

另外有一個問題,如果上方我們會出來的兩條線段(嗯,一條折線,一條直線),我們希望第一條折線是藍色的,第二條直線是紅色的,應當怎么做?

你會地很自然地做如下處理:

<script>
  var c = document.getElementById("myCanvas");
  var ctx = c.getContext("2d"); 
  ctx.moveTo(0,0);   
  ctx.lineTo(150,50);  
  ctx.lineTo(20,100); 
  ctx.strokeStyle = "blue";    //設定描邊顏色為藍色
  ctx.stroke();  
   
  ctx.moveTo(90,90); 
  ctx.lineTo(80,150);  
  ctx.strokeStyle = "red";    //設定描邊顏色為紅色
  ctx.stroke();  
</script>

但運行腳本會發現,折線除了被描了一遍藍色,也被描了一遍紅色:

這是因為canvas在第二次給路徑上色時,是把之前的所有路徑軌跡合在一起來上色的,除非咱們讓canvas知道那折線和直線應該是獨立開來的倆路徑。

我們可以使用.beginPath()來解決:

<script>
  var c = document.getElementById("myCanvas");
  var ctx = c.getContext("2d"); 
  ctx.moveTo(0,0);   
  ctx.lineTo(150,50);  
  ctx.lineTo(20,100); 
  ctx.strokeStyle = "blue";    //設定描邊顏色為藍色
  ctx.stroke();  
  
  ctx.beginPath();  //告訴canvas咱們要重新繪制一條全新的路徑了,之前畫的東西從此再無關系
  ctx.moveTo(90,90); 
  ctx.lineTo(80,150);  
  ctx.strokeStyle = "red";    //設定描邊顏色為紅色
  ctx.stroke();  
</script>

有的朋友一開始會搞不清楚beginPath()的用途,覺得有moveTo()就可以了,其實beginPath()可以做到上述的隔離路徑繪制效果的作用,防止之前的效果被污染。

接著嘮嗑.strokeStyle的賦值方式,咱們上方是直接用了 ctx.strokeStyle="red" 來定義描邊顏色為紅色,不過ctx.strokeStyle可獲值的形式有三種:

ctx.strokeStyle=color|gradient|pattern;  //即支持 “顏色/漸變/圖案筆刷” 的賦值

⑴ 先看看color賦值方式,和我們常規的css賦值是一樣的,支持css3顏色值標準,如下例:

//下面四種形式都是一樣的,表示描邊顏色為“橙色”
ctx.strokeStyle = "orange";
ctx.strokeStyle = "#FFA500";    //#rrggbb形式
ctx.strokeStyle = "rgb(255,165,0)";   //RGB形式
ctx.strokeStyle = "rgba(255,165,0,1)";   //比上面的rgb多了個a(Alpha),即透明度

 

⑵ 再看下漸變gradient,這個稍有復雜:

var c = document.getElementById("myCanvas");
var ctx = c.getContext("2d"); 
ctx.moveTo(0,0);   
ctx.lineTo(150,50); 
ctx.lineTo(20,100); 

var grd = ctx.createLinearGradient(0,0,170,0);  //定義線性漸變對象,設定漸變線起始點和結束點坐標,坐標格式為(起始點x,起始點y,結束點x,結束點y)
grd.addColorStop(0,"black");   //定義漸變線起點顏色
grd.addColorStop(0.5,"red");   //定義漸變線中間點的顏色
grd.addColorStop(1,"yellow");  //定義漸變線結束點的顏色

ctx.strokeStyle = grd;   //將漸變對象賦值給strokeStyle
ctx.stroke();  //描邊

效果如下:

 這里我們提到了一個概念叫“漸變線”,沒有玩過設計的朋友需要了解下漸變的知識點,我們可以把LinearGradient(線性漸變,另有放射狀/圓形漸變RadialGradient)范圍看成一個矩形(你可以通過Illustator、Photoshop等專業設計軟件來輔助你理解這點):

我們一開始定義線性漸變對象的代碼 var grd = ctx.createLinearGradient(0,0,170,0) 不外乎就是設定了線性漸變線起始點為(0,0),結束點為(170,0)。

緊接著我們通過 addColorStop( 漸變線位置<0~1>, 顏色 ) 來設定了漸變色值,分別在漸變線0、0.5、1的位置設置了黑色、紅色、黃色,其漸變效果如下:

通過 ctx.strokeStyle = grd 將漸變賦值給描邊方法,最終描邊得到了我們想要的漸變效果。

 

⑶ 最后看看pattern描邊方式,strokeStyle之所以不叫strokeColor是因為它除了支持顏色描邊還支持圖案描邊(搞設計的朋友或許稱作筆觸描邊會更有feel)。

線性漸變描邊需要先createLinerGradient(xstart,ystart,xend,yend),那么設置圖案描邊自然也要先新建一個canvasPattern對象:

createPattern(image, repetitionStyle)

其中參數 image 代表圖案對象,一般通過 document.createElement('img') 或者 new Image() ,再定義其src值來創建該對象。
而repetitionStyle參數很好理解,即圖案重復形式,其可選值有"repeat" 、"repeat-x"、"repeat-y" 和"no-repeat" (和css的background-repeat可選值一樣,不贅述)。

那我們可以試著這樣寫:

<body>
<canvas id="myCanvas" width="200" height="200" style="border:solid 1px #CCC; margin:30px;">
您的瀏覽器不支持canvas,建議使用最新版的Chrome
</canvas>

<script>
    var c = document.getElementById("myCanvas");
    var ctx = c.getContext("2d"); 

    pic = new Image();   //創建圖片對象,或者 pic = document.createElement('img')
    pic.src = "http://images.cnblogs.com/cnblogs_com/vajoy/558870/o_5.jpg";   //定義圖片的映射地址
    var redTexture = ctx.createPattern(pic, "repeat");   //定義Pattern對象,設定填充圖案為pic圖片,填充形式為平鋪
    ctx.strokeStyle = redTexture;     //定義描邊樣式為上一行設定的Pattern描邊
    ctx.moveTo(80,10);
    ctx.lineTo(10,90);
    ctx.stroke();
</script>

只是在執行效果的時候會發現,執行十次有九次不是我們預期的圖片填充效果,而是默認的黑色描邊效果,這是為何?

原因在于瀏覽器可能還未加載完圖片pic,就已經執行了ctx.stroke(),解決方法是讓ctx在圖片pic加載完畢之后才開始執行繪圖:

<canvas id="myCanvas" width="200" height="200" style="border:solid 1px #CCC; margin:30px;">
您的瀏覽器不支持canvas,建議使用最新版的Chrome
</canvas>

<script>
    var c = document.getElementById("myCanvas");
    var ctx = c.getContext("2d"); 

    pic = new Image();   //創建圖片對象,或者 pic = document.createElement('img')
    pic.src = "http://images.cnblogs.com/cnblogs_com/vajoy/558870/o_5.jpg"; 
    pic.onload = patternFill;     //在圖片加載完成時執行繪圖函數

    function patternFill() {     //定義繪圖函數
        var redTexture = ctx.createPattern(pic, "repeat");   
        ctx.strokeStyle = redTexture; 
        ctx.moveTo(80,10);
        ctx.lineTo(10,90);
        ctx.lineWidth = 8;    //定義線段粗度為8像素
        ctx.stroke();
    }
    
</script>

效果如下:

注意這里我還加了個 ctx.lineWidth = 8 來設定線段的粗度。

自此我們學習了strokeStyle的三個賦值方式,也學習了上述的通過 ctx.lineWidth = lineWeight 的形式來給線段設定粗度。

最后咱們再學習兩個很簡單的線段屬性 lineCap 和 lineJoin。

⑴ lineCap是設定線段端點的形狀(線帽),其值可以是

butt    默認,即線條端點為平直的邊緣
round   線條端點為圓角線帽
square  為線條端點添加正方形線帽



 

<canvas id="myCanvas" width="250" height="120" style="border:1px solid #DDD;">
</canvas>

<script>
var c=document.getElementById("myCanvas");
var ctx=c.getContext("2d");
ctx.lineWidth=10;

ctx.beginPath();
ctx.lineCap="butt";
ctx.moveTo(20,10);
ctx.lineTo(200,60);
ctx.strokeStyle="red";
ctx.stroke();

ctx.beginPath();
ctx.lineCap="round";
ctx.moveTo(30,90);
ctx.lineTo(200,40);
ctx.strokeStyle="blue";
ctx.stroke();

ctx.beginPath();
ctx.lineCap="square";
ctx.moveTo(10,30);
ctx.lineTo(200,80);
ctx.strokeStyle="green";
ctx.stroke();
</script>
View Code

代碼效果如下:

光看此圖可能看不太出“butt”和"square"的區別,但懂得使用AI繪制矢量的同學們應該比較了解:

⑵ lineJoin則是設定折線的交接處的外角類型,其值可為:

 

miter    默認,折線交接處為尖角

round   折線交接處為圓角

bevel   折線交接處為斜角

 
 
 
 
 
 
<canvas id="myCanvas" width="200" height="220" style="border:1px solid #DDD;">
</canvas>

<script>
var c=document.getElementById("myCanvas");
var ctx=c.getContext("2d");

ctx.lineWidth=13;
ctx.lineJoin="bevel";
ctx.moveTo(20,20);
ctx.lineTo(100,50);
ctx.lineTo(20,80);
ctx.strokeStyle="red";
ctx.stroke();

ctx.beginPath();
ctx.lineJoin="round";
ctx.moveTo(20,60);
ctx.lineTo(100,90);
ctx.lineTo(20,150);
ctx.strokeStyle="green";
ctx.stroke();

ctx.beginPath();
ctx.lineJoin="miter";
ctx.moveTo(20,90);
ctx.lineTo(100,150);
ctx.lineTo(20,200);
ctx.strokeStyle="blue";
ctx.stroke();
</script>
View Code

效果如下:

需要了解的是,miter還受到了屬性miterLimit的影響(點此查看詳細),但個人覺得它跟bevel實現的效果是一致的,故在此不做介紹。

開篇就先講到這里,主要是對canvas線段繪制功能的介紹,共勉~

donate

文章列表




Avast logo

Avast 防毒軟體已檢查此封電子郵件的病毒。
www.avast.com


arrow
arrow
    全站熱搜
    創作者介紹
    創作者 大師兄 的頭像
    大師兄

    IT工程師數位筆記本

    大師兄 發表在 痞客邦 留言(0) 人氣()