1. 程式人生 > 實用技巧 >初識canvas(二)

初識canvas(二)

上節文章中,給大家分享了canvas最基礎的用法,包括繪製線條、控制渲染方式、繪製圖形、作用域、新增陰影、清理、剪下等功能,本節將繼續為大家分享canvas的基礎用法,同時也是最後一節基礎知識分享,之後的內容,我們將會進入到對webgl的分享中。

1. 文字繪製

首先我們要提到的是繪製文字,在canvas 的開發過程中,文字的內容是肯定會存在的。本小節就來給大家分享下如何渲染文字。

1.1 繪製空心和實心文字

canvas中提供了兩種繪製文字的方法,分別是繪製實心文字和空心文字,它們的方法分別是:

  • strokeText(text, x, y, maxWidth): 繪製空心文字
  • fillText(text, x, y, maxWidth): 繪製實心文字

引數介紹:

  • text: 要繪製的文字內容
  • x, y: 座標點,文字左上角的座標點
  • maxWidth: 允許渲染的最大畫素寬度

栗子:

// 繪製實心文字
c.fillText('hello world', 100, 100); // 將 hello world 這個內容從 100 100 這個點開始渲染。

// 繪製空心文字
c.strokeText('hello world', 200, 200); 

如果這裡你的字型是預設大小,可能會看不到空心效果。改變字型大小在下文中有分享到。

1.2 改變文字樣式

如果我們想修改字型的顏色,可以根據 繪製圖形 一節中的方法,使用 strokeStyle、fillStyle

來修改。

1. 要設定實心文字字型為藍色
c.fillStyle = 'blue';
c.fillText('hello world', 100, 100);
2. 要設定空心字型為紅色
c.strokeStyle = 'yellow';
c.strokeText('hello world', 200, 200);

1.3 改變文字大小和字型樣式

修改字型大小和字型樣式需要用到 font = '字型大小 字型樣式’ 屬性。

如:把字型改為 宋體 40px

c.font = '40px 宋體';

如果是特殊的字型,會出現不支援的現象。

1.4 修改文字對齊方式

文字對齊方式分為 垂直水平 對齊方式。

1. 垂直方向對齊。

這裡需要用到 textBaseLine 這個屬性。意為按照基線對齊。它有6個值,分別是:

  • alphabetic: 預設值,意為普通的字母基線。可理解為四線三格的第三條線。
  • top:文字頭部對齊基線
  • hanging:懸掛基線,與top稍有不同
  • middle:文字中部對齊
  • ideographic:表意基線。稍難理解,可看示意圖
  • bottom:文字底部對齊基線

示例:

// 首先我們畫一條基準線,讓文字按照這條線對齊
c.strokeStyle = 'blue';
c.moveTo(5, 100);
c.lineTo(700, 100);
c.stroke();

c.font = '20px 宋體';

// 列舉可用的對齊方式。
c.textBaseline = 'top';
c.fillText('Top', 5, 100);
c.textBaseline = 'bottom';
c.fillText('Bottom', 100, 100);
c.textBaseline = 'middle';
c.fillText('Middle', 200, 100);
c.textBaseline = 'alphabetic';
c.fillText('Alphabetic', 300, 100);
c.textBaseline = 'hanging';
c.fillText('Hanging', 400, 100);
c.textBaseline = 'ideographic';
c.fillText('ideographic', 500, 100);

示例圖:

2. 水平居中

水平居中需要使用 textAlign 屬性。同樣也是基於基準線對齊,有5個屬性值。

  • start: 預設。文字在指定的位置開始。
  • end: 在指定的位置結束
  • center: 文字中心被放在指定位置
  • left: 文字左對齊
  • right: 文字右對齊

栗子:

// 建立基準線
c.strokeStyle = 'blue';
c.moveTo(150, 20);
c.lineTo(150, 400);
c.stroke();

c.font = '30px 宋體';

// 列舉可用的對齊方式。
c.textAlign = 'start';
c.fillText('start', 150, 50);
c.textAlign = 'end';
c.fillText('end', 150, 100);
c.textAlign = 'left';
c.fillText('left', 150, 150);
c.textAlign = 'center';
c.fillText('center', 150, 200);
c.textAlign = 'right';
c.fillText('right', 150, 250);

示例圖:

3. 不固定寬高的畫布水平居中

對於寬度不固定的畫布,我們使用 textAlign 的作用就不太大了。因為此時我們需要根據畫布的寬度進行實時計算。此時我們需要獲得兩個數值。畫布寬度和字型寬度。畫布寬度比較簡單,使用 ctx.width 就可以獲取。

字型的寬度。需要用到 measureText(text) 方法。此方法可根據你設定的字型大小來返回傳入文字的畫素寬度。

栗子:

c.font = '20px 宋體'
const textData = c.measureText('hello world');
console.log(textData.width) // 84

設定字型寬度為 20px ,則渲染後的 hello world 佔據的畫素寬度為 84px(不同瀏覽器之間會有差異)

這樣我們就可以得到水平居中的計算方式

