简易的颜色选择器

选择器截图:

下方为选择器代码:

<template>

  <div v-if="openFlag.isOpen" class="color-picker-content-div" @mouseleave="close" @mouseenter="enter"

    :style="`position: fixed;z-index: 100;${style}`">

    <div

      style="border: 1px solid #dee0e3; border-radius: 8px;width: 288px;background-color: rgb(255,255,255);">

      <div style="padding: 12px;padding-bottom: 0px;">

        <div>

          <a href="javascript:void(0);" @click="changeColor(item)" v-for="(item, index) in hList" class="color-item">

            <div :style="targetColor == item ? 'border-color: rgb(20,86,240);z-index: 2;' : ''"

              class="color-selected-border">

            </div>

            <div class="color-content"

              :style="`background-color:${item}; border:1px solid ${item == 'rgb(255,255,255)' ? '#d5d5d5' : item};`">

              <svg v-if="targetColor == item" width="17px" height="17px"

                :style="`color: ${item == 'rgb(255,255,255)' ? 'gray' : 'white'};`" viewBox="0 0 24 24" fill="none"

                xmlns="http://www.w3.org/2000/svg" data-icon="DoneOutlined">

                <path

                  d="M9.218 17.41 19.83 6.796a.99.99 0 1 1 1.389 1.415c-3.545 3.425-4.251 4.105-11.419 11.074a.997.997 0 0 1-1.375.017c-1.924-1.8-3.709-3.567-5.573-5.428a.999.999 0 0 1 1.414-1.415l4.95 4.95Z"

                  fill="currentColor"></path>

              </svg>

            </div>

          </a>

        </div>

        <div style="padding-top:14px;">

          <div v-for="(list, i) in colorTable">

            <a href="javascript:void(0);" @click="changeColor(item)" v-for="(item, index) in list" class="color-item">

              <div :style="targetColor == item ? 'border-color: rgb(20,86,240);z-index: 2;' : ''"

                class="color-selected-border">

              </div>

              <div class="color-content"

                :style="`background-color:${item}; border:1px solid ${item == 'rgb(255,255,255)' ? '#d5d5d5' : item};`">

                <svg v-if="targetColor == item" width="17px" height="17px" :style="`color: ${i == 0 ? 'gray' : 'white'};`"

                  viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" data-icon="DoneOutlined">

                  <path

                    d="M9.218 17.41 19.83 6.796a.99.99 0 1 1 1.389 1.415c-3.545 3.425-4.251 4.105-11.419 11.074a.997.997 0 0 1-1.375.017c-1.924-1.8-3.709-3.567-5.573-5.428a.999.999 0 0 1 1.414-1.415l4.95 4.95Z"

                    fill="currentColor"></path>

                </svg>

              </div>

            </a>

          </div>

        </div>

        <div style="padding-top:10px;">

          <div style="font-size: 12px;color: #646a73;">

            最近使用

          </div>

          <div style="padding: 10px 0px;height: 46px;">

            <a href="javascript:void(0);" @click="changeColor(item)" v-for="(item, index) in historyColorList"

              class="color-item">

              <div :style="targetColor == item ? 'border-color: rgb(20,86,240);z-index: 2;' : ''"

                class="color-selected-border">

              </div>

              <div class="color-content" :style="`background-color:${item}; border:1px solid #d5d5d5;`">

                <svg v-if="targetColor == item" width="17px" height="17px"

                  :style="`color: ${item == 'rgb(255,255,255)' ? 'gray' : 'white'};`" viewBox="0 0 24 24" fill="none"

                  xmlns="http://www.w3.org/2000/svg" data-icon="DoneOutlined">

                  <path

                    d="M9.218 17.41 19.83 6.796a.99.99 0 1 1 1.389 1.415c-3.545 3.425-4.251 4.105-11.419 11.074a.997.997 0 0 1-1.375.017c-1.924-1.8-3.709-3.567-5.573-5.428a.999.999 0 0 1 1.414-1.415l4.95 4.95Z"

                    fill="currentColor"></path>

                </svg>

              </div>

            </a>

          </div>

        </div>

      </div>



      <div style="border-top: 1px solid #dee0e3;"></div>



      <a href="javascript:void(0);" @click="openCirclePick" class="color-picker-circle-content">

        <div class="circle-icon"></div>

        <span style="font-size:14px; color:#646a73;">更多颜色</span>

        <div style="position: absolute; top: 8px; right: 2px;">

          <svg width="1em" height="1em" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"

            data-icon="RightBoldOutlined">

            <path

              d="m7.586 20.486.707.707a1 1 0 0 0 1.414 0l7.778-7.778a2 2 0 0 0 0-2.829L9.707 2.808a1 1 0 0 0-1.414 0l-.707.707a1 1 0 0 0 0 1.414l7.07 7.072-7.07 7.07a1 1 0 0 0 0 1.415Z"

              fill="currentColor"></path>

          </svg>

        </div>

      </a>

    </div>



    <div style="position: absolute;left: -285px;bottom: 0px;padding-right: 5px;">

      <div style="border: 1px solid #dee0e3; border-radius: 8px;background-color: rgb(255,255,255);"

        v-if="openCirclePickFlag">

        <div style="padding: 16px;padding-bottom: 5px;">

          <div ref="dragContainerEl" style="width: 250px;height: 150px;position: relative;">

            <div

              :style="`width: 100%; height: 100%;background-color: rgb(${circlePickData.sliderColor.r},${circlePickData.sliderColor.g},${circlePickData.sliderColor.b});`">

            </div>

            <div

              style="width: 100%; height: 100%;background: linear-gradient(to right,#fff,rgba(255,255,255,0));position: absolute;top: 0px;">

            </div>

            <div @mousedown="dragContainerMousedown"

              style="width: 100%; height: 100%;background: linear-gradient(to top,#000,rgba(0,0,0,0));position: absolute;top: 0px;">

            </div>

            <div class="drag-picker" ref="dragEl" @mousedown="dragPicker" href="javascript:void(0);">

              <div

                :style="`background-color: rgb(${circlePickData.pickerColor.r},${circlePickData.pickerColor.g},${circlePickData.pickerColor.b});`"

                class="drag-picker-cursor"></div>

            </div>

          </div>

        </div>

        <div style="padding: 6px 16px;position: relative;height: 32px;">

          <div

            :style="`width: 18px; height: 18px; background-color: rgb(${circlePickData.pickerColor.r},${circlePickData.pickerColor.g},${circlePickData.pickerColor.b});border:1px solid #d5d5d5;`">

          </div>

          <div style="position: absolute; top:0px; left: 40px;" class="color-picker-content-slider">

            <el-slider @input="changePickerBgColor" @change="changeEndPickerBgColor" :show-tooltip="false"

              v-model="circlePickData.sliderColorValue"></el-slider>

          </div>

        </div>

        <div class="color-picker-content-input" style="padding: 5px 16px;position: relative;height: 80px;">

          <div class="input-item" style="width: 80px;">

            <div style="position: absolute; top:0px; left: 0px;">

              <div style="text-align: center;padding: 5px 0px;">HEX</div>

              <div>

                <el-input @keydown="ColorUtil.checkHexKeydownValue" maxlength="7" @change="handleHexChange"

                  v-model="circlePickData.hex" />

              </div>

            </div>

          </div>

          <div class="input-item" style="width: 55px;">

            <div style="position: absolute; top:0px; left: 5px;">

              <div style="text-align: center;padding: 5px 0px;">R</div>

              <div style="text-align: center;">

                <el-input-number style="width: 50px;" v-model="circlePickData.pickerColor.r" :controls="false"

                  @change="handleInputChange" :min="0" :max="255"></el-input-number>

              </div>

            </div>

          </div>

          <div class="input-item" style="width: 55px;">

            <div style="position: absolute; top:0px; left: 5px;">

              <div style="text-align: center;padding: 5px 0px;">G</div>

              <div style="text-align: center;">

                <el-input-number style="width: 50px;" v-model="circlePickData.pickerColor.g" :controls="false"

                  @change="handleInputChange" :min="0" :max="255"></el-input-number>

              </div>

            </div>

          </div>

          <div class="input-item" style="width: 55px;">

            <div style="position: absolute; top:0px; left: 5px;">

              <div style="text-align: center;padding: 5px 0px;">B</div>

              <div style="text-align: center;">

                <el-input-number style="width: 50px;" v-model="circlePickData.pickerColor.b" :controls="false"

                  @change="handleInputChange" :min="0" :max="255"></el-input-number>

              </div>

            </div>

          </div>

        </div>

      </div>

    </div>



  </div>

