周老虎落網的時候,網易跟騰訊都推出了牛逼轟轟的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>
代碼效果如下:
光看此圖可能看不太出“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>
效果如下:
需要了解的是,miter還受到了屬性miterLimit的影響(點此查看詳細),但個人覺得它跟bevel實現的效果是一致的,故在此不做介紹。
開篇就先講到這里,主要是對canvas線段繪制功能的介紹,共勉~

文章列表