Canvas 運(yùn)用

HTML5 中新增的一個(gè)相當(dāng)于畫布的標(biāo)簽,自帶各種 API ,通過 JS 調(diào)用和搭配,實(shí)現(xiàn)圖案繪制和想要的效果。

下面這張截圖就是由 canvas 在瀏覽器中生成的。

First Step

想要使用 canvas 標(biāo)簽,首先當(dāng)然是要添加標(biāo)簽了。
<canvas></canvas>
在 JS 中獲取該節(jié)點(diǎn),
var canvas = document.getELementById('canvas')
然后獲取 canvas 的上下文環(huán)境來獲取繪制 API
var context = canvas.getContext('2d')

其中,一定要記住在 getContext 中傳入?yún)?shù) ' 2d '

不然是沒法獲取到上下文環(huán)境的。

Seconds Step

現(xiàn)在畫布有了,但是畫布的大小還不知道呢, canvas 的默認(rèn)大小是 300 x 150 個(gè)像素。
但如果想要自定義大小的話,用常規(guī)的 CSS 樣式設(shè)置的話并不適用于 canvas,
因?yàn)樵?canvas 內(nèi)部,并不是使用一般的像素來作為繪制單位,若想設(shè)置其大小,

- 使用內(nèi)嵌屬性 <canvas id= 'canvas' width= '300' height= '500></canvas>
- 使用 JS 設(shè)置節(jié)點(diǎn)屬性 canvas.width = 300; canvas.height = 500;

當(dāng)設(shè)置好這些后,就可以開始 “畫畫” 了,下面是各種常用的 API

說明一下:

canvas 繪制方法是一些基于狀態(tài)的繪制,即多數(shù)方法只是為了設(shè)置 canvas 的狀態(tài),并非即時(shí)產(chǎn)生繪制效果,當(dāng)設(shè)置好狀態(tài)后,需要執(zhí)行一些特定的繪制方法才能在瀏覽器中呈現(xiàn),具體后面會講到。
另外,在 context 上下文中設(shè)置屬性值時(shí),應(yīng)該設(shè)置成字符串形式,比如設(shè)置顏色是要設(shè)成 "#ccc" ,而上面的寬高的數(shù)字本應(yīng)也要字符串,但會自動轉(zhuǎn)化類型。

注意:以下方法前面都省略了 context

  • .moveTo (x, y)

可以想象一下畫布上有一支虛擬的畫筆,使用這個(gè)方法把畫筆的筆尖定位到 xy 坐標(biāo)上。

  • .lineTo (x, y)

將筆尖移動到 xy 坐標(biāo),并與筆尖原坐標(biāo)之間產(chǎn)生連線??梢岳斫獬晒P從原坐標(biāo)到 xy 畫一條線段。如果原坐標(biāo)未定義,則效果相當(dāng)于 .moveTo 。
結(jié)合上述兩個(gè)方法,這里有個(gè)栗子:

// 想要畫一條從(50,50)起筆,畫到(100,150)的線段。

context.moveTo (50, 50)
context.lineTo (100, 150)

// 若第一行使用 lineTo (50, 50) 有同樣效果。因?yàn)樵谀侵肮P尖未定義

需要注意的是,瀏覽器窗口中并未真正出現(xiàn)了線段,請繼續(xù)往后看。

  • .lineWidth = "10" / 10

設(shè)置線段的寬度(粗細(xì)),
注意,這里并不能加單位 px,canvas 里并不是用像素來做單位的。

  • .lineCap = "butt" (默認(rèn)) / "round" / "square"

按字面理解,就是線段的帽子。
線段兩端呈現(xiàn)的形狀是什么,默認(rèn)為平頭,另外還有圓頭和方頭。
方頭看著好像沒差別,但是其實(shí)和圓頭一樣是有突出來了,如下圖左。
有個(gè)小技巧,當(dāng)兩條粗線段相接時(shí),使用方頭可以完美接合,如下圖右兩個(gè)圈圈標(biāo)記。

  • .lineJoin = "miter" / "bevel" / "round"