</template>

<script setup lang="ts">



import { ref, onMounted, onUpdated } from 'vue'

import { ColorUtil } from './colorUtil'



const props = defineProps({

  style: { type: String, default: "top: 100px; right: 100px;" },

});




// 打开状态

const openFlag: any = ref({

  isOpen: false,

  isEnter: false,

  timeoutHandler: null,

});



// 变更回调

const emit = defineEmits(["change"]);



// 头部颜色列表

const hList = [

  "rgb(255,255,255)",

  "rgb(78,131,253)",

  "rgb(20,192,255)",

  "rgb(0,214,185)",

  "rgb(52,199,36)",

  "rgb(179,214,0)",

  "rgb(255,242,88)",

  "rgb(255,136,0)",

  "rgb(245,74,69)",

  "rgb(241,75,169)",

  "rgb(127,59,245)"

];



// 详细颜色列表

const colorTable = [

  [

    "rgb(248,249,250)",

    "rgb(225,234,255)",

    "rgb(217,243,253)",

    "rgb(213,246,242)",

    "rgb(217,245,214)",

    "rgb(238,246,198)",

    "rgb(250,241,209)",

    "rgb(254,212,164)",

    "rgb(251,191,188)",

    "rgb(253,221,239)",

    "rgb(236,226,254)"

  ],

  [

    "rgb(222,224,227)",

    "rgb(186,206,253)",

    "rgb(126,218,251)",

    "rgb(100,232,214)",

    "rgb(142,224,133)",

    "rgb(195,221,64)",

    "rgb(250,211,85)",

    "rgb(255,186,107)",

    "rgb(247,105,100)",

    "rgb(245,122,192)",

    "rgb(173,130,247)"

  ],

  [

    "rgb(143,149,158)",

    "rgb(51,112,255)",

    "rgb(4,159,215)",

    "rgb(4,180,156)",

    "rgb(46,161,33)",

    "rgb(143,172,2)",

    "rgb(255,198,10)",

    "rgb(222,120,2)",

    "rgb(216,57,49)",

    "rgb(240,29,148)",

    "rgb(100,37,208)"

  ],

  [

    "rgb(55,60,67)",

    "rgb(36,91,219)",

    "rgb(3,126,170)",

    "rgb(3,99,86)",

    "rgb(24,96,16)",

    "rgb(102,121,1)",

    "rgb(220,155,4)",

    "rgb(143,79,4)",

    "rgb(129,37,32)",

    "rgb(158,19,97)",

    "rgb(56,13,130)"

  ],

  [

    "rgb(31,35,41)",

    "rgb(19,60,154)",

    "rgb(0,97,133)",

    "rgb(2,75,65)",

    "rgb(18,75,12)",

    "rgb(73,87,0)",

    "rgb(121,81,1)",

    "rgb(107,57,0)",

    "rgb(98,28,24)",

    "rgb(122,15,75)",

    "rgb(39,5,97)"

  ]

];



