PPTist/src/components/ColorPicker/Saturation.vue
2021-01-03 13:47:52 +08:00

128 lines
3.3 KiB
Vue

<template>
<div
class="saturation"
ref="saturationRef"
:style="{ background: bgColor }"
@mousedown="$event => handleMouseDown($event)"
>
<div class="saturation-white"></div>
<div class="saturation-black"></div>
<div class="saturation-pointer"
:style="{
top: pointerTop,
left: pointerLeft,
}"
>
<div class="saturation-circle"></div>
</div>
</div>
</template>
<script lang="ts">
import { computed, defineComponent, onUnmounted, PropType, ref } from 'vue'
import tinycolor, { ColorFormats } from 'tinycolor2'
import throttle from 'lodash/throttle'
import clamp from 'lodash/clamp'
export default defineComponent({
name: 'saturation',
props: {
value: {
type: Object as PropType<ColorFormats.RGBA>,
required: true,
},
hue: {
type: Number,
required: true,
},
},
setup(props, { emit }) {
const color = computed(() => {
const hsva = tinycolor(props.value).toHsv()
if(hsva.s === 0) hsva.h = props.hue
return hsva
})
const bgColor = computed(() => `hsl(${color.value.h}, 100%, 50%)`)
const pointerTop = computed(() => (-(color.value.v * 100) + 1) + 100 + '%')
const pointerLeft = computed(() => color.value.s * 100 + '%')
const emitChangeEvent = throttle(function(param) {
emit('change', param)
}, 20, { leading: true, trailing: false })
const saturationRef = ref<HTMLElement | null>(null)
const handleChange = (e: MouseEvent) => {
e.preventDefault()
if(!saturationRef.value) return
const containerWidth = saturationRef.value.clientWidth
const containerHeight = saturationRef.value.clientHeight
const xOffset = saturationRef.value.getBoundingClientRect().left + window.pageXOffset
const yOffset = saturationRef.value.getBoundingClientRect().top + window.pageYOffset
const left = clamp(e.pageX - xOffset, 0, containerWidth)
const top = clamp(e.pageY - yOffset, 0, containerHeight)
const saturation = left / containerWidth
const bright = clamp(-(top / containerHeight) + 1, 0, 1)
emitChangeEvent({
h: color.value.h,
s: saturation,
v: bright,
a: color.value.a,
})
}
const unbindEventListeners = () => {
window.removeEventListener('mousemove', handleChange)
window.removeEventListener('mouseup', unbindEventListeners)
}
const handleMouseDown = (e: MouseEvent) => {
handleChange(e)
window.addEventListener('mousemove', handleChange)
window.addEventListener('mouseup', unbindEventListeners)
}
onUnmounted(unbindEventListeners)
return {
saturationRef,
bgColor,
handleMouseDown,
pointerTop,
pointerLeft,
}
},
})
</script>
<style>
.saturation,
.saturation-white,
.saturation-black {
cursor: pointer;
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
}
.saturation-white {
background: linear-gradient(to right, #fff, rgba(255, 255, 255, 0));
}
.saturation-black {
background: linear-gradient(to top, #000, rgba(0, 0, 0, 0));
}
.saturation-pointer {
cursor: pointer;
position: absolute;
}
.saturation-circle {
width: 4px;
height: 4px;
box-shadow: 0 0 0 1.5px #fff, inset 0 0 1px 1px rgba(0, 0, 0, .3), 0 0 1px 2px rgba(0, 0, 0, .4);
border-radius: 50%;
transform: translate(-2px, -2px);
}
</style>