兩條或多條連續(xù)線段,在它們的接口(轉(zhuǎn)折)處呈現(xiàn)的形狀。
默認(rèn)為兩條線段斜切出一個(gè)尖角,還可以呈現(xiàn)出斜切面和圓滑。如下圖左黃圈處。

  • .miterLimit = "20" / 20

在轉(zhuǎn)折處為尖角時(shí),會有一種情況:當(dāng)兩線段夾角越小,延伸出的尖角就越長,為了使畫面處于可控范圍,默認(rèn)對尖角長度做限制,長度為10;超過后自動把轉(zhuǎn)折效果設(shè)成 "bevel" 。如果想實(shí)現(xiàn)原效果,可手動增加 limit 值。長度判定方法如下圖粉色標(biāo)記。

  • .stroke ()

通過調(diào)用這個(gè)方法,將前面設(shè)置的路線結(jié)合樣式繪制出來,即成像。

  • .strokeStyle = "#ccc" / "rgb()" / "rgba()" / "pink"

用在 stroke 前,這個(gè)方法常用于設(shè)置線段的顏色,必須設(shè)置為字符串,能接受與 JS 相同的顏色表述模式。另外,該方法還能設(shè)置漸變和模式。

  • .beginPath ()

前面已經(jīng)說過,canvas 是基于狀態(tài)繪制的。當(dāng)設(shè)置了一種樣式狀態(tài)比如顏色之后,狀態(tài)會一直保留,會影響后面甚至之前的繪制的圖形,比如:

context.moveTo(50,50)
context.lineTo(100,100)
context.strokeStyle = "blue"
context.stroke()
《》
context.moveTo(200,200)
context.lineTo(300,300)
context.strokeStyle = "red"
context.stroke()
!! 這里看似兩次繪制了兩條線段,而實(shí)際上是第二個(gè)stroke時(shí)連上面的線段也重新繪制一次,所以:
??! 第二次設(shè)置的狀態(tài) “紅色” 也會影響到第一條線段。

而這里,引入一個(gè)我自己的理解——圖層(類似于ps中),
使用了 beginPath 方法后,程序?qū)⑿陆ㄒ粋€(gè)圖層,
這個(gè)圖層獨(dú)立于之前,圖形、樣式等狀態(tài)將得到初始化,
具體用法就是在上面書名號那行插入此方法,就可以得到兩條不同顏色的線段。

  • .closePath ()

看名字,我本以為這是和上面那個(gè)方法是一對,但事實(shí)上功能卻沒什么關(guān)系,該方法用于把線段封閉起來,形成一個(gè)封閉的區(qū)域,即你只要繪制兩條有夾角的線段,然后調(diào)用 closePath 就可以得到一個(gè)三角形。

  • .fill ()

填充,在封閉的區(qū)域中填充顏色。

  • .fillStyle = "#ccc" / "rgb()" / "rgba()" / "pink"

設(shè)置填充方法的顏色。同上面的 strokeStyle ,還能填充漸變和模式。

  • .fillRect (x, y, w, h)

繪制一個(gè)左上角位于 xy ,寬高為 wh 的矩形,默認(rèn)為黑色。

  • .clearRect (x,y,w,h)

類似于橡皮擦,清除左上角位于 xy ,寬高為 wh 的矩形區(qū)域。一般用于清空畫布。
context.clearRect (0, 0, canvas.width, canvas.height)

