zoom-slider双端点缩放滑块vue3组件

这是一个使用Vue.js编写的组件,演示了如何实现区域内容的缩放以及双端点滑块的基本功能。代码示例展示了如何添加、删除项目,并通过滑块调整显示内容的范围。该组件可用于实际项目中,可直接复制并进行改造以满足具体需求。
摘要由CSDN通过智能技术生成

单纯实现了区域内容缩放及双端点滑块的基本功能, 实际项目需要用到的话可以直接复制代码去改造

 

demo效果: 

demo调用:

<template>
  <button class="btn" @click="toAddOne">添加1项</button>
  <zoom-slider class="tp" :dataList="dataList" />
</template>

<script>
import { ref } from 'vue'

import ZoomSlider from './components/ZoomSlider.vue'

export default {
  name: 'App',
  components: {
    ZoomSlider
  },
  setup () {
    const arr = []
    for (let i = 0; i < 10; i ++) {
      arr.push(i)
    }

    const dataList = ref(arr)

    const toAddOne = () => {
      dataList.value.push(dataList.value.length)
    }

    return {
      dataList,

      toAddOne,
    }
  },
}
</script>

<style>
.btn { margin-left: 100px; margin-top: 30px; }
.tp { margin-left: 100px; margin-top: 30px; }
</style>

功能代码实现(ZoomSlider.vue):

<template>
  <div class="container" ref="container" :style="{ width: `${width}px`, height: `${height}px` }">
    <div class="mainBox">
      <div class="mainContent" :style="picStyle">
        <div
          :class="{
            itemBox: true,
            itemBox1: val % 2 === 0,
          }"
          :style="{ width: itemWidth }"
          v-for="val in dataList" :key="val"
        >
          {{ val + 1 }}
        </div>
      </div>
    </div>
    <div class="sliderBox">
      <div class="lineBox" ref="lineBox" :style="lineStyle" @mousedown="lineDown">
        <div class="sLeft" @mousedown.stop="leftDown"></div>
        <div class="sRight" @mousedown.stop="rightDown"></div>
      </div>
    </div>
  </div>
</template>

<script>
import { defineComponent, ref, computed, onBeforeUnmount } from 'vue'

/**
 * 给一个dom元素绑定事件 
 */
const _BD = (dom, eventName, fn, option = false) => {
  if(dom.addEventListener){
    dom.addEventListener(eventName, fn, option)
  }else if (dom.attachEvent){
    dom.attachEvent('on' + eventName, fn)
  }else {
    dom['on' + eventName] = fn
  }
}

/**
 * 解除一个dom元素的绑定事件 
 */
const _unBD = (dom, eventName, functionName) => {
  if (dom.attachEvent) {
    dom.detachEvent('on' + eventName, functionName)
  } else {
    dom.removeEventListener(eventName, functionName, false)
  }
}

/**
 * 获取元素到页面顶端的offset距离(计算结果已消除所有滚动条距离的影响)
 */
const _getOffset = el => {
  let offset = { x: 0, y: 0, }
  let target = el
  let parent = null
  while (parent = target.offsetParent) {
    offset.x += target.offsetLeft
    offset.y += target.offsetTop
    target = parent
    if (parent.tagName.toLowerCase() === 'body') parent = null
  }
  return offset
}