// 子界面打开标记

const openCirclePickFlag = ref(false);

// 拖拽点

const dragEl: any = ref(null);

// 拖拽容器

const dragContainerEl: any = ref(null);

// 选择器数据

const circlePickData = ref({

  hex: "",

  sliderColorValue: 0,

  sliderColor: {

    r: 255,

    g: 0,

    b: 0,

  },

  pickerPos: {

    x: 0,

    y: 1,

  },

  pickerColor: {

    r: 255,

    g: 255,

    b: 255,

  }

});



// 当前颜色

const targetColor = ref("");



// 历史记录

const historyColorList = ref([]);



onMounted(() => {



});



// 变更颜色

function changeColor(color: string) {

  circlePickData.value.pickerColor = (!!color ? ColorUtil.STRtoColor(color) : { r: 255, g: 255, b: 255 });

  handleInputChange();

}



// 添加历史数据

function addHistoryColor(color: string) {

  targetColor.value = color;

  const tempList = historyColorList.value;

  const newList: any = [color];

  for (let index = 0; index < tempList.length; index++) {

    const element = tempList[index];

    if (newList.indexOf(element) < 0) {

      newList[newList.length] = element;

    }

    if (newList.length >= 11) {

      break;

    }

  }



  historyColorList.value = newList;

  emit("change", {

    target: {

      value: circlePickData.value.hex,

    }

  });

}



