玩轉Canvas——給坤坤變個顏色

語言: CN / TW / HK
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上每個畫素點的顏色資訊,一個畫素點對應四個值,分別為rgba

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

設定色值: