原生js制作简易DOM拾色器实例教程

本教程的目的是为了帮助初学者更好的掌握DOM和数组相关操作,实例效果如下图所示。
在这里插入图片描述
可以看到实例中的拾色器分为三个部分:拾色区域、色系区域、显示颜色区域。先写出这三个部分的html代码,如下所示:

  <div class="color_container">
    <div class="main_wrap"> <!--拾色区域-->
      <div class="main_drag" id="mainDrag"></div>
      <div class="main_con" id="mainCon"></div>
    </div>
    <div class="side_wrap"> <!--色系区域-->
      <div class="side_drag" id="sideDrag"></div>
      <div class="side_con" id="sideCon"></div>
    </div>
    <div class="show_color" id="findColor"><!--显示区域-->
      <div class="color_full" id="colorFull"></div>
      <div class="color_text" id="colorText">
        R:<input type="text">
        G:<input type="text">
        B:<input type="text">
      </div>
    </div>
  </div>

读者可以自己编写样式,也可以从 《原生js制作简易DOM拾色器实例教程》源码 下载本教程关联实例中复制。

开始编写javascript代码,声明一个fnColorPicker函数,把拾色器功能封装起来。

	function fnColorPicker(){
	
	}

接下来完成函数内具体代码。分析一下功能,设定三个区域的开发顺序:色系区域 > 拾色区域 > 显示颜色区域。

  1. 生成色系区域的DOM

色系区域实际上是不同色系色块组成(DOM元素),所以现在要做的工作就是使用js生成色块。生成色块有两个条件,一是有多少个色块,二是每个色块的rgb值分别是多少。知道这两个条件就可以很轻松拼成区域内的DOM元素。

每个色块的高度为1,宽度和色系区域一致,需要根据色系区域的高度计算出需要多少个色块,也就是共有多少种颜色。

1.1 获取色系区域及高度,如下所示:

//获取色系区域
var eSideCon = document.getElementById('sideCon');
//获取色系区域的高度
var nSideH = eSideCon.offsetHeight;

色系区域的高度nSideH实际上就是色块的数量,给每一个色块添加正确的背景颜色就可达到效果。分析一下色系区域的颜色变化是:红>黄>绿>青>蓝>紫>红,共七种颜色从上至下的渐变就形成了色系区域。在下表中列出了颜色每次变化的rgb值及修改规则:

颜色渐变rgb颜色数组变化修改的rgb值在数组中的下标修改方向
红>黄[255,0,0] → [255,255,0]g1
黄>绿[255,255,0] → [0,255,0]r0
绿>青[0,255,0] → [0,255,255]b2
青>蓝[0,255,255] → [0,0,255]g1
蓝>紫[0,0,255] → [255,0,255]r0
紫>红[255,0,255] → [255,0,0]b2

知道了颜色变化规则,再来写代码就轻松很多。

1.2 声明相关变量。

根据上面表格列出的规则,声明一个rgb数组,代表色块颜色;
声明变量表示要修改的rgb值在数组中的下标;
声明变量表示要修改rgb数值是变大还是变小;
存储色系颜色的数组,用于选择色系;
存储拾色区域色块颜色的数组,用于拾取颜色。如下所示:

	//rgb颜色数组,默认是第一种颜色(红色)
	var aRGB = [255,0,0];
	//要修改的rgb值在数组中的下标,0表示r,1表示g,2表示b,默认先修改g的值
	var nRGBSub = 1;
	//颜色值修改的方向,默认修改g值,从0>255,设定为true;
	var bUp = true;
	//存储所有色系的数组
	var aSideColorStore = [];
	//存储拾色区色块颜色的数组
	var aMainColorStore = [];

1.3 计算颜色值每次变化的数值

这是一个很重要的数值,比如从0变成255,每次应该增加多少呢?颜色总共有6次渐变,每次渐变改变的值最多是255。所以总共颜色变化是255*6次。再除以高度,就得出每次变化的值,计算公式如下所示:

	//计算颜色变化值
	var nStep = Math.ceil(255*6/nMainH);

1.4 通过循环,往eSideCon色系区域中填充色块

之前已经计算出色块的数量nSideH,所以循环nSideH次,把每一个色块组合成一个字符串,再填入到eSideCon中。具体代码如下:

  //声明html的字符串
  var sSideHTML = '';
  //循环nSideH次
  for(let i=0;i<nSideH;i++){
    //判断颜色值改变方向是变大还是变小
    if(bUp){
      //根据上表列出规则,第一次修改g值,数组的下标是1,每次增加nStep的数量
      aRGB[nRGBSub] += nStep;
      //颜色值最大是255,所以当增加超过255时
      if(aRGB[nRGBSub]>=255){
        //需要修正颜色值
        aRGB[nRGBSub] = 255;
        //修改下标
        nRGBSub--;
        if(nRGBSub<0)nRGBSub = 2;
        //修改颜色值改变方向
        bUp = false;
      }
    }else{
      //每次减少nStep的数量
      aRGB[nRGBSub] -= nStep;
      //颜色最值小是0,所以当减少为0或以下时
      if(aRGB[nRGBSub]<=0){
        //修正颜色值为0
        aRGB[nRGBSub] = 0;
        //修改下标
        nRGBSub--;
        if(nRGBSub<0)nRGBSub = 2;
        //修改颜色值改变方向
        bUp = true;        
      }
    }
    //把色块颜色存到色系数组中
    aSideColorStore.push(JSON.stringify(aRGB));
    //把每一个色块添加到字符串中
    sSideHTML = sSideHTML + `<div style="background:rgb(${aRGB.join(',')})"></div>`;
  }
  //填充到色系区域
  eSideCon.innerHTML = sSideHTML;

