使用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>
相关推荐
Cobyte2 分钟前
23.Vue Vapor 组件 attrs 的实现
前端·javascript·vue.js
前端H32 分钟前
生成式 UI 实战:AI 如何重塑前端界面
前端·人工智能·ui
懂懂tty40 分钟前
Web前端性能指标
前端
绝世唐门三哥1 小时前
vue3中页面返回时刷新首页的处理方案
开发语言·前端·javascript
东方小月1 小时前
agent-skills:把资深工程师的‘工作流’,装进你的AI编程里
前端·架构·代码规范
Maynor9961 小时前
AI Coding 零基础实战教程|第五部分:完整项目案例实操
java·前端·人工智能·claude code·ai coding
binbin_522 小时前
React 井字棋教程:用一个小游戏理解组件和状态
前端·react.js·前端框架
梦曦i2 小时前
uni-router新推useUniEventChannel,彻底解决页面通信难题
前端·uni-app
柯克七七2 小时前
给老项目加了 TypeScript,本来只想自己爽,结果全公司代码审查标准被我抬高了
前端·vue.js·typescript
yuanlaile3 小时前
前端转 Flutter 自学完整规划,避开跨栈思维转换误区
前端·flutter·flutter跨平台开发