文字x座標 = (畫布寬 - 文字寬)/ 2

const x = (ctx.width - c.measureText('hello world')) / 2;

2. 動畫

2.1 狀態儲存與恢復

分享動畫之前 ,我們先來介紹 save 和 restore 這兩個方法。因為我們在處理動畫的過程中,避免不了對畫布進行旋轉、平移和縮放的操作。從而導致之前或之後的繪製出現錯亂。而``save 和 restore` 這兩個方法就可以避免這個問題。

  • save: 儲存當前canvas的狀態。

  • restore: 恢復之前儲存的狀態。

2.2 變換

說到動畫,無非就是對圖形的旋轉、縮放、平移這幾項內容。canvas 中提供了一系列的方法。

  • translate(x, y): 平移。可改變當前畫布的原點位置。
  • rotate(deg) :旋轉。其中 deg 代表的是弧度制。(角度轉弧度請看初始canvas(一)的內容)
  • scale(x,y): 縮放,x代表x軸縮放。y代表y軸縮放。

栗子:

// 原始矩形
c.save()
c.fillRect(10, 10, 100, 100);
c.restore()

// 平移50px
c.save()
c.translate(50, 50);
c.fillRect(10,10,100,100);
c.restore()

// 旋轉45度
c.save()
c.rotate(45 * Math.PI / 180);
c.fillRect(210, 110, 100, 100)
c.restore()

// x軸縮放0.5 y軸縮放1.1
c.save()
c.scale(0.5, 1.1)
c.fillRect(410, 210, 100, 100)
c.restore()

注意:translate 平移之後是可以改變畫布原點的位置的,此時如果我們需要讓某個圖形圍繞自身旋轉,則需要將影象的中心點位於畫布圓點上。如下方程式碼所示:

// 將畫布中心移入到 50,50 這個點
c.translate(50, 50);
setInterval(() => {
  // 每次繪製之前先清空畫布,這裡的原點已經在50,50這個位置了。所以需要從 -50這裡開始清理。
  c.clearRect(-50, -50, ctx.width, ctx.height);
  // 每次旋轉 1 度
  c.rotate(Math.PI / 180);
  // 繪製實心矩形。
  c.fillRect(-25,-25, 50,50);
}, 16)

這裡就可以得到一個圍繞自身旋轉的實心矩形。

2.3 transform 矩陣操作

方法接收 6 個引數。transform(a,b,c,d,e,f)

其中,

  • 平移:涉及到 e, f 兩個引數

  • 縮放:涉及到 a, d 兩個引數

  • 拉伸:涉及到 b, c 兩個引數

  • 旋轉:涉及到 a, b, c, d 四個引數

此方法在後續的講解中還會出現,並且方法不太好理解,這裡只簡單說明。不做過多介紹。

3. 漸變

本小節來說下如何給圖形新增漸變色。漸變色也是我們經常可以用到的功能。新增漸變色我們需要用到下面這兩個方法

  • createLinearGradient(sx,sy,ex,ey) 經向漸變
  • createRadialGradient (sx,sy,sr,ex,ey,er) 環形漸變

sx, sy, sr 代表的是起始點漸變圓的原點座標和半徑ex, ey, er 代表的是終止點漸變圓的原點座標和半徑。漸變方法返回一個物件。我們可以使用這個返回物件的 addColorStop(position, color)方法 執行新增顏色的操作。

position表示漸變的百分比,也就是漸變的位置。值是0-1 之間的浮點數。

color是要新增的顏色

下面,我們通過一個栗子來看下如何建立漸變圖形。

栗子1 -- 徑向漸變:

// 建立徑向漸變,得到漸變物件。
const lg = c.createLinearGradient(10, 10, 200, 200);
// 通過漸變物件來新增顏色
lg.addColorStop(0,"black");
lg.addColorStop(1, 'orange');

// 將得到的漸變色新增到填充樣式中。
c.fillStyle = lg;
c.fillRect(100, 100, 200, 200);

栗子2 -- 環形漸變:

// 起始點和終止點圓心座標相同,代表從同一點開始散發
const lg = c.createRadialGradient(150, 100, 5, 150, 100, 100);
lg.addColorStop(0,"black");
lg.addColorStop(1, 'orange');

c.fillStyle = lg;
c.arc(150, 100, 100, 0, 2 * Math.PI);
c.fill();

漸變示意圖:

4. 源目標和透明度

4.1 透明度設定

對於透明度設定我們只需要知道一個屬性就可以。

  • globalAlpha = value: value 的取值範圍在 0-1 之間。代表當前畫布的透明度是多少。

4.2 源目標設定

源與目標設定我們需要用到 globalCompositeOperation 這個屬性,它有N 個屬性值。這裡我們就不一一介紹了,這裡我們先繪製一個紅色的矩形,再繪製一個藍色的矩形,然後我們來看下設定不同的globalCompositeOperation會出現什麼情況。

圖解:

有需要的話可根據上圖進行篩查,看下自己需要哪種效果。對應設定。

5. 圖片繪製和背景設定

5.1 圖片繪製

對於圖片的操作在日常的工作中肯定會經常用到。canvas 中也提供了對於圖片操作的方法供開發人員使用。

注意:
在載入圖片的時候,一定要確保的是要在圖片載入完成之後再新增到畫布中。因此可以給圖片使用onload事件。

如:

const image = new Image();
image.onload = function () {
  // 繪製圖片操作
}
image.src = '圖片連結';

接下來繪製圖片的方法預設都是存在於 onload 事件中的。

  • drawImage可接收 3,5,9個引數;
    • 三個引數(img,x,y)
      • img是圖片物件,x,y是圖片在畫布中的原點位置
    • 五個引數(img,x,y,imageWidth,imageHeight);
      • 前三個引數不變,imageWidth,imageHeight設定的是圖片的寬和高
    • 九個引數(img,x,y,imageWidth,imageHeight,imgx,imgy,imgw,imgh);
      • 前五個引數不改變,imgx,imgy表示從圖片的這個點開始取畫素。imgw,imgh表示的是取多寬多高的畫素。

5.2 背景設定

通過createPattern(image, repetition)可以為畫布設定背景影象。

其中, image代表的是圖片物件。repetition代表的是是否平鋪,repeat:平鋪, no-repeat:不平鋪

6. 畫素操作

canvas最引入注目的功能就是對於畫素點的操作。這個功能可以讓我們實現眾多絢麗的特效。希望小夥伴們在領悟了畫素操作的真諦之後,也能做出絢麗的特效。這個我們也成為粒子效果;

對於畫素操作,canvas中提供了三個方法。

  • getImageData(x,y,w,h): 獲取畫布中指定位置的畫素集合 前兩個引數是你想要獲取的原點位置,後兩個引數是你想要獲取的範圍
  • putImageData(data, x,y): 設定畫布中指定位置的畫素集合,前兩個引數是你想要設定的原點位置
  • createImageData(data): 直接生成新的畫素矩陣,不用獲取

栗子:

const data = c.getImageData(0, 0, 10, 10);
console.log(data);
/*
[
	[1,2,3,4,5,6,7,8,9,10……],
	…………
]
*/

這裡我們通過 getImageData 可以獲取到一個二維陣列,代表獲取到的點點陣圖。

陣列中,每四個元素是一組。分別代表 r,g,b,a 中的每一項。每個r,g,b元素的取值範圍為 0-255a 的取值範圍為0-1;

如果我們想對畫素做操作,那就修改這些值,如圖片取反、復古風、底片…………等等,都可以實現。

有興趣的小夥伴可以探討一下喲~

7. 曲線

  • arcTo(startx, starty, endx, endy, r)
    • 此方法可以讓我們建立弧形線條。startx, starty 表示弧的起始點。endx, endy表示弧的終點。r 表示你弧的半徑。

示例:

c.beginPath()
c.moveTo(20,20); 
c.lineTo(100,20);
c.arcTo(150,20,150,70,10); // 建立弧
c.lineTo(150,120);
c.stroke()
  • quadraticCurveTo(cox, coy, endx, endy) 二次貝塞爾曲線
    • 貝塞爾曲線可以幫我們獲得更加多變的曲線內容。cox, coy 表示控制點座標。endx, endy 表示結束點座標。

示例:

c.beginPath()
c.moveTo(20,20);
// 將控制點設定線上段中間,
c.quadraticCurveTo(60, 80, 100, 20)
c.stroke()

示例圖:

除了這兩種曲線,還有三次貝塞爾曲線。小夥伴們可以自己試驗下,與二次貝塞爾曲線不同的是添加了一組控制點。

8. 事件

作為canvas中唯二的兩個可以獲取到當前圖形資訊的事件。isPointInPath和isPointInStroke的作用不可小覷。在很多針對圖形的操作我們都需要用到這個方法。

方法也是比較簡單。

  • isPointInPath(x, y) 接收兩個引數,一個x座標 一個y座標,判斷當前座標是否在圖形之內
  • isPointInStroke(x, y) 接收兩個引數,一個x座標 一個y座標,判斷當前座標是否在空心圖形邊框上。

9. 將canvas轉換為圖片 -- toDataURL

最後一個來分享下如何將我們繪製好的圖形儲存下來。對之後的分享自己繪製的圖形,在其他內容上顯示等等等等,都可以用到。

canvas轉換為圖片,我們需要用到 toDataURL(type, encoderOptions)

type 可以設定我們想得到的型別。如: image/png、image/jpg…………

encoderOptions 可以設定圖片的質量。

栗子:

const url = ctx.toDataURL('image/png')
console.log(url); // data:image/png;base64,……………………

注意:

這裡我們是使用的 ctx 來轉換圖片,也就是使用 canvas 這個標籤物件來轉換。

到這裡我們對於 canvas 的基礎內容就分享完了,從下節開始,我們就要進入到對 webgl 的學習中了。

好了,今天的分享就到這裡了,Bye~