之前我的一篇文章写的是自定义长方形滑杆,这次客户修改了,让改成圆形的。上代码吧
我这个是在NVUE中运行的代码,其他没有测试,主要是学习与参考,如果不能用,就学习其中的逻辑,主要在边界的处理,我当时也是看着别人的做的,不知道为什么。。。。-。-
78这个数字,是根据宽度和高度进行修改的,是他们的一般多一点,反正调试到中间就好,自己调试看看
<template><view class="slider-container" :style="containerStyle" @touchmove.prevent><view ref="sliderHandle" class="slider-handle" :style="handleStyle" @touchstart="onTouchStart"@touchmove.stop="onTouchMove($event)" @touchend="onTouchEnd"></view></view>
</template><script>export default {name: 'SliderControl',props: {width: {type: [Number, String],default: 220},height: {type: [Number, String],default: 220}},data() {return {position: {x: 78,y: 78},ZeroPoint:{x: 78,y: 78},isDragging: false,startTouch: null,startPosition: null,lastUpdate: 0,lastEmittedAngle: 0,lastEmittedDistance: 0,animationTimer: null}},methods: {onTouchStart(e) {uni.vibrateShort({success: () => {},fail: (err) => {console.error('短振动失败:', err);}});e.stopPropagation();if (this.animationTimer) {clearTimeout(this.animationTimer);this.animationTimer = null;}const touch = e.touches[0];if (!touch) return;this.isDragging = true;this.startTouch = {x: touch.pageX,y: touch.pageY};this.startPosition = {x: touch.screenX,y: touch.screenY};this.lastUpdate = Date.now();},onTouchMove(e) {//console.log(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.touches[0];if (!touch) return;const deltaX = touch.pageX - this.startTouch.x;const deltaY = touch.pageY - this.startTouch.y;let powV = Math.sqrt(Math.pow(deltaX, 2) + Math.pow(deltaY, 2))const maxDistance = Math.min(parseInt(this.width), parseInt(this.height)) / 2 - 25;const distance = Math.min(Math.sqrt(deltaX * deltaX + deltaY * deltaY), maxDistance);const deltaScX = touch.screenX - this.startPosition.x;const deltaScY = touch.screenY - this.startPosition.y;//console.log(deltaScX,deltaScY)const distanceSc = Math.sqrt(deltaScX * deltaScX + deltaScY * deltaScY);//const distance = Math.min(distanceSc, maxDistance);//超出边界时的处理if(distanceSc >= maxDistance){var templeft = maxDistance / distanceSc * deltaScX;var temptop = maxDistance / distanceSc * deltaScY;this.position.x = this.ZeroPoint.x + templeft;this.position.y = this.ZeroPoint.y + temptop;}else{const angle = Math.atan2(deltaY, deltaX) * (180 / Math.PI);const targetPos = {x: deltaX * (distance / Math.sqrt(deltaX * deltaX + deltaY * deltaY)),y: deltaY * (distance / Math.sqrt(deltaX * deltaX + deltaY * deltaY))};const smoothFactor = Math.min(0.08, timeDiff / 250);this.position.x = this.position.x + deltaX;this.position.y = this.position.y + deltaY;//console.log(this.position)const roundedAngle = Math.round(angle * 100) / 100;const roundedDistance = Math.round((distance / maxDistance) * 100) / 100;if (Math.abs(roundedAngle - this.lastEmittedAngle) >= 0.01 || Math.abs(roundedDistance - this.lastEmittedDistance) >= 0.01) {this.lastEmittedAngle = roundedAngle;this.lastEmittedDistance = roundedDistance;this.$emit('change', {angle: roundedAngle,distance: roundedDistance});}}},onTouchEnd(e) {if (!this.isDragging) return;e.stopPropagation();this.isDragging = false;this.startTouch = null;this.startPosition = null;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 = { ...this.ZeroPoint};console.log("end")this.$emit('change', {angle: 0,distance: 0});this.animationTimer = null;return;}const progress = elapsed / duration;const easeProgress = easeOutQuart(progress);this.position.x = startPos.x + (this.ZeroPoint.x - startPos.x) * easeProgress;this.position.y = startPos.y + (this.ZeroPoint.y- startPos.y) * easeProgress;console.log(this.position)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: 'rgba(0, 0, 0, 0.5)',borderRadius: '100px',position: 'relative',overflow: 'hidden'}},handleStyle() {return {position: 'absolute',width: '50px',height: '50px',backgroundColor: '#FFFFFF',borderRadius: '50px',transform: `translate(${this.position.x}px, ${this.position.y}px)`,top: '50%',left: '50%',marginLeft: '-25px',marginTop: '-25px',boxSizing: 'border-box'}}}}
</script><style>.slider-container {width: 100%;height: 100%;position: relative;background-color: rgba(255, 255, 255, 0.3);border-radius: 50%;overflow: hidden;/* 确保容器内内容不溢出 */}.slider-handle {position: absolute;background-color: #FFFFFF;border-radius: 50%;box-sizing: border-box;/* 确保手柄大小计算正确 */}
</style>
父类调用这样的样式:
<template><view :style="{width:screenWidth+'px',height:screenHeight+'px'}" style="position: relative;touch-action: none;">
<view class="noPlayVideo"style="position: absolute; right: 50px;bottom: 10px;z-index: 999;" @touchmove.prevent.stop><view class="bottomview flexRowCenter" @touchmove.prevent.stop><!-- 控车 -mode="vertical"--><AllDicrtion mode="vertical" :width="150" :height="150" @change="sliderChange_ver" /></view></view></view>
</template><script>import AllDicrtion from '@/components/all-dirction-hand-shank/index.vue'export default {components: {AllDicrtion}}</script>
<style>.noPlayVideo {background-color: #fff;}.bottomview {align-items: center;text-align: center;margin: 4px 3px;}.flexRowCenter {display: flex;flex-direction: row;align-items: center;}</style>