// 打开拖拽选择子界面

function openCirclePick() {

  openCirclePickFlag.value = !openCirclePickFlag.value;



  // 设置子界面颜色

  circlePickData.value.pickerColor = (!!targetColor.value ? ColorUtil.STRtoColor(targetColor.value) : { r: 255, g: 255, b: 255 });



  setTimeout(() => {

    if (!openCirclePickFlag.value) {

      return;

    }

    handleInputChange();

  }, 100);

}



// 变更拖拽背景颜色

function changePickerBgColor(value: any) {

  const h = value / 100;

  circlePickData.value.sliderColor = ColorUtil.HSVtoRGB(h, 1, 1);

  // 颜色选择器变更处理

  changePickerColor();

}



// 变更结束处理

function changeEndPickerBgColor() {

  // 添加历史颜色

  addHistoryColor(`rgb(${circlePickData.value.pickerColor.r},${circlePickData.value.pickerColor.g},${circlePickData.value.pickerColor.b})`);

}



// 颜色选择器变更处理

function changePickerColor() {

  const x = circlePickData.value.pickerPos.x;

  const y = circlePickData.value.pickerPos.y;

  const h = circlePickData.value.sliderColorValue / 100;

  circlePickData.value.pickerColor = ColorUtil.HSVtoRGB(h, x, y);

  circlePickData.value.hex = ColorUtil.RGBtoHEX(circlePickData.value.pickerColor);

}




// 拖拽点处理

