每个浏览器都有自己的特点,比如今天要做的colorpicker就是,一千个浏览器,一千个哈姆雷特,一千个colorpicker。今天canvas系列就用canvas做一个colorpicker。
**********************************************************************
效果图和demo
突然翻到了之前用js和dom写的一个colorpicker,比较挫,扔张图就好(old)
这个真的很挫,性能很差,因为每一个可选的颜色值都是一个dom,如果要实现256*256,那浏览器就爆了~~~~~
好,回到今天的demo(new)
demo链接: https://win7killer.github.io/can_ps/src/demo/color_picker.html
没错,就是照着PS的颜色选择器的样子仿的。
**********************************************************************
实现
首先我们来看效果图分析怎么做:
1.左侧colorbar
左侧提供一系列过渡色,不难看出,这个是“红黄绿青蓝紫”这六种颜色,然后加以过渡色处理来的。最后紫色还要过渡回到红色。
另外换成环状的可能更加好识别,如下图:
那么,我们就可以用canvas的过渡色来实现左侧这个区域,
代码如下:
1 function colorBar() { 2 var gradientBar = ctx.createLinearGradient(0, 0, 0, width); 3 gradientBar.addColorStop(0, '#f00'); 4 gradientBar.addColorStop(1 / 6, '#f0f'); 5 gradientBar.addColorStop(2 / 6, '#00f'); 6 gradientBar.addColorStop(3 / 6, '#0ff'); 7 gradientBar.addColorStop(4 / 6, '#0f0'); 8 gradientBar.addColorStop(5 / 6, '#ff0'); 9 gradientBar.addColorStop(1, '#f00'); 10 11 ctx.fillStyle = gradientBar; 12 ctx.fillRect(0, 0, 20, width); 13 }
这里涉及到canvas的fillStyle或者strokenStyle的填充对象,可以使用过渡色对象(自己瞎叫的名字),了解更多可以去w3cschool。
2.中间颜色区
中间这块乍看很简单,再看有点蒙bi,三看才搞清楚怎么搞。
乍看:其实就是左侧选中的那个颜色(比如A),然后进行过渡处理,不还是过渡么。
再看:恩,颜色,然后黑色,白色,三种颜色三个角怎么过渡~~~~(如果有快捷的过渡实现方式请留言告知我,THX)。
三看:那么,拆解一下,比如红色到白色过渡,然后加一层黑色到透明过渡?是滴,就是这么个方案。(我自己之前弯路到了红色到黑色,白色到透明)
那么就是借助两次过渡色的填充,实现中间色块区域。
代码如下:
1 function colorBox(color) { 2 // 底色填充,也就是(举例红色)到白色 3 var gradientBase = ctx.createLinearGradient(30, 0, width + 30, 0); 4 gradientBase.addColorStop(1, color); 5 gradientBase.addColorStop(0, 'rgba(255,255,255,1)'); 6 ctx.fillStyle = gradientBase; 7 ctx.fillRect(30, 0, width, width); 8 9 // 第二次填充,黑色到透明 10 var my_gradient1 = ctx.createLinearGradient(0, 0, 0, width); 11 my_gradient1.addColorStop(0, 'rgba(0,0,0,0)'); 12 my_gradient1.addColorStop(1, 'rgba(0,0,0,1)'); 13 ctx.fillStyle = my_gradient1; 14 ctx.fillRect(30, 0, width, width); 15 }
需要注意,第一次填充,是从横向填充,这时候中间色块的左边已经不是canvas的原点,所以加了偏移量30px
第二次填充纵向,Y轴还是0。
这个在实际应用中要注意。
到这里,左侧canvas绘制的东西就差不多了。
3. 颜色选择事件处理
首先明确交互事件:
选择左侧colorbar(比如#ff0),中间base颜色要跟着变化,右上角也要是对应颜色(#ff0)【这个时候其实也可以得到选择的颜色,可以结束交互】;
选择中间区域的颜色,左侧不变,可以获取到对应的颜色值,结束交互。
最终就是在右侧的dom区域展示所选到的颜色。
canvas中没有dom对象,所以鼠标点击事件要靠鼠标的位置来确定是否进行相应处理。而且我们绘制的不是path对象,也无法使用inpath之类的方法来判断。
点击事件代码:
1 can.addEventListener('click', function(e) { 2 var ePos = { 3 x: e.offsetX || e.layerX, 4 y: e.offsetY || e.layerY 5 } 6 var rgbaStr = '#000'; 7 if (ePos.x >= 0 && ePos.x < 20 && ePos.y >= 0 && ePos.y < width) { 8 // in 9 rgbaStr = getRgbaAtPoint(ePos, 'bar'); 10 colorBox('rgba(' + rgbaStr + ')'); 11 } else if (ePos.x >= 30 && ePos.x < 30 + width && ePos.y >= 0 && ePos.y < width) { 12 rgbaStr = getRgbaAtPoint(ePos, 'box'); 13 } else { 14 return; 15 } 16 outColor(rgbaStr.slice(0, 3).join()); 17 cur.style.left = ePos.x + 'px'; 18 cur.style.top = ePos.y + 'px'; 19 cur.style.outlineColor = (rgbaStr[0] > 256 / 2 || rgbaStr[1] > 256 / 2 || rgbaStr[2] > 256 / 2) ? '#000' : '#fff'; 20 });
其中,getRgbaAtPoint是最终的获取颜色值的方法,需要根据不同的鼠标位置传参来决定选取左侧还是右侧图像
获取颜色就比较简单了,就是拿到对应区域的imageData,然后从颜色数组中获取到对应位置