| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108 |
- <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" setup>
- import { computed, onUnmounted, useTemplateRef } from 'vue'
- import tinycolor, { type ColorFormats } from 'tinycolor2'
- import { throttle, clamp } from 'lodash'
- const props = defineProps<{
- value: ColorFormats.RGBA
- hue: number
- }>()
- const emit = defineEmits<{
- (event: 'colorChange', payload: ColorFormats.HSVA): void
- }>()
- const color = computed(() => {
- const hsva = tinycolor(props.value).toHsv()
- if (props.hue !== -1) 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: ColorFormats.HSVA) {
- emit('colorChange', param)
- }, 20, { leading: true, trailing: false })
- const saturationRef = useTemplateRef<HTMLElement>('saturationRef')
- 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)
- </script>
- <style lang="scss" scoped>
- .saturation,
- .saturation-white,
- .saturation-black {
- @include absolute-0();
- cursor: pointer;
- }
- .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>
|