說明
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 物件會有三個屬性,height
、width
和 data
。
ImageData.height
使用畫素描述 ImageData 的實際高度,這個值其實等於 getImageData() 方法中的引數 sh
ImageData.width
使用畫素描述 ImageData 的實際寬度。這個值其實等於 getImageData() 方法中的引數 sw
ImageData.data
一個一維陣列,包含以 RGBA 順序的資料,資料使用 0 至 255(包含)的整數表示。
注意
如果高度(sh)或者寬度(sw)變數為0,則會丟擲錯誤。
示例
以上是需要知道的基本知識,下來看幾個例子吧!
顏色選擇器
效果圖
實現思路
1、先把圖片畫到 canvas 上,
2、獲取滑鼠移動時的座標並通過 getImageData() 方法,獲取這一個點的畫素,
3、得到畫素資訊後,拼接出 rgba
的字串,再設定下面的小正方形的背景是這個顏色。
程式碼請點這裡
圖片灰度
效果圖
實現思路
1、先在 canvas 上畫出圖片,
2、通過 getImageData() 方法,獲取整個 canvas 上的畫素資訊,
3、點選按鈕時,遍歷獲取到的畫素資訊的每個畫素的 紅色值、綠色值 和 藍色值,相加求出平均值,再把平均值賦值給紅色、綠色和藍色,這麼做是為了求出每個畫素的灰度,
4、然後把改變後的畫素資訊,通過 putImageData() 方法重新新增到 canvas中,就實現了圖片灰度的效果。
程式碼請點這裡
除了圖片可以實現灰度的效果外,因為 canvas 中也可以放視訊,對於視訊也可以實現灰度的效果,原理都是一樣的,操作其實也一樣,其實視訊做灰度效果,可以理解為給很多張圖片做灰度效果。
效果圖
程式碼請點這裡
文字粒子
效果圖
實現思路
我們先不說,怎麼實現最後的動畫效果,我們先來想怎麼獲取文字所有畫素在 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,可以瞭解一下。
計算圖片中的圖形個數和麵積
問題
平面上有若干個不特定的形狀,如下圖所示。請寫程式求出物體的個數,以及每個不同物體的面積。
效果圖
實現思路
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() 方法 要考慮兩個問題
1、跨域問題
getImageData() 方法不允許操作非此域名外的圖片資源,所以如果想本地試試文章中的例子,直接寫圖片路徑就會報錯,
不過可以將圖片轉換成base64編碼,直接把base64編碼賦值給圖片的src屬性就可以簡單的解決這個問題了。這種方式僅僅適用於圖片,如果是視訊的話,還是需要伺服器端的配合。 具體可以看看這篇文章 解決canvas圖片getImageData,toDataURL跨域問題
2、效能問題
getImageData() 方法一般獲取的畫素資訊是很多的,所以這就要考慮效能的問題,至於怎麼優化也是要看具體場景了,比如在文中的文字粒子效果的示例中,可以先用 measureText() 方法計算文字寬度,結合 fontSize 就能知道 文字在哪塊區域,只通過這個塊區域獲取文字的畫素就好了。
getImageData() 方法,一句話總結就是獲取 canvas 上的畫素資訊,文中實現的各種效果不管是簡單的還是複雜的,都是在操作畫素。
通過getImageData() 方法,能做的事情還有很多,遠不止文章中提到的這些,比如實現其他的濾鏡效果,文中只是說了一個灰度,我們還可以去實現反相,模糊,浮雕等,文中實現了文字粒子效果,其實圖片也可以實現粒子效果,除此之外還有很多好玩的事情,但是本人才疏學淺,好多我也不知道。
最後,如果文中有不足或者錯誤的地方,還請小夥伴們指出,萬分感謝。
參考
Canvas基礎-粒子動畫Part3
前端面試的一道演算法題(使用canvas解答)
leetcode題解(遞迴和回溯法)