vue自定义颜色选择器(取色器)

示例图 

安装颜色转换算法插件

npm install color-convert

参考文章:hsv颜色选择器实现原理

取色器使用的是原生的EyeDropper,参考链接:MDN-EyeDropper

注: 此项功能仅在一些支持的浏览器的安全上下文(HTTPS)中可用。

 vue组件代码

<template>
  <div class="po">
    <!-- 取色器 -->
    <div class="eyedropper" @click="eyedropper">点击取色(自行替换图标)</div>
    <!-- 色盘 -->
    <div class="panel" ref="panel" @mousedown="panelMousedown">
      <div class="white-panel"></div>
      <div class="black-panel"></div>
      <div
        class="color-picker-panel-cursor"
        :style="{
          left: cursorPosition.x + 'px',
          top: cursorPosition.y + 'px',
          backgroundColor: `rgb(${cursorPosition.rgb.r}, ${cursorPosition.rgb.g}, ${cursorPosition.rgb.b})`,
        }"
      ></div>
    </div>
    <!-- 色相 -->
    <div class="color-slider is-vertical">
      <div class="color-slider-bar" ref="hueSlider" @mousedown="hueMousedown">
        <div ref="hueSliderThumb" class="color-slider-thumb"></div>
      </div>
    </div>
    <!-- 透明度 -->
    <div class="alpha-slider-bar">
      <!-- 背景图 -->
      <div ref="alphaSliderWrapper" class="alpha-slider-wrapper"></div>
      <!-- 背景色 -->
      <div class="alpha-slider-bg" @mousedown="alphaMousedown"></div>
      <!-- 滑块元素 -->
      <div ref="alphaSliderThumb" class="alpha-slider-thumb"></div>
    </div>
    <div
      :style="{
        width: '120px',
        height: '36px',
        background: `rgba(
          ${cursorPosition.rgb.r}
          ${cursorPosition.rgb.g},
          ${cursorPosition.rgb.b},
          ${alpha}
        )`,
        marginTop: '20px',
      }"
    ></div>
    <div>
      <div>rgb: {{ cursorPosition.rgb }}</div>
      <div>alpha: {{ alpha }}</div>
    </div>
  </div>
</template>

