当前位置: 首页 > 工具软件 > ColorPicker > 使用案例 >

色彩选择器(colorPicker)

袁炳
2023-12-01

跟随网上教程学习前端知识迄今为止3个月,这个色彩选择器是根据网上教程的任务所做的,在此之前,我完全不知道也没想过JavaScript居然还能做这样一个东西,所以刚开始看到这个任务的时候是一头雾水。以下是我做这个色彩选择器的过程。

  • .我学过用HTML和CSS做一个静态的网页,但是我是不了解HTML5的,我大概浏览了一下别人所写的代码,看到了<canvas>这个元素标签,查找之后,知道它是一个画布,那么如何产生色彩选择器的颜色就确定了。
    我在刚开始学的时候有上网了解过,他人的建议大概是:要自己多思考怎么写,不是拿别人的代码。而我碰到不会的任务时,我的做法是,比如这次的画布,我先看了别人的代码,然后我会记录下来别人代码里出现的我所没见过的元素标签、JS对象的属性和方法,但不是直接采用别人的想法(实际上我不太看的懂别人的代码,有些也没有注释,有时候自己有头绪写速度反而比看别人的更快),然后去查找,这会让我有一个头绪,接下来自己慢慢写,我不知道这样一个方法是不是比较好的学习方法,希望有好方法的人看到这段文字并且恰巧又有时间的话,可以帮忙提供给我,先说声谢谢!);
  • 做颜色选择器得了解颜色的知识,在那个任务下有学员提供了一份笔记,我自己理解了后,如下(我写的时候是用chorm看效果的):
    色彩选择器demo
    R——红色,G——绿色,B——blue,也就是三原色; H——色相,S——饱和度,L——明度;以及将RGB或者HSL转为十六进制来代表颜色。PS上的色彩选择器有一个色相条,它是将12色相环(所以H的取值就是0°~360°)头尾拆开变成条形,在色相环的画布上利用渐变来”上色“;
    //颜色棒的渐变 
    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)

    而左边大画布上的颜色变化是随着色相的变化而变化的,也就是H值0°~360°。在色相条上滑动圆形选择器时,利用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();
}
})

4.如果改变RGB和HSL值的任意一个值,那么色相条上的圆形选择器和画布上的圆形选择器的位置都会发生改变,画布颜色,色相条上圆形选择器背景色,颜色展示框的背景色都将发生改变,首先是RGB输入值发生改变:

//获取输入的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.圆形选择器背景色的变化。2.画布背景色的渐变(包括饱和度和明度)。3.画布当时圆形选择器中的颜色改变带来的RGB,HSL,颜色展示框的变化。
  • 画布上圆形选择器移动导致的:1.RGB,HSL,十六进制,颜色展示框的变化。2.考虑圆形选择器的位置。
  • RGB输入值的变化导致的:1.HSL,十六进制值变化。2.色相选择器位置和背景色的变化。3.画布上圆形选择器的位置变化。4.颜色展示框的变化。
  • HSL输入值变化导致的:1.RGB,十六进制值变化。2.色相选择器位置和背景色的变化。3.画布上圆形选择器的位置变化。4.颜色展示框的变化。

希望看到这篇文章恰巧又有时间的话,能给我多提提意见,比如我这样写的代码有哪些缺点,这样的方法有哪些缺点,应该用哪种方法更好,因为我只是用我想的方法去实现这样一个功能,没有考虑到其他因素,比如
1.那个setTimeout的方法,我是为了取的当前的值而不是以前的值push到数组中去,我试过,如果发生keydown事件,直接将值push到数组中,是前面的值,不是我当下输入的值,但是我自己也觉得这个方法有点牵强。
2.每次一输入数组的值就得push一遍,是不是一个很麻烦的方法(我找不到其他的词语来形容了),而且为了保证是当前最新的三个数值,我还得将以前的数组清除一遍。。。
非常感谢看完一篇这么啰嗦的文章!!

2017-5-31


 类似资料: