Vue3+Vite双手柄小时范围滑块组件报错求助
解决Vue3双手柄小时范围滑块拖动报错问题
错误原因
拖动手柄时触发的mousemove事件中,event.target并非你定义的<input type="range">元素,导致event.target.value为undefined,进而访问value[0]抛出Cannot read properties of undefined错误。同时原逻辑混用了原生range输入和手柄拖动的事件处理,导致逻辑冲突。
解决思路与修正代码
1. 核心修正点
- 拆分事件处理:将原生range输入的处理与手柄拖动的处理分开,避免事件对象混用。
- 基于鼠标位置计算手柄值:通过滑块容器的宽度和鼠标偏移量,计算手柄对应的百分比与小时值。
- 确保响应式更新:修改数组时创建新数组,保证Vue能检测到数据变化。
- 移除冗余的原生range输入(若不需要单滑块控制)。
2. 完整修正代码
模板部分
<template> <div class="range-container"> <div class="range-slider" ref="sliderContainer"> <!-- 滑块轨道 --> <div class="track" :style="{ left: leftHandle + '%', right: (100 - rightHandle) + '%' }"></div> <!-- 左手柄 --> <div class="handle" :style="{ left: leftHandle + '%' }" @mousedown="startDrag(1)"> {{ formatHour(rangeValue[0]) }} </div> <!-- 右手柄 --> <div class="handle" :style="{ left: rightHandle + '%' }" @mousedown="startDrag(2)"> {{ formatHour(rangeValue[1]) }} </div> </div> </div> </template>
脚本部分
<script setup> import { ref, computed } from 'vue' const rangeValue = ref([8, 18]) const sliderContainer = ref(null) let draggingHandle = 0 let startX = 0 let startValue = 0 const leftHandle = computed(() => { return (rangeValue.value[0] / 24) * 100 }) const rightHandle = computed(() => { return (rangeValue.value[1] / 24) * 100 }) const startDrag = (handle) => { draggingHandle = handle startX = event.clientX // 记录当前手柄的初始值 startValue = handle === 1 ? rangeValue.value[0] : rangeValue.value[1] document.addEventListener('mousemove', handleDrag) document.addEventListener('mouseup', stopDrag) // 阻止默认行为避免选中文字 event.preventDefault() } const handleDrag = (event) => { if (!draggingHandle || !sliderContainer.value) return const containerRect = sliderContainer.value.getBoundingClientRect() // 计算鼠标相对于滑块容器的位置百分比 const percent = ((event.clientX - containerRect.left) / containerRect.width) * 100 // 转换为0-24的小时值 const hour = Math.round((percent / 100) * 24) // 限制范围并更新值 if (draggingHandle === 1) { const newLeft = Math.max(0, Math.min(hour, rangeValue.value[1] - 1)) rangeValue.value = [newLeft, rangeValue.value[1]] } else { const newRight = Math.max(rangeValue.value[0] + 1, Math.min(hour, 24)) rangeValue.value = [rangeValue.value[0], newRight] } } const stopDrag = () => { draggingHandle = 0 document.removeEventListener('mousemove', handleDrag) document.removeEventListener('mouseup', stopDrag) } const formatHour = (value) => { return `${value}:00` } </script>
样式部分(补充基础样式)
<style scoped> .range-container { width: 80%; margin: 40px auto; } .range-slider { position: relative; height: 6px; background: #e0e0e0; border-radius: 3px; cursor: pointer; } .track { position: absolute; top: 0; height: 100%; background: #42b983; border-radius: 3px; } .handle { position: absolute; top: -12px; width: 28px; height: 28px; margin-left: -14px; background: white; border: 2px solid #42b983; border-radius: 50%; display: flex; align-items: center; justify-content: center; cursor: grab; font-size: 12px; } .handle:active { cursor: grabbing; } </style>
关键说明
- 用
(hour / 24) * 100将0-24小时转换为滑块的百分比位置,确保手柄在容器内正确分布。 - 拖动时限制左右手柄的边界:左手柄不能超过右手柄,右手柄不能低于左手柄,且范围在0-24之间。
- 使用Vue3的
<script setup>语法,更符合Vue3的开发习惯,同时保证响应式更新。
内容的提问来源于stack exchange,提问作者MAHA OUEGHLANI
相关产品推荐
相关产品推荐

