UNPKG

vuetify

Version:

Vue Material Component Framework

188 lines (187 loc) 5.98 kB
import { createVNode as _createVNode } from "vue"; // Styles import "./VColorPickerCanvas.css"; // Composables import { makeComponentProps } from "../../composables/component.js"; import { useResizeObserver } from "../../composables/resizeObserver.js"; // Utilities import { computed, onMounted, ref, shallowRef, watch } from 'vue'; import { clamp, convertToUnit, defineComponent, getEventCoordinates, propsFactory, useRender } from "../../util/index.js"; // Types export const makeVColorPickerCanvasProps = propsFactory({ color: { type: Object }, disabled: Boolean, dotSize: { type: [Number, String], default: 10 }, height: { type: [Number, String], default: 150 }, width: { type: [Number, String], default: 300 }, ...makeComponentProps() }, 'VColorPickerCanvas'); export const VColorPickerCanvas = defineComponent({ name: 'VColorPickerCanvas', props: makeVColorPickerCanvasProps(), emits: { 'update:color': color => true, 'update:position': hue => true }, setup(props, _ref) { let { emit } = _ref; const isInteracting = shallowRef(false); const canvasRef = ref(); const canvasWidth = shallowRef(parseFloat(props.width)); const canvasHeight = shallowRef(parseFloat(props.height)); const _dotPosition = ref({ x: 0, y: 0 }); const dotPosition = computed({ get: () => _dotPosition.value, set(val) { if (!canvasRef.value) return; const { x, y } = val; _dotPosition.value = val; emit('update:color', { h: props.color?.h ?? 0, s: clamp(x, 0, canvasWidth.value) / canvasWidth.value, v: 1 - clamp(y, 0, canvasHeight.value) / canvasHeight.value, a: props.color?.a ?? 1 }); } }); const dotStyles = computed(() => { const { x, y } = dotPosition.value; const radius = parseInt(props.dotSize, 10) / 2; return { width: convertToUnit(props.dotSize), height: convertToUnit(props.dotSize), transform: `translate(${convertToUnit(x - radius)}, ${convertToUnit(y - radius)})` }; }); const { resizeRef } = useResizeObserver(entries => { if (!resizeRef.el?.offsetParent) return; const { width, height } = entries[0].contentRect; canvasWidth.value = width; canvasHeight.value = height; }); function updateDotPosition(x, y, rect) { const { left, top, width, height } = rect; dotPosition.value = { x: clamp(x - left, 0, width), y: clamp(y - top, 0, height) }; } function handleMouseDown(e) { if (e.type === 'mousedown') { // Prevent text selection while dragging e.preventDefault(); } if (props.disabled) return; handleMouseMove(e); window.addEventListener('mousemove', handleMouseMove); window.addEventListener('mouseup', handleMouseUp); window.addEventListener('touchmove', handleMouseMove); window.addEventListener('touchend', handleMouseUp); } function handleMouseMove(e) { if (props.disabled || !canvasRef.value) return; isInteracting.value = true; const coords = getEventCoordinates(e); updateDotPosition(coords.clientX, coords.clientY, canvasRef.value.getBoundingClientRect()); } function handleMouseUp() { window.removeEventListener('mousemove', handleMouseMove); window.removeEventListener('mouseup', handleMouseUp); window.removeEventListener('touchmove', handleMouseMove); window.removeEventListener('touchend', handleMouseUp); } function updateCanvas() { if (!canvasRef.value) return; const canvas = canvasRef.value; const ctx = canvas.getContext('2d'); if (!ctx) return; const saturationGradient = ctx.createLinearGradient(0, 0, canvas.width, 0); saturationGradient.addColorStop(0, 'hsla(0, 0%, 100%, 1)'); // white saturationGradient.addColorStop(1, `hsla(${props.color?.h ?? 0}, 100%, 50%, 1)`); ctx.fillStyle = saturationGradient; ctx.fillRect(0, 0, canvas.width, canvas.height); const valueGradient = ctx.createLinearGradient(0, 0, 0, canvas.height); valueGradient.addColorStop(0, 'hsla(0, 0%, 0%, 0)'); // transparent valueGradient.addColorStop(1, 'hsla(0, 0%, 0%, 1)'); // black ctx.fillStyle = valueGradient; ctx.fillRect(0, 0, canvas.width, canvas.height); } watch(() => props.color?.h, updateCanvas, { immediate: true }); watch(() => [canvasWidth.value, canvasHeight.value], (newVal, oldVal) => { updateCanvas(); _dotPosition.value = { x: dotPosition.value.x * newVal[0] / oldVal[0], y: dotPosition.value.y * newVal[1] / oldVal[1] }; }, { flush: 'post' }); watch(() => props.color, () => { if (isInteracting.value) { isInteracting.value = false; return; } _dotPosition.value = props.color ? { x: props.color.s * canvasWidth.value, y: (1 - props.color.v) * canvasHeight.value } : { x: 0, y: 0 }; }, { deep: true, immediate: true }); onMounted(() => updateCanvas()); useRender(() => _createVNode("div", { "ref": resizeRef, "class": ['v-color-picker-canvas', props.class], "style": props.style, "onMousedown": handleMouseDown, "onTouchstartPassive": handleMouseDown }, [_createVNode("canvas", { "ref": canvasRef, "width": canvasWidth.value, "height": canvasHeight.value }, null), props.color && _createVNode("div", { "class": ['v-color-picker-canvas__dot', { 'v-color-picker-canvas__dot--disabled': props.disabled }], "style": dotStyles.value }, null)])); return {}; } }); //# sourceMappingURL=VColorPickerCanvas.js.map