<script>
import convert from "color-convert";
export default {
  data() {
    return {
      h: 0, // 色相
      cursorPosition: {
        x: 0, // 饱和度
        y: 0, // 明值
        rgb: { r: 0, g: 0, b: 0 },
      },
      alpha: 1,
    };
  },

  mounted() {
    console.log(convert);
    let panel = this.$refs.panel;
    let panelHeight = panel.offsetHeight;
    this.cursorPosition.y = panelHeight;
  },

  methods: {
    // 取色
    eyedropper(e) {
      // 需要在https并且浏览器支持的情况下才能运行
      if (!window.EyeDropper) {
        alert("当前浏览器不支持取色功能!!!");
        // this.$message.error("当前浏览器不支持取色功能!!!");
        return;
      }
      const dropper = new EyeDropper();
      dropper
        .open()
        .then((result) => {
          // hex 转 hsv
          let hsv = convert.hex.hsv(result.sRGBHex);
          // 设置色盘、色相、透明度值
          this.alpha = 1;
          this.$refs.alphaSliderThumb.style.left =
            this.$refs.alphaSliderWrapper.offsetWidth + "px";
          let hueSliderWidth = this.$refs.hueSlider.offsetWidth;
          this.h = hsv[0]; // 色相值
          let left = (hsv[0] / 360) * hueSliderWidth;
          this.$refs.hueSliderThumb.style.left = left + "px";
          // 计算色相值
          this.computeHue(left);
          let panel = this.$refs.panel;
          this.cursorPosition.x = (hsv[1] / 100) * panel.offsetWidth;
          this.cursorPosition.y = (-(hsv[2] - 100) / 100) * panel.offsetHeight;
          this.getRgb(this.cursorPosition.x, this.cursorPosition.y);
        })
        .catch((e) => {
          // this.$message.error("取色失败!!!");
          alert("取色失败!!!");
        });
    },
    panelMousedown(e) {
      // 色盘元素
      let panel = this.$refs.panel;
      let panelWidth = panel.offsetWidth;
      let panelHeight = panel.offsetHeight;
      // mousedown设置第一次位置并转换rgb值
      let x = e.pageX - panel.getBoundingClientRect().left;
      let y = e.pageY - panel.getBoundingClientRect().top;
      this.getRgb(x, y);

      let self = this;
      document.body.addEventListener("mousemove", panelMousemove);
      function panelMousemove(e) {
        // 更新光标位置和颜色值
        const panelRect = panel.getBoundingClientRect();
        let cursorX = e.clientX - panelRect.left;
        // 边界判断
        if (cursorX < 0) {
          cursorX = 0;
        } else if (cursorX > panelWidth) {
          cursorX = panelWidth;
        }
        let cursorY = e.clientY - panelRect.top;
        if (cursorY < 0) {
          cursorY = 0;
        } else if (cursorY > panelHeight) {
          cursorY = panelHeight;
        }
        self.getRgb(cursorX, cursorY);
      }
      document.body.addEventListener("mouseup", (e) => {
        document.body.removeEventListener("mousemove", panelMousemove);
      });
    },
    // 获取rgb值
    getRgb(x, y) {
      let panel = this.$refs.panel;
      let panelWidth = panel.offsetWidth;
      let panelHeight = panel.offsetHeight;
      let h = this.h;
      let s = parseFloat(((x / panelWidth) * 100).toFixed(2));
      let v = parseFloat(((-(y - panelHeight) / panelHeight) * 100).toFixed(2));
      let rgbArr = convert.hsv.rgb(h, s, v);
      this.cursorPosition = {
        x,
        y,
        rgb: {
          r: rgbArr[0],
          g: rgbArr[1],
          b: rgbArr[2],
        },
      };
    },
    // 色相滑动
    hueMousedown(e) {
      e.stopPropagation();
      // 获取色相背景元素
      let hueSlider = this.$refs.hueSlider;
      //   获取色相滑块元素
      let hueSliderThumb = this.$refs.hueSliderThumb;
      // 鼠标按下设置初始值
      hueSliderThumb.style.left = e.offsetX + "px";
      this.computeHue(e.offsetX);
      // 获取鼠标当前x轴位置
      let mousePageX = e.pageX;
      let sliderLeft = e.offsetX;
      document.body.addEventListener("mousemove", hueMousemove);
      document.body.addEventListener("mouseup", (e) => {
        e.stopPropagation();
        document.body.removeEventListener("mousemove", hueMousemove);
      });
      let self = this;
      function hueMousemove(e) {
        e.stopPropagation();
        // 偏移量
        let mouseOffsetX = e.pageX - mousePageX;
        // left最终值 当前位置 + 偏移量
        let left = sliderLeft + mouseOffsetX;
        if (left < 0) {
          left = 0;
        } else if (left > hueSlider.offsetWidth) {
          left = hueSlider.offsetWidth;
        }
        hueSliderThumb.style.left = left + "px";
        self.computeHue(left);
      }
    },
    // 计算色相值
    computeHue(left) {
      const sliderPercentage = left / this.$refs.hueSlider.clientWidth;
      const h = Math.round(sliderPercentage * 360);
      // 设置色相
      this.h = h;
      this.hue2rgb(h);
      this.getRgb(this.cursorPosition.x, this.cursorPosition.y);
    },
    // hue to rgb
    hue2rgb(h) {
      let doHandle = (num) => {
        if (num > 255) {
          return 255;
        } else if (num < 0) {
          return 0;
        } else {
          return Math.round(num);
        }
      };

      let hueRGB = (h / 60) * 255;
      let r = doHandle(Math.abs(hueRGB - 765) - 255);
      let g = doHandle(510 - Math.abs(hueRGB - 510));
      let b = doHandle(510 - Math.abs(hueRGB - 1020));
      let rgbString = "rgb(" + r + "," + g + "," + b + ")";
      this.$refs.panel.style.backgroundColor = rgbString;
    },
    // 透明度
    alphaMousedown(e) {
      e.stopPropagation();
      // 背景图元素
      let alphaSliderWrapper = this.$refs.alphaSliderWrapper;
      // 背景图宽度
      let wrapperWidth = alphaSliderWrapper.offsetWidth;
      //   滑块元素
      let alphaSliderThumb = this.$refs.alphaSliderThumb;
      //   设置滑块元素 left 值
      alphaSliderThumb.style.left = e.offsetX + "px";
      this.alpha = (e.offsetX / wrapperWidth).toFixed(2);
      console.log(this.alpha);
      // 获取鼠标当前x轴位置
      let mousePageX = e.pageX;
      let sliderLeft = e.offsetX;
      document.body.addEventListener("mousemove", alphaMousemove);
      document.body.addEventListener("mouseup", (e) => {
        e.stopPropagation();
        document.body.removeEventListener("mousemove", alphaMousemove);
      });
      let self = this;
      function alphaMousemove(e) {
        e.stopPropagation();
        // 偏移量
        let mouseOffsetX = e.pageX - mousePageX;
        // left最终值 当前位置 + 偏移量
        let left = sliderLeft + mouseOffsetX;
        if (left < 0) {
          left = 0;
        } else if (left > wrapperWidth) {
          left = wrapperWidth;
        }
        alphaSliderThumb.style.left = left + "px";
        self.alpha = (left / wrapperWidth).toFixed(2);
      }
    },
  },
};
</script>

