?
去我博客浏览吧:http://www.html-js.com/?p=1252
?
js的调色盘组件其实制作起来还算比较复杂的,网上有一些现成的,但是大部分组件只能提供部分颜色的选取,例如web色.那如何制作一个全色系的颜色选取器呢?
本文主要将如何从底层来讲全色系用js生成一张色盘.然后如果要使用色盘的时候,利用坐标就可以逆过来计算出选取到的颜色了.
首先看个demo吧,下面的demo,需要在现代浏览器里浏览,这个色盘使用canvas动态生成的,每个像素的点都是经过计算后生成的,拖动滑动条可以调节明暗动态更新色盘.是不是很神奇,下面就会介绍这里面用到的一系列知识.
?
首先色彩有几种表示方法,这里用到两种,所以只介绍两种,一种叫做HSL,一种叫做RGB.
RGB大家很常用的,这个应该很熟悉了,就是red,green,blue.设置值的时候分别设置红绿蓝三色的比例就可以组合成不同的颜色,通常每个色系都是用0-255来表示.
HSL这个算是新概念吧,百科上有介绍,还是比较详细的:
http://zh.wikipedia.org/wiki/HSL%E5%92%8CHSV%E8%89%B2%E5%BD%A9%E7%A9%BA%E9%97%B4
三个单词的意思分别是:hue(色相)、saturation(饱和度)、lightness(亮度).其中色相的取值是0-360,饱和度取值是0-1,亮度取值也是0-1.其实用模型来理解HSL更容易理解.HSL模型建立起来后其实就是一个色相环
如上面的例子中右边的色环所示.色相就代表角度,从0-360,正好转了一个圈,饱和度代表半径,亮度是第三个维度,用滑动条控制,三个维度结合起来就是一个完整的色系了.
可以看出来了吧,用HSL来建模更形象直观,所以在程序中我们用HSL来建模,然后在显示到屏幕上的时候,我们用一个算法把HSL转换成RGB,然后显示到屏幕上.这个算法是这样的:
点击查看源代码( powered by www.html-js.com )
vari;
varf,p,q,t;
varr,g,b;
if(s==0){
v=Math.floor(v*255);
return{
r:v,
g:v,
b:v
};
}
h/=60;
i=Math.floor(h);
f=h-i;
p=v*(1-s);
q=v*(1-s*f);
t=v*(1-s*(1-f));
switch(i){
case0:r=v;g=t;b=p;break;
case1:r=q;g=v;b=p;break;
case2:r=p;g=v;b=t;break;
case3:r=p;g=q;b=v;break;
case4:r=t;g=p;b=v;break;
default:r=v;g=p;b=q;break;
}
return{
r:r*255,
g:g*255,
b:b*255
};
}
其实就是一个公式,公式在维基百科上有给出,这里就不列出来了.网上有不少这种算法,但是发现有很多的算法有问题….我试了好几个最后才找出一个可用的.
然后,我们开始建模,将HSL中的HS表示成一个二维的平面,宽360,高100,然后将HS的值映射到这个平面上,L则映射到滑动条上,可以自由调节.
点击查看源代码( powered by www.html-js.com )
for(vari2=0;i2<360;i2++){
pixel=HSVToRGB(i2,i/100,0.8)
setData(imageData,i2,i,[
pixel.r,
pixel.g,
pixel.b,
255
]);
}
}
这样,我们在一个100*360的循环里,获取每个点实际应该显示的颜色,用HSVToRGB方法来获取,这个方法的第一个参数是H,第二个参数是S,第三个参数是F.
我们把i2直接映射到H上,把i/100后映射到S上,V先设一个默认值0.8.
这里的x轴就是H,y轴就是S
这时候,其实我们就完成工作了.
下面我们就用canvas来把颜色画到页面上.
利用了canvas的像素操作,
点击查看源代码( powered by www.html-js.com )
imageData.data[((y*(imageData.width*4))+(x*4))+0]=value[0]
imageData.data[((y*(imageData.width*4))+(x*4))+1]=value[1]
imageData.data[((y*(imageData.width*4))+(x*4))+2]=value[2]
imageData.data[((y*(imageData.width*4))+(x*4))+3]=value[3]
}
varcan=$("canvas");
varctx=can.getContext('2d');
varimageData=ctx.createImageData(360,100)
for(vari=0;i<100;i++){
for(vari2=0;i2<360;i2++){
pixel=HSVToRGB(i2,i/100,0.8)
setData(imageData,i2,i,[
pixel.r,
pixel.g,
pixel.b,
255
]);
}
}
ctx.putImageData(imageData,0,0)
在canvas的像素级操作的时候,其实就是在操作一个imageData对象,这个对象中有一个值叫做imageData.data.这是一个一维数组.
这里有点绕,这个一维数组的实现其实有点让人迷糊,它里面存储着所有像素的颜色和透明度信息,每个像素有四个值,分别是红,绿,蓝,透明度.所以如果你想读取一个位于x,y坐标的像素的红色信息的话,需要这样:
点击查看源代码( powered by www.html-js.com )
读取它的绿色信息就这样:
点击查看源代码( powered by www.html-js.com )
明白了么,就是把所有像素的信息全部展开到了一个一维数组里面.
利用上面的知识,我们就可以生成一张平铺的全色系图片了,在使用的时候,其实不需要动态生成,你只需要把这张图片截下来,然后放在组件中,用户点击的时候,获取到坐标,建模成HSL,然后用HSVToRGB方法转成RGB即可.
跟上面的过程正好是相反的.
右边其实有个色环,这个是一个更形象的建模.将颜色值分布到一个圆形的空间内,这里x轴和y轴不代表任何意义,我们用半径划过的角度表示H,半径表示S.然后将这个角度和半径转换成坐标值,最后画出一个这样的图形:
如源码中所示,最后只是一个这样的转换:
点击查看源代码( powered by www.html-js.com )
varyy=Math.ceil(70+Math.sin(i2*Math.PI/180)*i*0.70)
完整代码:
点击查看源代码( powered by www.html-js.com )
returndocument.getElementById(el)
}
varmix=function(target,prototypes){
for(variinprototypes){
target[i]=prototypes[i];
}
}
vargetXY=function(el){
if(document.documentElement.getBoundingClientRect){//IE,FF3.0+,Opera9.5+
varbox=el.getBoundingClientRect();
return{x:box.left+document.body.scrollLeft,
y:box.top+document.body.scrollTop}
}else{
varpos=[el.offsetLeft,el.offsetTop];
varop=el.offsetParent;
if(op!=el){
while(op){
pos[0]+=op.offsetLeft+parseInt(op.style.borderLeftWidth)||0;
pos[1]+=op.offsetTop+parseInt(op.style.borderTopWidth)||0;
op=op.offsetParent;
}
}
return{x:pos[0],y:pos[1]}
}
}
varsetData=function(imageData,x,y,value){
imageData.data[((y*(imageData.width*4))+(x*4))+0]=value[0]
imageData.data[((y*(imageData.width*4))+(x*4))+1]=value[1]
imageData.data[((y*(imageData.width*4))+(x*4))+2]=value[2]
imageData.data[((y*(imageData.width*4))+(x*4))+3]=value[3]
}
varcan=$("canvas");
varctx=can.getContext('2d');
varcan2=$("canvas2");
varctx2=can2.getContext('2d');
varimageData=ctx.createImageData(360,100)
varimageData2=ctx2.createImageData(140,140)
varpixel
functiontoRGB(TColor,q,p,H){
if(TColor<0){
TColor+=1;
}
if(TColor>1){
TColor-=1;
}
if(TColor<(1/6)){
returnp+q-p*6*TColor;
}elseif(TColor<(1/2)){
returnq;
}elseif(TColor<(2/3)){
returnp+q-p*6*2/3-TColor;
}else{
returnp;
}
}
functionHSVToRGB(h,s,v){
vari;
varf,p,q,t;
varr,g,b;
if(s==0){
v=Math.floor(v*255);
return{
r:v,
g:v,
b:v
};
}
h/=60;
i=Math.floor(h);
f=h-i;
p=v*(1-s);
q=v*(1-s*f);
t=v*(1-s*(1-f));
switch(i){
case0:r=v;g=t;b=p;break;
case1:r=q;g=v;b=p;break;
case2:r=p;g=v;b=t;margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; pa