本文首发于我的个人博客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,所以,我用了画图软件...)。
需求分析
现在要实现拾色器的功能,无非就是点击上面的矩形小色块时,拿到当前点击区域对应的颜色值。再细分一下就是要实现两点功能:
- 把每一个小矩形的位置信息和颜色信息保存起来。这一点可以用一个二维数组或者对象数组(姑且这么叫吧)来实现。我是用的后者,因为语义会更清晰一点。
- 根据鼠标点击的位置判断出点击区域属于哪个矩形,并从上面保存的二维数组/对象数组中取出相应的颜色值。
开始实现
实现第一点
// 初始化一个数组用来保存小矩形的位置信息和颜色信息
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.clientX
和e.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颜色值和十六进制颜色值之间转换的处理。
结语
以上只是简单记录一下拾色器实现的思路,真正的拾色器远不止这么一点颜色,而且颜色都是渐变过度的,更细粒化,这样选取范围的判断应该要复杂一点。后续如果有整理的话会再更新。