跟随网上教程学习前端知识迄今为止3个月,这个色彩选择器是根据网上教程的任务所做的,在此之前,我完全不知道也没想过JavaScript居然还能做这样一个东西,所以刚开始看到这个任务的时候是一头雾水。以下是我做这个色彩选择器的过程。
<canvas>
这个元素标签,查找之后,知道它是一个画布,那么如何产生色彩选择器的颜色就确定了。 //颜色棒的渐变
var context2=bar.getContext("2d");
var grd2 = context2.createLinearGradient(0,0,0,600);
grd2.addColorStop(0,"rgb(255,0,0)");
grd2.addColorStop(1/6,"rgb(255,255,0)");
grd2.addColorStop(1/3,"rgb(0,255,0)");
grd2.addColorStop(1/2,"rgb(0,255,255)");
grd2.addColorStop(2/3,"rgb(0,0,255)");
grd2.addColorStop(5/6,"rgb(255,0,255)");
grd2.addColorStop(1,"rgb(255,0,0)");
context2.fillStyle=grd2;
context2.fillRect(0,0,10,600)
getImageData()
的方法来获取发生变化时相应的颜色值,但是不是利用putImageData()
将颜色填充到画布上,因为画布上的颜色还有一个饱和度和明度的变化,所以画布上的颜色应该是从左至右有一个饱和度的变化,即从白色渐变到色相条上被选择的颜色,这个颜色是变化的,所以可以在其变化时将其作为一个函数的参数传递到函数,执行函数,然后进行渐变;同时画布上明度的变化,也可以被包括在该函数中,下面是从色相条上获取颜色的方法,有一点值得注意的是,色相条上的颜色变化来自色相条上颜色的变化,而右下角方框的颜色变化来自画布上圆形选择器的颜色变化,所以色相条上的颜色变化导致的一系列变化有:色相条上圆形选择器的背景色变化和画布上颜色的变化 → 画布上颜色发生变化,那么位于画布上圆形选择器选中颜色的变化 → RGB和HSL值的变化,展示方框颜色的变化:move.οnmοusedοwn=function(event){ flag=true; //这里的全局变量是让鼠标移动的时候可以取点击时的值 moveTop=move.offsetTop; nowY=event.clientY; } document.addEventListener("mousemove",function(event){ var moveY=event.clientY; dis=moveY-nowY; finalTop=moveTop+dis; if(flag){ if(bar.offsetTop+bar.offsetHeight-(move.offsetHeight/2)-1>=finalTop && finalTop>=bar.offsetTop- (move.offsetHeight/2)){ //if里面的条件是色相条上的圆形选择器从圆中心开始计算选色的位置,所以要减去它高度的一般,另外,前面的再减去1,是因为getImageData()不能从色相的最底部取一像素的值,这样取来的是浏览器的背景色,所以应该让圆心的位置上移一个像素,即减1。 move.style.top=finalTop+"px"; colorH=finalTop+move.offsetHeight/2-barTop; var imgData=context2.getImageData(0,colorH,1,1); //将rgb转为16进制 var strHex="#"; for(var i=0 ; i〈3; i++){ var hex = Number(imgData.data[i]).toString(16); if(hex.length===1){ //基于rgb转16进制的原理,如果长度为1,那么在前面加0 hex="0"+hex; } strHex += hex; } bgcolor=strHex; render(bgcolor); //渲染主色板 move.style.backgroundColor=bgcolor; //颜色棒上的圆形选择器移动时,以下函数改变RGB和Hsl pickRenderShow(); } } })
render()的函数如下:
function render(color){ //水平渐变 var context1=myCanvas.getContext("2d"); var grd1 = context1.createLinearGradient(0,0,600,0); grd1.addColorStop(0,'rgba(255,255,255,1)'); grd1.addColorStop(1,color); context1.fillStyle=grd1; context1.fillRect(0,0,600,600); //垂直渐变 var grd3 = context1.createLinearGradient(0,0,0,600); grd3.addColorStop(0,'rgba(0, 0, 0, 0)'); grd3.addColorStop(1,'rgba(0, 0, 0, 1)'); context1.fillStyle=grd3; context1.fillRect(0,0,600,600); }
另外关于颜色之间转换的公式,我是抄的别人的代码,即javascript HEX十六进制与RGB, HSL颜色的相互转换
求出RGB值和HSL值的函数pickRenderShow()如下:
//通过渲染板上圆形选择器移动而产生的拾色,右下角展示板渲染,各个值的显示变化 function pickRenderShow(){ //渲染板上的圆形择色器 circleCenterY=circleSelect.offsetTop-myCanvas.offsetTop+circleSelect.offsetWidth/2; circleCenterX=circleSelect.offsetLeft-myCanvas.offsetLeft+circleSelect.offsetWidth/2; //用来到达渲染板上圆形选择器中心点位置,然后取宽高各为一像素的像素点var imageData=context1.getImageData(circleCenterX,circleCenterY,1,1); showBGcolorR=imageData.data[0]; showBGcolorG=imageData.data[1]; showBGcolorB=imageData.data[2]; colorR.value=showBGcolorR; colorG.value=showBGcolorG; colorB.value=showBGcolorB; //根据渲染板上圆形择色器的rgb,转为16进制,将值填入右边一栏 var str="#"; for(var i=0; i〈3; i++){ var hex = Number(imageData.data[i]).toString(16); if(hex.length===1){ hex="0"+hex; //基于rgb转16进制的原理,如果长度为1,那么在前面加0 } str += hex; } //将rgb转换为Hsl showBGcolorR /= 255, showBGcolorG /= 255, showBGcolorB /= 255; var max=Math.max(showBGcolorR,showBGcolorG,showBGcolorB); var min=Math.min(showBGcolorR,showBGcolorG,showBGcolorB); L=(max+min)/2; if(max==min){ H=0; S=0; }else{ var d=max-min; S=L>0.5 ? d/(2-max-min):d/(max+min); switch(max){ case showBGcolorR: H=60*((showBGcolorG - showBGcolorB)/d+(showBGcolorG 〈showBGcolorB ? 6 : 0)); break; case showBGcolorG: H=60*( (showBGcolorB - showBGcolorR) / d + 2); break; case showBGcolorB: H=60*((showBGcolorR - showBGcolorG) / d + 4); break; } } colorh.value=Math.round(H); colorS.value=Number(S.toFixed(2)); //将小数点转换为2位,再将字符串转换为数字 colorL.value=Number(L.toFixed(2)); //使右边颜色展示板能够随左边圆形择色器的改变而改变 var context3=show.getContext("2d"); context3.fillStyle=str; context3.fillRect(0,0,100,100); colorHex.value=str; }
3.如果移动画布上圆形选择器,那么RGB和HSL,颜色展示框都会随之变化,这和上面的pickRenderShow()一样,就是多加了一个,不能让圆形选择器移出画布之外:
//渲染板上圆形选择器的移动
circleSelect.addEventListener("mousedown",function(event){
selectflag=true;
downX=event.clientX;
downY=event.clientY;
circleTop=circleSelect.offsetTop;
circleLeft=circleSelect.offsetLeft;
})
document.addEventListener("mousemove",function(event){
var moveX=event.clientX;
var moveY=event.clientY;
var circleNowL=(moveX-downX)+circleLeft;
var circleNowT=(moveY-downY)+circleTop;
if(selectflag){
//这里计算的原理和色相条上圆形选择器的位置计算原理是一样的
if(circleNowL>=myCanvas.offsetLeft-circleSelect.offsetWidth/2 && circleNowL<=myCanvasWidth+myCanvas.offsetLeft-(circleSelect.offsetWidth/2)-1){
circleSelect.style.left=circleNowL+"px";
}
if(circleNowT>=myCanvas.offsetTop-circleSelect.offsetHeight/2 && circleNowT<=myCanvasHeight+myCanvasHeight+myCanvas.offsetTop-(circleSelect.offsetHeight/2)-1){
circleSelect.style.top=circleNowT+"px";
}
pickRenderShow();
}
})
//获取输入的RGB值,并作出相应变化 var inputNum=[]; for(var j=0;j〈3;j++){ input[j].addEventListener("keydown",function(){ setTimeout(function(){ //setTimeout方法暂定,这是为了在键盘按下后,能够更新rgb,获得最新值,从而改变HSL的值,不然键盘按下的一瞬间,获得的数据是上一次的数据; //文本一发生改动,将原先数组的值清空,避免了留下的j变量值,在原有数组的基础上继续添加数组 inputNum.splice(0,3); for(var i=0;i〈 3;i++){ inputNum.push(input[i].value); } //利用延迟而获得的最新数值,来改变HSL和十六进制 if(inputNum[0]〈=255&& inputNum[1]〈=255 && inputNum[2]〈=255){ rgbTOhex(inputNum[0],inputNum[1],inputNum[2]); rgbTOhsl(inputNum[0],inputNum[1],inputNum[2]); var moveCurrentH=(600*colorh.value)/360-move.offsetHeight/2+bar.offsetTop;//利用返回的HSL中的H值,按照比列来确定move的位置 move.style.top=moveCurrentH+"px"; var imgData=context2.getImageData(0,moveCurrentH-bar.offsetTop+move.offsetHeight/2,1,1); //变化move的背景色 //将rgb转为16进制 var strHex="#"; for(var i=0; i〈3; i++){ var hex = Number(imgData.data[i]).toString(16); if(hex.length===1){ hex="0"+hex; } strHex += hex; } bgcolor=strHex; move.style.backgroundColor=bgcolor; render(bgcolor); //渲染主色板 //RGB改变时同时改变渲染板上圆形择色器的位置,根据HSV的原理好定位 var max=Math.max(inputNum[0],inputNum[1],inputNum[2]); var min=Math.min(inputNum[0],inputNum[1],inputNum[2]); var V=Number((max/255).toFixed(2)); var S=Number(((max-min)/max).toFixed(2)); //S的值是画布从左至右,取值为0-1,那么它本身的值可以视为在画布中的一个位置比列,所以要乘以画布的宽,然后减掉它的半径,以圆心为位置,还要加上画布的左边距;同理V的值,只不过V的值是从下开始计算,所以要1减去相应的比例算出相反的比例,才能得出圆形选择器的上偏移量 var circleL=S*myCanvasWidth-1-circleSelect.offsetWidth/2+myCanvas.offsetLeft; var circleT=(1-V)*myCanvasHeight-1-circleSelect.offsetHeight/2+myCanvas.offsetTop; circleSelect.style.top=circleT+"px" circleSelect.style.left=circleL+"px" //渲染板上圆形择色器的位置改变时,改变展示板的颜色 var context3=show.getContext("2d"); context3.fillStyle=colorHex.value; context3.fillRect(0,0,100,100); }else{ return; } },500) }) }
以下是改变HSL值发生的变化:
//获取输入的HSL值,并作出相应变化 var inputHSL=[]; for(var j=3;j〈6;j++){ input[j].addEventListener("keydown",function(){ setTimeout(function(){ inputHSL.splice(0,3); for(var i=3;i〈 6;i++){ inputHSL.push(input[i].value); } if(inputHSL[0]〈=360 &&inputHSL[1]〈=1 && inputHSL[1]〉=0 && inputHSL[2]〈=1 && inputHSL[2]〉=0){ hslToRgb(Number(inputHSL[0]),Number(inputHSL[1]),Number(inputHSL[2])); var moveCurrentH=(600*colorh.value)/360-15+18; move.style.top=moveCurrentH+"px";//变化move位置 var imgData=context2.getImageData(0,moveCurrentH-18+15,1,1); //变化move的背景色 var strHex="#"; for(var i=0; i〈3; i++){ var hex = Number(imgData.data[i]).toString(16); if(hex.length===1){ hex="0"+hex; } strHex += hex; } bgcolor=strHex; move.style.backgroundColor=bgcolor; render(bgcolor); //渲染主色板 //RGB改变时同时改变渲染板上圆形择色器的位置,根据HSV的原理好定位 var max=Math.max(colorR.value,colorB.value,colorG.value); var min=Math.min(colorR.value,colorB.value,colorG.value); var V=Number((max/255).toFixed(2)); var S=Number(((max-min)/max).toFixed(2)); var circleL=S*myCanvasWidth-1-10+18; var circleT=(1-V)*myCanvasHeight-1-10+18; circleSelect.style.top=circleT+"px" circleSelect.style.left=circleL+"px" //渲染板上圆形择色器的位置改变时,改变展示板的颜色 var context3=show.getContext("2d"); context3.fillStyle=colorHex.value; context3.fillRect(0,0,100,100); }else{ return; } },500); }) }
上面rgbTOhex();rgbTOhsl();是转换公式的函数,可以参考上面给出链接,也可以自己上网查,我把js的整个内容都写到html页面中了,在demo中也可以查看转换的函数;
以上就是我的色彩选择器的制作过程,归纳为以下:
希望看到这篇文章恰巧又有时间的话,能给我多提提意见,比如我这样写的代码有哪些缺点,这样的方法有哪些缺点,应该用哪种方法更好,因为我只是用我想的方法去实现这样一个功能,没有考虑到其他因素,比如
1.那个setTimeout的方法,我是为了取的当前的值而不是以前的值push到数组中去,我试过,如果发生keydown事件,直接将值push到数组中,是前面的值,不是我当下输入的值,但是我自己也觉得这个方法有点牵强。
2.每次一输入数组的值就得push一遍,是不是一个很麻烦的方法(我找不到其他的词语来形容了),而且为了保证是当前最新的三个数值,我还得将以前的数组清除一遍。。。
非常感谢看完一篇这么啰嗦的文章!!
2017-5-31