技术版本如下:
vue 2.6.14
less 3.13.1
element-ui 2.15.6
less-loader 5.0.0
需求:
支持RGB、HEX编码、支持吸管吸取颜色、颜色选择器、颜色模板、透明度、色板、线性渐变颜色
效果图:
1.引入选择器的color-all文件
<template>
<div>
<div><div class="colorpicker-box" :style="{background:handStyle()}"></div></div>
<h3>带编码显示的color选择器</h3>
<div> <ColorPicker
:data="pickerColor"
@update:isLinear="pickerColor.isLinear = $event"
@update:direction="pickerColor.direction = $event"
@update:leftColor="handUpdateElement($event, 0)"
@update:rightColor="handUpdateElement($event, 1)"
></ColorPicker></div>
<h3>无编码显示的color选择器</h3>
<div>
<ColorPicker
:data="pickerColor"
:isText="false"
@update:isLinear="pickerColor.isLinear = $event"
@update:direction="pickerColor.direction = $event"
@update:leftColor="handUpdateElement($event, 0)"
@update:rightColor="handUpdateElement($event, 1)"
></ColorPicker>
</div>
</div>
</template>
<script>
import ColorPicker from '@/components/color-modal/color-picker.vue'
export default {
components: {
ColorPicker,
},
data() {
return {
pickerColor: {
isLinear: false, //是否线性
color: ['#334251'], //选择的颜色
direction: 'right', //方向
},
}
},
methods: {
handStyle() {
return this.pickerColor.isLinear ? `linear-gradient(to ${this.pickerColor.direction},${this.pickerColor.color[0]} ,${this.pickerColor.color.length >= 2 ? this.pickerColor.color[1] : '#AE7DFF'})` : this.pickerColor.color[0]
},
handUpdateElement(value, cursor = null) {
this.$set(this.pickerColor.color, cursor, value)
},
},
}
</script>
<style lang="less" scoped>
.colorpicker{
&-box{
width: 200px;
border-radius: 5px;
height: 30px;
}
div{
padding: 10px;
}
}
</style>
2.重点,选择器的封装组件文件color-picker.vue
<template>
<el-popover placement="left-start" width="264" trigger="hover">
<div class="colorPickerBody">
<!-- tabs -->
<div class="colorPickerBody-headers">
<el-radio-group v-model="activeName" size="mini" @change="handActive">
<el-radio-button :label="0">颜色填充</el-radio-button>
<el-radio-button :label="1">线性填充</el-radio-button>
</el-radio-group>
</div>
<!-- top部分 -->
<div class="colorPickerBody-top">
<div class="colorPickerBody-top-one">
<!-- 颜色块color -->
<div class="color-diamond">
<div :style="'background-color: ' +
bgColor +
';width: 100%;height: 100%;box-shadow: inset 0 0 0 1px rgba(0, 0, 0, .15), inset 0 0 4px rgba(0, 0, 0, .25);'
"></div>
</div>
<!-- select下拉 -->
<el-dropdown @command="handleCommand" class="eldropdownColor">
<span class="el-dropdown-link">
{{ colorType }}<i class="el-icon-arrow-down el-icon--right"></i>
</span>
<el-dropdown-menu slot="dropdown" :append-to-body="false">
<el-dropdown-item command="HEX">HEX</el-dropdown-item>
<el-dropdown-item command="RGB">RGB</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
<div v-if="colorType === 'HEX'">
<el-input v-model="bgColor" class="colorValcss" @input="bgColor = $event; handHEXtoRGB($event)"></el-input>
</div>
<div v-else class="colorRGBBox"> <el-input type="number" v-model="red" min="0" max="255"
class="colorRGBValcss" @input="handredChange"></el-input><el-input type="number" v-model="green" min="0"
max="255" class="colorRGBValcss" @input="handgreenChange"></el-input><el-input type="number"
v-model="blue" min="0" max="255" class="colorRGBValcss" @input="handblueChange"></el-input></div>
<div class="strawcss" title="吸管" @click="nativePick"><img src="@/assets/image/strawImg.svg"></div>
</div>
<!-- 线性特性box -->
<div v-if="activeName === 1" class="colorPickerBody-top-two">
<div class="colorPickerBody-top-two-body">
<div class="colorPickerBody-top-two-body-tag" title="首部" @click="handAlphaColor(true)"><img
:src="handLineImg(true)"></div>
<div class="colorPickerBody-top-two-body-center" :style="'background: linear-gradient(to right, ' + handleftRightColor(true) + ', ' +
handleftRightColor(false) +
');'
"></div>
<div class="colorPickerBody-top-two-body-tagRight" title="尾部" @click="handAlphaColor(false)"><img
:src="handLineImg(false)"></div>
</div>
<div class="strawcss iconright" :title="data.direction === 'right' ? '旋转方向:上下' : '旋转方向:左右'"
@click="handDirection"><i class="el-icon-refresh-right"></i>
</div>
</div>
<!-- 透明度块 -->
<div class="colorPickerBody-top-three">
<div class="colorPickerBody-top-three-Alpha" ref="saturation_value">
<div class="alpha-slider" ref="alpha_slider" @mousedown="mousedownAlpha">
<div class="slider" :style="alphaSliderStyle + ';background:' + bgColor"></div>
<div :style="'background: linear-gradient(to right, rgba(0,0,0,0), ' +
bgColor +
');width: 100%;height: 100%;border-radius: 10px;'
"></div>
</div>
</div>
<div class="colorPickerBody-top-three-number"> <el-input v-model="alpha" class="alphaValcss" type="number"
min="0" max="100" @input="handAlpha"> <i slot="suffix" class="suffixInput">%</i></el-input></div>
</div>
</div>
<!-- 中间线 -->
<div class="colorPickerBody-border"></div>
<!-- bottom的颜色选择器 -->
<div class="colorPickerBody-body">
<div class="colorPickerBody-body-headers">
<div class="colorPickerBody-body-headers-title">颜色选择器</div>
<div class="colorPickerBody-body-headers-svg">
<div :class="colorselecotr ? 'boxbuttom checkbuttom' : 'boxbuttom '" @click="colorselecotr = true"><img
src="@/assets/image/colordemo.svg"> </div>
<div :class="!colorselecotr ? 'boxbuttom checkbuttom' : 'boxbuttom '" @click="colorselecotr = false"><img
src="@/assets/image/colorpick.svg"></div>
</div>
</div class="colorPickerBody-body-box">
<div v-if="colorselecotr">
<MyColorSelect class="MyColorSelect" :color="bgColor" :alphaValue="alpha"
@update:color="bgColor = $event; handHEXtoRGB($event)">
</MyColorSelect>
</div>
<ul v-else class="colorDemoList">
<li v-for="(item, index) in colorAll" :key="index" :style="'background: ' + item"
@click="bgColor = item; handHEXtoRGB(item)"></li>
</ul>
</div>
</div>
<!-- 仿input框,默认显示第一个color -->
<div class="colorInputBody" slot="reference" v-if="isText">
<div class="colorBox" :style="'background:' + data.color[0]"></div>
<div class="colorInputBody-title">{{ data.color[0] }}</div>
</div>
<div class="colorNoTextBody" slot="reference" v-else>
<div class="colorNoTextBody-colorBox" :style="'background:' + data.color[0]"></div>
</div>
</el-popover>
</template>
<script>
import MyColorSelect from "./MyColorSelect.vue"
import moveCursorImg from '@/assets/image/moveCursorImg.png'
import checkmoveCursorImg from '@/assets/image/checkmoveCursorImg.png'
import message from "@/utils/message.js";
import { debounce } from '@/utils/util.js'
export default {
props: {
data: {
type: Object,
default: () => ({
isLinear: false, //是否线性
color: ["#000"], //选择的颜色
direction: "right", //方向
}),
},
//是否显示color编码
isText: {
type: Boolean,
default: true
}
},
components: {
MyColorSelect
},
data() {
return {
colorAll: ["#FF0000", "#FFA500", "#FFFF00", "#008000", "#0000FF",
"#4B0082", "#EE82EE", "#FF1493", "#FF69B4", "#FF4500",
"#FF6347", "#FFD700", "#ADFF2F", "#00FFFF", "#87CEEB",
"#4169E1", "#800080", "#9932CC", "#8A2BE2", "#FF00FF",
"#FFC0CB", "#F08080", "#CD5C5C", "#FA8072", "#FFA07A",
"#FF7F50", "#FFDAB9", "#FFE4B5", "#FFDEAD", "#FFE4E1",
"#FFF0F5", "#F0FFF0", "#F5FFFA", "#B0E0E6", "#ADD8E6",
"#87CEFA", "#87CEEB", "#00BFFF", "#1E90FF", "#6495ED",
"#4682B4", "#5F9EA0", "#20B2AA", "#008B8B", "#008080"
],
alphaSliderStyle: "left: calc(100% - 6px);",
alpha: 100,
colorType: "HEX",
bgColor: "",
colorselecotr: true,
activeName: 0,
red: 255,
green: 0,
blue: 0,
isLinearStart: true,
moveCursorImg,
checkmoveCursorImg,
hasEyeDrop: 'EyeDropper' in window
};
},
mounted() {
this.bgColor = this.data.color[0]
this.activeName = this.data.isLinear ? 1 : 0
},
methods: {
handAlphaColor(item) {
this.isLinearStart = item;
let alphaNew = 1;
if (item) {
const { r, g, b, a } = this.parseColor(this.data.color[0]);
alphaNew = a;
} else {
const { r, g, b, a } = this.parseColor(this.data.color[1]);
alphaNew = a;
}
this.alpha = alphaNew.toFixed(2) * 100
this.handAlpha()
},
// tag
handActive(e) {
if (e === 0) {
this.bgColor = this.data.color[0]
this.handAlphaColor(true)
this.$emit('update:isLinear', false)
} else {
this.$emit('update:isLinear', true)
}
},
// 方向
handDirection() {
if (this.data.direction === 'right') {
this.$emit('update:direction', 'top')
} else {
this.$emit('update:direction', 'right')
}
},
//取色
async nativePick(e) {
const val = e ? e.target.value : null
if (val) {
console.log('获得颜色: ' + val)
} else {
const eyeDropper = new window.EyeDropper() // 初始化一个EyeDropper对象
try {
const result = await eyeDropper.open() // 开始拾取颜色
this.bgColor = result.sRGBHex;
this.handHEXtoRGB(result.sRGBHex)
} catch (e) {
console.log('用户取消了取色')
}
}
},
//判断线性条
handleftRightColor(val) {
let color = '#AE7DFF';
if (val) {
color = this.data.color[0]
} else {
if (this.data.color.length >= 2) color = this.data.color[1]
}
return color;
},
//判断首尾游标
handLineImg(val) {
let icon = moveCursorImg;
if (val) {
if (this.isLinearStart) {
this.bgColor = this.data.color[0]
icon = checkmoveCursorImg;
}
} else {
if (!this.isLinearStart) {
this.bgColor = this.data.color.length >= 2 ? this.data.color[1] : '#AE7DFF'
icon = checkmoveCursorImg;
}
}
return icon;
},
handredChange(val) {
if ((val ?? '') !== '') {
this.bgColor = this.rgba2hex(val, this.green, this.blue, this.matchHexA())
this.handHEXtoRGB(this.bgColor)
}
},
handgreenChange(val) {
if ((val ?? '') !== '') {
this.bgColor = this.rgba2hex(this.red, val, this.blue, this.matchHexA())
this.handHEXtoRGB(this.bgColor)
}
},
handblueChange(val) {
if ((val ?? '') !== '') {
this.bgColor = this.rgba2hex(this.red, this.green, val, this.matchHexA())
this.handHEXtoRGB(this.bgColor)
}
},
matchHexA() {
let percentage = this.alpha * 0.01;
return percentage;
},
rgba2hex(r, g, b, a = 1) {
r = parseInt(r);
let r1 =
r.toString(16).length !== 2 ? "0" + r.toString(16) : r.toString(16);
g = parseInt(g);
let g1 =
g.toString(16).length !== 2 ? "0" + g.toString(16) : g.toString(16);
b = parseInt(b);
let b1 =
b.toString(16).length !== 2 ? "0" + b.toString(16) : b.toString(16);
a = parseFloat(a);
let a1 = "";
if (a !== 1) {
let temp = Math.floor(256 * a);
a1 =
temp.toString(16).length !== 2
? "0" + temp.toString(16)
: temp.toString(16);
}
return `#${r1}${g1}${b1}${a1}`.toUpperCase();
},
handAlpha() {
this.alphaSliderStyle = `left: ${this.alpha >= 96 ? "calc(100% - 9px)" : this.alpha + '%'
};`;
},
// 透明度
handleChangeAlpha(e) {
let w = this.$refs.alpha_slider.clientWidth;
let x =
e.pageX - this.$refs.saturation_value.getBoundingClientRect().left;
x = x < w && x > 0 ? x : x > w ? w : 0;
// 计算透明度
this.alpha = Math.floor((x / w) * 100 + 0.5);
// 移动滑块
this.alphaSliderStyle = `left: ${x >= w - 6 ? w - 9 : x}px;`;
if (!this.colorselecotr) {
this.bgColor = this.rgba2hex(this.red, this.green, this.blue, this.matchHexA())
this.handHEXtoRGB(this.bgColor)
}
},
mousedownAlpha(e) {
this.handleChangeAlpha(e);
window.addEventListener("mousemove", this.handleChangeAlpha);
window.addEventListener("mouseup", this.mouseupAlpha);
},
mouseupAlpha(e) {
window.removeEventListener("mousemove", this.handleChangeAlpha);
window.removeEventListener("mouseup", this.mouseupAlpha);
},
handleCommand(command) {
this.colorType = command;
if (command === "RGB") {
this.handHEXtoRGB(this.bgColor)
}
},
isHexColorCode(str) {
return /^#?([A-Fa-f0-9]{6}|[A-Fa-f0-9]{8})$/.test(str);
},
handHEXtoRGB: debounce(function (color) {
if (!this.isHexColorCode(color)) {
message.message("当前非HEX编码,请重新输入!", "error");
return
}
const { r, g, b, a } = this.parseColor(color);
this.red = r;
this.green = g;
this.blue = b;
if (this.isLinearStart) {
this.$emit('update:leftColor', color)
} else {
this.$emit('update:rightColor', color)
}
}, 500),
parseColor(color) {
if (color) {
let r, g, b, a;
if (typeof color === "string") {
if (
/^#?([0-9a-fA-F]{6}|[0-9a-fA-F]{8}|[0-9a-fA-F]{3}|[0-9a-fA-F]{4})$/.test(
color
)
) {
return this.hex2rgba(color);
}
} else {
r = color.r > 255 ? 255 : color.r < 0 ? 0 : color.r;
g = color.g > 255 ? 255 : color.g < 0 ? 0 : color.g;
b = color.b > 255 ? 255 : color.b < 0 ? 0 : color.b;
a = color.a > 1 ? 1 : color.a < 0 ? 0 : color.a;
return { r, g, b, a };
}
} else {
return null;
}
},
hex2rgba(s) {
if (/^#?[0-9a-fA-F]{3}$/.test(s)) {
let b = s.substring(s.length - 1, s.length);
let g = s.substring(s.length - 2, s.length - 1);
let r = s.substring(s.length - 3, s.length - 2);
return hex2rgba(`${r + r}${g + g}${b + b}`);
}
if (/^#?[0-9a-fA-F]{4}$/.test(s)) {
let a = s.substring(s.length - 1, s.length);
let b = s.substring(s.length - 2, s.length - 1);
let g = s.substring(s.length - 3, s.length - 2);
let r = s.substring(s.length - 4, s.length - 3);
return hex2rgba(`${r + r}${g + g}${b + b}${a + a}`);
}
if (/^#?[0-9a-fA-F]{6}$/.test(s)) {
let b = parseInt("0x" + s.substring(s.length - 2, s.length));
let g = parseInt("0x" + s.substring(s.length - 4, s.length - 2));
let r = parseInt("0x" + s.substring(s.length - 6, s.length - 4));
return { r, g, b, a: 1 };
}
if (/^#?[0-9a-fA-F]{8}$/.test(s)) {
let a = parseInt("0x" + s.substring(s.length - 2, s.length));
a = a / 255;
let b = parseInt("0x" + s.substring(s.length - 4, s.length - 2));
let g = parseInt("0x" + s.substring(s.length - 6, s.length - 4));
let r = parseInt("0x" + s.substring(s.length - 8, s.length - 6));
return { r, g, b, a };
}
},
},
};
</script>
<style scoped lang="less">
.colorDemoList {
width: 100%;
list-style-type: none;
padding: 13px 0 0 0;
margin: 0;
li {
display: inline-block;
width: 16px;
cursor: pointer;
border-radius: 2px;
height: 16px;
margin: 0 9px 8px 0;
}
}
/* 颜色方块 */
.color-diamond {
position: relative;
width: 24px;
height: 24px;
border-radius: 2px;
overflow: hidden;
background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAB4AAAAeCAYAAAA7MK6iAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAWElEQVRIiWM8fubkfwYygKWJOSM5+mCAhRLNoxaPWjxq8ajFoxbTyeL/DAfJ0Xjs3Cl7Siwmu4Yht1aDgZEYx6MWj1o8avGoxaMWD3qLya5X//4nqx6HAQC7RBGFzolqTAAAAABJRU5ErkJggg==");
background-size: 10px 10px;
margin-right: 8px;
}
.iconright {
font-size: 15px;
cursor: pointer;
}
.strawcss {
display: flex;
align-items: center;
justify-content: center;
width: 24px;
padding-left: 5px;
img {
cursor: pointer;
}
}
.eldropdownColor {
width: 56px;
color: #fff;
}
.colorPickerBody {
height: 100%;
width: 100%;
padding: 5px 11px 13px 11px;
&-top {
padding: 10px 0 12px 0;
width: 100%;
height: 100%;
&-one {
display: flex;
align-items: center;
}
&-two {
display: flex;
align-items: center;
justify-content: space-between;
padding-top: 12px;
width: 100%;
&-body {
width: 100%;
display: flex;
flex-direction: row;
align-items: center;
position: relative;
&-tag {
position: absolute;
left: -1px;
top: -2px;
cursor: pointer;
z-index: 1;
}
img {
width: 14px;
height: 18px;
}
&-tagRight {
position: absolute;
right: -1px;
top: -2px;
cursor: pointer;
}
&-center {
left: 6px;
position: absolute;
width: calc(100% - 12px);
height: 12px;
box-shadow: 1px 1px 1px rgba(0, 0, 0, 0.1);
background: #fff url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAB4AAAAeCAYAAAA7MK6iAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAWElEQVRIiWM8fubkfwYygKWJOSM5+mCAhRLNoxaPWjxq8ajFoxbTyeL/DAfJ0Xjs3Cl7Siwmu4Yht1aDgZEYx6MWj1o8avGoxaMWD3qLya5X//4nqx6HAQC7RBGFzolqTAAAAABJRU5ErkJggg==");
background-size: 10px 10px;
}
}
}
&-three {
width: 100%;
display: flex;
justify-content: space-between;
align-items: center;
padding-top: 20px;
&-Alpha {
width: 80%
}
&-number {
width: 15%;
::v-deep.el-input__suffix {
right: 2px;
top: 1px;
}
.suffixInput {
color: #fff;
font-size: 12px;
}
.alphaValcss {
width: 44px;
::v-deep .el-input__inner {
text-align: center;
height: 24px;
font-size: 12px;
padding: 0 3px;
border-radius: 5px;
border: 1px solid #484849;
background: #29292cFF;
color: #fff;
}
}
}
}
.colorValcss {
width: 119px;
::v-deep .el-input__inner {
height: 24px;
border-radius: 2px 2px 2px 2px;
border: 1px solid #484849;
background: #29292cFF;
color: #fff;
}
}
.colorRGBBox {
display: flex;
}
.colorRGBValcss {
width: 40px;
::v-deep .el-input__inner {
height: 24px;
padding: 0 3px;
text-align: center;
border-radius: 2px 2px 2px 2px;
border: 1px solid #484849;
background: #29292cFF;
color: #fff;
}
}
}
&-border {
height: 1px;
background: #484849;
margin: 0 0 12px 0;
width: 100%;
}
&-body {
width: 100%;
height: 100%;
&-headers {
display: flex;
justify-content: space-between;
align-items: center;
&-title {
color: #fff;
font-size: 14px;
}
&-svg {
width: 43px;
display: flex;
justify-content: space-between;
.checkbuttom {
background: #3C3C3F;
border-radius: 2px 2px 2px 2px;
}
.boxbuttom {
width: 22px;
height: 22px;
display: flex;
justify-content: center;
align-items: center;
}
img {
width: 16px;
height: 16px;
cursor: pointer;
}
}
}
}
&-headers {
::v-deep .el-radio-group {
display: flex;
}
::v-deep .el-radio-button__inner {
background: #29292c;
border-radius: 2px 2px 2px 2px;
border: 1px solid #484849;
color: #99999b;
width: 116px;
height: 28px;
box-shadow: none;
}
// 修改激活后的样式
::v-deep .el-radio-button__orig-radio:checked+.el-radio-button__inner {
background: #373739;
border-radius: 2px 2px 2px 2px;
border: 1px solid #484849;
color: #fff;
}
}
}
.colorInputBody {
display: flex;
flex-direction: Row;
align-items: center;
height: 30px;
width: 120px;
padding-left: 8px;
border: 1px solid #000;
border-radius: 5px;
&-title {
padding-left: 8px;
color: #000;
}
}
.colorNoTextBody {
width: 30px;
height: 30px;
border: none;
padding: 3px;
&-colorBox {
width: 24px;
height: 24px;
border-radius: 4px;
}
}
.colorBox {
width: 26px;
height: 14px;
}
/* 透明度滑块条 */
.alpha-slider {
border-radius: 10px;
position: relative;
height: 12px;
box-shadow: 1px 1px 1px rgba(0, 0, 0, 0.1);
background: #fff url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAB4AAAAeCAYAAAA7MK6iAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAWElEQVRIiWM8fubkfwYygKWJOSM5+mCAhRLNoxaPWjxq8ajFoxbTyeL/DAfJ0Xjs3Cl7Siwmu4Yht1aDgZEYx6MWj1o8avGoxaMWD3qLya5X//4nqx6HAQC7RBGFzolqTAAAAABJRU5ErkJggg==");
background-size: 10px 10px;
}
/* 滑块 */
.slider {
border-radius: 20px;
position: absolute;
box-shadow: 0 0 2px rgba(0, 0, 0, 0.6);
box-sizing: border-box;
width: 12px;
height: 100%;
border: 3px solid #fff;
}
</style>
3.选择器的颜色板,MyColorSelect.vue页面
<template>
<div class="color-select">
<div class="saturation-value" ref="saturation_value" @mousedown="mousedownSV">
<div :style="'background-color: hsl(' + hue + ', 100%, 50%);'">
<div class="point" :style="pointStyle"></div>
</div>
<div class="saturation-value-2"></div>
<div class="saturation-value-3"></div>
</div>
<div class="color-select-middle">
<div style="flex: auto">
<div class="hue-slider" ref="hue_slider" @mousedown="mousedownHue">
<div class="slider" :style="hueSliderStyle"></div>
</div>
</div>
</div>
</div>
</template>
<script>
export default {
name: "MyColorSelect",
props: {
color: {
type: String,
default: "",
},
alphaValue: {
type: Number,
default: 100,
},
},
data() {
return {
pointStyle: "top: 25%; left: 80%;",
hueSliderStyle: "left: 0;",
hue: 0,
saturation: 1,
value: 1,
red: 255,
green: 0,
blue: 0,
alpha: 1,
};
},
watch: {
color: {
handler(val) {
this.handParse(val);
},
},
alphaValue: {
handler(val) {
this.$emit(
"update:color",
this.rgba2hex(this.red, this.green, this.blue, this.matchHexA())
);
},
},
},
mounted() {
this.handParse(this.color);
},
methods: {
handParse(color) {
if ((this.parseColor(color) ?? "") !== "") {
const { r, g, b, a } = this.parseColor(color);
this.red = r;
this.green = g;
this.blue = b;
this.alpha = a;
this.updateColor();
}
},
updateColor() {
let { h, s, v } = this.rgb2hsv(this.red, this.green, this.blue);
this.hue = h;
this.saturation = s;
this.value = v;
// 移动背景板圆圈
this.pointStyle = `top: ${100 - v * 100}%;left: ${s * 100}%;`;
// 移动色调滑块
this.hueSliderStyle = `left: ${(this.hue / 360) * 100}%;`;
},
rgb2hsv(r, g, b) {
let r1 = r / 255;
let g1 = g / 255;
let b1 = b / 255;
let cmax = Math.max(r1, g1, b1);
let cmin = Math.min(r1, g1, b1);
let d = cmax - cmin;
let h, s, v;
if (d === 0) {
h = 0;
} else if (cmax === r1) {
h = ((60 * (g1 - b1)) / d + 360) % 360;
} else if (cmax === g1) {
h = 60 * ((b1 - r1) / d + 2);
} else if (cmax === b1) {
h = 60 * ((r1 - g1) / d + 4);
}
if (cmax === 0) {
s = 0;
} else {
s = d / cmax;
}
v = cmax;
h = Math.floor(h + 0.5);
s = Math.floor(s * 100 + 0.5) / 100;
v = Math.floor(v * 100 + 0.5) / 100;
return { h, s, v };
},
// 色调
handleChangeHue(e) {
let w = this.$refs.hue_slider.clientWidth;
let x =
e.pageX - this.$refs.saturation_value.getBoundingClientRect().left;
x = x < w && x > 0 ? x : x > w ? w : 0;
// 计算色调
this.hue = Math.floor((x / w) * 360 + 0.5);
// hsv转化为rgb
let { r, g, b } = this.hsv2rgb(this.hue, this.saturation, this.value);
this.red = r;
this.green = g;
this.blue = b;
// 移动滑块
this.hueSliderStyle = `left: ${x >= w - 6 ? w - 6 : x}px;`;
this.$emit("update:color", this.rgba2hex(r, g, b, this.matchHexA()));
},
mousedownHue(e) {
this.handleChangeHue(e);
window.addEventListener("mousemove", this.handleChangeHue);
window.addEventListener("mouseup", this.mouseupHue);
},
mouseupHue(e) {
window.removeEventListener("mousemove", this.handleChangeHue);
window.removeEventListener("mouseup", this.mouseupHue);
},
// 饱和度和亮度
handleChangeSV(e) {
let w = this.$refs.saturation_value.clientWidth;
let h = this.$refs.saturation_value.clientHeight;
let x =
e.pageX - this.$refs.saturation_value.getBoundingClientRect().left;
let y = e.pageY - this.$refs.saturation_value.getBoundingClientRect().top;
x = x < w && x > 0 ? x : x > w ? w : 0;
y = y < h && y > 0 ? y : y > h ? h : 0;
// 计算饱和度和亮度
this.saturation = Math.floor((x / w) * 100 + 0.5) / 100;
this.value = Math.floor((1 - y / h) * 100 + 0.5) / 100;
// hsv转化为rgb
let { r, g, b } = this.hsv2rgb(this.hue, this.saturation, this.value);
this.red = r;
this.green = g;
this.blue = b;
// 移动背景板圆圈
this.pointStyle = `top: ${y}px;left: ${x}px;`;
this.$emit("update:color", this.rgba2hex(r, g, b, this.matchHexA()));
},
matchHexA() {
let percentage = this.alphaValue * 0.01;
return percentage;
},
mousedownSV(e) {
// 鼠标按下计算饱和度和亮度并添加事件
this.handleChangeSV(e);
// 添加整个页面的鼠标事件
window.addEventListener("mousemove", this.handleChangeSV);
window.addEventListener("mouseup", this.mouseupSV);
},
mouseupSV(e) {
// 鼠标松开后移除事件
window.removeEventListener("mousemove", this.handleChangeSV);
window.removeEventListener("mouseup", this.mouseupSV);
},
parseColor(color) {
if (color) {
let r, g, b, a;
if (typeof color === "string") {
if (
/^#?([0-9a-fA-F]{6}|[0-9a-fA-F]{8}|[0-9a-fA-F]{3}|[0-9a-fA-F]{4})$/.test(
color
)
) {
return this.hex2rgba(color);
}
} else {
r = color.r > 255 ? 255 : color.r < 0 ? 0 : color.r;
g = color.g > 255 ? 255 : color.g < 0 ? 0 : color.g;
b = color.b > 255 ? 255 : color.b < 0 ? 0 : color.b;
a = color.a > 1 ? 1 : color.a < 0 ? 0 : color.a;
return { r, g, b, a };
}
} else {
return null;
}
},
hsv2rgb(h, s, v) {
h === 360 && (h = 0);
let i = Math.floor(h / 60) % 6;
let f = h / 60 - i;
let p = v * (1 - s);
let q = v * (1 - s * f);
let t = v * (1 - s * (1 - f));
let r, g, b;
if (i === 0) {
r = v;
g = t;
b = p;
} else if (i === 1) {
r = q;
g = v;
b = p;
} else if (i === 2) {
r = p;
g = v;
b = t;
} else if (i === 3) {
r = p;
g = q;
b = v;
} else if (i === 4) {
r = t;
g = p;
b = v;
} else if (i === 5) {
r = v;
g = p;
b = q;
}
r = Math.floor(r * 255 + 0.5);
g = Math.floor(g * 255 + 0.5);
b = Math.floor(b * 255 + 0.5);
return { r, g, b };
},
rgba2hex(r, g, b, a = 1) {
r = parseInt(r);
let r1 =
r.toString(16).length !== 2 ? "0" + r.toString(16) : r.toString(16);
g = parseInt(g);
let g1 =
g.toString(16).length !== 2 ? "0" + g.toString(16) : g.toString(16);
b = parseInt(b);
let b1 =
b.toString(16).length !== 2 ? "0" + b.toString(16) : b.toString(16);
a = parseFloat(a);
let a1 = "";
if (a !== 1) {
let temp = Math.floor(256 * a);
a1 =
temp.toString(16).length !== 2
? "0" + temp.toString(16)
: temp.toString(16);
}
return `#${r1}${g1}${b1}${a1}`.toUpperCase();
},
hex2rgba(s) {
if (/^#?[0-9a-fA-F]{3}$/.test(s)) {
let b = s.substring(s.length - 1, s.length);
let g = s.substring(s.length - 2, s.length - 1);
let r = s.substring(s.length - 3, s.length - 2);
return hex2rgba(`${r + r}${g + g}${b + b}`);
}
if (/^#?[0-9a-fA-F]{4}$/.test(s)) {
let a = s.substring(s.length - 1, s.length);
let b = s.substring(s.length - 2, s.length - 1);
let g = s.substring(s.length - 3, s.length - 2);
let r = s.substring(s.length - 4, s.length - 3);
return hex2rgba(`${r + r}${g + g}${b + b}${a + a}`);
}
if (/^#?[0-9a-fA-F]{6}$/.test(s)) {
let b = parseInt("0x" + s.substring(s.length - 2, s.length));
let g = parseInt("0x" + s.substring(s.length - 4, s.length - 2));
let r = parseInt("0x" + s.substring(s.length - 6, s.length - 4));
return { r, g, b, a: 1 };
}
if (/^#?[0-9a-fA-F]{8}$/.test(s)) {
let a = parseInt("0x" + s.substring(s.length - 2, s.length));
a = a / 255;
let b = parseInt("0x" + s.substring(s.length - 4, s.length - 2));
let g = parseInt("0x" + s.substring(s.length - 6, s.length - 4));
let r = parseInt("0x" + s.substring(s.length - 8, s.length - 6));
return { r, g, b, a };
}
},
},
};
</script>
<style scoped>
.color-select {
position: relative;
user-select: none;
width: 100%;
padding: 12px 0 0 0;
}
/* 饱和度和亮度 */
.saturation-value {
cursor: pointer;
width: 100%;
height: 148px;
position: relative;
margin-bottom: 12px;
box-shadow: 1px 1px 1px rgba(0, 0, 0, 0.1);
}
.saturation-value>div {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
}
/* 圆圈 */
.point {
box-sizing: border-box;
width: 6px;
height: 6px;
background-color: transparent;
border: 2px solid #ccc;
border-radius: 50%;
transform: translate(-50%, -50%);
position: absolute;
z-index: 9;
}
.saturation-value-2 {
background: linear-gradient(to right, white, #ffffff00);
}
.saturation-value-3 {
background: linear-gradient(to top, black, #ffffff00);
}
/* 色调滑块条 */
.hue-slider {
position: relative;
height: 10px;
background: linear-gradient(90deg,
red 0,
#ff0 17%,
#0f0 33%,
#0ff 50%,
#00f 67%,
#f0f 83%,
red);
box-shadow: 1px 1px 1px rgba(0, 0, 0, 0.1);
}
/* 滑块 */
.slider {
position: absolute;
box-shadow: 0 0 2px rgba(0, 0, 0, 0.6);
box-sizing: border-box;
width: 6px;
height: 100%;
background-color: #fff;
}
</style>
4.其中utils文件夹里的两个文件如下
message.js
import { Message, MessageBox } from 'element-ui';
let message = {
message: function(str, type = 'success') {
Message[type](str);
},
confirm: function(text, title = '提示', confirm = null, cancel = null) {
MessageBox.confirm(text, title, {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then((res) => {
confirm && confirm(res);
}).catch((res) => {
cancel && cancel(res);
});
}
};
export default message;
util.js
//防抖
export const debounce=(fn, t) =>{
const delay = t || 100;
let timer = null;
return function() {
const args = arguments;
const context = this;
if (timer) {
clearTimeout(timer);
}
timer = setTimeout(() => {
timer = null;
fn.apply(context, args);
}, delay);
};
}
5.重点:elementStyle.css样式文件,用于更改选择器的覆盖颜色
elementStyle.css文件需要在main.js引入
import '@/assets/css/elementStyle.css'
6.elementStyle.css代码如下
.el-popover{
background: #303030 ;
color: #ffffff;
font-size: 12px;
border: 1px solid rgba(255, 255, 255, 0.06);
padding: 5px 5px;
min-width: 40px;
}
.popper__arrow {
display: none !important;
}
.el-dropdown-menu{
background: #303030;
border: 1px solid rgba(255, 255, 255, 0.06);
}
.el-dropdown-menu__item {
color: #fff;
width: 70px;
}
.el-dropdown-menu__item:focus,
.el-dropdown-menu__item:not(.is-disabled):hover {
background: #373739FF;
}
7.引入的图片和svg图片在项目源码包里src/assets/image路径下
8.注意点
如果下载源码的话先
先执行
yarn install
再执行
yarn serve
运行成功后直接使用
http://127.0.0.1:8000/home访问