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