用canvas做一个简易取色器

2,024 阅读3分钟

本文首发于我的个人博客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颜色值和十六进制颜色值之间转换的处理。

结语

以上只是简单记录一下拾色器实现的思路,真正的拾色器远不止这么一点颜色,而且颜色都是渐变过度的,更细粒化,这样选取范围的判断应该要复杂一点。后续如果有整理的话会再更新。