下面會說到三個(gè)圖形轉(zhuǎn)換,如果熟悉 CSS3 的 transform 應(yīng)該會挺了解的。
下面說列出幾點(diǎn)要點(diǎn):

  • 圖形轉(zhuǎn)換是針對整個(gè)畫布坐標(biāo)系的轉(zhuǎn)換,而不是單獨(dú)某個(gè)對象。
  • 設(shè)置了圖形轉(zhuǎn)換,之后設(shè)置的繪圖狀態(tài)將會受到影響,而之前的則不會。
  • 多次設(shè)置圖形轉(zhuǎn)換,效果是傳遞的。
  • beginPath 也沒法清除影響,使用反向轉(zhuǎn)換或儲存狀態(tài)(后面介紹)解決。
  • .translate (x, y)

位移,使坐標(biāo)系在 xy 方向上位移,即改變原點(diǎn)位置(默認(rèn)在畫布左上角)。
當(dāng)多次設(shè)置位移而不清除的話,每次位移都是基于上次位移后的狀態(tài)繼續(xù)位移的。
注意,在 JS 坐標(biāo)系中 y 軸向下為正方向。

  • .rotate (45*Math.PI/180)

旋轉(zhuǎn),以原點(diǎn)為圓心,坐標(biāo)系旋轉(zhuǎn)角度。角度必須轉(zhuǎn)化為弧度制
表現(xiàn)特點(diǎn)同上。即,連續(xù)設(shè)置兩個(gè)45°等于設(shè)置一個(gè)90°

  • .scale (0.5, 2)

伸縮,坐標(biāo)系在 xy 方向上的伸縮(倍數(shù))。表現(xiàn)特點(diǎn)也如上所述。
下圖中我用線段描繪三個(gè)起始點(diǎn)在 50,50 長寬都為50的矩形,
但是中間插入了兩次 scale (2,2) ,而最終出現(xiàn)了三個(gè)位置大小都不一樣的矩形。
觀察以下可以看到,第一個(gè)最小的矩形就是代碼描述的樣子。
而第二個(gè)矩形,因?yàn)?xy 軸伸長成了兩倍,所以起始位置,和長寬都是原來兩倍。
第三個(gè)矩形,則是在第二個(gè)矩形的基礎(chǔ)上,繼續(xù)兩倍。
所以可以看出,圖形轉(zhuǎn)換的效果,是會傳遞的。
最有趣的,三個(gè)矩形連線段寬度都是成倍增長的。

既然圖形轉(zhuǎn)化這么厲害,設(shè)置了之后怎么復(fù)原呢?下面介紹
  • .save ()

儲存。該方法可以儲存當(dāng)前的狀態(tài),包括圖形轉(zhuǎn)換的狀態(tài)。它恢復(fù)的是狀態(tài)設(shè)置,而不影響已經(jīng)繪制的內(nèi)容。
一般用于儲存畫布最初始的狀態(tài),然后在一頓繪制之后使用 restore 恢復(fù)初始狀態(tài)。或者在設(shè)置圖形轉(zhuǎn)換之前儲存一下,轉(zhuǎn)換使用完后再 restore ,避免轉(zhuǎn)換持續(xù)影響繪圖。

  • .restore ()

恢復(fù)成上次儲存的狀態(tài),用法見上。

上面再介紹填充的時(shí)候有介紹到,除了顏色還可以填充漸變,

填充漸變,首先就要創(chuàng)建一個(gè)漸變模式。

  • .createLinearGradient (x1, y1, x2, y2)

線性漸變,創(chuàng)建一個(gè)從 x1y1 點(diǎn)到 x2y2 點(diǎn)的線性漸變。默認(rèn)為沒有顏色。

  • Object.addColorStop (0.5, "red")

為上面創(chuàng)建的漸變模式添加顏色錨點(diǎn),至少兩個(gè)或以上才會產(chǎn)生漸變效果,例如
Object.addColorStop(0.5,"red"); Object.addColorStop(1,"blue");
即表示從兩個(gè)坐標(biāo)之間,50%的位置開始,由紅色漸變到 100% 的位置藍(lán)色。
而前面未涉及的50%則由臨近的顏色填充,即紅色。

以上僅僅是創(chuàng)建了漸變狀態(tài)而已,下面來演示一下整個(gè)填充的過程:
  • 為了方便起見一般會創(chuàng)建一個(gè)對象來儲存漸變模式,
  • 給這個(gè)對象(漸變狀態(tài))添加錨點(diǎn),
  • 把填充樣式設(shè)置為該對象(漸變狀態(tài)),
  • 最后在想要的范圍內(nèi)執(zhí)行填充動作。(過程中請注意屬性的對象)
var gradient = context.createLinearGradient(0,0,1200,0)

gradient.addColorStop(0,"red")
gradient.addColorStop(.16,"orange")
gradient.addColorStop(.34,"yellow")
gradient.addColorStop(.51,"#0ef51f")
gradient.addColorStop(.68,"#0beffb")
gradient.addColorStop(.84,"blue")
gradient.addColorStop(1,"#8000e8")

context.fillStyle = gradient

context.fillRect(0,0,1200,800)
  • .createRadialGradient (x1, y1, r1, x2, y2, r2)

徑向漸變,另一種漸變模式,實(shí)現(xiàn)效果的方法和特點(diǎn)同上。
由一個(gè)以 x1y1 為圓心半徑為 r1 的圓漸變到以 x2y2 為圓心半徑為 r2 的圓。
一般我們會把 r1 設(shè)置為零,實(shí)現(xiàn)由圓心漸變到圓周的效果。

  • .createPattern (img || canvas || video, repeatStyle)

填充模式,這種更加強(qiáng)大,可以使圖片、視頻甚至是一個(gè)新的畫布。
使用方式與上面漸變相似,首先要創(chuàng)建想要填充的模式,傳入對象和重復(fù)模式。
重復(fù)模式可以選擇 "no-repeat" / "repeat-x" / "repeat-y" / "repeat" ,下面有個(gè)栗子:

// 創(chuàng)建一個(gè)新的畫布
var pattern = function () {
      var newCanvas = document.createElement('canvas')
      var context2 = newCanvas.getContext('2d')

      newCanvas.width = 100
      newCanvas.height = 100
      
      context2.fillStyle = "#000"
      context2.fillRect(0,0,100,100)

      retern newCanvas
}

// 將新畫布作為舊畫布的填充模式,傳入新畫布并設(shè)置重復(fù)
var fillPattern = context.createPattern(pattern, "repeat") 
context.fillStyle = fillPatern
context.fillRect(0,0,400,400)
  • .arc (x, y, r, star, end, acw)

畫弧。在畫布上畫一段圓弧,這是畫圓圈的主要方法。
以 xy 為圓心,r 為半徑的一個(gè)圓,圓弧開始點(diǎn)為 star 結(jié)束 為 end,
計(jì)算起止點(diǎn)時(shí),以右切點(diǎn)為 0 點(diǎn),順時(shí)針方向增加,并且始終不變。

這里還有順逆時(shí)針的概念,即從起始點(diǎn)繪制結(jié)束點(diǎn)是按順時(shí)針還是逆時(shí)針。當(dāng) acw 為 false 或不填時(shí),為順時(shí)針。當(dāng) acw 為 true 時(shí)為逆時(shí)針。但無論如何,位置標(biāo)點(diǎn)都不會變。
以下舉例:
1、false;2、false, closePath;3、true, closePath;
4、true;5、false, fill;6、true, fill

圖片來自慕課網(wǎng)

利用圓弧來畫圓角矩形

  • .arcTo (x1, y1, x2, y2, r)

以參考線畫一個(gè)弧,相對于上面那個(gè),比較復(fù)雜,下面只簡要說明要點(diǎn)。
1、原筆尖所在的位置為起始點(diǎn),到 x1y1 位置畫一條線段。
2、x1y1 位置到 x2y2 位置畫一條線段。
3、兩條線段形成一個(gè)夾角,所畫的圓弧就在夾角之間,半徑為 r。
4、圓弧與剛才的兩條線段相切,起始點(diǎn)到第一個(gè)切點(diǎn)為直線。
5、第一個(gè)切點(diǎn)之后開始圓弧,第二個(gè)切點(diǎn)曲線結(jié)束。
6、圓弧起止點(diǎn)不一定在線段上,可以是在延長線上。

  • .quadraticCurveTo (x1, y1, x2, y2)

二次貝塞爾曲線。
x2y2 為終止點(diǎn),x1y1 為控制點(diǎn),將起始點(diǎn)和終止點(diǎn)之間的線段拉扯成弧。

  • .bezeirCurveTo (x1, y1, x2, y2, x3, y3 )

三次貝塞爾曲線,x1y1 和 x2y2 為控制點(diǎn)。

除了幾何圖形,文字肯定是不能少的。
  • .font = "bold 40px Arial"

就像 CSS 中的 font 屬性一樣,可以并排設(shè)置多種字體屬性。其中有五種:
font-style || font-variant || font-weight || font-size || font-family

  • .strokeText (string, x, y, maxLength)

描邊字,這個(gè)方法傳入的字符串,呈現(xiàn)出中空只有描邊的文字,并將字符串的左上角定位在 xy,若設(shè)置了最大長度,當(dāng)超過后會強(qiáng)制壓縮字寬。該方法的樣式同樣受 strokeStyle 等相關(guān)狀態(tài)影響。

  • .fillText (string, x, y, maxLength)

填充字,出現(xiàn)經(jīng)過填充的實(shí)心字,特點(diǎn)同上。并且接受 fillStyle 的各種。

  • .textAlign = "left" || "center" || "right"

文字對齊,設(shè)置上面繪制的文字的對齊方式,以文字設(shè)置的起始點(diǎn)為基準(zhǔn)。

  • .shadowColor

  • .shadowOffsetX

  • .shadowOffsetY

  • .shadowBlur)

設(shè)置陰影狀態(tài)。之后繪制的圖形會有陰影。(顏色,X位移,Y位移,虛化程度)

  • .globalAlpha = 0.5

全局透明度。

  • .globalCompositeOperation

疊合操作模式。畫布中先后繪制的內(nèi)容會根據(jù)不同的模式來呈現(xiàn)效果。
默認(rèn)為后繪制的覆蓋先繪制的,也可以設(shè)置為先繪制的覆蓋后繪制的,甚至可以讓兩次繪制重疊部分鏤空或者提亮顏色。
屬性模式有以下是一種,并且我個(gè)人制作了個(gè)演示頁面,展示不同模式的效果。
source-over || source-atop || source-in || source-out || destination-over || destination-atop || destination-in || destination-out || lighter || copy || xor

  • .clip()

剪輯區(qū)域。先設(shè)置路徑再調(diào)用。把畫布裁剪成剛才設(shè)置的路徑區(qū)域。canvas 標(biāo)簽并沒有變小,但是之后的繪制只會在該裁剪區(qū)域內(nèi)可見。

  • .isPointInPath(x, y)

判定 xy 坐標(biāo)是否在當(dāng)前設(shè)置的路徑內(nèi)。傳回布爾值。Demo (暫時(shí),可能日后會更新)


寫在后面

可以看到 canvas 標(biāo)簽還是很強(qiáng)大的,它的功能當(dāng)然不止這些,canvas 的 context 支持自定義,可以自己添加一些原創(chuàng)的方法和規(guī)則進(jìn)去;canvas 甚至能進(jìn)行一些圖像處理,例如打馬賽克,添加濾鏡等等,在此篇章中就暫不深入介紹了。
待把這些基礎(chǔ)的玩出花,再去進(jìn)一步擴(kuò)展也不遲,
同時(shí),canvas 也還在不斷地發(fā)展,各個(gè)瀏覽器也慢慢一步步兼容,所以了解 canvas 的最新情況是有必要的。這里可以參考以下W3C 的 canvas 文檔。



Wait me back

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容