zoukankan      html  css  js  c++  java
  • 实现一个颜色选择器

    最近经历了一波辞职,找工作,搬家这样一个过程,所以没有空写博客,现在稳定了下来,写一下过年时写过的一些东西;

    这次要写的是一个颜色选择器,也许很多人都认为是不需要的,因为有h5的 api 提供类似的功能,但是作为一个探索者,怎么能不直接实现一个呢

    1. 首先是样式的编写
      关于样式方面我仿照的是 elementUI 的结构:

      <div >
      <div id="chooseColor" ></div>
      <div id="pickBox" style="display: none">
          <div class="colorBox">
              <div class="color" style="background-color: rgb(255, 0, 0);">
                  <div class="white"></div>
                  <div class="black"></div>
                  <div class="point">
                      <div class="p"></div>
                  </div>
              </div>
              <div class="colorSelect">
                  <div class="colorBar"></div>
                  <div class="thumb bar"></div>
              </div>
          </div>
          <div class="transparency" style="background-color: rgb(255, 255, 255);">
              <div class="transparencyBar"></div>
              <div class="thumb trans"></div>
          </div>
          <div class="operate">
              <input autocomplete="off" class="rgbaText" type="text" value="rgba(255,255,255,1)">
              <button id="confirm">确认</button>
          </div>
      </div>
      </div>
      
    2. css 的添加
      说到这里得说一个 css 的渐变色的使用:
      linear-gradient 该 API 可以让颜色变成渐变色,相当好用,现在使用率很高
      如这般使用:

      background: linear-gradient(to left, #3f87a6, #ebf8e1, #f69d3c);
      

      第一个参数是颜色旋转角度,后面的参数都是颜色,也可以加上比例

      完成后的样式,如下图:

    3. 初始化

      chooseColor.addEventListener('click',()=>{
          pick.style.display = 'block';
          init();
      },false);
        
      document.getElementById('confirm').addEventListener('click',()=>{
          pick.style.display = 'none';
      });
      

      这2个事件是启动与确认关闭的事件

      再就是说到 'init()' 这个函数,他是用来初始化查询器, div 的长宽和距幕距离,至于为什么要这样做呢,具体可以了解一下 display:none和选择器的关系;

      const init = (): void => {
          pick = <HTMLElement>document.getElementById('pickBox');
          colorElement = <HTMLElement>pick.querySelector('.color');
      
          colorPoint = <HTMLElement>pick.querySelector('.point');
          colorBar = <HTMLElement>pick.querySelector('.colorBar');
          rgbaText = <HTMLInputElement>pick.querySelector('.rgbaText');
          colorBarThumb = <HTMLElement>pick.querySelector('.bar.thumb');
          transparency = <HTMLElement>pick.querySelector('.transparency');
          transparencyBar = <HTMLElement>pick.querySelector('.transparencyBar');
          transparencyThumb = <HTMLElement>pick.querySelector('.transparency .thumb');
         
      
          //color长宽
          colorWidth = colorElement.clientWidth;
          colorHeight = colorElement.clientHeight;
          transparencyBarWidth = transparencyBar.clientWidth;
         
      
          pickBoxOffsetTop = pick.getBoundingClientRect().top;
          pickBoxOffsetLeft = pick.getBoundingClientRect().left;   
      };
      

      一些工具函数:

      const pxToNumber = (px: string = '0px'): number => {
          return Number(px.slice(0, -2));
      };
      
      const objToRGBA = (obj: rgb): string => {
          return `rgba(${obj.r},${obj.g},${obj.b},${transparencyCache})`;
      };
      
      const objToRGB = (obj: rgb): string => {
          return `rgb(${obj.r},${obj.g},${obj.b})`;
      };
      
      const rgbToObj = (rgbString: string): rgb => {
          let array: string[] = rgbString.split(',');
          return {r: Number(array[0].split('(')[1]), g: Number(array[1]), b: Number(array[2].slice(0, -1))};
      };
      
    4. 添加 click 事件

      pick.addEventListener('click', (ev) => {
          const target = <HTMLElement>ev.target;
          const x = ev.offsetX, y = ev.offsetY;
          switch (target.className) {
              case 'colorBar':
                  colorBarThumb.style.top = y + 'px';
                  const result = changeColorBar(y / colorHeight);
                  colorElement.style.backgroundColor = objToRGB(result);
                  return changeColor(pxToNumber(colorPoint.style.left), pxToNumber(colorPoint.style.top));
              case 'black':
                  colorPoint.style.left = x + 'px';
                  colorPoint.style.top = y + 'px';
                  return changeColor(x, y);
              case 'transparencyBar':
                  return changeTransparency(x);
          }
      }, false);
      

      根据点击元素的 className 判断点击到的元素是什么,做出判断和对于的事件
      4.1 先说下 colorBar 的选择
      需要实现的是点击 bar 之后,根据点击的位置到bar顶部的长度,通过 bar 颜色的分布来判断出点击位置的颜色
      听起来比较繁琐,其实还是很容易的
      先获取鼠标点击距离顶端的长度:y = ev.offsetY,改变 thumb 的位置;
      colorBarRange 函数:

          const colorBarRange = (scale: number): colorBarRangeType => {
              switch (true) {
                  case scale < .17:
                      return {rank: scale / .17, arr: [{r: 255, g: 0, b: 0}, {r: 255, g: 255, b: 0}]};
                  case scale < .33:
                      return {rank: (scale - .17) / .16, arr: [{r: 255, g: 255, b: 0}, {r: 0, g: 255, b: 0}]};
                  case scale < .5:
                      return {rank: (scale - .33) / .17, arr: [{r: 0, g: 255, b: 0}, {r: 0, g: 255, b: 255}]};
                  case scale < .67:
                      return {rank: (scale - .5) / .17, arr: [{r: 0, g: 255, b: 255}, {r: 0, g: 0, b: 255}]};
                  case scale < .83:
                      return {rank: (scale - .67) / .16, arr: [{r: 0, g: 0, b: 255}, {r: 255, g: 0, b: 255}]};
                  default:
                      return {rank: (scale - .83) / .17, arr: [{r: 255, g: 0, b: 255}, {r: 255, g: 0, b: 0}]};
              }
          };
      

      根据比例获取出颜色的域值和比例值
      changeColorBar 函数:

          const changeColorBar = (scale: number) => {
              const range = colorBarRange(scale);
              let rangeArr: rgb[] = range.arr;
              let diff: rgb = {
                  r: rangeArr[0].r - rangeArr[1].r,
                  g: rangeArr[0].g - rangeArr[1].g,
                  b: rangeArr[0].b - rangeArr[1].b
              };
              let result = rangeArr[1];
              for (let i in diff) {
                  result[i] = result[i] + diff[i] * (1 - range.rank) | 0;
              }
              return result;
          };
      

      通过上面的函数通过和255满值的计算,乘以比例值,获取出颜色值;
      changeColor的作用是改变 black的值,具体下面会说
      4.2 当前选中项为 black 时:
      改变点的位置:

      colorPoint.style.left = x + 'px';
      colorPoint.style.top = y + 'px';
      

      再次调用 changeColor 函数

      const changeColor = (x: number = 0, y: number = 0): void => {
          let {r, g, b} = rgbToObj(colorElement.style.backgroundColor);
            
      
          const difference = {
              r: 255 - r,
              g: 255 - g,
              b: 255 - b
          };
          const scaleX = x / colorWidth;
          scaleChange(difference, scaleX);
          const result = {
              r: 255 - difference.r,
              g: 255 - difference.g,
              b: 255 - difference.b
          };
          const scaleY = y / colorHeight;
        
          scaleChange(result, 1 - scaleY);
          const RGBA = objToRGBA(result);
          chooseColor.style.backgroundColor = RGBA;
          rgbaText.value = RGBA;
          transparency.style.backgroundColor = objToRGB(result);
      
      };
      

      传入2个值,一个是x,一个是 y, 各自对比 black 的长宽,得到比例,通过满值 rgb(255,255,255)
      乘以比例, 获取 rgb 值,默认透明度为1,转换为 rgba 格式
      4.3 当选中项为 transparency 时:
      changeTransparency 函数:

      const changeTransparency = (x: number) => {
          const transparency = getTransparency(x);
          transparencyThumb.style.left = x + 'px';
          transparencyCache = transparency;
        
          let currentColor = rgbaText.value.split(',');
          currentColor.splice(currentColor.length - 1, 1, transparency + ')');
          const changeTransparencyColor = currentColor.join(',');
        
          rgbaText.value = changeTransparencyColor;
          chooseColor.style.backgroundColor = changeTransparencyColor;
      };
      

      getTransparency 的作用是将宽度转换为比例,转化为满值为1,保留2位小数的值;

    5. 鼠标移动
      鼠标按下时改变值:

      pick.addEventListener('mousedown', (ev) => {
          const target = <HTMLElement>ev.target;
          switch (target.className) {
              case 'p':
                  return isMoveColor = true;
              case 'point':
                  return isMoveColor = true;
              case 'thumb bar':
                  return isMoveColorBar = true;
              case 'thumb trans':
                  return isMoveTransparency = true;   
          }
      }, false);
      

      监听mousemove事件:
      5.1 当 isMoveColor 变化时:

          let diffX = cx - pickBoxOffsetLeft - 7,
          diffY = cy - pickBoxOffsetTop - 7;
          if (diffX < 0) diffX = 0;
          if (diffY < 0) diffY = 0;
          if (diffX > colorWidth) diffX = colorWidth;
          if (diffY > colorHeight) diffY = colorHeight;
          changeColor(diffX, diffY);
          colorPoint.style.left = diffX + 'px';
          colorPoint.style.top = diffY + 'px';
      

      检测值鼠标是否移动到了选择器外,并且更新颜色值
      5.2 当 isMoveColorBar 变化时:

      let diffY = cy - pickBoxOffsetTop - 7;
      if (diffY < 0) diffY = 0;
      if (diffY > colorHeight) diffY = colorHeight;
        
      colorBarThumb.style.top = diffY + 'px';
      const result = changeColorBar(diffY / colorHeight);
      colorElement.style.backgroundColor = objToRGB(result);
      changeColor(pxToNumber(colorPoint.style.left), pxToNumber(colorPoint.style.top));
      

      同样地判断是否到边框外,以上面改变颜色值的方式同样地获取;
      5.3 当 isMoveTransparency 变化时:

      let diffX = cx - pickBoxOffsetLeft - 7;
      if (diffX < 0) diffX = 0;  
      if (diffX > transparencyBarWidth) diffX = transparencyBarWidth;   
      changeTransparency(diffX);
      

      检验是否到边框外即可;

    6. 结语
      虽然现在很多情况下,不用写这个东西了,但其中还是蕴含了很多知识点的,尤其是要接触了图形的人,实现这的关键在于,将坐标转换为颜色这一步,如果能够搞懂,那么这么对于你来说就会显得异常简单了;

    demo: 点击查看
    github: 点击查看

    完;

  • 相关阅读:
    第三周星期三
    第三周星期二
    导入oc动态库(framework)到C++项目中混编出现的问题
    webrtc项目编译报错(image not found)
    OpenGL-综合案例:旋转的大小球
    使用OpenGL来画个甜甜圈
    OpenGL-深度测试和颜色混合
    OpenGL 绘制正方形的基本步骤
    数据结构与算法-基础
    关于iOS离屏渲染的深入研究
  • 原文地址:https://www.cnblogs.com/Grewer/p/8652078.html
Copyright © 2011-2022 走看看