选择器截图:
下方为选择器代码:
<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>