mirror of
https://github.com/pipipi-pikachu/PPTist.git
synced 2025-04-15 02:20:00 +08:00
128 lines
3.3 KiB
Vue
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> |