<template>
<div class="wzc_color_picker" :style="styleVar">
<div class="wzc_color_wrap">
<div class="wzc_color_left" @mousedown="mouseClick($event)">
<div class="white_panel"></div>
<div class="black_panel"></div>
<div class="wzc_color_pointer" ref="wzcColorPointer" >
<div ></div>
</div>
</div>
<div class="wzc_color_right" >
<div class="wzc_hue_slider" @mousedown="thumbClick($event)"></div>
<div class="wzc_hue_slider_thumb" ref="wzcthumb"></div>
</div>
</div>
<div class="wzc_color_btns">
<input type="text" class="wzc_input" v-model="currentColor" @input="inputHex">
<div class="wzc_color_show" :style="{ 'background-color': currentColor }"></div>
</div>
</div>
</template>
<script>
export default {
name:"wzc_color_picker",
components: {},
props: {
color: {
type: String,
default: "#FF0000"
}
},
data() {
return {
currentColor: "",
backgroundColor: "",
};
},
created() {},
mounted() {
this.dragColorPointer(this.$refs.wzcColorPointer);
this.dragColorPointer(this.$refs.wzcthumb, "thumb");
this.backgroundColor = this.color;
this.currentColor = this.rgbToHex(this.color);
this.$refs.wzcColorPointer.style.left = '280px';
this.$refs.wzcColorPointer.style.top = '0px';
},
watch: {
},
computed: {
styleVar() {
return {
'--wzc-picker-color': this.backgroundColor ,
}
}
},
methods: {
dragColorPointer (el, dom) {
let dragBox = el;
dragBox.onmousedown = (e) => {
e = e || window.event;
let disX = e.clientX - dragBox.offsetLeft;
let disY = e.clientY - dragBox.offsetTop;
document.onmousemove = e => {
let left = e.clientX - disX;
let top = e.clientY - disY;
if(left > 280){ left = 280; }
if(left < 0) { left = 0; }
if(top > 180) { top = 180; }
if(top < 0) { top = 0; }
dragBox.style.top = top + "px";
if( dom == "thumb" ) {
dragBox.style.left = "0px";
this.changeThumbColor(top);
} else {
dragBox.style.left = left + "px";
this.changeColor(left, top )
}
};
document.onmouseup = e => {
document.onmousemove = null;
document.onmouseup = null;
};
}
},
mouseClick (e) {
if(e.target.className.indexOf("black_panel") != -1) {
this.$refs.wzcColorPointer.style.left = e.offsetX + 'px';
this.$refs.wzcColorPointer.style.top = e.offsetY + 'px';
} else {
if(e.target.className.indexOf("wzc_color_pointer") != -1) {
if(this.$refs.wzcColorPointer.offsetLeft + e.offsetX <= 280){
this.$refs.wzcColorPointer.style.left = this.$refs.wzcColorPointer.offsetLeft + e.offsetX + 'px';
} else {
this.$refs.wzcColorPointer.style.left = '280px';
}
if(this.$refs.wzcColorPointer.offsetTop + e.offsetY <= 180) {
this.$refs.wzcColorPointer.style.top = this.$refs.wzcColorPointer.offsetTop + e.offsetY + 'px';
} else {
this.$refs.wzcColorPointer.style.top = '180px';
}
} else{
this.$refs.wzcColorPointer.style.left = this.$refs.wzcColorPointer.offsetLeft + e.offsetX - 6 + 'px';
this.$refs.wzcColorPointer.style.top = this.$refs.wzcColorPointer.offsetTop + e.offsetY - 6 + 'px';
if((this.$refs.wzcColorPointer.offsetLeft + e.offsetX - 6) < 0){
this.$refs.wzcColorPointer.style.left = "0px"
}
if((this.$refs.wzcColorPointer.offsetLeft + e.offsetX - 6) >= 280){
this.$refs.wzcColorPointer.style.left = '280px';
}
if((this.$refs.wzcColorPointer.offsetTop + e.offsetY - 6) < 0){
this.$refs.wzcColorPointer.style.top = "0px";
}
if((this.$refs.wzcColorPointer.offsetTop + e.offsetY - 6) >= 180){
this.$refs.wzcColorPointer.style.top = "180px";
}
}
}
this.changeColor(parseInt(this.$refs.wzcColorPointer.style.left), parseInt(this.$refs.wzcColorPointer.style.top) )
},
thumbClick (e) {
this.$refs.wzcthumb.style.top = e.offsetY + 'px';
this.changeThumbColor(e.offsetY);
},
// 计算颜色 HSV方式计算rgb
changeColor (left, top) {
let saturation = Math.round(left / 280 * 100) / 100;
let value = Math.round((1 - top / 180) * 100) / 100;
let hue = this.getHue(this.getRGB(""+this.backgroundColor));
this.currentColor = this.rgbToHex(this.HSVtoRGB(hue, saturation, value));
this.$emit('update:color', this.currentColor);
},
changeThumbColor (top) {
let hue = Math.round((top / 180) * 360 * 100) / 100;
this.backgroundColor = this.HuetoRGB(hue);
this.changeColor(parseInt(this.$refs.wzcColorPointer.style.left), parseInt(this.$refs.wzcColorPointer.style.top) )
},
getRGB (str){
if(str.indexOf('rgb') == -1 && str.indexOf('#') > -1){
// str = "rgba(" + str.match(/[A-Za-z0-9]{2}/g).map(function(v) { return parseInt(v, 16) }).join(",") + ")";
str = this.HexTorgb( str );
}
let match = str.match(/rgba?\((\d{1,3}), ?(\d{1,3}), ?(\d{1,3})\)?(?:, ?(\d(?:\.\d?))\))?/);
return match ? {
red: match[1],
green: match[2],
blue: match[3]
} : {};
},
// Hex(16进制颜色值) and RGB
rgbToHex (color){
if(color.indexOf("#") != -1) {
return color;
}
let arr = color.split(',');
let r = +arr[0].split('(')[1];
let g = +arr[1];
let b = +arr[2].split(')')[0];
let value = (1 << 24) + r * (1 << 16) + g * (1 << 8) + b;
value = value.toString(16);
return '#' + value.slice(1);
},
HexTorgb (hex){
var hexNum = hex.substring(1);
hexNum = '0x' + (hexNum.length < 6 ? repeatLetter(hexNum, 2) : hexNum);
var r = hexNum >> 16;
var g = hexNum >> 8 & '0xff';
var b = hexNum & '0xff';
return `rgb(${r},${g},${b})`;
function repeatWord(word, num){
var result = '';
for(let i = 0; i < num; i ++){
result += word;
}
return result;
}
function repeatLetter(word, num){
var result = '';
for(let letter of word){
result += repeatWord(letter, num);
}
return result;
}
},
// 根据Hue色相计算rgb纯色
HuetoRGB(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));
return 'rgb(' +r + ',' + g + ',' + b + ')';
},
// rgb to Hue(色相)
getHue (rgbArray) {
let r, g, b, max, min;
for(let i = 0; i < 3; i++){
r = parseInt(rgbArray.red);
g = parseInt(rgbArray.green);
b = parseInt(rgbArray.blue);
}
max = Math.max(r, g, b)
min = Math.min(r, g, b)
if(max == min) {
return 0;
} else {
if( max == r && g >= b) {
return 60 * (g - b)/(max - min);
} else if ( max == r && g < b) {
return 60 * (g - b)/(max - min) + 360;
} else if (max == g) {
return 60 * (b - r)/(max - min) + 120;
} else if (max == b) {
return 60 * (r - g)/(max - min) + 240;
}
}
},
// HSV(色相、饱和度、亮度) and RGB
RGBtoHSV(rgb) {
rgb = this.getRGB(rgb)
var rr, gg, bb,
r = parseInt(rgb.red) / 255,
g = parseInt(rgb.green) / 255,
b = parseInt(rgb.blue) / 255,
h, s,
v = Math.max(r, g, b),
diff = v - Math.min(r, g, b),
diffc = function(c){
return (v - c) / 6 / diff + 1 / 2;
};
if (diff == 0) {
h = s = 0;
} else {
s = diff / v; rr = diffc(r); gg = diffc(g); bb = diffc(b);
if (r === v) {
h = bb - gg;
}else if (g === v) {
h = (1 / 3) + rr - bb;
}else if (b === v) {
h = (2 / 3) + gg - rr;
}
if (h < 0) {
h += 1;
}else if (h > 1) {
h -= 1;
}
}
return {
h: Math.round(h * 360),
s: Math.round(s * 100),
v: Math.round(v * 100)
};
},
HSVtoRGB(h, s, v) {
let i, f, p1, p2, p3;
let r = 0, g = 0, b = 0;
if (s < 0) s = 0;
if (s > 1) s = 1;
if (v < 0) v = 0;
if (v > 1) v = 1;
h %= 360;
if (h < 0) h += 360;
h /= 60;
i = Math.floor(h);
f = h - i;
p1 = v * (1 - s);
p2 = v * (1 - s * f);
p3 = v * (1 - s * (1 - f));
switch(i) {
case 0: r = v; g = p3; b = p1; break;
case 1: r = p2; g = v; b = p1; break;
case 2: r = p1; g = v; b = p3; break;
case 3: r = p1; g = p2; b = v; break;
case 4: r = p3; g = p1; b = v; break;
case 5: r = v; g = p1; b = p2; break;
}
return 'rgb(' + Math.round(r * 255) + ',' + Math.round(g * 255) + ',' + Math.round(b * 255) + ')';
},
// 根据HSV计算位置
HSVtoPos (hsv) {
let left = hsv.s / 100 * 280;
let top = 180 - ( hsv.v / 100 * 180 );
this.$refs.wzcColorPointer.style.left = Math.round(left) + 'px';
this.$refs.wzcColorPointer.style.top = Math.round(top) + 'px';
},
inputHex (item){
let str = item.target.value;
if( str.length < 4 ) return ;
if( this.getHue(this.getRGB(str)) == undefined || this.getHue(this.getRGB(str)) > 360 || this.getHue(this.getRGB(str)) < 0 ) {
return ;
} else {
let hsv = this.RGBtoHSV(str);
let backgroundHue = this.getHue(this.getRGB(this.backgroundColor));
if (hsv.h == backgroundHue) {
this.HSVtoPos(hsv);
} else {
this.backgroundColor = str;
this.$refs.wzcColorPointer.style.left = '280px';
this.$refs.wzcColorPointer.style.top = '0px';
}
}
this.$emit('update:color', this.currentColor);
}
},
};
</script>
<style scoped>
.wzc_color_picker {
width: 314px;
height: 228px;
padding: 6px;
box-sizing: content-box;
background-color: #fff;
border: 1px solid #ebeef5;
border-radius: 4px;
box-shadow: 0 2px 12px 0 rgba(0,0,0,.1);
}
.wzc_color_wrap {
width: 100%;
height: 180px;
display: flex;
justify-content: space-around;
}
.wzc_color_left {
width: 280px;
height: 100%;
position: relative;
background-color: var(--wzc-picker-color);
overflow: hidden;
}
.wzc_color_right {
width: 12px;
height: 100%;
position: relative;
}
.wzc_color_left .white_panel,
.wzc_color_left .black_panel{
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
}
.wzc_color_left .white_panel {
background: linear-gradient(90deg,#fff,hsla(0,0%,100%,0));
}
.wzc_color_left .black_panel {
background: linear-gradient(0deg,#000,transparent);
}
.wzc_color_pointer {
position: absolute;
top: 0px;
left: 280px;
}
.wzc_color_pointer > div {
width: 12px;
height: 12px;
border-radius: 6px;
box-shadow: rgb(255, 255, 255) 0px 0px 0px 1px inset;
transform: translate(-6px, -6px);
}
.wzc_color_right .wzc_hue_slider {
height: 100%;
background: linear-gradient(180deg,red 0,#ff0 17%,#0f0 33%,#0ff 50%,#00f 67%,#f0f 83%,red);
}
.wzc_hue_slider_thumb {
position: absolute;
cursor: pointer;
box-sizing: border-box;
left: 0;
top: 0;
width: 12px;
height: 4px;
border-radius: 1px;
background: #fff;
border: 1px solid #f0f0f0;
box-shadow: 0 0 2px rgba(0,0,0,.6);
z-index: 1;
}
.wzc_color_btns .wzc_input {
width: 155px;
height: 28px;
line-height: 28px;
background-color: #fff;
background-image: none;
border-radius: 4px;
border: 1px solid #dcdfe6;
box-sizing: border-box;
color: #606266;
display: inline-block;
font-size: inherit;
outline: none;
padding: 0 15px;
margin-left: 5px;
}
.wzc_color_show {
width: 28px;
height: 28px;
border-radius: 5px;
margin-left: 15px;
}
.wzc_color_btns {
width: 100%;
height: 28px;
margin-top: 10px;
display: flex;
}
</style>
评论