此时效果如图所示:
在这里插入图片描述

  1. 生成拾色区域的DOM

有了色系之后,可以根据当前色系生成左边的拾色区域。拾色区域也是由很多个色块组成,需要根据拾色区域的高度和宽度来获取色块的数量。

2.1 获取拾色区域的宽度和高度

	//获取拾色区域
	var eMainCon = document.getElementById('mainCon');
	//获取拾色区域的宽度和高度
	var nMainW = eMainCon.offsetWidth;
	var nMainH = eMainCon.offsetHeight;

每一个色块的大小 1 * 1,那拾色区域的色块就有nMainH行,每行nMainW个,总共数量为nMainW*nMainH。分析一下拾色区域的颜色变化,其实就是从左至右,从上至下颜色的渐变。下表列出默认拾色区域颜色变化规则:

左侧颜色渐变方向右侧颜色
白色[255,255,255]>红色[255,0,0]
……>……
黑色[0,0,0]>黑色[0,0,0]

上表列出了拾色区域四个角的颜色,可以得出规则是从左至右先创建第一行色块,白色到红色的渐变;再从上至下颜色逐步加深渐变为黑色。

2.2 声明相关变量

因为每一次选择色系都需要修改拾色区的颜色。所以把生成拾色区域色块功能封装到函数中,传入color参数表示当前色系颜色。

	//生成拾色区域
	function fnColorSet(color){
	
	}

在fnColorSet函数中再编写代码。拾色区其实是三种颜色的渐变,

左上角是白色,往下逐步加深;
右上角是选择色系的颜色,往下逐步加深,通过color参数传入;
下方是固定的黑色。
还需要再声明一个可变的色块颜色、html字符串和存储色块颜色的数组(用于拾取颜色),如下所示:

	function fnColorSet(color){
		//左侧可变颜色,默认为白色
	    var aLeftColor = [255,255,255];
	    //右侧可变颜色,因为color参数是字符串,所以要转换为数组
	    var aRightColor = JSON.parse(color);
	    //底部颜色固定黑色
	    var aBottomColor = [0,0,0] ;
	    //因为色块可变颜色从左上角开始,所以默认设置为白色
	    var aMainColor = [255,255,255];
	    //拾色区域html字符串
	    var sMainHTML = '';
	}

2.3 通过循环,往eMainCon拾色区域中填充色块

因为区域中的色块总共是 nMainH*nMainW 个,就像一个表格。按钮表格组成原则,先循环 nMainH 次,生成每一行。再在里面嵌套循环,循环 nMainW 次,即可在每行中生成正确的列数,如下所示:

  //生成拾色区域
  function fnColorSet(color){
    
    /* ... */

    //重置拾色区颜色
    aMainColorStore = [];
    //循环每一行色块
    for(let i=0;i<nMainH;i++){
      //因为每一列的颜色都是往下加深渐变,所以除第一行之外每行循环都需要修改左侧和右侧颜色
      if(i>0){
        for(let n = 0;n<aMainColor.length;n++){
          aLeftColor[n] -= Math.ceil((aLeftColor[n]-aBottomColor[n])/(nMainH-i));
          aRightColor[n] -= Math.ceil((aRightColor[n]-aBottomColor[n])/(nMainH-i));
        }
      }
      //每一行的色块颜色单独存到一个新的数组中
      aMainColorStore.push([]);
      //每一次循环色块都要重置为左侧颜色
      aMainColor = JSON.parse(JSON.stringify(aLeftColor));
      //在每一行中循环每一列色块
      for(let j=0;j<nMainW;j++){
        if(j!=0){ //第一个色块颜色不需要修改
          //从左至右渐变颜色,因为有rgb三种颜色,所以需要再加一个循环
          for(let n = 0;n<aMainColor.length;n++){
            //下面的公式用于rgb值颜色渐变
            aMainColor[n] -= Math.ceil((aMainColor[n]-aRightColor[n])/(nMainW-j));
          }
        }
        //存储色块颜色
        aMainColorStore[i].push(JSON.stringify(aMainColor));
        //每一个色块添加到字符串中
        sMainHTML = sMainHTML + `<div style="background:rgb(${aMainColor.join(',')})"></div>`;
      }
    }
    //填充拾色区域
    eMainCon.innerHTML = sMainHTML;
  }

   //调用函数,填充拾色区域,初始化传入红色
   fnColorSet('[255,0,0]');

