<template>
<view class="slider-container" :style="containerStyle" @touchmove.prevent>
<view ref="sliderHandle" class="slider-handle" :style="handleStyle" @touchstart="onTouchStart"
@touchmove.stop="onTouchMove" @touchend="onTouchEnd"></view>
</view>
</template>
<script>
export default {
name: 'SliderControl',
props: {
mode: {
type: String,
default: 'horizontal'
},
width: {
type: [Number, String],
default: 220
},
height: {
type: [Number, String],
default: 60
}
},
data() {
return {
position: 70,
isDragging: false,
startTouch: null,
startPosition: null,
lastUpdate: 0,
lastEmittedValue: 0,
currentDelta: 0,
animationTimer: null
}
},
methods: {
onTouchStart(e) {
uni.vibrateShort({
success: () => {
console.log('短振动成功');
},
fail: (err) => {
console.error('短振动失败:', err);
}
});
e.stopPropagation()
if (this.animationTimer) {
clearTimeout(this.animationTimer)
this.animationTimer = null
}
const touch = e.changedTouches[0]
if (!touch) return
this.isDragging = true
this.startTouch = {
x: touch.pageX,
y: touch.pageY
}
this.startPosition = this.position
this.lastUpdate = Date.now()
this.currentDelta = 0
},
onTouchMove(e) {
if (!this.isDragging || !this.startTouch) return
e.stopPropagation()
const now = Date.now()
const timeDiff = now - this.lastUpdate
if (timeDiff < 16) return
this.lastUpdate = now
const touch = e.changedTouches[0]
if (!touch) return
const delta = this.mode === 'horizontal' ?
touch.pageX - this.startTouch.x :
touch.pageY - this.startTouch.y
this.currentDelta = this.currentDelta + (delta - this.currentDelta) * 0.9
let targetPos = this.startPosition + this.currentDelta * 1
targetPos = Math.max(0, Math.min(targetPos, 180))
if (Math.abs(targetPos - this.position) > 0.05) {
const smoothFactor = Math.min(0.08, timeDiff / 250)
this.position = this.position + (targetPos - this.position) * smoothFactor
}
const value = this.mode === 'horizontal' ?
(this.position - 70) / 70 :
(70 - this.position) / 70
const roundedValue = Math.round(value * 100) / 100
if (Math.abs(roundedValue - this.lastEmittedValue) >= 0.01) {
this.lastEmittedValue = roundedValue
this.$emit('change', {
value: Math.max(-1, Math.min(1, roundedValue))
})
}
},
onTouchEnd(e) {
if (!this.isDragging) return
e.stopPropagation()
this.isDragging = false
this.startTouch = null
this.startPosition = null
this.currentDelta = 0
const startPos = this.position
const startTime = Date.now()
const duration = 500
const easeOutQuart = t => 1 - Math.pow(1 - t, 4)
const animate = () => {
const elapsed = Date.now() - startTime
if (elapsed >= duration) {
this.position = 70
this.$emit('change', {
value: 0
})
this.animationTimer = null
return
}
const progress = elapsed / duration
const easeProgress = easeOutQuart(progress)
this.position = startPos + (70 - startPos) * easeProgress
this.animationTimer = setTimeout(animate, 16)
}
animate()
}
},
beforeDestroy() {
if (this.animationTimer) {
clearTimeout(this.animationTimer)
}
},
computed: {
containerStyle() {
return {
width: typeof this.width === 'number' ? `${this.width}px` : this.width,
height: typeof this.height === 'number' ? `${this.height}px` : this.height,
backgroundColor: '#000000',
borderRadius: '30px',
position: 'relative'
}
},
trackStyle() {
if (this.mode === 'horizontal') {
return {
position: 'absolute',
left: '20px',
right: '20px',
height: '2px',
top: '30%',
backgroundColor: 'rgba(255, 255, 255, 0.3)',
transform: 'translateY(-1px)'
}
} else {
return {
position: 'absolute',
top: '20px',
bottom: '20px',
width: '2px',
left: '40%',
backgroundColor: 'rgba(255, 255, 255, 0.3)',
transform: 'translateX(-1px)'
}
}
},
handleStyle() {
const baseStyle = {
position: 'absolute',
width: '50px',
height: '50px',
backgroundColor: '#FFFFFF',
borderRadius: '30px'
}
if (this.mode === 'horizontal') {
return {
...baseStyle,
transform: `translateX(${this.position}px)`,
top: '0px'
}
} else {
return {
...baseStyle,
transform: `translateY(${this.position}px)`,
left: '0px'
}
}
}
}
}
</script>
<style>
.slider-container {
flex: 1;
position: relative;
background-color: #000000;
border-radius: 30px;
}
.slider-track {
position: absolute;
background-color: rgba(255, 255, 255, 0.3);
}
.slider-handle {
position: absolute;
background-color: #FFFFFF;
}
</style>
为这个代码创建一个文件SlidePlugin.vue
然后再其他页面进行调用:
<template>
<view>
<SlidePlugin
mode="vertical"
:width="50"
:height="180"
@change="sliderChange_ver"
/>
</view>
</template>
<script>
import SlidePlugin from '@/components/SlidePlugin.vue'
export default {
components: {
SlidePlugin
}
}
</script>
官方提供的无法在一个页面同时滑动2个,所以自己做了一个,这个滑动后会自动返回中间位置,如果中间位置偏离,自己调整下