forked from opentiny/tiny-vue
feat(color-select-panel): add color-update event (#884)
* feat(color-select-panel): add color-select event * fix(color-picker): extract functions to index.ts * style: remove semicolon
This commit is contained in:
parent
c2277b8d5b
commit
9ea7373a0a
|
@ -117,6 +117,15 @@ export default {
|
|||
'en-US': 'When click cancel or click out-side will trigger cancel event'
|
||||
},
|
||||
demoId: 'event'
|
||||
},
|
||||
{
|
||||
name: 'color-update',
|
||||
type: '(color:Color) => void',
|
||||
defaultValue: '',
|
||||
desc:{
|
||||
'zh-cn': '当颜色更新的时候会触发该事件,包括:点击预定义颜色、点击历史记录',
|
||||
'en-US': 'when click predefine color or history, will trigger color-update event'
|
||||
}
|
||||
}
|
||||
],
|
||||
'slots': []
|
||||
|
|
|
@ -1,23 +0,0 @@
|
|||
import type { IColorSelectPanel } from '@/types'
|
||||
|
||||
export const calcLeftByAlpha = (wrapper: HTMLElement, thumb: HTMLElement, alpha: number) => {
|
||||
return Math.round((alpha * (wrapper.offsetWidth - thumb.offsetWidth / 2)) / 100)
|
||||
}
|
||||
|
||||
export const updateThumb = (alpha: number, thumb: HTMLElement, wrapper: HTMLElement) => {
|
||||
thumb.style.left = `${calcLeftByAlpha(wrapper, thumb, alpha)}px`
|
||||
}
|
||||
|
||||
export const onDrag = (
|
||||
event: MouseEvent,
|
||||
bar: IColorSelectPanel<HTMLElement>,
|
||||
thumb: IColorSelectPanel<HTMLElement>,
|
||||
alpha: IColorSelectPanel<number>
|
||||
) => {
|
||||
const rect = bar.value.getBoundingClientRect()
|
||||
const { clientX } = event
|
||||
let left = clientX - rect.left
|
||||
left = Math.max(thumb.value.offsetWidth / 2, left)
|
||||
left = Math.min(left, rect.width - thumb.value.offsetWidth / 2)
|
||||
alpha.value = Math.round(((left - thumb.value.offsetWidth / 2) / (rect.width - thumb.value.offsetWidth)) * 100)
|
||||
}
|
|
@ -1,58 +0,0 @@
|
|||
import { IColorPickerRef as Ref } from '@/types'
|
||||
import Color from '../utils/color'
|
||||
import { draggable } from '../utils/use-drag'
|
||||
import { onDrag, updateThumb } from '.'
|
||||
|
||||
export const api = ['state', 'color', 'slider', 'alphaWrapper', 'alphaThumb']
|
||||
|
||||
export const renderless = (props, context, { emit }) => {
|
||||
const hex = props.color
|
||||
const color = new Color(hex, props.alpha)
|
||||
const [rr, gg, bb] = color.getRGB()
|
||||
const r = context.ref(rr)
|
||||
const g = context.ref(gg)
|
||||
const b = context.ref(bb)
|
||||
const slider: Ref<HTMLElement> = context.ref()
|
||||
const alphaWrapper: Ref<HTMLElement> = context.ref()
|
||||
const alphaThumb: Ref<HTMLElement> = context.ref()
|
||||
const alpha = context.ref(color.get('a'))
|
||||
context.watch(
|
||||
() => props.color,
|
||||
(hex: string) => {
|
||||
color.reset(hex)
|
||||
const [rr, gg, bb] = color.getRGB()
|
||||
r.value = rr
|
||||
g.value = gg
|
||||
b.value = bb
|
||||
}
|
||||
)
|
||||
context.watch(alpha, (newAlpha) => {
|
||||
updateThumb(newAlpha, alphaThumb.value, alphaWrapper.value)
|
||||
emit('alpha-update', alpha.value)
|
||||
})
|
||||
const background = context.computed(() => {
|
||||
return `linear-gradient(to right, rgba(${r.value}, ${g.value}, ${b.value}, 0) 0%, rgba(${r.value}, ${g.value}, ${b.value}, 1) 100%)`
|
||||
})
|
||||
const state = context.reactive({
|
||||
background,
|
||||
hex
|
||||
})
|
||||
const api = {
|
||||
state,
|
||||
slider,
|
||||
alphaWrapper,
|
||||
alphaThumb
|
||||
}
|
||||
context.onMounted(() => {
|
||||
updateThumb(alpha.value, alphaThumb.value, slider.value)
|
||||
draggable(slider.value, {
|
||||
drag(event) {
|
||||
onDrag(event as MouseEvent, slider, alphaThumb, alpha)
|
||||
},
|
||||
start(event) {
|
||||
onDrag(event as MouseEvent, slider, alphaThumb, alpha)
|
||||
}
|
||||
})
|
||||
})
|
||||
return api
|
||||
}
|
|
@ -1,80 +0,0 @@
|
|||
import { IColorSelectPanel } from '@/types'
|
||||
import type Color from '../utils/color'
|
||||
|
||||
export const setPosition = (el: HTMLElement, x: number, y: number) => {
|
||||
el.style.top = `${y}px`
|
||||
el.style.left = `${x}px`
|
||||
}
|
||||
export const getXBySaturation = (s: number, width: number) => (s * width) / 100
|
||||
export const getYByLight = (l: number, height: number) => ((100 - l) * height) / 100
|
||||
export const updatePosition = (
|
||||
event: MouseEvent | TouchEvent,
|
||||
rect: DOMRect,
|
||||
cursor: IColorSelectPanel<HTMLElement>
|
||||
) => {
|
||||
let x = (event as MouseEvent).clientX - rect.left
|
||||
let y = (event as MouseEvent).clientY - rect.top
|
||||
x = Math.max(0, x)
|
||||
x = Math.min(x, rect.width)
|
||||
y = Math.max(0, y)
|
||||
y = Math.min(y, rect.height)
|
||||
|
||||
setPosition(cursor.value, x - (1 / 2) * cursor.value.offsetWidth, y - (1 / 2) * cursor.value.offsetWidth)
|
||||
return { x, y }
|
||||
}
|
||||
export const calcSaturation = (x: number, width: number) => x / width
|
||||
export const calcBrightness = (y: number, height: number) => 100 - (y / height) * 100
|
||||
export const getThumbTop = (wrapper: HTMLElement, thumb: HTMLElement, hue: number) => {
|
||||
return Math.round((hue * (wrapper.offsetHeight - thumb.offsetHeight / 2)) / 360)
|
||||
}
|
||||
|
||||
export const resetCursor = (
|
||||
s: number,
|
||||
v: number,
|
||||
wrapper: IColorSelectPanel<HTMLElement>,
|
||||
cursor: IColorSelectPanel<HTMLElement>,
|
||||
thumb: IColorSelectPanel<HTMLElement>,
|
||||
color: Color,
|
||||
h: IColorSelectPanel<number>
|
||||
) => {
|
||||
const { width, height } = wrapper.value.getBoundingClientRect()
|
||||
const x = getXBySaturation(s, width) - (1 / 2) * cursor.value.offsetWidth
|
||||
const y = getYByLight(v, height) - (1 / 2) * cursor.value.offsetWidth
|
||||
setPosition(cursor.value, x < 0 ? 0 : x, y < 0 ? 0 : y)
|
||||
const thummbTop = getThumbTop(wrapper.value, thumb.value, color.get('h'))
|
||||
thumb.value.style.top = `${thummbTop}px`
|
||||
h.value = color.get('h')
|
||||
}
|
||||
|
||||
export const updateCursor = (wrapper: IColorSelectPanel<HTMLElement>, cursor: IColorSelectPanel<HTMLElement>, emit) => {
|
||||
return (color: Color, event: MouseEvent) => {
|
||||
const rect = wrapper.value.getBoundingClientRect()
|
||||
const { x, y } = updatePosition(event, rect, cursor)
|
||||
color.set({
|
||||
s: calcSaturation(x, rect.width) * 100,
|
||||
v: calcBrightness(y, rect.height)
|
||||
})
|
||||
emit('sv-update', {
|
||||
s: color.get('s'),
|
||||
v: color.get('v')
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
export const updateThumb = (
|
||||
bar: IColorSelectPanel<HTMLElement>,
|
||||
thumb: IColorSelectPanel<HTMLElement>,
|
||||
h: IColorSelectPanel<Number>,
|
||||
emit
|
||||
) => {
|
||||
return (event: MouseEvent) => {
|
||||
const e = event as MouseEvent
|
||||
const rect = bar.value.getBoundingClientRect()
|
||||
let top = e.clientY - rect.top
|
||||
top = Math.min(top, rect.height - thumb.value.offsetHeight / 2)
|
||||
top = Math.max(thumb.value.offsetHeight / 2, top)
|
||||
thumb.value.style.top = `${top}px`
|
||||
h.value = Math.round(((top - thumb.value.offsetHeight / 2) / (rect.height - thumb.value.offsetHeight)) * 360)
|
||||
emit('hue-update', h.value)
|
||||
}
|
||||
}
|
|
@ -1,55 +0,0 @@
|
|||
import { IColorSelectPanel as Ref } from '@/types'
|
||||
import { draggable } from '../utils/use-drag'
|
||||
import Color from '../utils/color'
|
||||
import { getThumbTop, resetCursor, updateThumb, updateCursor } from './index'
|
||||
|
||||
export const api = ['state', 'cursor', 'wrapper', 'bar', 'thumb']
|
||||
export const renderless = (props, context, { emit }) => {
|
||||
const cursor: Ref<HTMLElement> = context.ref()
|
||||
const wrapper: Ref<HTMLElement> = context.ref()
|
||||
const thumb: Ref<HTMLElement> = context.ref()
|
||||
const bar: Ref<HTMLElement> = context.ref()
|
||||
const color = new Color(props.color)
|
||||
const h = context.ref(color.get('h'))
|
||||
|
||||
const background = context.computed(() => {
|
||||
return `hsl(${h.value}deg, 100%, 50%)`
|
||||
})
|
||||
const state = context.reactive({
|
||||
background
|
||||
})
|
||||
const api = { state, cursor, wrapper, bar, thumb }
|
||||
context.watch(
|
||||
() => props.color,
|
||||
(newColor) => {
|
||||
color.reset(newColor)
|
||||
resetCursor(color.get('s'), color.get('v'), wrapper, cursor, thumb, color, h)
|
||||
}
|
||||
)
|
||||
context.onMounted(() => {
|
||||
const update = {
|
||||
thumb: updateThumb(bar, thumb, h, emit),
|
||||
cursor: updateCursor(wrapper, cursor, emit)
|
||||
}
|
||||
const thumbTop = getThumbTop(wrapper.value, thumb.value, h.value)
|
||||
thumb.value.style.top = `${thumbTop}px`
|
||||
resetCursor(color.get('s'), color.get('v'), wrapper, cursor, thumb, color, h)
|
||||
draggable(wrapper.value, {
|
||||
drag(event) {
|
||||
update.cursor(color, event as MouseEvent)
|
||||
},
|
||||
start(event) {
|
||||
update.cursor(color, event as MouseEvent)
|
||||
}
|
||||
})
|
||||
draggable(bar.value, {
|
||||
drag(event) {
|
||||
update.thumb(event as MouseEvent)
|
||||
},
|
||||
start(event) {
|
||||
update.thumb(event as MouseEvent)
|
||||
}
|
||||
})
|
||||
})
|
||||
return api
|
||||
}
|
|
@ -1,68 +1,61 @@
|
|||
import { IColorPickerRef, IColorSelectPanelRef } from '@/types'
|
||||
import { IColorPickerRef as Ref } from '@/types'
|
||||
import type Color from './utils/color'
|
||||
|
||||
export const onConfirm = (
|
||||
hex: IColorSelectPanelRef<string>,
|
||||
triggerBg: IColorSelectPanelRef<string>,
|
||||
res: IColorSelectPanelRef<string>,
|
||||
emit,
|
||||
isShow: IColorSelectPanelRef<boolean>,
|
||||
prev: IColorPickerRef<string>
|
||||
) => {
|
||||
return (color: string) => {
|
||||
prev.value = res.value;
|
||||
res.value = color
|
||||
hex.value = res.value
|
||||
triggerBg.value = res.value
|
||||
emit('confirm', res.value)
|
||||
isShow.value = false
|
||||
}
|
||||
}
|
||||
|
||||
export const onCancel = (
|
||||
res: IColorSelectPanelRef<string>,
|
||||
triggerBg: IColorSelectPanelRef<string>,
|
||||
emit,
|
||||
isShow: IColorSelectPanelRef<boolean>,
|
||||
hex: IColorSelectPanelRef<string>,
|
||||
color: Color, prev: IColorPickerRef<string>
|
||||
tmpColor: Color,
|
||||
triggerBg: Ref<string>,
|
||||
isShow: Ref<boolean>,
|
||||
pre: Ref<string>,
|
||||
emit
|
||||
) => {
|
||||
return () => {
|
||||
if (isShow.value) {
|
||||
res.value = prev.value;
|
||||
hex.value = prev.value;
|
||||
triggerBg.value = prev.value;
|
||||
color.reset(hex.value)
|
||||
emit('cancel')
|
||||
}
|
||||
return (color: Ref<Color>)=>{
|
||||
tmpColor.reset(color.value.getHex())
|
||||
triggerBg.value = pre.value
|
||||
isShow.value = false
|
||||
}
|
||||
}
|
||||
export const onColorUpdate = (color: Color, res: IColorSelectPanelRef<string>, triggerBg: IColorPickerRef<string>) => {
|
||||
res.value = color.getHex()
|
||||
triggerBg.value = color.getHex();
|
||||
}
|
||||
|
||||
export const onHSVUpdate = (color: Color, res: IColorSelectPanelRef<string>, hex: IColorSelectPanelRef<string>, triggerBg: IColorSelectPanelRef<string>) => {
|
||||
return {
|
||||
onHueUpdate: (hue: number) => {
|
||||
color.set({ h: hue })
|
||||
onColorUpdate(color, res, triggerBg)
|
||||
hex.value = color.getHex()
|
||||
},
|
||||
onSVUpdate: ({ s, v }: { s: number; v: number }) => {
|
||||
color.set({ h: color.get('h'), s, v })
|
||||
onColorUpdate(color, res, triggerBg)
|
||||
hex.value = color.getHex();
|
||||
}
|
||||
emit('cancel')
|
||||
}
|
||||
}
|
||||
|
||||
export const onAlphaUpdate = (color: Color, res: IColorSelectPanelRef<string>, triggerBg: IColorSelectPanelRef<string>) => {
|
||||
return {
|
||||
update: (alpha: number) => {
|
||||
color.set({ a: alpha })
|
||||
onColorUpdate(color, res, triggerBg)
|
||||
}
|
||||
export const onConfirm = (
|
||||
triggerBg: Ref<string>,
|
||||
pre: Ref<string>,
|
||||
hex: Ref<string>,
|
||||
isShow: Ref<boolean>,
|
||||
emit
|
||||
) => {
|
||||
return (color: string)=>{
|
||||
pre.value = triggerBg.value
|
||||
triggerBg.value = color
|
||||
hex.value = color
|
||||
isShow.value = false
|
||||
emit('confirm', color)
|
||||
}
|
||||
}
|
||||
|
||||
export const onHueUpdate = (
|
||||
tmpColor: Color,
|
||||
triggerBg: Ref<string>
|
||||
) => {
|
||||
return (h: number) => {
|
||||
triggerBg.value = tmpColor.getHex();
|
||||
tmpColor.set({h})
|
||||
}
|
||||
}
|
||||
|
||||
export const onSVUpdate = (
|
||||
tmpColor: Color,
|
||||
triggerBg: Ref<string>
|
||||
) => {
|
||||
return ({s,v})=>{
|
||||
triggerBg.value = tmpColor.getHex();
|
||||
tmpColor.set({s,v})
|
||||
}
|
||||
}
|
||||
|
||||
export const onColorUpdate = (
|
||||
triggerBg: Ref<string>
|
||||
) => {
|
||||
return (color: Ref<Color>) => {
|
||||
triggerBg.value = color.value.getHex();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,7 +7,7 @@ function hexToRgb(hex: string) {
|
|||
let a = parseInt(hex.slice(7), 16) / 255
|
||||
return { r, g, b, a: a * 100 }
|
||||
}
|
||||
const normalizeHexColor = (color: string) => {
|
||||
export const normalizeHexColor = (color: string) => {
|
||||
let normalizedColor: string = color.replace('#', '')
|
||||
if (normalizedColor.length === 3) {
|
||||
normalizedColor = normalizedColor
|
||||
|
@ -40,6 +40,7 @@ export default class Color {
|
|||
private s = 0
|
||||
private v = 0
|
||||
private a = 100
|
||||
private preH = 0;
|
||||
private enableAlpha = false
|
||||
constructor(value: string, alpha = false) {
|
||||
this.reset(value)
|
||||
|
@ -61,14 +62,17 @@ export default class Color {
|
|||
this.s = s
|
||||
this.v = v
|
||||
this.a = a
|
||||
this.preH = h;
|
||||
}
|
||||
|
||||
set({ h, s, v, a }: { h?: number; s?: number; v?: number; a?: number }) {
|
||||
this.h = h ?? this.h
|
||||
this.s = s ?? this.s
|
||||
this.v = v ?? this.v
|
||||
this.a = a ?? this.a
|
||||
}
|
||||
setPrevH(val: number){
|
||||
this.preH = val;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
|
@ -104,7 +108,7 @@ export default class Color {
|
|||
}
|
||||
}
|
||||
|
||||
get(key: 'h' | 's' | 'v' | 'a') {
|
||||
get(key: 'h' | 's' | 'v' | 'a' |'preH') {
|
||||
return this[key]
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,43 +0,0 @@
|
|||
let isDragging = false
|
||||
|
||||
export interface DraggableOptions {
|
||||
drag?: (event: MouseEvent | TouchEvent) => void
|
||||
start?: (event: MouseEvent | TouchEvent) => void
|
||||
end?: (event: MouseEvent | TouchEvent) => void
|
||||
}
|
||||
|
||||
export function draggable(element: HTMLElement, options: DraggableOptions) {
|
||||
const moveFn = function (event: MouseEvent | TouchEvent) {
|
||||
options.drag?.(event)
|
||||
}
|
||||
|
||||
const upFn = function (event: MouseEvent | TouchEvent) {
|
||||
document.removeEventListener('mousemove', moveFn)
|
||||
document.removeEventListener('mouseup', upFn)
|
||||
document.removeEventListener('touchmove', moveFn)
|
||||
document.removeEventListener('touchend', upFn)
|
||||
document.onselectstart = null
|
||||
document.ondragstart = null
|
||||
|
||||
isDragging = false
|
||||
|
||||
options.end?.(event)
|
||||
}
|
||||
|
||||
const downFn = function (event: MouseEvent | TouchEvent) {
|
||||
if (isDragging) return
|
||||
event.preventDefault()
|
||||
document.onselectstart = () => false
|
||||
document.ondragstart = () => false
|
||||
document.addEventListener('mousemove', moveFn)
|
||||
document.addEventListener('mouseup', upFn)
|
||||
document.addEventListener('touchmove', moveFn)
|
||||
document.addEventListener('touchend', upFn)
|
||||
|
||||
isDragging = true
|
||||
|
||||
options.start?.(event)
|
||||
}
|
||||
element.addEventListener('mousedown', downFn)
|
||||
element.addEventListener('touchstart', downFn)
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
import { IColorSelectPanelRef as Ref } from '@/types'
|
||||
import Color from './utils/color'
|
||||
import { onConfirm, onCancel, onHSVUpdate, onAlphaUpdate } from '.'
|
||||
import { onConfirm, onCancel, onHueUpdate, onSVUpdate, onColorUpdate } from './index'
|
||||
|
||||
export const api = [
|
||||
'state',
|
||||
|
@ -17,12 +17,11 @@ export const api = [
|
|||
|
||||
export const renderless = (props, context, { emit }) => {
|
||||
const { modelValue, visible, predefine, size, history } = context.toRefs(props)
|
||||
const pre = context.ref(modelValue.value ?? 'transparent')
|
||||
const triggerBg = context.ref(pre.value ?? 'transparent')
|
||||
const tmpColor = new Color(triggerBg.value ?? 'transparent')
|
||||
const hex = context.ref(modelValue.value ?? 'transparent')
|
||||
const res = context.ref(modelValue.value ?? 'transparent')
|
||||
const prev = context.ref(modelValue.value ?? 'transparent')
|
||||
const triggerBg = context.ref(modelValue.value ?? 'transparent')
|
||||
const isShow = context.ref(visible?.value ?? false)
|
||||
const cursor: Ref<HTMLElement> = context.ref()
|
||||
const changeVisible = (state: boolean) => {
|
||||
isShow.value = state
|
||||
}
|
||||
|
@ -32,46 +31,38 @@ export const renderless = (props, context, { emit }) => {
|
|||
const predefineStack: Ref<string[]> = context.ref(
|
||||
[...(predefine?.value ?? [])]
|
||||
)
|
||||
const color = new Color(hex.value, props.alpha)
|
||||
const state = context.reactive({
|
||||
isShow,
|
||||
hex,
|
||||
color,
|
||||
triggerBg,
|
||||
defaultValue: modelValue,
|
||||
res,
|
||||
stack,
|
||||
predefineStack,
|
||||
size: size ?? '',
|
||||
stack
|
||||
size: size ?? ''
|
||||
})
|
||||
const api = {
|
||||
state,
|
||||
changeVisible,
|
||||
onCancel: onCancel(tmpColor, triggerBg, isShow, pre, emit),
|
||||
onConfirm: onConfirm(triggerBg, pre, hex, isShow, emit),
|
||||
onHueUpdate: onHueUpdate(tmpColor, triggerBg),
|
||||
onSVUpdate: onSVUpdate(tmpColor, triggerBg),
|
||||
onColorUpdate: onColorUpdate(triggerBg)
|
||||
}
|
||||
context.watch(predefine, (newPredefine: string[]) => {
|
||||
predefineStack.value = [...newPredefine];
|
||||
predefineStack.value = [...newPredefine]
|
||||
}, { deep: true })
|
||||
context.watch(history, (newHistory: string[]) => {
|
||||
stack.value = [...newHistory]
|
||||
}, { deep: true })
|
||||
context.watch(modelValue, (newValue) => {
|
||||
context.watch(modelValue, (newValue)=>{
|
||||
pre.value = newValue
|
||||
hex.value = newValue
|
||||
res.value = newValue
|
||||
triggerBg.value = newValue
|
||||
prev.value = newValue;
|
||||
color.reset(hex.value)
|
||||
state.hex = newValue
|
||||
state.triggerBg = newValue;
|
||||
})
|
||||
context.watch(visible, (visible) => {
|
||||
context.watch(visible, (visible)=>{
|
||||
isShow.value = visible
|
||||
})
|
||||
|
||||
const { onHueUpdate, onSVUpdate } = onHSVUpdate(color, res, hex, triggerBg)
|
||||
const { update } = onAlphaUpdate(color, res, triggerBg)
|
||||
const api = {
|
||||
state,
|
||||
changeVisible,
|
||||
onHueUpdate,
|
||||
onSVUpdate,
|
||||
onConfirm: onConfirm(hex, triggerBg, res, emit, isShow, prev),
|
||||
onCancel: onCancel(res, triggerBg, emit, isShow, hex, color, prev),
|
||||
onAlphaUpdate: update,
|
||||
cursor
|
||||
}
|
||||
return api
|
||||
}
|
||||
|
|
|
@ -6,8 +6,7 @@ import { onDrag, updateThumb } from '.'
|
|||
export const api = ['state', 'color', 'slider', 'alphaWrapper', 'alphaThumb']
|
||||
|
||||
export const renderless = (props, context, { emit }) => {
|
||||
const hex = props.color
|
||||
const color = new Color(hex, props.alpha)
|
||||
const color:Color = props.color
|
||||
const [rr, gg, bb] = color.getRGB()
|
||||
const r = context.ref(rr)
|
||||
const g = context.ref(gg)
|
||||
|
@ -18,14 +17,13 @@ export const renderless = (props, context, { emit }) => {
|
|||
const alpha = context.ref(color.get('a'))
|
||||
context.watch(
|
||||
() => props.color,
|
||||
(hex: string) => {
|
||||
color.reset(hex)
|
||||
() => {
|
||||
const [rr, gg, bb] = color.getRGB()
|
||||
r.value = rr
|
||||
g.value = gg
|
||||
b.value = bb
|
||||
alpha.value = color.get('a')
|
||||
}
|
||||
}, {deep: true}
|
||||
)
|
||||
context.watch(alpha, (newAlpha) => {
|
||||
updateThumb(newAlpha, alphaThumb.value, alphaWrapper.value)
|
||||
|
@ -36,7 +34,6 @@ export const renderless = (props, context, { emit }) => {
|
|||
})
|
||||
const state = context.reactive({
|
||||
background,
|
||||
hex
|
||||
})
|
||||
const api = {
|
||||
state,
|
||||
|
|
|
@ -56,11 +56,13 @@ export const updateCursor = (
|
|||
const { x, y } = updatePosition(event, rect, cursor)
|
||||
color.set({
|
||||
s: calcSaturation(x, rect.width) * 100,
|
||||
v: calcBrightness(y, rect.height)
|
||||
v: calcBrightness(y, rect.height),
|
||||
h: color.get('h')
|
||||
})
|
||||
emit('sv-update', {
|
||||
s: color.get('s'),
|
||||
v: color.get('v')
|
||||
v: color.get('v'),
|
||||
h: color.get('h')
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,31 +1,30 @@
|
|||
import { IColorSelectPanelRef as Ref } from '@/types'
|
||||
import { draggable } from '../utils/use-drag'
|
||||
import Color from '../utils/color'
|
||||
import { getThumbTop, resetCursor, updateThumb, updateCursor } from './index'
|
||||
import Color from '../utils/color'
|
||||
|
||||
export const api = ['state', 'cursor', 'wrapper', 'bar', 'thumb']
|
||||
export const renderless = (props, context, { emit }) => {
|
||||
export const renderless = (props, context, { emit, expose }) => {
|
||||
const cursor: Ref<HTMLElement> = context.ref()
|
||||
const wrapper: Ref<HTMLElement> = context.ref()
|
||||
const thumb: Ref<HTMLElement> = context.ref()
|
||||
const bar: Ref<HTMLElement> = context.ref()
|
||||
const color = new Color(props.color)
|
||||
const color:Color = props.color
|
||||
const h = context.ref(color.get('h'))
|
||||
|
||||
const background = context.computed(() => {
|
||||
return `hsl(${h.value}deg, 100%, 50%)`
|
||||
})
|
||||
const background:string = `hsl(${h.value}deg, 100%, 50%)`
|
||||
const state = context.reactive({
|
||||
background
|
||||
})
|
||||
|
||||
const api = { state, cursor, wrapper, bar, thumb }
|
||||
context.watch(
|
||||
() => props.color,
|
||||
(newColor) => {
|
||||
color.reset(newColor)
|
||||
resetCursor(color.get('s'), color.get('v'), wrapper, cursor, thumb, color, h)
|
||||
}
|
||||
)
|
||||
context.watch(()=>props, ()=>{
|
||||
h.value = color.get('h')
|
||||
resetCursor(color.get('s'), color.get('v'), wrapper, cursor, thumb, color, h)
|
||||
}, {deep: true})
|
||||
context.watch(h,(newHue: string) => {
|
||||
state.background = `hsl(${newHue}deg, 100%, 50%)`
|
||||
})
|
||||
context.onMounted(() => {
|
||||
const update = {
|
||||
thumb: updateThumb(bar, thumb, h, emit),
|
||||
|
|
|
@ -1,17 +1,18 @@
|
|||
import { IColorSelectPanelRef } from '@/types'
|
||||
import type Color from './utils/color'
|
||||
import Color from './utils/color'
|
||||
|
||||
export const onConfirm = (
|
||||
hex: IColorSelectPanelRef<string>,
|
||||
triggerBg: IColorSelectPanelRef<string>,
|
||||
pre: IColorSelectPanelRef<string>,
|
||||
res: IColorSelectPanelRef<string>,
|
||||
emit,
|
||||
stack: IColorSelectPanelRef<string[]>,
|
||||
enableHistory: boolean
|
||||
enableHistory: boolean,
|
||||
color: IColorSelectPanelRef<Color>
|
||||
) => {
|
||||
return () => {
|
||||
pre.value = hex.value
|
||||
hex.value = res.value
|
||||
triggerBg.value = res.value
|
||||
if (enableHistory) {
|
||||
const itemIdx = Math.max(
|
||||
stack.value.indexOf(res.value),
|
||||
|
@ -23,56 +24,61 @@ export const onConfirm = (
|
|||
}
|
||||
stack.value.unshift(res.value)
|
||||
}
|
||||
color.value.setPrevH(color.value.get('h'))
|
||||
emit('confirm', res.value)
|
||||
}
|
||||
}
|
||||
|
||||
export const onCancel = (
|
||||
res: IColorSelectPanelRef<string>,
|
||||
triggerBg: IColorSelectPanelRef<string>,
|
||||
pre: IColorSelectPanelRef<string>,
|
||||
emit,
|
||||
isShow: IColorSelectPanelRef<boolean>,
|
||||
hex: IColorSelectPanelRef<string>,
|
||||
color: Color
|
||||
color: IColorSelectPanelRef<Color>
|
||||
) => {
|
||||
return () => {
|
||||
if (isShow.value) {
|
||||
res.value = triggerBg.value
|
||||
hex.value = triggerBg.value
|
||||
color.reset(hex.value)
|
||||
emit('cancel')
|
||||
res.value = pre.value
|
||||
hex.value = pre.value
|
||||
const tmpColor = new Color(pre.value)
|
||||
color.value.set({
|
||||
...tmpColor.getHSV(),
|
||||
h: color.value.get('preH')
|
||||
})
|
||||
emit('cancel', color)
|
||||
}
|
||||
}
|
||||
}
|
||||
export const onColorUpdate = (color: Color, res: IColorSelectPanelRef<string>) => {
|
||||
res.value = color.getHex()
|
||||
export const onColorUpdate = (color: IColorSelectPanelRef<Color>, res: IColorSelectPanelRef<string>) => {
|
||||
res.value = color.value.getHex()
|
||||
}
|
||||
|
||||
export const onHSVUpdate = (
|
||||
color: Color,
|
||||
color: IColorSelectPanelRef<Color>,
|
||||
res: IColorSelectPanelRef<string>,
|
||||
hex: IColorSelectPanelRef<string>,
|
||||
emit
|
||||
) => {
|
||||
return {
|
||||
onHueUpdate: (hue: number) => {
|
||||
color.set({ h: hue })
|
||||
color.value.set({ h: hue })
|
||||
onColorUpdate(color, res)
|
||||
hex.value = color.getHex()
|
||||
hex.value = color.value.getHex()
|
||||
emit('hue-update', hue)
|
||||
},
|
||||
onSVUpdate: ({ s, v }: { s: number; v: number }) => {
|
||||
color.set({ s, v })
|
||||
hex.value = color.value.getHex()
|
||||
onColorUpdate(color, res)
|
||||
emit('sv-update', { s, v })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const onAlphaUpdate = (color: Color, res: IColorSelectPanelRef<string>) => {
|
||||
export const onAlphaUpdate = (color: IColorSelectPanelRef<Color>, res: IColorSelectPanelRef<string>) => {
|
||||
return {
|
||||
update: (alpha: number) => {
|
||||
color.set({ a: alpha })
|
||||
color.value.set({ a: alpha })
|
||||
onColorUpdate(color, res)
|
||||
}
|
||||
}
|
||||
|
@ -81,23 +87,33 @@ export const onAlphaUpdate = (color: Color, res: IColorSelectPanelRef<string>) =
|
|||
export const handleHistoryClick = (
|
||||
hex: IColorSelectPanelRef<string>,
|
||||
res: IColorSelectPanelRef<string>,
|
||||
color: Color
|
||||
color: IColorSelectPanelRef<Color>,
|
||||
emit
|
||||
) => {
|
||||
return (history: string) => {
|
||||
hex.value = history
|
||||
res.value = history
|
||||
color.reset(history)
|
||||
const tmpColor = new Color(history)
|
||||
color.value.set({
|
||||
...tmpColor.getHSV(),
|
||||
})
|
||||
emit('color-update', color)
|
||||
}
|
||||
}
|
||||
|
||||
export const handlePredefineClick = (
|
||||
hex: IColorSelectPanelRef<string>,
|
||||
res: IColorSelectPanelRef<string>,
|
||||
color: Color
|
||||
color: IColorSelectPanelRef<Color>,
|
||||
emit
|
||||
) => {
|
||||
return (selectedColor: string) => {
|
||||
hex.value = selectedColor
|
||||
res.value = selectedColor
|
||||
color.reset(selectedColor)
|
||||
const tmpColor = new Color(selectedColor)
|
||||
color.value.set({
|
||||
...tmpColor.getHSV(),
|
||||
})
|
||||
emit('color-update', color)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -40,6 +40,7 @@ export default class Color {
|
|||
private s = 0
|
||||
private v = 0
|
||||
private a = 100
|
||||
private preH = 0
|
||||
private enableAlpha = false
|
||||
constructor(value: string, alpha = false) {
|
||||
this.reset(value)
|
||||
|
@ -57,18 +58,21 @@ export default class Color {
|
|||
this.hex = normalizeHexColor(hex)
|
||||
const { r, g, b, a } = hexToRgb(this.hex)
|
||||
const { h, s, v } = rgb([r, g, b, a]).hsv().object()
|
||||
this.preH = h
|
||||
this.h = h
|
||||
this.s = s
|
||||
this.v = v
|
||||
this.a = a
|
||||
}
|
||||
|
||||
set({ h, s, v, a }: { h?: number; s?: number; v?: number; a?: number }) {
|
||||
this.h = h ?? this.h
|
||||
this.s = s ?? this.s
|
||||
this.v = v ?? this.v
|
||||
this.a = a ?? this.a
|
||||
}
|
||||
setPrevH(val: number){
|
||||
this.preH = val
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
|
@ -104,7 +108,7 @@ export default class Color {
|
|||
}
|
||||
}
|
||||
|
||||
get(key: 'h' | 's' | 'v' | 'a') {
|
||||
get(key: 'h' | 's' | 'v' | 'a' |'preH') {
|
||||
return this[key]
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,6 +20,7 @@ export const renderless = (props, context, { emit }) => {
|
|||
const { modelValue, visible, history, predefine } = context.toRefs(props)
|
||||
const hex = context.ref(modelValue?.value ?? 'transparent')
|
||||
const res = context.ref(modelValue?.value ?? 'transparent')
|
||||
const pre = context.ref(res.value)
|
||||
const triggerBg = context.ref(modelValue?.value ?? 'transparent')
|
||||
const isShow = context.ref(visible?.value ?? false)
|
||||
const cursor: Ref<HTMLElement> = context.ref()
|
||||
|
@ -30,7 +31,7 @@ export const renderless = (props, context, { emit }) => {
|
|||
const changeVisible = (state: boolean) => {
|
||||
isShow.value = state
|
||||
}
|
||||
const color = new Color(hex.value, props.alpha)
|
||||
const color:Ref<Color> = context.ref(new Color(hex.value, props.alpha))
|
||||
const state = context.reactive({
|
||||
isShow,
|
||||
hex,
|
||||
|
@ -57,11 +58,15 @@ export const renderless = (props, context, { emit }) => {
|
|||
},
|
||||
{ deep: true }
|
||||
)
|
||||
context.watch(state,()=>{
|
||||
state.color = state.color
|
||||
}, {deep: true})
|
||||
context.watch(modelValue, (newValue) => {
|
||||
pre.value = res.value
|
||||
hex.value = newValue
|
||||
res.value = newValue
|
||||
triggerBg.value = newValue
|
||||
color.reset(hex.value)
|
||||
color.value.reset(newValue)
|
||||
state.color.reset(newValue)
|
||||
})
|
||||
context.watch(visible, (visible) => {
|
||||
isShow.value = visible
|
||||
|
@ -73,11 +78,11 @@ export const renderless = (props, context, { emit }) => {
|
|||
changeVisible,
|
||||
onHueUpdate,
|
||||
onSVUpdate,
|
||||
onConfirm: onConfirm(hex, triggerBg, res, emit, stack, enableHistory),
|
||||
onCancel: onCancel(res, triggerBg, emit, isShow, hex, color),
|
||||
onConfirm: onConfirm(hex, pre, res, emit, stack, enableHistory, color),
|
||||
onCancel: onCancel(res, pre, emit, isShow, hex, color),
|
||||
onAlphaUpdate: update,
|
||||
onHistoryClick: handleHistoryClick(hex, res, color),
|
||||
onPredefineColorClick: handlePredefineClick(hex, res, color),
|
||||
onHistoryClick: handleHistoryClick(hex, res, color, emit),
|
||||
onPredefineColorClick: handlePredefineClick(hex, res, color, emit),
|
||||
cursor
|
||||
}
|
||||
return api
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
@cancel="onCancel"
|
||||
@hue-update="onHueUpdate"
|
||||
@sv-update="onSVUpdate"
|
||||
@color-update="onColorUpdate"
|
||||
v-model="state.hex"
|
||||
:visible="state.isShow"
|
||||
:alpha="alpha"
|
||||
|
|
|
@ -25,7 +25,7 @@ export default defineComponent({
|
|||
emits: ['alpha-update'],
|
||||
props: {
|
||||
color: {
|
||||
type: String
|
||||
type: Object
|
||||
}
|
||||
},
|
||||
setup(props, context) {
|
||||
|
|
|
@ -25,7 +25,7 @@ export default defineComponent({
|
|||
emits: ['hue-update', 'sv-update'],
|
||||
props: {
|
||||
color: {
|
||||
type: String
|
||||
type: Object
|
||||
},
|
||||
alpha: {
|
||||
type: Boolean
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<template>
|
||||
<div class="tiny-color-select-panel__wrapper" @click.stop v-if="state.isShow" v-clickoutside="onCancel">
|
||||
<hue-select :color="state.hex" @hue-update="onHueUpdate" @sv-update="onSVUpdate" />
|
||||
<alpha-select v-if="alpha" :color="state.res" @alpha-update="onAlphaUpdate" />
|
||||
<hue-select :color="state.color" @hue-update="onHueUpdate" @sv-update="onSVUpdate" />
|
||||
<alpha-select v-if="alpha" :color="state.color" @alpha-update="onAlphaUpdate" />
|
||||
<div class="tiny-color-select-panel__wrapper__tools">
|
||||
<tiny-input v-model="state.res" />
|
||||
<tiny-button-group>
|
||||
|
@ -65,7 +65,7 @@ import Clickoutside from '@opentiny/vue-renderless/common/deps/clickoutside'
|
|||
import { t } from '@opentiny/vue-locale'
|
||||
|
||||
export default defineComponent({
|
||||
emits: ['update:modelValue', 'cancel', 'confirm', 'hue-update', 'sv-update'],
|
||||
emits: ['update:modelValue', 'cancel', 'confirm', 'hue-update', 'sv-update', 'color-update'],
|
||||
props: [...props, 'modelValue', 'visible', 'alpha', 'history', 'predefine'],
|
||||
components: {
|
||||
hueSelect: HueSelect,
|
||||
|
|
Loading…
Reference in New Issue