Vue双向滑动输入条(Slider)

2022-06-17 10:15:34

效果图如下:

组件支持传入最小值(min)最大值(max)的范围,并可设置初始最小值(initialMin)和初始最大值(initialMax)

通过鼠标拖动滑块来调整最大最小值,或者直接点击输入条上某个位置,直接切换数值

①创建滑动输入条组件Slider.vue

<template>
  <div class="m-slider" ref="slider">
    <div class="u-slider-rail" @click="onClickPoint"></div>
    <div class="u-slider-track" @click="onClickPoint" :style="`left: ${left}px; right: auto; width: ${right - left}px;`"></div>
    <div class="u-slider-handle" ref="left" @mousedown="onLeftMouseDown" :style="`left: ${left}px; right: auto; transform: translateX(-50%);`"></div>
    <div class="u-slider-handle" ref="right" @mousedown="onRightMouseDown" :style="`left: ${right}px; right: auto; transform: translateX(-50%);`"></div>
  </div>
</template>
<script>
export default {
  name: 'Slider',
  props: {
    min: { // 滑动输入条最小值
      type: Number,
      default: 0
    },
    max: { // 滑动输入条最大值
      type: Number,
      default: 100
    },
    initialMin: { // 滑动输入条初始最小值,默认在最左侧
      type: Number,
      default: 0
    },
    initialMax: { // 滑动输入条初始最大值,默认在最右侧
      type: Number,
      default: 100
    }
  },
  data () {
    return {
      left: '', // 左滑块距离滑动条左端的距离
      right: '', // 右滑动距离滑动条左端的距离
      width: '' // 滑动输入条在页面中的宽度
    }
  },
  mounted () {
    this.width = this.$refs.slider.clientWidth
    this.left = this.initialMin * this.width / this.max
    this.right = this.initialMax * this.width / this.max
  },
  computed: {
    low () {
      return Math.round(this.left / this.width * this.max)
    },
    high () {
      return Math.round(this.right / this.width * this.max)
    }
  },
  watch: {
    low (to, from) {
      this.$emit('lowChange', to) // 左滑块对应数字回调
    },
    high (to, from) {
      this.$emit('highChange', to) // 右滑块对应数字回调
    }
  },
  methods: {
    onClickPoint (e) { // 点击滑动条,移动滑块
      var offsetLeft = this.$refs.slider.offsetLeft // 滑动条左端距离屏幕左侧的距离
      var moveX = e.clientX - offsetLeft
      if (moveX <= this.left) {
        this.left = moveX
      } else if (this.moveX >= this.right) {
        this.right = this.moveX
      } else {
        if ((moveX - this.left) < (this.right - moveX)) {
          this.left = moveX
        } else {
          this.right = moveX
        }
      }
    },
    onLeftMouseDown (e) { // 在滚动条上拖动左滑块
      // 滑块水平方向上距离屏幕左端的距离,减去滑块距离滑动条最左端的距离:e.clientX - this.$refs.left.offsetLeft
      var offsetLeft = this.$refs.slider.offsetLeft // 滑动条左端距离屏幕左侧的距离
      document.onmousemove = (e) => {
        var moveX = e.clientX - offsetLeft
        if (moveX < 0) {
          this.left = 0
        } else if (moveX >= this.right) {
          this.left = this.right
        } else {
          this.left = moveX
        }
      }
      document.onmouseup = (e) => {
        document.onmousemove = null
        document.onmouseup = null
      }
    },
    onRightMouseDown (e) { // 在滚动条上拖动右滑块
      /// 滑块水平方向上距离屏幕左端的距离,减去滑块距离滑动条最左端的距离:e.clientX - this.$refs.left.offsetLeft
      var offsetLeft = this.$refs.slider.offsetLeft // 滑动条左端距离屏幕左侧的距离
      console.log('offsetLeft:', offsetLeft)
      document.onmousemove = (e) => {
        var moveX = e.clientX - offsetLeft
        if (moveX > this.width) {
          this.right = this.width
        } else if (moveX <= this.left) {
          this.right = this.left
        } else {
          this.right = moveX
        }
      }
      document.onmouseup = (e) => {
        document.onmousemove = null
        document.onmouseup = null
      }
    }
  }
}
</script>
<style lang="less" scoped>
.m-slider {
  height: 4px;
  position: relative;
  cursor: pointer;
  touch-action: none;
  &:hover {
    .u-slider-rail { // 灰色未选择滑动条背景色
      background: #E3E3E3;
    }
    .u-slider-track { // 蓝色已选择滑动条背景色
      background: #1890ff;
    }
  }
  .u-slider-rail { // 灰色未选择滑动条背景色
    position: absolute;
    z-index: 99;
    height: 4px;
    width: 100%;
    background: #f5f5f5;
    border-radius: 2px;
    transition: background .3s;
  }
  .u-slider-track { // 蓝色已选择滑动条背景色
    background: #91d5ff;
    border-radius: 4px;
    position: absolute;
    z-index: 99;
    height: 4px;
    cursor: pointer;
    transition: background .3s;
  }
  .u-slider-handle { // 滑块
    position: absolute;
    z-index: 999;
    width: 14px;
    height: 14px;
    margin-top: -7px;
    box-shadow: 0;
    background: #fff;
    border: 2px solid #91d5ff;
    border-radius: 50%;
    cursor: pointer;
    transition: border-color .3s,box-shadow .6s,transform .3s cubic-bezier(.18,.89,.32,1.28);
    &:hover {
      border-color: #1890ff;
    }
  }
}
</style>

②在要使用的页面引入

<div class="m-num">
  <p class="u-num">{{ low }}</p>
  <p class="u-num">{{ high }}</p>
</div>
<div class="slider">
  <Slider
    :min="0"
	:max="100"
	:initialMin="low"
	:initialMax="high"
	@lowChange="lowChange"
	@highChange="highChange"
	/>
</div>
import Slider from '@/components/Slider'
components: {
  Slider
}
data () {
  return {
    low: 20,
    high: 80
  }
}
lowChange (val) {
  this.low = val
  console.log('lowChange:', val)
},
highChange (val) {
  this.high = val
  console.log('highChange:', val)
},
.m-num {
  min-width: 1200px;
  width: 1200px;
  margin: 0 auto;
  display: flex;
  justify-content: space-between;
  .u-num {
    color: #333;
    font-size: 24px;
  }
}
.slider {
  min-width: 1200px;
  width: 1200px;
  margin: 30px auto 120px;
}
  • 作者:theMuseCatcher
  • 原文链接:https://blog.csdn.net/Dandrose/article/details/121124338
    更新时间:2022-06-17 10:15:34