用 canvas 的 getImageData 做點有趣的事

FEWY發表於2018-09-18

說明

canvas元素標籤強大之處在於可以直接在HTML上進行圖形操作,具有極大的應用價值。

canvas 可以實現對影像的畫素操作,這就要說到 getImageData() 方法了。

解釋

CanvasRenderingContext2D.getImageData() 返回一個 ImageData 物件,用來描述 canvas 區域隱含的畫素資料,這個區域通過矩形表示,起始點為(sx, sy)、寬為sw、高為sh。

語法

ctx.getImageData(sx, sy, sw, sh);

引數

sx:將要被提取的影像資料矩形區域的左上角 x 座標。

sy:將要被提取的影像資料矩形區域的左上角 y 座標。

sw:將要被提取的影像資料矩形區域的寬度。

sh:將要被提取的影像資料矩形區域的高度。

返回值

一個 ImageData 物件,包含 canvas 給定的矩形影像資料。

ImageData 物件會有三個屬性,heightwidthdata

ImageData.height
使用畫素描述 ImageData 的實際高度,這個值其實等於 getImageData() 方法中的引數 sh

ImageData.width
使用畫素描述 ImageData 的實際寬度。這個值其實等於 getImageData() 方法中的引數 sw

ImageData.data
一個一維陣列,包含以 RGBA 順序的資料,資料使用 0 至 255(包含)的整數表示。

用 canvas 的 getImageData 做點有趣的事

注意

如果高度(sh)或者寬度(sw)變數為0,則會丟擲錯誤。

示例

以上是需要知道的基本知識,下來看幾個例子吧!

顏色選擇器

效果圖

用 canvas 的 getImageData 做點有趣的事

實現思路

1、先把圖片畫到 canvas 上,
2、獲取滑鼠移動時的座標並通過 getImageData() 方法,獲取這一個點的畫素,
3、得到畫素資訊後,拼接出 rgba 的字串,再設定下面的小正方形的背景是這個顏色。

程式碼請點這裡

圖片灰度

效果圖

用 canvas 的 getImageData 做點有趣的事

實現思路

1、先在 canvas 上畫出圖片,
2、通過 getImageData() 方法,獲取整個 canvas 上的畫素資訊,
3、點選按鈕時,遍歷獲取到的畫素資訊的每個畫素的 紅色值、綠色值 和 藍色值,相加求出平均值,再把平均值賦值給紅色、綠色和藍色,這麼做是為了求出每個畫素的灰度,
4、然後把改變後的畫素資訊,通過 putImageData() 方法重新新增到 canvas中,就實現了圖片灰度的效果。

程式碼請點這裡

除了圖片可以實現灰度的效果外,因為 canvas 中也可以放視訊,對於視訊也可以實現灰度的效果,原理都是一樣的,操作其實也一樣,其實視訊做灰度效果,可以理解為給很多張圖片做灰度效果。

效果圖

用 canvas 的 getImageData 做點有趣的事

程式碼請點這裡

文字粒子

效果圖

用 canvas 的 getImageData 做點有趣的事

實現思路

我們先不說,怎麼實現最後的動畫效果,我們先來想怎麼獲取文字所有畫素在 canvas 上的座標,

1、先獲取文字在canvas上的畫素資訊

  • 1.1 先在 canvas 上填充一個白色矩形
  • 1.2 用紅色,在 canvas 上寫文字
  • 1.3 獲取 canvas 的畫素資訊 ImageData
  • 1.4 清除整個 canvas
  • 1.5 在 ImageData 中找出是紅色的畫素,記錄他們的 x 和 y 座標,找出的每個畫素的座標都儲存在一個物件(Dot)中,所有的物件又都儲存在一個陣列(dotList)中,

計算機的速度是很快的,所以使用者是看不到紅色文字的,如果你覺得這種方式不好,也可以在用一個 canvas 專門來獲取文字畫素,這個canvas 不要讓使用者看到就好了。

我們知道文字的畫素資訊,知道每個畫素的座標,就能實現各種效果,像示例中的效果,僅僅只是改變一個 x 座標的值而已。