<style lang="scss" scoped>
.po {
  position: absolute;
  top: 0;
  left: 0;
  width: 200%;
  height: 100%;
  background-color: #f5f5f5;
  z-index: 99999;
  padding: 20px;
}
.eyedropper {
  cursor: pointer;
}
.panel {
  width: 280px;
  height: 180px;
  position: relative;
  //   border: 1px solid #fff;
  background-color: rgb(255, 38, 0);
  // box-sizing: border-box;
  & > div {
    position: absolute;
    left: 0;
    right: 0;
    top: 0;
    bottom: 0;
  }
  .white-panel {
    background: linear-gradient(90deg, #fff, rgba(255, 255, 255, 0));
  }
  .black-panel {
    background: linear-gradient(0deg, #000, transparent);
  }
  .color-picker-panel-cursor {
    width: 6px;
    height: 6px;
    border-radius: 50%;
    position: absolute;
    // left: 100%;
    // top: 0;
    transform: translate(-50%, -50%);
    box-shadow: 0 0 0 3px #fff, inset 0 0 2px 2px rgb(0 0 0 / 40%),
      0 0 2px 3px rgb(0 0 0 / 50%);
    background-color: transparent;
  }
}

.color-slider,
.color-slider-bar {
  position: relative;
}
.color-slider.is-vertical {
  //   width: 28px;
  //   height: 180px;
  width: 280px;
  height: 12px;
  margin: 14px 0;
  cursor: pointer;
}
.color-slider.is-vertical .color-slider-bar {
  height: 100%;
  border-radius: 6px;
  background: linear-gradient(
    90deg,
    red 0,
    #ff0 17%,
    #0f0 33%,
    #0ff 50%,
    #00f 67%,
    #f0f 83%,
    red
  );
}
.color-slider-thumb {
  background-color: #fff;
  border-radius: 4px;
  position: absolute;
  box-shadow: 0 0 2px rgba(0, 0, 0, 0.5);
  border: 1px solid #dcdee2;
  left: 0;
  top: 0;
  width: 5px;
  height: 150%;
  transform: translateY(-20%);
  box-sizing: border-box;
  position: absolute;
  pointer-events: none;
}

// 透明度
.alpha-slider-bar {
  width: 280px;
  height: 12px;
  position: relative;
  cursor: pointer;
}
.alpha-slider-wrapper,
.alpha-slider-bg {
  position: absolute;
  left: 0;
  top: 0;
  bottom: 0;
  right: 0;
  border-radius: 6px;
  background: linear-gradient(
    90deg,
    rgba(255, 255, 255, 0),
    rgba(255, 255, 255, 1)
  );
}
.alpha-slider-bar.is-vertical .alpha-slider-bg {
  /* 这里先暂时写死 */
  background: linear-gradient(
    to top,
    rgba(255, 0, 0, 0) 0%,
    rgba(255, 0, 0) 100%
  );
}
.alpha-slider-wrapper {
  background: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAwAAAAMCAIAAADZF8uwAAAAGUlEQVQYV2M4gwH+YwCGIasIUwhT25BVBADtzYNYrHvv4gAAAABJRU5ErkJggg==");
}
.alpha-slider-thumb {
  background-color: #fff;
  border-radius: 4px;
  position: absolute;
  box-shadow: 0 0 2px rgba(0, 0, 0, 0.5);
  border: 1px solid #dcdee2;
  left: 100%;
  top: 0;
  width: 5px;
  height: 150%;
  transform: translateY(-20%);
  box-sizing: border-box;
  position: absolute;
  pointer-events: none;
}
</style>

颜色选择器重置版

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值