function dragPicker(event: any) {

  let disX = event.clientX - dragEl.value.offsetLeft;

  let disY = event.clientY - dragEl.value.offsetTop;

  //2, 获取拖动元素的高度和容器的高度 为了下面进行限制元素拖动的范围

  let dragHeight = dragEl.value.offsetHeight;

  let dragWidth = dragEl.value.offsetWidth;

  let dragContainerWidth = dragContainerEl.value.offsetWidth; //获取容器的高度和宽度

  let dragContainerHeight = dragContainerEl.value.offsetHeight;

  // 添加鼠标移动事件

  document.onmousemove = (el) => {

    if(!dragEl.value) {

      return;

    }



    // 3,获取鼠标移动位置

    let moveX = el.clientX - disX;

    let moveY = el.clientY - disY;

    // 左边界

    if (moveX <= 0) {

      moveX = 0;

    }

    // 上边界

    if (moveY <= 0) {

      moveY = 0;

    }

    //下边界  容器高度 - 拖动元素高度

    if (moveY >= dragContainerHeight - dragHeight) {

      moveY = dragContainerHeight - dragHeight;

    }

    //右边界   容器宽度 - 拖动元素宽度

    if (moveX >= dragContainerWidth - dragWidth) {

      moveX = dragContainerWidth - dragWidth;

    }



    // 拖拽点移动

    dragEl.value.style.left = moveX + "px";

    dragEl.value.style.top = moveY + "px";



    // 拖拽点位置记录(0,0)-(1,1)

    circlePickData.value.pickerPos.x = moveX / 250;

    circlePickData.value.pickerPos.y = 1 - moveY / 150;



    // 颜色选择器变更处理

    changePickerColor();

  };



  document.onmousemove(event);



  // 取消拖拽

  document.onmouseup = () => {

    document.onmousemove = null;

    document.onmouseup = null;

    // 添加历史颜色

    addHistoryColor(`rgb(${circlePickData.value.pickerColor.r},${circlePickData.value.pickerColor.g},${circlePickData.value.pickerColor.b})`);

  };

}



// 点击拖拽面板处理

function dragContainerMousedown(e: any) {

  // 拖拽点移动到点击位置

  dragEl.value.style.left = e.offsetX + "px";

  dragEl.value.style.top = e.offsetY + "px";

  dragPicker(e);

}




// 颜色变更处理

function handleInputChange() {



  // 矫正颜色

  circlePickData.value.pickerColor = {

    r: circlePickData.value.pickerColor.r == null ? 0 : circlePickData.value.pickerColor.r,

    g: circlePickData.value.pickerColor.g == null ? 0 : circlePickData.value.pickerColor.g,

    b: circlePickData.value.pickerColor.b == null ? 0 : circlePickData.value.pickerColor.b,

  };



  // 转换颜色

  const hsv = ColorUtil.RGBtoHSV(circlePickData.value.pickerColor.r, circlePickData.value.pickerColor.g, circlePickData.value.pickerColor.b);

  circlePickData.value.sliderColorValue = hsv.h / 3.6;

  circlePickData.value.sliderColor = ColorUtil.HSVtoRGB(hsv.h / 360, 1, 1);



  // 拖拽点位置修改

  let moveX = 250 * hsv.s;

  let moveY = 150 * (1 - hsv.v);

  if (openCirclePickFlag.value) {

    dragEl.value.style.left = moveX + "px";

    dragEl.value.style.top = moveY + "px";

  }



  // 拖拽点位置记录(0,0)-(1,1)

  circlePickData.value.pickerPos.x = moveX / 250;

  circlePickData.value.pickerPos.y = 1 - moveY / 150;



  // 16进制颜色设置

  circlePickData.value.hex = ColorUtil.RGBtoHEX(circlePickData.value.pickerColor);



  // 添加历史颜色

  addHistoryColor(`rgb(${circlePickData.value.pickerColor.r},${circlePickData.value.pickerColor.g},${circlePickData.value.pickerColor.b})`);

}



// 16进制颜色输入变更

function handleHexChange() {

  let str = circlePickData.value.hex;

  // 移除#号

  if (!!str) {

    str = str.replaceAll("#", "");

  }



  // 非正确颜色则重置回修改前状态

  if (!ColorUtil.isHEXColor(str)) {

    circlePickData.value.hex = ColorUtil.RGBtoHEX(circlePickData.value.pickerColor);

    return;

  }



  // 设置选择器颜色

  circlePickData.value.pickerColor = ColorUtil.HEXtoRGB(str);



  // 颜色变更处理

  handleInputChange();

}



// 打开颜色选择器

function open() {

  openFlag.value.isOpen = true;

  openFlag.value.isEnter = false;



  // 超时关闭

  if(!!openFlag.value.timeoutHandler) {

    clearTimeout(openFlag.value.timeoutHandler)

  }

  openFlag.value.timeoutHandler = setTimeout(() => {

    openFlag.value.timeoutHandler = null;

    if(openFlag.value.isOpen && !openFlag.value.isEnter) {

      close();

    }

  }, 5000);



  clear();

}



