之前我的一篇文章写的是自定义长方形滑杆,这次客户修改了,让改成圆形的。上代码吧
我这个是在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>