feat: 轮播改造为 只要有一条数据就能轮播
This commit is contained in:
@@ -1,7 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<view
|
<view
|
||||||
class="card-swiper"
|
class="card-swiper"
|
||||||
:class="{ 'is-single': !canLoop }"
|
:class="{ 'is-single': props.list.length <= 1 }"
|
||||||
@touchstart="handleTouchStart"
|
@touchstart="handleTouchStart"
|
||||||
@touchmove.stop.prevent="handleTouchMove"
|
@touchmove.stop.prevent="handleTouchMove"
|
||||||
@touchend="handleTouchEnd"
|
@touchend="handleTouchEnd"
|
||||||
@@ -34,7 +34,7 @@
|
|||||||
</view>
|
</view>
|
||||||
|
|
||||||
<view
|
<view
|
||||||
v-if="canLoop"
|
v-if="canSwipe"
|
||||||
class="card-mask"
|
class="card-mask"
|
||||||
:style="getMaskStyle(slot.role)"
|
:style="getMaskStyle(slot.role)"
|
||||||
/>
|
/>
|
||||||
@@ -71,7 +71,7 @@ const { windowWidth = 375 } = uni.getWindowInfo();
|
|||||||
const sideOffset = Math.max(108, Math.min(windowWidth * 0.26, 148));
|
const sideOffset = Math.max(108, Math.min(windowWidth * 0.26, 148));
|
||||||
const hiddenOffset = sideOffset + 92;
|
const hiddenOffset = sideOffset + 92;
|
||||||
|
|
||||||
const activeIndex = ref(0);
|
const activeCursor = ref(0);
|
||||||
const deltaX = ref(0);
|
const deltaX = ref(0);
|
||||||
const isDragging = ref(false);
|
const isDragging = ref(false);
|
||||||
const isAnimating = ref(false);
|
const isAnimating = ref(false);
|
||||||
@@ -87,9 +87,14 @@ const hasExternalModel = Object.prototype.hasOwnProperty.call(
|
|||||||
"modelValue"
|
"modelValue"
|
||||||
);
|
);
|
||||||
|
|
||||||
const canLoop = computed(() => props.list.length > 1);
|
const actualCount = computed(() => props.list.length);
|
||||||
|
const virtualCount = computed(() => {
|
||||||
|
if (actualCount.value === 1) return 3;
|
||||||
|
return actualCount.value;
|
||||||
|
});
|
||||||
|
const canSwipe = computed(() => virtualCount.value > 1);
|
||||||
const progress = computed(() => {
|
const progress = computed(() => {
|
||||||
if (!canLoop.value) return 0;
|
if (!canSwipe.value) return 0;
|
||||||
return clamp(deltaX.value / sideOffset, -1, 1);
|
return clamp(deltaX.value / sideOffset, -1, 1);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -98,8 +103,18 @@ const normalizeIndex = (index, total) => {
|
|||||||
return ((index % total) + total) % total;
|
return ((index % total) + total) % total;
|
||||||
};
|
};
|
||||||
|
|
||||||
const syncActiveIndex = (incomingIndex = 0) => {
|
const getActualIndex = (virtualIndex) => {
|
||||||
activeIndex.value = normalizeIndex(incomingIndex, props.list.length);
|
if (!actualCount.value) return 0;
|
||||||
|
return normalizeIndex(virtualIndex, actualCount.value);
|
||||||
|
};
|
||||||
|
|
||||||
|
const getItemByVirtualIndex = (virtualIndex) => {
|
||||||
|
if (!actualCount.value) return null;
|
||||||
|
return props.list[getActualIndex(virtualIndex)] || null;
|
||||||
|
};
|
||||||
|
|
||||||
|
const syncActiveCursor = (incomingIndex = 0) => {
|
||||||
|
activeCursor.value = normalizeIndex(incomingIndex, virtualCount.value);
|
||||||
};
|
};
|
||||||
|
|
||||||
function clearSettleTimer() {
|
function clearSettleTimer() {
|
||||||
@@ -116,10 +131,10 @@ watch(
|
|||||||
deltaX.value = 0;
|
deltaX.value = 0;
|
||||||
isDragging.value = false;
|
isDragging.value = false;
|
||||||
isAnimating.value = false;
|
isAnimating.value = false;
|
||||||
const nextIndex = hasExternalModel
|
const nextCursor = hasExternalModel
|
||||||
? props.modelValue
|
? props.modelValue
|
||||||
: activeIndex.value;
|
: activeCursor.value;
|
||||||
syncActiveIndex(nextIndex);
|
syncActiveCursor(nextCursor);
|
||||||
},
|
},
|
||||||
{ deep: true, immediate: true }
|
{ deep: true, immediate: true }
|
||||||
);
|
);
|
||||||
@@ -129,23 +144,20 @@ watch(
|
|||||||
(value) => {
|
(value) => {
|
||||||
if (!hasExternalModel) return;
|
if (!hasExternalModel) return;
|
||||||
if (isAnimating.value || isDragging.value) return;
|
if (isAnimating.value || isDragging.value) return;
|
||||||
syncActiveIndex(value);
|
syncActiveCursor(value);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
const getItemKey = (index, role) => {
|
const getItemKey = (virtualIndex, role) => {
|
||||||
const item = props.list[index] || {};
|
const item = getItemByVirtualIndex(virtualIndex) || {};
|
||||||
const baseKey =
|
const baseKey =
|
||||||
item.id ??
|
item.id ??
|
||||||
item.commodityId ??
|
item.commodityId ??
|
||||||
item.tabLabel ??
|
item.tabLabel ??
|
||||||
item.title ??
|
item.title ??
|
||||||
index;
|
virtualIndex;
|
||||||
const prevIndex = normalizeIndex(activeIndex.value - 1, props.list.length);
|
|
||||||
const nextIndex = normalizeIndex(activeIndex.value + 1, props.list.length);
|
|
||||||
const hasDuplicateSide = prevIndex === nextIndex && role !== "current";
|
|
||||||
|
|
||||||
return hasDuplicateSide ? `${baseKey}-${role}` : `${baseKey}`;
|
return `${baseKey}-${virtualIndex}-${role}`;
|
||||||
};
|
};
|
||||||
|
|
||||||
const states = {
|
const states = {
|
||||||
@@ -216,7 +228,7 @@ const getCardZIndex = (role) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const getSlotState = (role) => {
|
const getSlotState = (role) => {
|
||||||
if (!canLoop.value) return states.center;
|
if (!canSwipe.value) return states.center;
|
||||||
|
|
||||||
const currentProgress = progress.value;
|
const currentProgress = progress.value;
|
||||||
if (currentProgress < 0) {
|
if (currentProgress < 0) {
|
||||||
@@ -251,7 +263,7 @@ const buildCardStyle = (role) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const getMaskOpacity = (role) => {
|
const getMaskOpacity = (role) => {
|
||||||
if (!canLoop.value) return 0;
|
if (!canSwipe.value) return 0;
|
||||||
|
|
||||||
const currentProgress = progress.value;
|
const currentProgress = progress.value;
|
||||||
const baseOpacity = 0.42;
|
const baseOpacity = 0.42;
|
||||||
@@ -279,43 +291,31 @@ const getMaskStyle = (role) => ({
|
|||||||
});
|
});
|
||||||
|
|
||||||
const renderSlots = computed(() => {
|
const renderSlots = computed(() => {
|
||||||
if (!props.list.length) return [];
|
if (!actualCount.value) return [];
|
||||||
|
|
||||||
if (!canLoop.value) {
|
const prevIndex = normalizeIndex(activeCursor.value - 1, virtualCount.value);
|
||||||
return [
|
const nextIndex = normalizeIndex(activeCursor.value + 1, virtualCount.value);
|
||||||
{
|
|
||||||
key: getItemKey(activeIndex.value, "current"),
|
|
||||||
role: "current",
|
|
||||||
index: activeIndex.value,
|
|
||||||
item: props.list[activeIndex.value],
|
|
||||||
style: buildCardStyle("current"),
|
|
||||||
},
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
const prevIndex = normalizeIndex(activeIndex.value - 1, props.list.length);
|
|
||||||
const nextIndex = normalizeIndex(activeIndex.value + 1, props.list.length);
|
|
||||||
|
|
||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
key: getItemKey(prevIndex, "prev"),
|
key: getItemKey(prevIndex, "prev"),
|
||||||
role: "prev",
|
role: "prev",
|
||||||
index: prevIndex,
|
index: getActualIndex(prevIndex),
|
||||||
item: props.list[prevIndex],
|
item: getItemByVirtualIndex(prevIndex),
|
||||||
style: buildCardStyle("prev"),
|
style: buildCardStyle("prev"),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: getItemKey(activeIndex.value, "current"),
|
key: getItemKey(activeCursor.value, "current"),
|
||||||
role: "current",
|
role: "current",
|
||||||
index: activeIndex.value,
|
index: getActualIndex(activeCursor.value),
|
||||||
item: props.list[activeIndex.value],
|
item: getItemByVirtualIndex(activeCursor.value),
|
||||||
style: buildCardStyle("current"),
|
style: buildCardStyle("current"),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: getItemKey(nextIndex, "next"),
|
key: getItemKey(nextIndex, "next"),
|
||||||
role: "next",
|
role: "next",
|
||||||
index: nextIndex,
|
index: getActualIndex(nextIndex),
|
||||||
item: props.list[nextIndex],
|
item: getItemByVirtualIndex(nextIndex),
|
||||||
style: buildCardStyle("next"),
|
style: buildCardStyle("next"),
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
@@ -335,16 +335,17 @@ const finishSwipe = (step) => {
|
|||||||
deltaX.value = step > 0 ? -sideOffset : sideOffset;
|
deltaX.value = step > 0 ? -sideOffset : sideOffset;
|
||||||
clearSettleTimer();
|
clearSettleTimer();
|
||||||
settleTimer = setTimeout(() => {
|
settleTimer = setTimeout(() => {
|
||||||
const nextIndex = normalizeIndex(activeIndex.value + step, props.list.length);
|
const nextCursor = normalizeIndex(activeCursor.value + step, virtualCount.value);
|
||||||
activeIndex.value = nextIndex;
|
activeCursor.value = nextCursor;
|
||||||
emit("update:modelValue", nextIndex);
|
const actualIndex = getActualIndex(nextCursor);
|
||||||
emit("change", nextIndex);
|
emit("update:modelValue", actualIndex);
|
||||||
|
emit("change", actualIndex);
|
||||||
resetGesture();
|
resetGesture();
|
||||||
}, DURATION);
|
}, DURATION);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleTouchStart = (event) => {
|
const handleTouchStart = (event) => {
|
||||||
if (!canLoop.value || isAnimating.value) return;
|
if (!canSwipe.value || isAnimating.value) return;
|
||||||
|
|
||||||
const touch = event.touches?.[0] || event.changedTouches?.[0];
|
const touch = event.touches?.[0] || event.changedTouches?.[0];
|
||||||
if (!touch) return;
|
if (!touch) return;
|
||||||
@@ -357,7 +358,7 @@ const handleTouchStart = (event) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const handleTouchMove = (event) => {
|
const handleTouchMove = (event) => {
|
||||||
if (!canLoop.value || !isDragging.value || isAnimating.value) return;
|
if (!canSwipe.value || !isDragging.value || isAnimating.value) return;
|
||||||
|
|
||||||
const touch = event.touches?.[0] || event.changedTouches?.[0];
|
const touch = event.touches?.[0] || event.changedTouches?.[0];
|
||||||
if (!touch) return;
|
if (!touch) return;
|
||||||
@@ -378,7 +379,7 @@ const handleTouchMove = (event) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const handleTouchEnd = () => {
|
const handleTouchEnd = () => {
|
||||||
if (!canLoop.value || !isDragging.value) return;
|
if (!canSwipe.value || !isDragging.value) return;
|
||||||
|
|
||||||
isDragging.value = false;
|
isDragging.value = false;
|
||||||
|
|
||||||
@@ -396,7 +397,7 @@ const handleTouchEnd = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const handleTouchCancel = () => {
|
const handleTouchCancel = () => {
|
||||||
if (!canLoop.value) return;
|
if (!canSwipe.value) return;
|
||||||
clearSettleTimer();
|
clearSettleTimer();
|
||||||
deltaX.value = 0;
|
deltaX.value = 0;
|
||||||
isDragging.value = false;
|
isDragging.value = false;
|
||||||
|
|||||||
Reference in New Issue
Block a user