Based on VUE, to achieve a function similar to PS color picker, can be used for a variety of customized color selection value.
Key principle calculation: the main use of three layers of div hierarchy, the bottom is a fixed color layer (i.e., tempColor); The middle layer is a gradual change from left to right transparency from 1 to 0. According to the width of box, the transparency value alpha of the current contact of this layer is converted. The transparency of the upper layer gradually changes from 1 to 0 from bottom to top, and the transparency value alpha of the current contact of the layer is also converted according to the width of box.
- Calculate the new alpha value after stacking alpha of middle layer and upper layer, formula:
function new_alpha(a1, a2) {
return 1 - (1 - a1) * (1 - a2);
},
Copy the code
- Calculate two colors, one is translucent, one is opaque, there is a hierarchy, opaque at the bottom (c1), translucent at the top (C2) formula:
function getNewRgb(c1, alpha, c2) { return c1 * (1 - alpha) + c2 * alpha; } // note that c1 and c2 are passed in the same way, such as r, g, or B.Copy the code
- Finally, the color of the middle layer and the upper layer should be converted again. This is the new_r_g_B method in the code.
<template>
<div class="tricolor_wheel_box">
<div class="inputBox">
<div class="r">
<span>r:</span>
<input type="text" v-model="tempColor.r" />
</div>
<div class="g">
<span>g:</span>
<input type="text" v-model="tempColor.g" />
</div>
<div class="b">
<span>b:</span>
<input type="text" v-model="tempColor.b" />
</div>
</div>
<div
class="content"
:style="{
width: `${triColorWH.width}`,
height: `${triColorWH.height}`,
backgroundColor: `rgb(${tempColor.r},${tempColor.g},${tempColor.b})`
}"
@touchstart="Touchstart"
@touchmove="Touchmove"
@touchend="Touchend"
ref="tri_color_ref"
>
<div class="color_inset1"></div>
<div class="color_inset2"></div>
<div
class="dot"
:style="{
top: `${dotPosition.top}px`,
left: `${dotPosition.left}px`,
width: `${contactWH.width}`,
height: `${contactWH.height}`
}"
></div>
</div>
<div
class="current_rgb"
:style="{
backgroundColor: `rgb(${newColor.r},${newColor.g},${newColor.b})`
}"
>
<div class="msg">当前颜色:</div>
<div class="rgb_text">
{{ `rgb(${newColor.r},${newColor.g},${newColor.b})` }}
</div>
</div>
</div>
</template>
<script>
export default {
name: "tricolor-wheel-components",
components: {},
data() {
return {
Touch: {
// 刚开始触摸的位置
firstX: 0,
firstY: 0,
// 移动中的位置
moveX: 0,
moveY: 0,
// 离开屏幕的位置
endX: 0,
endY: 0,
// 手指与圆心之间的距离,,两点距离公式,,平方开方
sqrtNum: 0,
powX: 0,
powY: 0
},
colorBoxInfo: {
width: 0,
height: 0,
top: 0,
left: 0,
right: 0,
bottom: 0
},
//输入固定的值,非透明,a固定为1
tempColor: {
r: 255,
g: 0,
b: 0,
a: 1
}
};
},
props: {
triColorWH: {
type: Object,
default: function() {
return {
width: "50vw",
height: "50vw"
};
}
},
contactWH: {
type: Object,
default: function() {
return {
width: "4vw",
height: "4vw"
};
}
}
},
computed: {
//触点的位置
dotPosition() {
let p = {
top: 0,
left: 0
};
//相对于取色器盒子的左上角为原点
let x = this.Touch.moveX - this.colorBoxInfo.left;
let y = this.Touch.moveY - this.colorBoxInfo.top;
//触点超出取色器盒子无效
x < 0 ? (x = 0) : "";
y < 0 ? (y = 0) : "";
this.colorBoxInfo.width < x ? (x = this.colorBoxInfo.width) : "";
this.colorBoxInfo.height < y ? (y = this.colorBoxInfo.height) : "";
p.top = y;
p.left = x;
return p;
},
//某个触点的白色层的颜色值, 根据触点位置换算出该点的透明度
whiteColor() {
let a = 1;
//排除NAN的情况
if (this.dotPosition.left === 0 && this.colorBoxInfo.width === 0) {
a = 1;
} else {
a = 1 - this.dotPosition.left / this.colorBoxInfo.width;
}
return {
r: 255,
g: 255,
b: 255,
a
};
},
//某个触点的黑色层的颜色值, 根据触点距离换算出该点的透明度
blackColor() {
let a = 1;
//排除NAN的情况
if (this.dotPosition.top === 0 && this.colorBoxInfo.height === 0) {
a = 0;
} else {
a =
1 -
(this.colorBoxInfo.height - this.dotPosition.top) /
this.colorBoxInfo.height;
}
return {
r: 0,
g: 0,
b: 0,
a
};
},
//白色黑色两个半透明的颜色叠加后的颜色值
w_b_color() {
return {
r: this.new_r_g_b(
this.whiteColor.r,
this.whiteColor.a,
this.blackColor.r,
this.blackColor.a
),
g: this.new_r_g_b(
this.whiteColor.g,
this.whiteColor.a,
this.blackColor.g,
this.blackColor.a
),
b: this.new_r_g_b(
this.whiteColor.b,
this.whiteColor.a,
this.blackColor.b,
this.blackColor.a
),
a: this.new_alpha(this.whiteColor.a, this.blackColor.a)
};
},
//由黑白叠加出的w_b_color跟固定的不透明颜色再次叠加,得出最终的颜色
newColor() {
let r = this.getNewRgb(
this.tempColor.r,
this.w_b_color.a,
this.w_b_color.r
);
let g = this.getNewRgb(
this.tempColor.g,
this.w_b_color.a,
this.w_b_color.g
);
let b = this.getNewRgb(
this.tempColor.b,
this.w_b_color.a,
this.w_b_color.b
);
r = Math.round(r);
g = Math.round(g);
b = Math.round(b);
return {
r,
g,
b
};
}
},
methods: {
//计算两个半透明度颜色叠加后的颜色
new_r_g_b(c1, a1, c2, a2) {
let newColor = 0;
let molecule = 1;
let denominator = 1;
molecule = c1 * a1 * (1 - a2) + c2 * a2;
denominator = a1 + a2 - a1 * a2;
//排除NAN的情况
if (molecule === 0 && denominator === 0) {
newColor = 255;
} else {
newColor = molecule / denominator;
}
return newColor;
},
new_alpha(a1, a2) {
return 1 - (1 - a1) * (1 - a2);
},
getNewRgb(c1, alpha, c2) {
return c1 * (1 - alpha) + c2 * alpha;
},
Touchstart(event) {
event.preventDefault();
this.Touch.firstX = event.targetTouches[0].clientX;
this.Touch.firstY = event.targetTouches[0].clientY;
const rectInfo = this.$refs.tri_color_ref.getBoundingClientRect();
this.colorBoxInfo.width = rectInfo.width;
this.colorBoxInfo.height = rectInfo.height;
this.colorBoxInfo.top = rectInfo.top;
this.colorBoxInfo.left = rectInfo.left;
this.colorBoxInfo.right = rectInfo.right;
this.colorBoxInfo.bottom = rectInfo.bottom;
this.Touch.moveX = this.Touch.firstX;
this.Touch.moveY = this.Touch.firstY;
},
Touchmove(event) {
event.preventDefault();
this.Touch.moveX = event.targetTouches[0].clientX;
this.Touch.moveY = event.targetTouches[0].clientY;
},
Touchend(event) {
this.Touch.endX = event.changedTouches[0].clientX;
this.Touch.endY = event.changedTouches[0].clientY;
}
},
mounted() {}
};
</script>
<style lang="scss" scoped>
.tricolor_wheel_box {
.inputBox {
.r,
.g,
.b {
width: 100%;
height: 30px;
span {
font-size: 16px;
display: inline-block;
width: 24px;
height: inherit;
}
input {
width: 60px;
height: 24px;
font-size: 14px;
}
}
}
.content {
position: relative;
top: 30px;
left: 0;
width: 60vw;
height: 60vw;
background: rgb(255, 0, 0);
.color_inset1,
.color_inset2 {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
}
.color_inset1 {
background: linear-gradient(90deg, #fff, transparent);
}
.color_inset2 {
background: linear-gradient(0deg, #000, transparent);
}
.dot {
position: absolute;
top: 0;
left: 0;
width: 4vw;
height: 4vw;
background-color: transparent;
border-radius: 50%;
border: 1px solid #000;
box-sizing: border-box;
transform: translate(-50%, -50%);
box-shadow: 0px 0px 5px 2px rgba(200, 200, 200, 0.5);
}
}
.current_rgb {
width: 100px;
height: 100px;
border: 1px solid #333;
margin: 50px 0 0 0;
position: relative;
.msg {
position: absolute;
left: 0;
bottom: -26px;
}
.rgb_text {
position: absolute;
left: 0;
bottom: -60px;
width: 220px;
height: 30px;
border-radius: 6px;
border: 1px solid rgba(142, 142, 142, 0.5);
box-sizing: border-box;
font-size: 24px;
}
}
}
</style>
Copy the code
Note: I use touch event processing, everyone is PC terminal, please replace yourself with move
Codepen link https://codepen.io/maomaoliangliang/pen/XWRbWEv
Finally, the renderings: