mirror of
https://github.com/pipipi-pikachu/PPTist.git
synced 2025-04-15 02:20:00 +08:00
133 lines
3.1 KiB
Vue
133 lines
3.1 KiB
Vue
<template>
|
|
<div class="hue">
|
|
<div
|
|
class="hue-container"
|
|
ref="hueRef"
|
|
@mousedown="$event => handleMouseDown($event)"
|
|
>
|
|
<div
|
|
class="hue-pointer"
|
|
:style="{ left: pointerLeft }"
|
|
>
|
|
<div class="hue-picker"></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<script lang="ts">
|
|
import { computed, defineComponent, onUnmounted, PropType, ref, watch } from 'vue'
|
|
import tinycolor, { ColorFormats } from 'tinycolor2'
|
|
|
|
export default defineComponent({
|
|
name: 'hue',
|
|
props: {
|
|
value: {
|
|
type: Object as PropType<ColorFormats.RGBA>,
|
|
required: true,
|
|
},
|
|
hue: {
|
|
type: Number,
|
|
required: true,
|
|
},
|
|
},
|
|
setup(props, { emit }) {
|
|
const oldHue = ref(0)
|
|
const pullDirection = ref('')
|
|
|
|
const color = computed(() => {
|
|
const hsla = tinycolor(props.value).toHsl()
|
|
if(hsla.s === 0) hsla.h = props.hue
|
|
return hsla
|
|
})
|
|
|
|
const pointerLeft = computed(() => {
|
|
if(color.value.h === 0 && pullDirection.value === 'right') return '100%'
|
|
return color.value.h * 100 / 360 + '%'
|
|
})
|
|
|
|
watch(() => props.value, () => {
|
|
const hsla = tinycolor(props.value).toHsl()
|
|
const h = hsla.s === 0 ? props.hue : hsla.h
|
|
if(h !== 0 && h - oldHue.value > 0) pullDirection.value = 'right'
|
|
if(h !== 0 && h - oldHue.value < 0) pullDirection.value = 'left'
|
|
oldHue.value = h
|
|
})
|
|
|
|
const hueRef = ref<HTMLElement>()
|
|
const handleChange = (e: MouseEvent) => {
|
|
e.preventDefault()
|
|
if(!hueRef.value) return
|
|
|
|
const containerWidth = hueRef.value.clientWidth
|
|
const xOffset = hueRef.value.getBoundingClientRect().left + window.pageXOffset
|
|
const left = e.pageX - xOffset
|
|
let h, percent
|
|
|
|
if(left < 0) h = 0
|
|
else if(left > containerWidth) h = 360
|
|
else {
|
|
percent = left * 100 / containerWidth
|
|
h = (360 * percent / 100)
|
|
}
|
|
if(color.value.h !== h) {
|
|
emit('change', {
|
|
h,
|
|
l: color.value.l,
|
|
s: color.value.s,
|
|
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 {
|
|
hueRef,
|
|
handleMouseDown,
|
|
pointerLeft,
|
|
}
|
|
},
|
|
})
|
|
</script>
|
|
|
|
<style lang="scss" scoped>
|
|
.hue {
|
|
position: absolute;
|
|
top: 0;
|
|
right: 0;
|
|
bottom: 0;
|
|
left: 0;
|
|
background: linear-gradient(to right, #f00 0%, #ff0 17%, #0f0 33%, #0ff 50%, #00f 67%, #f0f 83%, #f00 100%);
|
|
}
|
|
.hue-container {
|
|
cursor: pointer;
|
|
margin: 0 2px;
|
|
position: relative;
|
|
height: 100%;
|
|
}
|
|
.hue-pointer {
|
|
z-index: 2;
|
|
position: absolute;
|
|
top: 0;
|
|
}
|
|
.hue-picker {
|
|
cursor: pointer;
|
|
margin-top: 1px;
|
|
width: 4px;
|
|
height: 8px;
|
|
box-shadow: 0 0 2px rgba(0, 0, 0, .6);
|
|
background: #fff;
|
|
transform: translateX(-2px);
|
|
}
|
|
</style> |