用canvas做一個簡易取色器

nikolausliu發表於2017-10-04

本文首發於我的個人部落格nikolausliu.top

MDN上學習canvas相關API時,看到有個demo(下圖)跟平時用的拾色器有點像。於是我想canvas能不能用來做拾色器呢?仔細想了一下,答案是可以的,並做了一個簡單的demo(雙擊拾色)。在這裡把思路簡單記錄一下。

先看MDN上的例子

直接貼程式碼

function draw() {
  var ctx = document.getElementById('canvas').getContext('2d');
  for (var i=0;i<6;i++){
    for (var j=0;j<6;j++){
      ctx.fillStyle = 'rgb(' + Math.floor(255-42.5*i) + ',' + 
                       Math.floor(255-42.5*j) + ',0)';
      ctx.fillRect(j*25,i*25,25,25);
    }
  }
}}複製程式碼

示例程式碼用for套for迴圈定義了6x6共36個小矩形的背景顏色(ctx.fillStyle),並把這些小矩形繪製了出來(ctx.fillRect)。

結合fillRect(x, y, width, height)ctx.fillRect(j*25,i*25,25,25);可以推出:for迴圈中的i控制著矩形在y軸方向上的繪製。j控制著巨星在x軸方向上的繪製。這一點很重要,待會往上面示例程式碼中新增程式碼會用到(ps:有好的思維導圖軟體的希望推薦一波,xmind不適合畫下面這樣的,又不想開ps,所以,我用了畫圖軟體...)。

需求分析

現在要實現拾色器的功能,無非就是點選上面的矩形小色塊時,拿到當前點選區域對應的顏色值。再細分一下就是要實現兩點功能:

  1. 把每一個小矩形的位置資訊和顏色資訊儲存起來。這一點可以用一個二維陣列或者物件陣列(姑且這麼叫吧)來實現。我是用的後者,因為語義會更清晰一點。
  2. 根據滑鼠點選的位置判斷出點選區域屬於哪個矩形,並從上面儲存的二維陣列/物件陣列中取出相應的顏色值。

開始實現

實現第一點

// 初始化一個陣列用來儲存小矩形的位置資訊和顏色資訊
var arr = [];
function draw() {
  var ctx = document.getElementById('canvas').getContext('2d');
  for (var i=0;i<6;i++){
    for (var j=0;j<6;j++){
      ctx.fillStyle = 'rgb(' + Math.floor(255-42.5*i) + ',' + 
                       Math.floor(255-42.5*j) + ',0)';
      ctx.fillRect(j*25,i*25,25,25);
      // 每繪製一個矩形就儲存起來
      arr.push({
        x1: 25 * j,       // 矩形的左上角x座標
        y1: 25 * i,       // 矩形的左上角y座標
        x2: 25 * j + 25,  // 矩形的右下角x座標
        y2: 25 * i + 25,  // 矩形的右下角y座標
        // 矩形的顏色
        color: 'rgb(' + Math.floor(255 - 42.5 * i) + ',' + Math.floor(255 - 42.5 * j) + ',0)'
      });
    }
  }
}}複製程式碼

這樣每一個矩形的位置資訊和顏色資訊都被包裝成一個物件,並被推送到陣列中了。第一點完成。

實現第二點

滑鼠的位置資訊可以在滑鼠事件物件MouseEvent上拿到,分別為e.clientXe.clientY

canvas.addEventListener('dblclick', function (e) {
  var x = e.clientX;
  var y = e.clientY;
  for (var i = 0; i < arr.length; i++) {
    if (x >= arr[i].x1
      && x < arr[i].x2
      && y >= arr[i].y1
      && y < arr[i].y2) {
        // 拿到顏色啦
        console.log(arr[i].color);
    }
  }
}, true);複製程式碼

為了簡化計算,我設定了body {margin: 0;},實際的情況可能不會有這麼理想。

完整程式碼裡有對rgb顏色值和十六進位制顏色值之間轉換的處理。

結語

以上只是簡單記錄一下拾色器實現的思路,真正的拾色器遠不止這麼一點顏色,而且顏色都是漸變過度的,更細粒化,這樣選取範圍的判斷應該要複雜一點。後續如果有整理的話會再更新。

相關文章