// 状态清楚

function clear() {

  openCirclePickFlag.value = false;

  targetColor.value = "";

}





// 关闭选择器

function close() {

  openFlag.value.isOpen = false;

}



// 鼠标移入事件监听

function enter() {

  openFlag.value.isEnter = true;

}



defineExpose({

  open,

});



</script>



<style type="text/css">

.color-picker-content-div .color-item {

  height: 23px;

  width: 23px;

  display: inline-block;

  position: relative;

}



.color-picker-content-div .color-item .color-selected-border {

  width: 22px;

  height: 22px;

  border: 2px solid white;

  border-radius: 6px;

  position: absolute;

  top: 0px;

  left: 0px;

}



.color-picker-content-div .color-item:hover .color-selected-border {

  border: 2px solid rgb(109, 201, 255);

  z-index: 1;

}



.color-picker-content-div .color-item .color-content {

  width: 18px;

  height: 18px;

  position: absolute;

  top: 2px;

  left: 2px;

  border-radius: 4px;

}



.color-picker-content-div .color-item .color-content svg {

  position: relative;

  top: -2px;

}



.color-picker-content-div .color-picker-circle-content {

  display: block;

  margin: 5px;

  padding: 8px 5px;

  background-color: white;

  border-radius: 4px;

  position: relative;

}



.color-picker-content-div .color-picker-circle-content:hover {

  background-color: rgb(241, 241, 241);

}



.color-picker-content-div .color-picker-circle-content .circle-icon {

  background: conic-gradient(from 90deg at 50% 50%, red 0deg, red 3.09deg, #f60 66.27deg, #fdff00 131.88deg, #83ff00 167.11deg, #0091ff 214.06deg, #3800ff 265.42deg, #c700ff 312deg, red 1turn);

  border-radius: 7.5px;

  width: 14px;

  height: 14px;

  display: inline-block;

  margin-right: 10px;

}



.color-picker-content-div .drag-picker {

  width: 0px;

  height: 0px;

  position: absolute;

  top: 0px;

  left: 0px;

}



.color-picker-content-div .drag-picker .drag-picker-cursor {

  width: 16px;

  height: 16px;

  background-color: rgb(255, 255, 255);

  border-radius: 10px;

  border: 2px solid rgb(201 201 201);

  position: absolute;

  top: -10px;

  left: -10px;

  cursor: pointer;

}



.color-picker-content-div .color-picker-content-slider {

  width: 226px;

}



.color-picker-content-div .color-picker-content-slider .el-slider__runway {

  height: 20px;

  background: linear-gradient(to right, red 0, #ff0 17%, #0f0 33%, #0ff 50%, #00f 67%, #f0f 83%, red 100%);

}



.color-picker-content-div .color-picker-content-slider .el-slider__button {

  border: none;

  border-radius: 0px;

  background-color: rgb(235 235 235);

  position: relative;

  top: 18px;

  width: 10px;

}



.color-picker-content-div .color-picker-content-slider .el-slider__button:before {

  box-sizing: border-box;

  content: "";

  position: absolute;

  width: 0px;

  height: 0px;

  border-left: 5px solid rgba(0, 0, 0, 0);

  border-bottom: 4px solid rgb(235 235 235);

  border-right: 5px solid rgba(0, 0, 0, 0);

  border-top: 0 solid rgba(0, 0, 0, 0);

  top: -3px;

  left: 0px;

}



.color-picker-content-div .color-picker-content-slider .el-slider__bar {

  display: none;

}



.color-picker-content-div .color-picker-content-input .input-item {

  display: inline-block;

  height: 70px;

  width: 100px;

  position: relative;

}

</style>

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

随与坏

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值