2、遍歷儲存文字畫素的陣列(dotList),每個畫素(Dot)物件還有一個 nowX 屬性,初始值是0,每次畫最後的圓點的時候,都是用這個屬性作為圓的 x 座標,nowX 屬性不斷的增加,直到最後等於畫素(Dot)物件的 x 屬性值就停止。

程式碼請點這裡

這種效果我也是在 Canvas基礎-粒子動畫Part3 這篇文章中看到的,作者寫的很好,推薦看看。
在github上也有一個不錯的專案 shape-shifter,可以瞭解一下。

計算圖片中的圖形個數和麵積

問題

平面上有若干個不特定的形狀,如下圖所示。請寫程式求出物體的個數,以及每個不同物體的面積。

用 canvas 的 getImageData 做點有趣的事

效果圖

用 canvas 的 getImageData 做點有趣的事

實現思路

1、建立儲存圖片畫素點的二維陣列(coordinates ),圖中只有兩種顏色,空白區域一種顏色,形狀區域一種顏色,空白區域的畫素標記為0,形狀區域標記為1,類似下面這樣

 0,0,0,0,0,0,0,0,0,0,0,0
 0,0,1,1,1,0,0,0,0,0,0,0
 0,1,1,1,1,0,0,0,0,0,0,0
 0,1,1,1,0,0,0,1,1,1,1,0
 0,0,0,0,0,0,1,1,1,0,0,0
 0,0,0,0,0,0,1,1,1,0,0,0
 0,0,0,0,0,0,0,0,0,0,0,0
複製程式碼

2、計算有多少個形狀,就是看二維陣列中有多少個連續為1的塊,計算形狀的面積,就是算一個連續為1的塊,有多少個1。 這就要通過 遞迴回溯演算法 來計算了。

回溯演算法實際上一個類似列舉的搜尋嘗試過程,主要是在搜尋嘗試過程中尋找問題的解,當發現已不滿足求解條件時,就“回溯”返回,嘗試別的路徑。
回溯法是一種選優搜尋法,按選優條件向前搜尋,以達到目標。但當探索到某一步時,發現原先選擇並不優或達不到目標,就退回一步重新選擇,這種走不通就退回再走的技術為回溯法。

程式碼請點這裡

這裡有一個示例,展示下用回溯法怎麼找到這些形狀的。

用 canvas 的 getImageData 做點有趣的事

這個例子出自 前端面試的一道演算法題(使用canvas解答)這篇文章,文章講的也很好,推薦看看

總結

getImageData() 方法 要考慮兩個問題

1、跨域問題
getImageData() 方法不允許操作非此域名外的圖片資源,所以如果想本地試試文章中的例子,直接寫圖片路徑就會報錯,

用 canvas 的 getImageData 做點有趣的事

不過可以將圖片轉換成base64編碼,直接把base64編碼賦值給圖片的src屬性就可以簡單的解決這個問題了。這種方式僅僅適用於圖片,如果是視訊的話,還是需要伺服器端的配合。 具體可以看看這篇文章 解決canvas圖片getImageData,toDataURL跨域問題

2、效能問題
getImageData() 方法一般獲取的畫素資訊是很多的,所以這就要考慮效能的問題,至於怎麼優化也是要看具體場景了,比如在文中的文字粒子效果的示例中,可以先用 measureText() 方法計算文字寬度,結合 fontSize 就能知道 文字在哪塊區域,只通過這個塊區域獲取文字的畫素就好了。

getImageData() 方法,一句話總結就是獲取 canvas 上的畫素資訊,文中實現的各種效果不管是簡單的還是複雜的,都是在操作畫素。 通過getImageData() 方法,能做的事情還有很多,遠不止文章中提到的這些,比如實現其他的濾鏡效果,文中只是說了一個灰度,我們還可以去實現反相,模糊,浮雕等,文中實現了文字粒子效果,其實圖片也可以實現粒子效果,除此之外還有很多好玩的事情,但是本人才疏學淺,好多我也不知道。
最後,如果文中有不足或者錯誤的地方,還請小夥伴們指出,萬分感謝。

參考

Canvas基礎-粒子動畫Part3
前端面試的一道演算法題(使用canvas解答)
leetcode題解(遞迴和回溯法)

用 canvas 的 getImageData 做點有趣的事

相關文章