feat: 实现相关商品的样式调整

This commit is contained in:
2025-10-31 01:16:00 +08:00
parent ee6aacd974
commit 98e6c55ccf
3 changed files with 313 additions and 172 deletions

View File

@@ -0,0 +1,155 @@
<template>
<view class="swipe-cards" @touchmove.stop>
<view
v-for="(card, index) in visibleCards"
:key="`${card.commodityId}-${index}`"
class="card"
:style="cardStyle(index, card)"
@touchstart="touchStart($event, index)"
@touchmove="touchMove($event, index)"
@touchend="touchEnd($event, index)"
>
<slot :card="card" />
</view>
</view>
</template>
<script setup>
import { ref, computed, nextTick } from "vue";
const props = defineProps({
cardsData: Array,
threshold: { type: Number, default: 100 },
});
const cards = ref([...props.cardsData]);
const startX = ref(0);
const startY = ref(0);
const moveX = ref(0);
const moveY = ref(0);
const currentIndex = ref(null);
const isHorizontal = ref(false); // 是否为水平滑动
const visibleCards = computed(() => cards.value.slice(0, 3));
function touchStart(e, index) {
const touch = e.touches[0];
startX.value = touch.clientX;
startY.value = touch.clientY;
currentIndex.value = index;
isHorizontal.value = false;
}
function touchMove(e, index) {
if (currentIndex.value !== index) return;
const touch = e.touches[0];
moveX.value = touch.clientX - startX.value;
moveY.value = touch.clientY - startY.value;
// 第一次移动时判断方向
if (
!isHorizontal.value &&
(Math.abs(moveX.value) > 10 || Math.abs(moveY.value) > 10)
) {
if (Math.abs(moveX.value) > Math.abs(moveY.value)) {
isHorizontal.value = true;
}
}
// 如果是水平滑动,阻止页面滚动
if (isHorizontal.value) {
e.preventDefault?.();
}
}
function touchEnd(e, index) {
if (!isHorizontal.value) return; // 如果不是水平滑动,不处理
if (Math.abs(moveX.value) > props.threshold) {
const direction = moveX.value > 0 ? 1 : -1;
animateSwipe(index, direction);
} else {
resetCard(index);
}
moveX.value = 0;
moveY.value = 0;
currentIndex.value = null;
}
function animateSwipe(index, direction) {
const card = cards.value[index];
if (!card) return;
card.swiped = true;
card.direction = direction;
setTimeout(async () => {
const removed = cards.value.splice(index, 1)[0];
removed.swiped = false;
removed.direction = 0;
cards.value.push(removed);
await nextTick();
}, 300);
}
function resetCard(index) {
const card = cards.value[index];
if (!card) return;
card.swiped = false;
card.direction = 0;
}
function cardStyle(index, card) {
const baseIndex = visibleCards.value.length - index;
const zIndex = baseIndex;
const scale = 1 - index * 0.05;
const translateY = index * 28;
const translateDrag =
currentIndex.value === index
? `translate(${moveX.value}px, ${moveY.value}px)`
: `translate(0, ${translateY}rpx)`;
const rotate =
currentIndex.value === index
? `rotate(${moveX.value / 15}deg)`
: "rotate(0)";
let opacity = 1;
if (card.swiped) opacity = 0;
let transform = `${translateDrag} scale(${scale}) ${rotate}`;
if (card.swiped) {
const x = card.direction * 500;
transform = `translate(${x}px, ${moveY.value}px) rotate(${
card.direction * 45
}deg)`;
}
return `
z-index: ${zIndex};
background: ${card.color};
transform: ${transform};
opacity: ${opacity};
transition: ${
card.swiped ? "all 0.3s ease-out" : "transform 0.2s ease-out"
};
`;
}
</script>
<style scoped lang="scss">
.swipe-cards {
position: relative;
height: 320px;
display: flex;
justify-content: center;
padding: 6px 6px 12px 6px;
}
.card {
position: absolute;
width: calc(100% - 12px);
height: calc(100% - 30px);
border-radius: 20px;
box-shadow: 0 8px 8px rgba(0, 0, 0, 0.08);
background: #fff;
}
</style>