|
|
@@ -177,6 +177,12 @@ const touchStartTimestamp = ref(0);
|
|
|
/** 触摸开始时的初始偏移量 */
|
|
|
const initialOffset = ref(0);
|
|
|
|
|
|
+/** 是否正在触摸中 */
|
|
|
+const isTouching = ref(false);
|
|
|
+
|
|
|
+/** 位置更新防抖定时器 */
|
|
|
+let positionUpdateTimer: number = 0;
|
|
|
+
|
|
|
/**
|
|
|
* 更新轮播容器的位置
|
|
|
* 根据当前激活索引计算并设置容器的偏移量
|
|
|
@@ -184,22 +190,32 @@ const initialOffset = ref(0);
|
|
|
function updateSlidePosition() {
|
|
|
if (bannerWidth.value == 0) return;
|
|
|
|
|
|
- // 计算累积偏移量,考虑每个位置的动态边距
|
|
|
- let totalOffset = 0;
|
|
|
-
|
|
|
- // 遍历当前索引之前的所有项,累加它们的宽度
|
|
|
- for (let i = 0; i < activeIndex.value; i++) {
|
|
|
- const itemPreviousMargin = i == 0 ? 0 : props.previousMargin;
|
|
|
- const itemNextMargin = i == props.list.length - 1 ? 0 : props.nextMargin;
|
|
|
- const itemWidthAtIndex = bannerWidth.value - itemPreviousMargin - itemNextMargin;
|
|
|
- totalOffset += itemWidthAtIndex;
|
|
|
+ // 防抖处理,避免频繁更新
|
|
|
+ if (positionUpdateTimer != 0) {
|
|
|
+ clearTimeout(positionUpdateTimer);
|
|
|
}
|
|
|
|
|
|
- // 当前项的左边距
|
|
|
- const currentPreviousMargin = activeIndex.value == 0 ? 0 : props.previousMargin;
|
|
|
+ // @ts-ignore
|
|
|
+ positionUpdateTimer = setTimeout(() => {
|
|
|
+ // 计算累积偏移量,考虑每个位置的动态边距
|
|
|
+ let totalOffset = 0;
|
|
|
+
|
|
|
+ // 遍历当前索引之前的所有项,累加它们的宽度
|
|
|
+ for (let i = 0; i < activeIndex.value; i++) {
|
|
|
+ const itemPreviousMargin = i == 0 ? 0 : props.previousMargin;
|
|
|
+ const itemNextMargin = i == props.list.length - 1 ? 0 : props.nextMargin;
|
|
|
+ const itemWidthAtIndex = bannerWidth.value - itemPreviousMargin - itemNextMargin;
|
|
|
+ totalOffset += itemWidthAtIndex;
|
|
|
+ }
|
|
|
|
|
|
- // 设置最终的偏移量:负方向移动累积宽度,然后加上当前项的左边距
|
|
|
- slideOffset.value = -totalOffset + currentPreviousMargin;
|
|
|
+ // 当前项的左边距
|
|
|
+ const currentPreviousMargin = activeIndex.value == 0 ? 0 : props.previousMargin;
|
|
|
+
|
|
|
+ // 设置最终的偏移量:负方向移动累积宽度,然后加上当前项的左边距
|
|
|
+ slideOffset.value = -totalOffset + currentPreviousMargin;
|
|
|
+
|
|
|
+ positionUpdateTimer = 0;
|
|
|
+ }, 10);
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
@@ -262,18 +278,24 @@ function startAutoplay() {
|
|
|
|
|
|
if (props.autoplay) {
|
|
|
clearAutoplay();
|
|
|
- }
|
|
|
|
|
|
- isAnimating.value = true;
|
|
|
-
|
|
|
- // @ts-ignore
|
|
|
- autoplayTimer = setInterval(() => {
|
|
|
- if (activeIndex.value >= props.list.length - 1) {
|
|
|
- activeIndex.value = 0;
|
|
|
- } else {
|
|
|
- activeIndex.value++;
|
|
|
+ // 只有在非触摸状态下才启动自动轮播
|
|
|
+ if (!isTouching.value) {
|
|
|
+ isAnimating.value = true;
|
|
|
+
|
|
|
+ // @ts-ignore
|
|
|
+ autoplayTimer = setInterval(() => {
|
|
|
+ // 再次检查是否在触摸中,避免触摸时自动切换
|
|
|
+ if (!isTouching.value) {
|
|
|
+ if (activeIndex.value >= props.list.length - 1) {
|
|
|
+ activeIndex.value = 0;
|
|
|
+ } else {
|
|
|
+ activeIndex.value++;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }, props.interval);
|
|
|
}
|
|
|
- }, props.interval);
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
@@ -281,13 +303,16 @@ function startAutoplay() {
|
|
|
* 记录触摸起始状态,准备手势识别
|
|
|
* @param e 触摸事件对象
|
|
|
*/
|
|
|
-function onTouchStart(e: UniTouchEvent) {
|
|
|
+function onTouchStart(e: TouchEvent) {
|
|
|
// 如果禁用触摸,则不进行任何操作
|
|
|
if (props.disableTouch) return;
|
|
|
|
|
|
// 单项或空列表不支持滑动
|
|
|
if (props.list.length <= 1) return;
|
|
|
|
|
|
+ // 设置触摸状态
|
|
|
+ isTouching.value = true;
|
|
|
+
|
|
|
// 清除自动轮播
|
|
|
clearAutoplay();
|
|
|
|
|
|
@@ -303,8 +328,8 @@ function onTouchStart(e: UniTouchEvent) {
|
|
|
* 实时更新容器位置,实现跟手效果
|
|
|
* @param e 触摸事件对象
|
|
|
*/
|
|
|
-function onTouchMove(e: UniTouchEvent) {
|
|
|
- if (props.list.length <= 1 || props.disableTouch) return;
|
|
|
+function onTouchMove(e: TouchEvent) {
|
|
|
+ if (props.list.length <= 1 || props.disableTouch || !isTouching.value) return;
|
|
|
|
|
|
// 计算手指移动距离,实时更新偏移量
|
|
|
const deltaX = e.touches[0].clientX - touchStartPoint.value;
|
|
|
@@ -316,7 +341,10 @@ function onTouchMove(e: UniTouchEvent) {
|
|
|
* 根据滑动距离和速度判断是否切换轮播项
|
|
|
*/
|
|
|
function onTouchEnd() {
|
|
|
- if (props.list.length <= 1) return;
|
|
|
+ if (props.list.length <= 1 || !isTouching.value) return;
|
|
|
+
|
|
|
+ // 重置触摸状态
|
|
|
+ isTouching.value = false;
|
|
|
|
|
|
// 恢复动画效果
|
|
|
isAnimating.value = true;
|
|
|
@@ -324,7 +352,7 @@ function onTouchEnd() {
|
|
|
// 计算滑动距离、时间和速度
|
|
|
const deltaX = slideOffset.value - initialOffset.value;
|
|
|
const deltaTime = Date.now() - touchStartTimestamp.value;
|
|
|
- const velocity = Math.abs(deltaX) / deltaTime; // px/ms
|
|
|
+ const velocity = deltaTime > 0 ? Math.abs(deltaX) / deltaTime : 0; // px/ms
|
|
|
|
|
|
let newIndex = activeIndex.value;
|
|
|
|
|
|
@@ -343,9 +371,14 @@ function onTouchEnd() {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- // 更新索引
|
|
|
- activeIndex.value = newIndex;
|
|
|
- updateSlidePosition();
|
|
|
+ // 更新索引 - 如果索引没有变化,需要手动恢复位置
|
|
|
+ if (newIndex == activeIndex.value) {
|
|
|
+ // 索引未变化,恢复到正确位置
|
|
|
+ updateSlidePosition();
|
|
|
+ } else {
|
|
|
+ // 索引变化,watch会自动调用updateSlidePosition
|
|
|
+ activeIndex.value = newIndex;
|
|
|
+ }
|
|
|
|
|
|
// 恢复自动轮播
|
|
|
setTimeout(() => {
|
|
|
@@ -379,6 +412,8 @@ onMounted(() => {
|
|
|
|
|
|
&__list {
|
|
|
@apply flex flex-row h-full w-full overflow-visible;
|
|
|
+ // HBuilderX 4.8.2 bug,临时处理
|
|
|
+ width: 100000px;
|
|
|
|
|
|
&.is-transition {
|
|
|
transition-property: transform;
|