fix: smooth issue
This commit is contained in:
@@ -19,57 +19,26 @@ const emit = defineEmits<{
|
||||
(e: 'mousedown', event: MouseEvent): void
|
||||
}>()
|
||||
|
||||
const sliderRef = ref<HTMLElement | null>(null)
|
||||
|
||||
function clamp(value: number, min: number, max: number): number {
|
||||
return Math.min(Math.max(value, min), max)
|
||||
}
|
||||
|
||||
const sliderRef = ref<HTMLElement | null>(null)
|
||||
const isDragging = ref(false)
|
||||
|
||||
// Scale factor for internal calculations
|
||||
const SCALE_FACTOR = 100
|
||||
|
||||
function getStepPrecision(step: number): number {
|
||||
const stepStr = step.toString()
|
||||
if (stepStr.includes('e-')) {
|
||||
return Number.parseInt(stepStr.split('e-')[1], 10)
|
||||
}
|
||||
|
||||
const decimals = stepStr.includes('.') ? stepStr.split('.')[1].length : 0
|
||||
return decimals
|
||||
}
|
||||
|
||||
function scaleUp(value: number): number {
|
||||
return value * SCALE_FACTOR
|
||||
}
|
||||
|
||||
function scaleDown(value: number): number {
|
||||
return value / SCALE_FACTOR
|
||||
}
|
||||
|
||||
function roundToStep(value: number, step: number): number {
|
||||
const scaledValue = scaleUp(value)
|
||||
const scaledStep = scaleUp(step)
|
||||
const precision = getStepPrecision(step)
|
||||
|
||||
const rounded = Math.round(scaledValue / scaledStep) * scaledStep
|
||||
const scaledBack = scaleDown(rounded)
|
||||
|
||||
return Number(scaledBack.toFixed(precision))
|
||||
}
|
||||
|
||||
function valueToPercent(value: number, min: number, max: number) {
|
||||
const scaledValue = scaleUp(value)
|
||||
const scaledMin = scaleUp(min)
|
||||
const scaledMax = scaleUp(max)
|
||||
return ((scaledValue - scaledMin) * 100) / (scaledMax - scaledMin)
|
||||
}
|
||||
|
||||
function percentToValue(percent: number, min: number, max: number) {
|
||||
const scaledMin = scaleUp(min)
|
||||
const scaledMax = scaleUp(max)
|
||||
const scaledValue = (scaledMax - scaledMin) * percent + scaledMin
|
||||
const value = scaleDown(scaledValue)
|
||||
return roundToStep(value, props.step)
|
||||
const factor = 10 ** (precision + 3)
|
||||
return Number.parseFloat((value * factor / factor).toFixed(precision))
|
||||
}
|
||||
|
||||
const currentValue = computed(() => {
|
||||
@@ -77,10 +46,10 @@ const currentValue = computed(() => {
|
||||
})
|
||||
|
||||
const sliderStyle = computed(() => {
|
||||
const sliderPercent = valueToPercent(currentValue.value, props.min, props.max)
|
||||
const percent = ((currentValue.value - props.min) / (props.max - props.min)) * 100
|
||||
return {
|
||||
width: `${sliderPercent}%`,
|
||||
backgroundSize: `${sliderPercent}% 100%`,
|
||||
width: `${percent}%`,
|
||||
backgroundSize: `${percent}% 100%`,
|
||||
}
|
||||
})
|
||||
|
||||
@@ -88,9 +57,23 @@ function getNewValue(event: MouseEvent) {
|
||||
if (!sliderRef.value)
|
||||
return currentValue.value
|
||||
|
||||
const { width, left } = sliderRef.value.getBoundingClientRect()
|
||||
const percent = clamp((event.clientX - left) / width, 0, 1)
|
||||
return percentToValue(percent, props.min, props.max)
|
||||
const rect = sliderRef.value.getBoundingClientRect()
|
||||
const x = event.clientX - rect.left
|
||||
const percent = x / rect.width
|
||||
const range = props.max - props.min
|
||||
const value = props.min + (range * percent)
|
||||
|
||||
// Round to nearest step
|
||||
const steps = Math.round(value / props.step) * props.step
|
||||
return Math.min(Math.max(steps, props.min), props.max)
|
||||
}
|
||||
|
||||
function handleMouseMove(event: MouseEvent) {
|
||||
if (props.disabled)
|
||||
return
|
||||
|
||||
const newValue = getNewValue(event)
|
||||
emit('update:modelValue', newValue)
|
||||
}
|
||||
|
||||
function handleMouseDown(event: MouseEvent) {
|
||||
@@ -98,33 +81,24 @@ function handleMouseDown(event: MouseEvent) {
|
||||
return
|
||||
|
||||
event.preventDefault()
|
||||
isDragging.value = true
|
||||
emit('mousedown', event)
|
||||
|
||||
const newValue = getNewValue(event)
|
||||
emit('update:modelValue', newValue)
|
||||
}
|
||||
|
||||
function handleMouseMove(event: MouseEvent) {
|
||||
if (!isDragging.value || props.disabled)
|
||||
return
|
||||
|
||||
const newValue = getNewValue(event)
|
||||
emit('update:modelValue', newValue)
|
||||
window.addEventListener('mousemove', handleMouseMove)
|
||||
window.addEventListener('mouseup', handleMouseUp)
|
||||
}
|
||||
|
||||
function handleMouseUp(_: MouseEvent) {
|
||||
if (!isDragging.value)
|
||||
return
|
||||
|
||||
isDragging.value = false
|
||||
window.removeEventListener('mousemove', handleMouseMove)
|
||||
window.removeEventListener('mouseup', handleMouseUp)
|
||||
}
|
||||
|
||||
function handleMouseLeave(event: MouseEvent) {
|
||||
if (!isDragging.value)
|
||||
return
|
||||
handleMouseUp(event)
|
||||
}
|
||||
// Lifecycle hooks
|
||||
onUnmounted(() => {
|
||||
window.removeEventListener('mousemove', handleMouseMove)
|
||||
window.removeEventListener('mouseup', handleMouseUp)
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -134,15 +108,12 @@ function handleMouseLeave(event: MouseEvent) {
|
||||
:class="{ disabled }"
|
||||
bg="[#e6e1fc] dark:[#676085]" touch-action-none relative inline-block w-full cursor-ew-resize rounded-sm
|
||||
@mousedown="handleMouseDown"
|
||||
@mousemove="handleMouseMove"
|
||||
@mouseup="handleMouseUp"
|
||||
@mouseleave="handleMouseLeave"
|
||||
>
|
||||
<span :style="sliderStyle" bg="[#cabeff] dark:[#4e34b9]" relative block rounded-sm h="[14px]" />
|
||||
<span
|
||||
role="slider"
|
||||
class="slider-thumb"
|
||||
:style="{ left: `${valueToPercent(currentValue, min, max)}%` }"
|
||||
:style="{ left: `${((currentValue - min) / (max - min)) * 100}%` }"
|
||||
absolute rounded-sm w="[1px]" h="[14px]" bg="zinc-100 dark:zinc-400" top="50%" transform="translate-x-[50%] translate-y-[-50%]"
|
||||
/>
|
||||
</span>
|
||||
|
||||
@@ -37,7 +37,7 @@ const vrmModelPositionZ = ref(-0.3)
|
||||
<label w-full flex items-center gap-2>
|
||||
<DataGuiRange v-model="cameraPositionX" :min="-10" :max="10" :step="0.01" />
|
||||
</label>
|
||||
<div>
|
||||
<div text-right>
|
||||
<span>{{ cameraPositionX }}</span>
|
||||
</div>
|
||||
|
||||
@@ -47,7 +47,7 @@ const vrmModelPositionZ = ref(-0.3)
|
||||
<label w-full flex items-center gap-2>
|
||||
<DataGuiRange v-model="cameraPositionY" :min="-10" :max="10" :step="0.01" />
|
||||
</label>
|
||||
<div>
|
||||
<div text-right>
|
||||
<span>{{ cameraPositionY }}</span>
|
||||
</div>
|
||||
|
||||
@@ -57,7 +57,7 @@ const vrmModelPositionZ = ref(-0.3)
|
||||
<label w-full flex items-center gap-2>
|
||||
<DataGuiRange v-model="cameraPositionZ" :min="-10" :max="10" :step="0.01" />
|
||||
</label>
|
||||
<div>
|
||||
<div text-right>
|
||||
<span>{{ cameraPositionZ }}</span>
|
||||
</div>
|
||||
</div>
|
||||
@@ -74,7 +74,7 @@ const vrmModelPositionZ = ref(-0.3)
|
||||
<label w-full flex items-center gap-2>
|
||||
<DataGuiRange v-model="vrmModelPositionX" :min="-10" :max="10" :step="0.01" />
|
||||
</label>
|
||||
<div>
|
||||
<div text-right>
|
||||
<span>{{ vrmModelPositionX }}</span>
|
||||
</div>
|
||||
|
||||
@@ -84,7 +84,7 @@ const vrmModelPositionZ = ref(-0.3)
|
||||
<label w-full flex items-center gap-2>
|
||||
<DataGuiRange v-model="vrmModelPositionY" :min="-10" :max="10" :step="0.01" />
|
||||
</label>
|
||||
<div>
|
||||
<div text-right>
|
||||
<span>{{ vrmModelPositionY }}</span>
|
||||
</div>
|
||||
|
||||
@@ -94,7 +94,7 @@ const vrmModelPositionZ = ref(-0.3)
|
||||
<label w-full flex items-center gap-2>
|
||||
<DataGuiRange v-model="vrmModelPositionZ" :min="-10" :max="10" :step="0.01" />
|
||||
</label>
|
||||
<div>
|
||||
<div text-right>
|
||||
<span>{{ vrmModelPositionZ }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user