uniapp 自定义的滑杆(支持一个屏幕2个滑杆同时滑动)

发布于:2025-02-10 ⋅ 阅读:(47) ⋅ 点赞:(0)
<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个,所以自己做了一个,这个滑动后会自动返回中间位置,如果中间位置偏离,自己调整下


网站公告

今日签到

点亮在社区的每一天
去签到