使用vue3实现简单的滑块组件,实现了基本的滑块功能和交互行为。

滑块组件主要是利用鼠标事件,让滑块跟着鼠标跟着x轴动,效果如下。

创建了一个名为 CustomSlider 的自定义滑块组件。它接受 minmaxstep 作为 props,用于设置滑块的取值范围和步长。使用 emits 属性声明了一个名为 update:modelValue 的自定义事件,用于向父组件发送滑块值的更新。

yaml 复制代码
name: 'CustomSlider',
props: { min: { type: Number, default: 0 }, 
    max: { type: Number, default: 100 }, 
    step: { type: Number, default: 1 } },
emits: ['update:modelValue'],

setup 函数中,我们使用了 Vue 3 的组合式 API 来定义滑块组件的逻辑。我们使用了 ref 来创建了 thumbPositionisDragging 两个响应式变量。thumbPosition 用于控制滑块的位置,isDragging 用于标记是否正在拖动滑块。

通过计算属性 trackWidth,我们根据最小值和最大值计算出滑块轨道的宽度,用于动态设置样式。

ini 复制代码
   const thumbPosition = ref('0%');
    const isDragging = ref(false);

    const trackWidth = computed(() => {
      const range = props.max - props.min;
      return `${(100 * range) / (props.max - props.min)}%`;
    });

startDrag 方法中,我们监听了滑块的鼠标按下事件,并在按下时开始拖动操作。在 handleDrag 方法中,我们根据鼠标位置计算出滑块的值,并通过 emit 方法触发 update:modelValue 事件,将滑块的值发送给父组件。

ini 复制代码
 const startDrag = (event) => {
      isDragging.value = true;
      document.addEventListener('mousemove', handleDrag);
      document.addEventListener('mouseup', stopDrag);
    };

    const handleDrag = (event) => {
      if (isDragging.value) {
        const sliderWidth = event.target.parentNode.offsetWidth;
        const offsetX = event.pageX - event.target.parentNode.offsetLeft;
        const percentage = (offsetX / sliderWidth) * 100;
        const value = (percentage * (props.max - props.min)) / 100 + props.min;
        const snappedValue = Math.round(value / props.step) * props.step;
        const clampedValue = Math.max(props.min, Math.min(props.max, snappedValue));
        thumbPosition.value = `${((clampedValue - props.min) / (props.max - props.min)) * 100}%`;
        emit('update:modelValue', clampedValue);
      }
    };

    const stopDrag = () => {
      isDragging.value = false;
      document.removeEventListener('mousemove', handleDrag);
      document.removeEventListener('mouseup', stopDrag);
    };

onMountedonUnmounted 钩子中,我们分别添加和移除了监听鼠标抬起事件的事件处理函数,以确保在组件销毁时正确清理事件监听器。

javascript 复制代码
  onMounted(() => {
      document.addEventListener('mouseup', stopDrag);
    });

    onUnmounted(() => {
      document.removeEventListener('mouseup', stopDrag);
    });

完整代码如下:

xml 复制代码
<template>
  <div class="slider">
    <div class="track" :style="{ width: trackWidth }"></div>
    <div class="thumb" :style="{ left: thumbPosition }" @mousedown="startDrag"></div>
  </div>
</template>

<script>
import { ref, computed, onMounted, onUnmounted } from 'vue';

export default {
  name: 'CustomSlider',
  props: {
    min: {
      type: Number,
      default: 0
    },
    max: {
      type: Number,
      default: 100
    },
    step: {
      type: Number,
      default: 1
    }
  },
  emits: ['update:modelValue'],
  setup(props, { emit }) {
    const thumbPosition = ref('0%');
    const isDragging = ref(false);

    const trackWidth = computed(() => {
      const range = props.max - props.min;
      return `${(100 * range) / (props.max - props.min)}%`;
    });

    const startDrag = (event) => {
      isDragging.value = true;
      document.addEventListener('mousemove', handleDrag);
      document.addEventListener('mouseup', stopDrag);
    };

    const handleDrag = (event) => {
      if (isDragging.value) {
        const sliderWidth = event.target.parentNode.offsetWidth;
        const offsetX = event.pageX - event.target.parentNode.offsetLeft;
        const percentage = (offsetX / sliderWidth) * 100;
        const value = (percentage * (props.max - props.min)) / 100 + props.min;
        const snappedValue = Math.round(value / props.step) * props.step;
        const clampedValue = Math.max(props.min, Math.min(props.max, snappedValue));
        thumbPosition.value = `${((clampedValue - props.min) / (props.max - props.min)) * 100}%`;
        emit('update:modelValue', clampedValue);
      }
    };

    const stopDrag = () => {
      isDragging.value = false;
      document.removeEventListener('mousemove', handleDrag);
      document.removeEventListener('mouseup', stopDrag);
    };

    onMounted(() => {
      document.addEventListener('mouseup', stopDrag);
    });

    onUnmounted(() => {
      document.removeEventListener('mouseup', stopDrag);
    });

    return {
      thumbPosition,
      trackWidth,
      startDrag
    };
  }
};
</script>

<style>
.slider {
  position: relative;
  width: 100%;
  height: 10px;
  background-color: #ccc;
}

.track {
  position: absolute;
  top: 0;
  left: 0;
  height: 100%;
  background-color: #409eff;
}

.thumb {
  position: absolute;
  top: -5px;
  left: 0;
  width: 20px;
  height: 20px;
  border-radius: 50%;
  background-color: #409eff;
  cursor: pointer;
}
</style>
相关推荐
excel1 小时前
HLS TS 文件损坏的元凶:Git 提交与拉取
前端
Aphasia3111 小时前
https连接传输流程
前端·面试
徐小夕1 小时前
万字长文!千万级文档 RAG 知识库系统落地实践
前端·算法·github
梦梦代码精1 小时前
2026年PHP开源商城系统实测对比:架构、多商户、商用授权,谁才是真·省心?
vue.js·docker·架构·开源·代码规范
threelab1 小时前
Three.js 物理模拟着色器 | 三维可视化 / AI 提示词
开发语言·前端·javascript·人工智能·3d·着色器
kyriewen2 小时前
CSS Container Queries:彻底告别 @media 写到手软,附 5 个真实布局案例
前端·css·面试
小小小小宇3 小时前
OpenMemory MCP
前端
和平宇宙4 小时前
AI笔记005. hermes-DeepSeek V4 Pro, 128K上下文引发的探索
前端·人工智能·笔记
IT_陈寒4 小时前
Redis持久化这个坑,我爬了一整天才出来
前端·人工智能·后端