为了实现鼠标悬停在滑块区域时通过滚轮(Mouse Wheel)改变数值,我们需要做以下两件事:
监听滚轮事件 (wheel):在包裹滑块的容器上监听该事件。
计算新数值:根据滚动的方向(向上或向下)以及设定的 step(步长)来增加或减少数值,同时确保不超出 min 和 max 的范围,并处理好精度 (precision)。
下面是修改后的完整代码。我使用了 Vue 的事件修饰符 @wheel.prevent,这样当你在滑块上滚动时,页面不会跟着滚动,体验会更好。
javascript
<template>
<div class="slider-container">
<div class="slider-header">
<span class="slider-label">{{ label }}</span>
<el-input-number
v-if="showInput"
v-model="inputValue"
:min="min"
:max="max"
:step="step"
:precision="precision"
:size="size"
controls-position="right"
class="value-input"
@change="handleInputChange"
/>
<span v-else class="slider-value"> {{ formattedValue }}{{ unit }} </span>
</div>
<div class="slider-with-labels" @wheel.prevent="handleWheel">
<span class="min-label" v-if="showLimitLabels">{{ min }}{{ unit }}</span>
<el-slider
:model-value="modelValue"
:min="min"
:max="max"
:step="step"
:show-tooltip="showTooltip"
:format-tooltip="formatTooltip"
:size="size"
:disabled="disabled"
@update:model-value="handleInput"
@change="handleChange"
class="slider"
/>
<span class="max-label" v-if="showLimitLabels">{{ max }}{{ unit }}</span>
</div>
</div>
</template>
<script setup>
import { computed } from "vue";
const props = defineProps({
modelValue: {
type: Number,
required: true,
},
label: {
type: String,
default: "",
},
min: {
type: Number,
default: 0,
},
max: {
type: Number,
default: 100,
},
step: {
type: Number,
default: 1,
validator: (value) => value > 0,
},
precision: { // elInputNumber数值精度
type: Number,
default: 0,
validator: (value) => value >= 0,
},
unit: {
type: String,
default: "mm",
},
size: {
type: String,
default: "small",
validator: (value) => ["small", "medium", "large"].includes(value),
},
showTooltip: {
type: Boolean,
default: false,
},
showInput: {
type: Boolean,
default: false,
},
disabled: {
type: Boolean,
default: false,
},
showLimitLabels: {
type: Boolean,
default: true,
},
});
const emit = defineEmits(["update:modelValue", "change"]);
const inputValue = computed({
get: () => props.modelValue,
set: (value) => emit("update:modelValue", value),
});
const formattedValue = computed(() => {
return props.precision > 0 ? props.modelValue.toFixed(props.precision) : props.modelValue;
});
const formatTooltip = (value) => {
return props.precision > 0 ? value.toFixed(props.precision) + props.unit : value + props.unit;
};
const handleInput = (value) => {
emit("update:modelValue", value);
};
const handleChange = (value) => {
emit("change", value);
};
const handleInputChange = (value) => {
emit("update:modelValue", value);
emit("change", value);
};
// 👇👇👇 handleWheel
const handleWheel = (event) => {
// 如果禁用了组件,则不处理
if (props.disabled) return;
// deltaY < 0 表示向上滚动(数值增加),> 0 表示向下滚动(数值减少)
const isIncrease = event.deltaY < 0;
// 根据 step 计算变化量
const changeAmount = isIncrease ? props.step : -props.step;
// 计算新值
let newValue = props.modelValue + changeAmount;
// 边界检查:确保不超出 min 和 max
if (newValue > props.max) newValue = props.max;
if (newValue < props.min) newValue = props.min;
//计算 step 本身的小数位数
const stepString = props.step.toString()
const dotIndex = stepString.indexOf('.')
let stepPrecision = 0
if (dotIndex !== -1) {
stepPrecision = stepString.length - dotIndex - 1
}
//取 props.precision 和 stepPrecision 中的较大值
const finalPrecision = Math.max(props.precision, stepPrecision)
newValue = parseFloat(newValue.toFixed(finalPrecision))
// 如果数值确实发生了变化,则触发更新
if (newValue !== props.modelValue) {
emit("update:modelValue", newValue);
// 同时也触发 change 事件,保持与 el-slider 行为一致
emit("change", newValue);
}
};
</script>
在 .slider-with-labels div 上添加了 @wheel.prevent="handleWheel"。
之所以加在.slider-with-labels而不是直接加在<el-slider>上,是因为 el-slider 的实际 DOM 结构比较复杂,且有时鼠标悬停在轨道边缘(Label 附近)时用户也期望能滚动。加在父容器上能增大"热区",体验更流畅。