vue3实现一个可拖拽滑动条的封装

滑块组件代码:

<template>
  <div class="sliderBox">
    <div class="signBox flex align-center">
      <div class="sign hasmove flex align-center space-between column" :style="{left: sliderLeft + 'px'}">
        <div>{{ sliderVal }}</div>
        <div class="circular" @mousedown.prevent="handleMousedown($event, sliderVal)"></div>
      </div>
    </div>
    <div class="lineBox flex align-center">
      <div class="line flex-1" v-for="item in data.length - 1" :key="item"></div>
    </div>
    <div class="scaleBox flex space-between align-center">
      <div class="scale" v-for="item in data" :key="item">{{ item }}</div>
    </div>
  </div>
</template>

<script lang="ts" setup name="Slider">
import { ref, watch } from 'vue'

const emit = defineEmits();
const data = [200, 1000, 2000, 3000, 4000, 14000, 26000]

const props = defineProps({
  modelValue: {
    type: Number,
    default: ''
  }
})

const sliderLeft = ref(0)
const sliderVal = ref(props.modelValue)

// 根据值计算当前值的位置
const handlePosition = (val) => {
  let left = 0
  for (let i = 0; i < data.length; i++) {
    if (val >= data[i]) {
      if (val >= data[i + 1]) left += 100
      if (val < data[i + 1]) {
        const num = val - data[i]
        left += num / (data[i + 1] - data[i]) * 100
      }
    }
  }
  return left
}
sliderLeft.value = handlePosition(sliderVal.value)

// 根据鼠标移动距离获取对应数值
const handleGetVal = (num) => {
  let val = 0
  const step = Math.floor(num / 100)
  const range = data.slice(step, step + 2)
  val = num === 600 ? 26000 : range[0] + (range[1] - range[0]) * (num % 100 / 100)
  return val
}

watch(() => props.modelValue, (newValue, oldValue) => {
  sliderVal.value = newValue
  sliderLeft.value = handlePosition(newValue)
}, { immediate: true })

let startX = 0
let left = 0
const containerWidth = 600

// 鼠标按下
const handleMousedown = (event, num) => {
  startX = event.clientX;
  left = sliderLeft.value;
  document.addEventListener('mousemove', handleDragstart);  
  document.addEventListener('mouseup', handleDragend);
}

// 鼠标开始移动
const handleDragstart = (event) => {
  const diffX = event.clientX - startX;
  sliderLeft.value = Math.min(Math.max(0, left + diffX), containerWidth);
  sliderVal.value = handleGetVal(sliderLeft.value)
}

// 鼠标移动结束
const handleDragend = (event) => {
  document.removeEventListener('mousemove', handleDragstart);
  document.removeEventListener('mouseup', handleDragend);
  emit('update:modelValue', sliderVal.value)
}
</script>

<style lang="scss" scoped>
@import "@/styles/theme.scss";
.sliderBox {
  padding: 40px 120px;
  width: 880px;
  box-sizing: border-box;
  user-select: none;
  .lineBox {
    width: 600px;
    border-radius: 12px;
    margin: 0 auto;
    background: #090D0F;
    .line {
      height: 12px;
    }
  }
  .scaleBox {
    margin-top: 20px;
    .scale {
      width: 40px;
      text-align: center;
    }
  }
  .signBox {
    width: 600px;
    height: 60px;
    position: relative;
    left: 10px;
    .hasmove {
      font-weight: 600;
      z-index: 99;
    }
    .sign {
      position: absolute;
      top: 15px;
      height: 60px;
      width: 18px;
      color: $primary-color;
      cursor: pointer;
      .circular {
        width: 18px;
        height: 28px;
        border-radius: 18px;
        background: $primary-color;
      }
    }
  }
}
</style>

父组件中使用:

<template>
  <Slider v-model="sliderNum" />
</template>

<script setup lang="ts">
import { ref, watch } from "vue";
import Slider from "@/components/Slider/index.vue";

const sliderNum = ref(2000);

// 测试
watch(() => sliderNum.value, (newValue, oldValue) => {
  console.log('sliderNum', newValue, oldValue);
})

setTimeout(() => {
  sliderNum.value = 4000
}, 2000)

</script>
<style scoped lang="scss">
</style>

实现效果:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值