此时效果如图所示:
在这里插入图片描述

  1. 填充显示颜色区域

因为每次拾取颜色,显示颜色区域都要修改,所以封装到fnColorFull函数中。如下所示:

  //获取显示颜色块
  var eColorFull = document.getElementById('colorFull');
  var eColorText = document.getElementById('colorText');
  var aColorInput = eColorText.getElementsByTagName('input');
  function fnColorFull(color){
    //颜色参数是字符串,需要转换为数组
    var color = JSON.parse(color);
    // 修改显示颜色
    eColorFull.style.background = 'rgb('+color.join(',')+')';
    //修改RGB颜色值
    for(let i=0;i<aColorInput.length;i++){
      aColorInput[i].value = color[i];
    }
  }
  //默认显示白色
  fnColorFull('[255,255,255]')
  1. 拾色区域拾取颜色
    在拾色区域有一个圆形吸管,可以通过拖动吸管来拾取颜色。在吸管上绑定事件,如下所示:
  //获取吸管元素
  var eMainDrag = document.getElementById('mainDrag');
  //aMainColorStore数组中颜色行下标
  var nSX = 0;
  //aMainColorStore数组中颜色列下标
  var nSY = 0;
  eMainDrag.addEventListener('mousedown',function(event){
    //初始化鼠标开始拖拽的点击位置
    var nInitX = event.clientX;
    var nInitY = event.clientY;
    //初始化吸管位置
    var nInitTop = this.offsetTop;
    var nInitLeft = this.offsetLeft;
    //选中吸管后,在document上绑定鼠标移动事件
    document.onmousemove = event=>{
      //鼠标移动时取消默认行为,避免选中其他元素或文字
      event.preventDefault();
      //获取鼠标位置
      let nX = event.clientX - nInitX + nInitLeft;
      let nY = event.clientY - nInitY + nInitTop;
      //以下的条件用于限制吸管不能移出拾色区域
      if(nY>=122){
        nY = 122;
      }
      if(nY<=0){
        nY = 0;
      }
      if(nX<=0){
        nX = 0;
      }
      if(nX>=192){
        nX = 192;
      }
      //因为用的是箭头函数,所以this还是指向吸管,修改吸管位置
      this.style.top = nY + 'px';
      this.style.left = nX + 'px';
      //颜色赋值,因为没办法选到最后一个颜色,所以加这个公式,这样中间有些颜色选不到
      nSX = nX+Math.floor(nX/26); //Math.floor(192/26) = 7;(192+7)=199,是宽度最后一个位置
      nSY = nY+Math.floor(nY/16);
      //填充显示颜色区域
      fnColorFull(aMainColorStore[nSY][nSX]);
    }
    //松开鼠标后释放document上的事件
    document.onmouseup = event=>{
      document.onmouseup = null;
      document.onmousemove = null;
    } 
  });
  1. 色系区域选择颜色
    通过色系上的滑块,可以修改拾色区域的颜色,代码如下所示:
  //获取色系吸管
  var eSideDrag = document.getElementById('sideDrag');
  //在色系吸管上绑定鼠标按下事件
  eSideDrag.addEventListener('mousedown',function(event){
    //初始化鼠标开始拖拽的点击位置
    var nInitY = event.clientY;
    //初始化色系吸管位置
    var nInitTop = this.offsetTop;
    //色系吸管位置
    var nY = null;
    document.onmousemove = event=>{
      //鼠标移动时取消默认行为,避免选中其他元素或文字
      event.preventDefault();
      //根据鼠标设置色系吸管位置
      nY = event.clientY - nInitY + nInitTop;
      //下面的条件限制色系吸管不能超出范围
      if(nY>=122){
        nY = 122;
      }
      if(nY<=-4){
        nY = 0;
      }
      //因为用的是箭头函数,所以this还是指向滑块,修改滑块位置
      this.style.top = nY + 'px';
    }
    //鼠标释放事件
    document.onmouseup = event=>{
      document.onmouseup = null;
      document.onmousemove = null;
      if(nY!==null){
        //修改拾色区颜色
        let n = nY+Math.floor(nY/16); //Math.floor(122/16) = 7;(122+7)=129,是高度最后一个位置
        fnColorSet(aSideColorStore[n]);
        //修改显示颜色区域颜色
        fnColorFull(aMainColorStore[nSY][nSX]);
      }
    }
  });

最后调用fnColorPicker函数就可以看到最终效果。本实例主要用于学习,不适合实际开发工作中使用。不但性能不太友好,而且bug也不少。想要流畅且友好的效果,可以查看我另外一篇文章 《js配合css3开发流畅的web拾色器功能》。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值