玩轉Canvas——給坤坤變個顏色
ead>theme: awesome-green
Canvas可以繪製出強大的效果,讓我們給坤坤換個色。
先看看效果圖:
要怎麼實現這樣一個可以點選換色的效果呢? 話不多說,入正題。
第一步,建立基本元素,無須多言: ```html
我們先載入坤坤的圖片,然後給canvas新增基礎事件:
javascript
const cvs = document.querySelector('canvas');
const ctx = cvs.getContext('2d', {
willReadFrequently: true
});
//載入圖片並繪製
const img = new Image();
img.src = './img/cxk.png';
img.onload = () => {
cvs.width = img.width;
cvs.height = img.height;
ctx.drawImage(img, 0, 0);
};
再給canvas註冊一個點選事件:
javascript
//監聽canvas點選
cvs.addEventListener('click', clickCb);
function clickCb(e) { const x = e.offsetX; const y = e.offsetY; } ``` 這樣就拿到了點選的座標,接下來的問題是,我們要如何拿到點選座標的顏色值呢?
其實,canvas早就給我們提供了一個強大的api:getImageData。
我們可以通過它獲取整個canvas上每個畫素點的顏色資訊,一個畫素點對應四個值,分別為r
、g
、b
、a
。
ctx.getImageData
返回資料結構如下:
所以我們便可以利用它拿到點選座標的顏色值: ```javascript function clickCb(e) { //省略之前程式碼 //...
const imgData = ctx.getImageData(0, 0, cvs.width, cvs.height);
//獲取點選座標的rgba資訊
const clickRgba = getColor(x, y, imgData.data);
}
//通過座標獲取rgba陣列 function getColor(x, y, data) { const i = getIndex(x, y); return [data[i], data[i + 1], data[i + 2], data[i + 3]]; }
//通過座標x,y獲取對應imgData陣列的索引
function getIndex(x, y) {
return (y * cvs.width + x) * 4;
}
接下來便是在點選處繪製我們的顏色值:
javascript
//為了方便,這裡將變色值寫死為原諒綠
const colorRgba = [0, 255, 0, 255];
function clickCb(e) { //省略之前程式碼 //...
//座標變色
function changeColor(x, y, imgData) {
imgData.data.set(colorRgba, getIndex(x, y));
ctx.putImageData(imgData, 0, 0);
}
changeColor(x, y, imgData);
} ``` 此時如果點選坤坤的頭髮,會發現頭髮上僅僅帶一點綠。要如何才能綠得徹底呢?
我們新增一個判斷rgba值變化幅度的方法getDeff
,當兩者顏色相差過大,則視為不同區域。
javascript
//簡單地根據絕對值之和判斷是否為同顏色區域
function getDiff(rgba1, rgba2) {
return (
Math.abs(rgba2[0] - rgba1[0]) +
Math.abs(rgba2[1] - rgba1[1]) +
Math.abs(rgba2[2] - rgba1[2]) +
Math.abs(rgba2[3] - rgba1[3])
);
}
再新增一個判斷座標是否需要變色的方法:
```javascript
function clickCb(e) {
//省略之前程式碼
//...
//判斷該座標是否無需變色
function stopChange(x, y, imgData) {
if (x < 0 || y < 0 || x > cvs.width || y > cvs.height) {
//超出canvas邊界
return true
}
const rgba = getColor(x, y, imgData.data);
if (getDiff(rgba, clickRgba) >= 100) {
//色值差距過大
return true;
}
if (getDiff(rgba, colorRgba) === 0) {
//同顏色,不用改
return true;
}
}
}
我們更改`changeColor`方法,接下來便可以綠得徹底了:
javascript
function clickCb(e) {
//省略之前程式碼
//...
//座標變色
function changeColor(x, y, imgData) {
if (stopChange(x, y, imgData)) {
return
}
imgData.data.set(colorRgba, getIndex(x, y));
ctx.putImageData(imgData, 0, 0);
//遞迴變色
changeColor(x - 1, y, imgData);
changeColor(x + 1, y, imgData);
changeColor(x, y + 1, imgData);
changeColor(x, y - 1, imgData);
}
//省略之前程式碼
//...
} ``` 效果已然實現。但是上面通過遞迴呼叫函式去變色,如果變色區域過大,可能會導致棧溢位報錯。
為了解決這個問題,我們得改用迴圈實現了。
這一步的實現需要一定的想象力。讀者可以自己試試,看看能不能改用迴圈方式實現出來。
鑑於迴圈實現的程式碼略多,這裡不再解釋,直接上最終程式碼:
```html