export default defineComponent({
  props: {
    width: {
      type: Number,
      default: 500,
    },
    height: {
      type: Number,
      default: 100,
    },
    dataList: {
      type: Array,
      default () {
        return []
      },
    },
  },
  setup (props, { expose }) {
    const container = ref(null)
    const lineBox = ref(null)
    const picStyle = ref({ width: '100%', left: '0px' })
    const lineStyle = ref({ width: `${props.width}px`, transformOrigin: 'left' })

    const itemWidth = computed(() => {
      return !!props.dataList.length ? `${100 / props.dataList.length}%` : 0
    })

    let downState = null
    let originX = 0
    let originWidth = 0
    let originLeft = 0
    let originRight = 0
    let clientOffset = 0

    const handleMove = ev => {
      if (downState == 'left') {
        const max = lineBox.value.offsetLeft + lineBox.value.offsetWidth
        let width = originWidth - (ev.pageX - originX - clientOffset)

        if (width < 0) {
          width = 0
        } else if (width > max) {
          width = max
        }

        lineStyle.value.width = `${width}px`

        const result = {
          start: lineBox.value.offsetLeft,
          end: lineBox.value.offsetLeft + lineBox.value.offsetWidth,
          pecentStart: lineBox.value.offsetLeft / props.width,
          pecentEnd: (lineBox.value.offsetLeft + lineBox.value.offsetWidth) / props.width,
          ratio: props.width / lineBox.value.offsetWidth,
        }
        // console.log(result)

        const json = {
          width: `${result.ratio * 100}%`,
          right: `-${result.ratio * props.width * (1 - result.pecentEnd)}px`,
        }

        picStyle.value = json
      } else if (downState == 'right') {
        const max = container.value.offsetWidth - lineBox.value.offsetLeft
        let width = originWidth + (ev.pageX - originX + 7 - clientOffset) // 这里的数值7代表的是可拖动的端点元素的直径

        if (width < 0) {
          width = 0
        } else if (width > max) {
          width = max
        }

        lineStyle.value.width = `${width}px`

        const result = {
          start: lineBox.value.offsetLeft,
          end: lineBox.value.offsetLeft + lineBox.value.offsetWidth,
          pecentStart: lineBox.value.offsetLeft / props.width,
          pecentEnd: (lineBox.value.offsetLeft + lineBox.value.offsetWidth) / props.width,
          ratio: props.width / lineBox.value.offsetWidth,
        }
        // console.log(result)

        const json = {
          width: `${result.ratio * 100}%`,
          left: `-${result.ratio * props.width * result.pecentStart}px`,
        }

        picStyle.value = json
      } else if (downState == 'line') {
        if (lineStyle.value.right === undefined) {
          const max = props.width - lineBox.value.offsetWidth
          let left = originLeft + (ev.pageX - originX)

          if (left < 0) {
            left = 0
          } else if (left > max) {
            left = max
          }

          lineStyle.value.left = `${left}px`

          const ratio = props.width / lineBox.value.offsetWidth

          picStyle.value = {
            width: `${ratio * 100}%`,
            left: `-${ratio * left}px`,
          }
        } else {
          const max = props.width - lineBox.value.offsetWidth
          let right = originRight - (ev.pageX - originX)

          if (right < 0) {
            right = 0
          } else if (right > max) {
            right = max
          }

          lineStyle.value.right = `${right}px`

          const ratio = props.width / lineBox.value.offsetWidth

          picStyle.value = {
            width: `${ratio * 100}%`,
            right: `-${ratio * (props.width - lineBox.value.offsetLeft - lineBox.value.offsetWidth)}px`,
          }
        }
      }
    }

    const handleUp = () => {
      downState = null
      _unBD(document, 'mousemove', handleMove)
      _unBD(document, 'mouseup', handleUp)
    }

    const leftDown = ev => {
      downState = 'left'
      originX = _getOffset(container.value).x + lineBox.value.offsetLeft
      originWidth = lineBox.value.offsetWidth
      clientOffset = ev.offsetX
      _BD(document, 'mousemove', handleMove)
      _BD(document, 'mouseup', handleUp)

      const json = {
        width: lineStyle.value.width,
        transformOrigin: 'right',
        right: `${props.width - lineBox.value.offsetWidth - lineBox.value.offsetLeft}px`,
      }

      lineStyle.value = json
    }

    const rightDown = ev => {
      downState = 'right'
      originX = _getOffset(container.value).x + lineBox.value.offsetWidth + lineBox.value.offsetLeft
      originWidth = lineBox.value.offsetWidth
      clientOffset = ev.offsetX
      _BD(document, 'mousemove', handleMove)
      _BD(document, 'mouseup', handleUp)

      const json = {
        width: lineStyle.value.width,
        transformOrigin: 'left',
        left: `${lineBox.value.offsetLeft}px`,
      }

      lineStyle.value = json
    }

    const lineDown = ev => {
      downState = 'line'
      clientOffset = ev.offsetX
      originX = _getOffset(container.value).x + lineBox.value.offsetLeft + clientOffset
      if (lineStyle.value.right === undefined) {
        originLeft = lineBox.value.offsetLeft
      } else {
        originRight = props.width - lineBox.value.offsetWidth - lineBox.value.offsetLeft
      }
      _BD(document, 'mousemove', handleMove)
      _BD(document, 'mouseup', handleUp)
    }

    expose({})

    onBeforeUnmount(() => {
      _unBD(document, 'mousemove', handleMove)
      _unBD(document, 'mouseup', handleUp)
    })

    return {
      container,
      lineBox,
      picStyle,
      itemWidth,
      lineStyle,

      leftDown,
      rightDown,
      lineDown,
    }
  }
})
</script>

<style scoped>
.container { user-select: none; -webkit-user-select: none; -moz-user-select: none; -ms-user-select: none; position: relative; }

.mainBox { width: 100%; height: calc(100% - 5px); background: rgba(0,0,0,.05); overflow: hidden; position: relative; }
.mainContent { height: 100%; position: absolute; top: 0; background: red; }
.itemBox { height: 100%; display: inline-block; text-align: center; line-height: 100px; background: rgb(233, 230, 180); }
.itemBox1 { background: rgb(228, 174, 112); }

.sliderBox { width: 100%; height: 5px; background: rgba(0,0,0,.1); position: absolute; left: 0; bottom: 0; }
.lineBox { min-width: 20px; height: 7px; border-radius: 3px; background: rgba(156, 245, 252, .4); position: absolute; top: -1px; }
.sLeft { width: 7px; height: 7px; border-radius: 50%; background: rgb(109, 107, 204); cursor: pointer; position: absolute; left: 0; top: 0; }
.sRight { width: 7px; height: 7px; border-radius: 50%; background: rgb(109, 107, 204); cursor: pointer; position: absolute; right: 0; top: 0; }
</style>

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值