mirror of
https://github.com/element-plus/element-plus.git
synced 2026-03-13 07:51:17 +08:00
refactor(components): [color-picker] alpha-slider (#10332)
* refactor(components): [color-picker] alpha-slider * Extract logic code out of color-picker/alpha-slider * fix: exposing methods * chore: expose refs --------- Co-authored-by: JeremyWuuuuu <15975785+JeremyWuuuuu@users.noreply.github.com>
This commit is contained in:
@@ -1,191 +1,49 @@
|
||||
<template>
|
||||
<div :class="[ns.b(), ns.is('vertical', vertical)]">
|
||||
<div
|
||||
ref="bar"
|
||||
:class="ns.e('bar')"
|
||||
:style="{
|
||||
background,
|
||||
}"
|
||||
@click="handleClick"
|
||||
/>
|
||||
<div
|
||||
ref="thumb"
|
||||
:class="ns.e('thumb')"
|
||||
:style="{
|
||||
left: thumbLeft + 'px',
|
||||
top: thumbTop + 'px',
|
||||
}"
|
||||
/>
|
||||
<div :class="rootKls">
|
||||
<div ref="bar" :class="barKls" :style="barStyle" @click="handleClick" />
|
||||
<div ref="thumb" :class="thumbKls" :style="thumbStyle" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
<script lang="ts" setup>
|
||||
import { alphaSliderProps } from '../props/alpha-slider'
|
||||
import {
|
||||
defineComponent,
|
||||
getCurrentInstance,
|
||||
onMounted,
|
||||
ref,
|
||||
shallowRef,
|
||||
watch,
|
||||
} from 'vue'
|
||||
import { getClientXY } from '@element-plus/utils'
|
||||
import { useNamespace } from '@element-plus/hooks'
|
||||
import { draggable } from '../utils/draggable'
|
||||
useAlphaSlider,
|
||||
useAlphaSliderDOM,
|
||||
} from '../composables/use-alpha-slider'
|
||||
|
||||
import type { PropType } from 'vue'
|
||||
import type Color from '../utils/color'
|
||||
const COMPONENT_NAME = 'ElColorAlphaSlider'
|
||||
|
||||
export default defineComponent({
|
||||
name: 'ElColorAlphaSlider',
|
||||
props: {
|
||||
color: {
|
||||
type: Object as PropType<Color>,
|
||||
required: true,
|
||||
},
|
||||
vertical: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
},
|
||||
setup(props) {
|
||||
const ns = useNamespace('color-alpha-slider')
|
||||
defineOptions({
|
||||
name: COMPONENT_NAME,
|
||||
})
|
||||
|
||||
const instance = getCurrentInstance()!
|
||||
// ref
|
||||
const thumb = shallowRef<HTMLElement>()
|
||||
const bar = shallowRef<HTMLElement>()
|
||||
const props = defineProps(alphaSliderProps)
|
||||
|
||||
// data
|
||||
const thumbLeft = ref(0)
|
||||
const thumbTop = ref(0)
|
||||
const background = ref<string>()
|
||||
const { bar, thumb, handleDrag, handleClick } = useAlphaSlider(props)
|
||||
|
||||
watch(
|
||||
() => props.color.get('alpha'),
|
||||
() => {
|
||||
update()
|
||||
}
|
||||
)
|
||||
watch(
|
||||
() => props.color.value,
|
||||
() => {
|
||||
update()
|
||||
}
|
||||
)
|
||||
const { rootKls, barKls, barStyle, thumbKls, thumbStyle, update } =
|
||||
useAlphaSliderDOM(props, {
|
||||
bar,
|
||||
thumb,
|
||||
handleDrag,
|
||||
})
|
||||
|
||||
//methods
|
||||
function getThumbLeft() {
|
||||
if (!thumb.value) return 0
|
||||
|
||||
if (props.vertical) return 0
|
||||
const el = instance.vnode.el
|
||||
const alpha = props.color.get('alpha')
|
||||
|
||||
if (!el) return 0
|
||||
return Math.round(
|
||||
(alpha * (el.offsetWidth - thumb.value.offsetWidth / 2)) / 100
|
||||
)
|
||||
}
|
||||
|
||||
function getThumbTop() {
|
||||
if (!thumb.value) return 0
|
||||
|
||||
const el = instance.vnode.el
|
||||
if (!props.vertical) return 0
|
||||
const alpha = props.color.get('alpha')
|
||||
|
||||
if (!el) return 0
|
||||
return Math.round(
|
||||
(alpha * (el.offsetHeight - thumb.value.offsetHeight / 2)) / 100
|
||||
)
|
||||
}
|
||||
|
||||
function getBackground() {
|
||||
if (props.color && props.color.value) {
|
||||
const { r, g, b } = props.color.toRgb()
|
||||
return `linear-gradient(to right, rgba(${r}, ${g}, ${b}, 0) 0%, rgba(${r}, ${g}, ${b}, 1) 100%)`
|
||||
}
|
||||
return ''
|
||||
}
|
||||
|
||||
function handleClick(event: MouseEvent | TouchEvent) {
|
||||
const target = event.target
|
||||
|
||||
if (target !== thumb.value) {
|
||||
handleDrag(event)
|
||||
}
|
||||
}
|
||||
|
||||
function handleDrag(event: MouseEvent | TouchEvent) {
|
||||
if (!bar.value || !thumb.value) return
|
||||
|
||||
const el = instance.vnode.el as HTMLElement
|
||||
const rect = el.getBoundingClientRect()
|
||||
const { clientX, clientY } = getClientXY(event)
|
||||
|
||||
if (!props.vertical) {
|
||||
let left = clientX - rect.left
|
||||
left = Math.max(thumb.value.offsetWidth / 2, left)
|
||||
left = Math.min(left, rect.width - thumb.value.offsetWidth / 2)
|
||||
|
||||
props.color.set(
|
||||
'alpha',
|
||||
Math.round(
|
||||
((left - thumb.value.offsetWidth / 2) /
|
||||
(rect.width - thumb.value.offsetWidth)) *
|
||||
100
|
||||
)
|
||||
)
|
||||
} else {
|
||||
let top = clientY - rect.top
|
||||
top = Math.max(thumb.value.offsetHeight / 2, top)
|
||||
top = Math.min(top, rect.height - thumb.value.offsetHeight / 2)
|
||||
|
||||
props.color.set(
|
||||
'alpha',
|
||||
Math.round(
|
||||
((top - thumb.value.offsetHeight / 2) /
|
||||
(rect.height - thumb.value.offsetHeight)) *
|
||||
100
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
function update() {
|
||||
thumbLeft.value = getThumbLeft()
|
||||
thumbTop.value = getThumbTop()
|
||||
background.value = getBackground()
|
||||
}
|
||||
|
||||
// mounded
|
||||
onMounted(() => {
|
||||
if (!bar.value || !thumb.value) return
|
||||
|
||||
const dragConfig = {
|
||||
drag: (event: MouseEvent | TouchEvent) => {
|
||||
handleDrag(event)
|
||||
},
|
||||
end: (event: MouseEvent | TouchEvent) => {
|
||||
handleDrag(event)
|
||||
},
|
||||
}
|
||||
|
||||
draggable(bar.value, dragConfig)
|
||||
draggable(thumb.value, dragConfig)
|
||||
update()
|
||||
})
|
||||
|
||||
return {
|
||||
thumb,
|
||||
bar,
|
||||
thumbLeft,
|
||||
thumbTop,
|
||||
background,
|
||||
handleClick,
|
||||
update,
|
||||
ns,
|
||||
}
|
||||
},
|
||||
defineExpose({
|
||||
/**
|
||||
* @description update alpha slider manually
|
||||
* @type {Function}
|
||||
*/
|
||||
update,
|
||||
/**
|
||||
* @description bar element ref
|
||||
* @type {HTMLElement}
|
||||
*/
|
||||
bar,
|
||||
/**
|
||||
* @description thumb element ref
|
||||
* @type {HTMLElement}
|
||||
*/
|
||||
thumb,
|
||||
})
|
||||
</script>
|
||||
|
||||
@@ -0,0 +1,166 @@
|
||||
import {
|
||||
computed,
|
||||
getCurrentInstance,
|
||||
onMounted,
|
||||
ref,
|
||||
shallowRef,
|
||||
watch,
|
||||
} from 'vue'
|
||||
import { addUnit, getClientXY } from '@element-plus/utils'
|
||||
import { useNamespace } from '@element-plus/hooks'
|
||||
import { draggable } from '../utils/draggable'
|
||||
|
||||
import type { AlphaSliderProps } from '../props/alpha-slider'
|
||||
|
||||
export const useAlphaSlider = (props: AlphaSliderProps) => {
|
||||
const instance = getCurrentInstance()!
|
||||
|
||||
const thumb = shallowRef<HTMLElement>()
|
||||
const bar = shallowRef<HTMLElement>()
|
||||
|
||||
function handleClick(event: MouseEvent | TouchEvent) {
|
||||
const target = event.target
|
||||
|
||||
if (target !== thumb.value) {
|
||||
handleDrag(event)
|
||||
}
|
||||
}
|
||||
|
||||
function handleDrag(event: MouseEvent | TouchEvent) {
|
||||
if (!bar.value || !thumb.value) return
|
||||
|
||||
const el = instance.vnode.el as HTMLElement
|
||||
const rect = el.getBoundingClientRect()
|
||||
const { clientX, clientY } = getClientXY(event)
|
||||
|
||||
if (!props.vertical) {
|
||||
let left = clientX - rect.left
|
||||
left = Math.max(thumb.value.offsetWidth / 2, left)
|
||||
left = Math.min(left, rect.width - thumb.value.offsetWidth / 2)
|
||||
|
||||
props.color.set(
|
||||
'alpha',
|
||||
Math.round(
|
||||
((left - thumb.value.offsetWidth / 2) /
|
||||
(rect.width - thumb.value.offsetWidth)) *
|
||||
100
|
||||
)
|
||||
)
|
||||
} else {
|
||||
let top = clientY - rect.top
|
||||
top = Math.max(thumb.value.offsetHeight / 2, top)
|
||||
top = Math.min(top, rect.height - thumb.value.offsetHeight / 2)
|
||||
|
||||
props.color.set(
|
||||
'alpha',
|
||||
Math.round(
|
||||
((top - thumb.value.offsetHeight / 2) /
|
||||
(rect.height - thumb.value.offsetHeight)) *
|
||||
100
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
thumb,
|
||||
bar,
|
||||
handleDrag,
|
||||
handleClick,
|
||||
}
|
||||
}
|
||||
|
||||
export const useAlphaSliderDOM = (
|
||||
props: AlphaSliderProps,
|
||||
{
|
||||
bar,
|
||||
thumb,
|
||||
handleDrag,
|
||||
}: Pick<ReturnType<typeof useAlphaSlider>, 'bar' | 'thumb' | 'handleDrag'>
|
||||
) => {
|
||||
const instance = getCurrentInstance()!
|
||||
|
||||
const ns = useNamespace('color-alpha-slider')
|
||||
// refs
|
||||
|
||||
const thumbLeft = ref(0)
|
||||
const thumbTop = ref(0)
|
||||
const background = ref<string>()
|
||||
|
||||
function getThumbLeft() {
|
||||
if (!thumb.value) return 0
|
||||
|
||||
if (props.vertical) return 0
|
||||
const el = instance.vnode.el
|
||||
const alpha = props.color.get('alpha')
|
||||
|
||||
if (!el) return 0
|
||||
return Math.round(
|
||||
(alpha * (el.offsetWidth - thumb.value.offsetWidth / 2)) / 100
|
||||
)
|
||||
}
|
||||
|
||||
function getThumbTop() {
|
||||
if (!thumb.value) return 0
|
||||
|
||||
const el = instance.vnode.el
|
||||
if (!props.vertical) return 0
|
||||
const alpha = props.color.get('alpha')
|
||||
|
||||
if (!el) return 0
|
||||
return Math.round(
|
||||
(alpha * (el.offsetHeight - thumb.value.offsetHeight / 2)) / 100
|
||||
)
|
||||
}
|
||||
|
||||
function getBackground() {
|
||||
if (props.color && props.color.value) {
|
||||
const { r, g, b } = props.color.toRgb()
|
||||
return `linear-gradient(to right, rgba(${r}, ${g}, ${b}, 0) 0%, rgba(${r}, ${g}, ${b}, 1) 100%)`
|
||||
}
|
||||
return ''
|
||||
}
|
||||
|
||||
function update() {
|
||||
thumbLeft.value = getThumbLeft()
|
||||
thumbTop.value = getThumbTop()
|
||||
background.value = getBackground()
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
if (!bar.value || !thumb.value) return
|
||||
|
||||
const dragConfig = {
|
||||
drag: (event: MouseEvent | TouchEvent) => {
|
||||
handleDrag(event)
|
||||
},
|
||||
end: (event: MouseEvent | TouchEvent) => {
|
||||
handleDrag(event)
|
||||
},
|
||||
}
|
||||
|
||||
draggable(bar.value, dragConfig)
|
||||
draggable(thumb.value, dragConfig)
|
||||
update()
|
||||
})
|
||||
|
||||
watch(
|
||||
() => props.color.get('alpha'),
|
||||
() => update()
|
||||
)
|
||||
watch(
|
||||
() => props.color.value,
|
||||
() => update()
|
||||
)
|
||||
|
||||
const rootKls = computed(() => [ns.b(), ns.is('vertical', props.vertical)])
|
||||
const barKls = computed(() => ns.e('bar'))
|
||||
const thumbKls = computed(() => ns.e('thumb'))
|
||||
const barStyle = computed(() => ({ background: background.value }))
|
||||
const thumbStyle = computed(() => ({
|
||||
left: addUnit(thumbLeft.value),
|
||||
top: addUnit(thumbTop.value),
|
||||
}))
|
||||
|
||||
return { rootKls, barKls, barStyle, thumbKls, thumbStyle, update }
|
||||
}
|
||||
17
packages/components/color-picker/src/props/alpha-slider.ts
Normal file
17
packages/components/color-picker/src/props/alpha-slider.ts
Normal file
@@ -0,0 +1,17 @@
|
||||
import { buildProps, definePropType } from '@element-plus/utils'
|
||||
|
||||
import type { ExtractPropTypes } from 'vue'
|
||||
import type Color from '../utils/color'
|
||||
|
||||
export const alphaSliderProps = buildProps({
|
||||
color: {
|
||||
type: definePropType<Color>(Object),
|
||||
required: true,
|
||||
},
|
||||
vertical: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
} as const)
|
||||
|
||||
export type AlphaSliderProps = ExtractPropTypes<typeof alphaSliderProps>
|
||||
Reference in New Issue
Block a user