topfans/frontend/pages/castlove/craft-select.vue

347 lines
11 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<view class="page">
<image class="bg-wrapper" src="/static/castlove/beijingban.png" mode="aspectFill"></image>
<view class="main-container">
<view class="cards-container" @touchstart="onTouchStart" @touchmove="onTouchMove" @touchend="onTouchEnd">
<view v-for="(card, index) in currentCardList" :key="index" class="card-item"
:class="{ 'no-transition': disableTransition }" :style="getCardStyle(index)">
<view class="card-frame"
:class="{ 'no-border': card.comingSoon, 'card-frame--tappable': !card.comingSoon }"
@tap.stop="onCardFrameTap(index)">
<image class="card-image" :src="card.image" mode="aspectFill" />
<image v-if="card.comingSoon" class="coming-soon-badge"
src="/static/castlove/jinqingqidai.png" />
</view>
</view>
</view>
<view class="text-panel">
<view class="arrow-btn arrow-up" @click="scrollUp">
<image class="arrow-icon" src="/static/castlove/jiantou.png" style="transform:rotate(180deg)" />
</view>
<view class="text-list">
<view v-for="(category, index) in categoryList" :key="index" class="text-item"
:class="{ active: selectedCategoryIndex === index, 'font-large': index === 1, 'font-mid': index === 0 || index === 2 }"
@click="selectCategory(index)">
<text>{{ category.name }}</text>
</view>
</view>
<view class="arrow-btn arrow-down" @click="scrollDown">
<image class="arrow-icon" src="/static/castlove/jiantou.png" />
</view>
</view>
</view>
</view>
</template>
<script>
export default {
onShow() {
try {
uni.hideToast()
uni.hideLoading()
} catch (e) { }
},
data() {
return {
selectedCategoryIndex: 0,
categoryList: [{ name: '星卡' }, { name: '吧唧' }, { name: '海报' }],
cardListMap: {
'星卡': [
{ name: '光栅卡', image: '/static/castlove/guangshanka.png', comingSoon: false },
{ name: '拍立得', image: '/static/castlove/pailide.png', comingSoon: false },
{ name: '开发中', image: '/static/castlove/daikaifa.png', comingSoon: true },
{ name: '镭射卡', image: '/static/castlove/leisheka.png', comingSoon: false },
{ name: '撕拉片', image: '/static/castlove/silapian.png', comingSoon: false },
],
'吧唧': [],
'海报': []
},
cardList: [
{ name: '光栅卡', image: '/static/castlove/guangshanka.png', comingSoon: false },
{ name: '拍立得', image: '/static/castlove/pailide.png', comingSoon: false },
{ name: '镭射卡', image: '/static/castlove/leisheka.png', comingSoon: false },
{ name: '撕拉片', image: '/static/castlove/silapian.png', comingSoon: false },
{ name: '开发中', image: '/static/castlove/daikaifa.png', comingSoon: true }
],
cardRoutes: {
'光栅卡': '/pages/castlove/lenticular/lenticular-create',
'拍立得': '/pages/castlove/create',
'镭射卡': '/pages/castlove/create',
'撕拉片': '/pages/castlove/create',
},
totalCard: 5,
selectedIndex: 1, // 默认第2张
touchStartY: 0,
dragOffset: 0,
isDragging: false,
disableTransition: false,
SWIPE_STEP: 100,
}
},
computed: {
currentCardList() {
const name = this.categoryList[this.selectedCategoryIndex]?.name
return this.cardListMap[name] || this.cardList
}
},
methods: {
selectCategory(index) {
this.selectedCategoryIndex = index
this.selectedIndex = 1
this.dragOffset = 0
this.isDragging = false
},
onTouchStart(e) {
this.touchStartY = e.touches[0].clientY
this.isDragging = true
this.disableTransition = true
},
onTouchMove(e) {
if (!this.isDragging) return
const moveY = e.touches[0].clientY
this.dragOffset = moveY - this.touchStartY
},
onTouchEnd() {
if (!this.isDragging) return
this.isDragging = false
this.disableTransition = false
const moveCount = Math.round(-this.dragOffset / this.SWIPE_STEP)
let newIdx = this.selectedIndex + moveCount
newIdx = ((newIdx % 5) + 5) % 5
this.selectedIndex = newIdx
this.dragOffset = 0
},
// ====================== 核心修复z-index + 层级 + 循环 ======================
getCardStyle(index) {
const positions = [
{ left: 288, top: 64, rotate: 25, scale: 0.75 },
{ left: 120, top: 288, rotate: 12, scale: 0.95 },
{ left: 60, top: 580, rotate: 0, scale: 1 },
{ left: 120, top: 888, rotate: -12, scale: 0.95 },
{ left: 224, top: 1096, rotate: -25, scale: 0.75 },
]
const progress = -this.dragOffset / this.SWIPE_STEP
const centerIdx = this.selectedIndex + progress
// 循环最短差值(关键)
let diff = index - centerIdx
if (diff > 2) diff -= 5
if (diff < -2) diff += 5
const cardPos = diff + 2
// 插值计算
let pos
if (Number.isInteger(cardPos)) {
pos = positions[cardPos] ?? positions[2]
} else {
const prev = Math.floor(cardPos)
const next = (prev + 1) % 5
const t = cardPos - prev
const p = positions[prev] ?? positions[2]
const n = positions[next] ?? positions[2]
pos = {
left: p.left + (n.left - p.left) * t,
top: p.top + (n.top - p.top) * t,
rotate: p.rotate + (n.rotate - p.rotate) * t,
scale: p.scale + (n.scale - p.scale) * t,
}
}
// ====================== ZINDEX 终极修复 ======================
const distance = Math.abs(cardPos - 2)
const zIndex = Math.round(100 - distance * 30) // 越靠近中间层级越高
const isCenter = distance < 0.02
if (isCenter) {
return {
left: pos.left + 'rpx',
top: pos.top + 'rpx',
transform: `scale(${pos.scale * 1.15}) rotate(${pos.rotate}deg)`,
zIndex: 999, // 中心永远最高
}
}
return {
left: pos.left + 'rpx',
top: pos.top + 'rpx',
transform: `scale(${pos.scale}) rotate(${pos.rotate}deg)`,
zIndex,
}
},
getCardStackPosition(index) {
const progress = -this.dragOffset / this.SWIPE_STEP
const centerIdx = this.selectedIndex + progress
let diff = index - centerIdx
if (diff > 2) diff -= 5
if (diff < -2) diff += 5
return diff + 2
},
onCardFrameTap(index) {
const card = this.currentCardList[index]
if (!card) return
const pos = this.getCardStackPosition(index)
if (Math.abs(pos - 2) < 0.2) {
if (card.name === '光栅卡') {
uni.navigateTo({
url: this.cardRoutes['光栅卡'] + '?name=' + encodeURIComponent(card.name),
})
} else {
uni.showToast({ title: '激情开发中', icon: 'none' })
}
return
}
this.selectedIndex = index
},
scrollUp() {
if (this.selectedCategoryIndex > 0) this.selectCategory(this.selectedCategoryIndex - 1)
},
scrollDown() {
if (this.selectedCategoryIndex < this.categoryList.length - 1) {
this.selectCategory(this.selectedCategoryIndex + 1)
}
},
},
}
</script>
<style lang="scss" scoped>
.page {
height: 100vh;
position: relative;
overflow: hidden;
}
.bg-wrapper {
position: fixed;
top: -16rpx;
left: 0;
width: 100%;
height: 110%;
z-index: 0;
}
.main-container {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
display: flex;
z-index: 10;
}
.cards-container {
flex: 1;
position: relative;
}
.card-item {
position: absolute;
width: 344rpx;
height: 344rpx;
transition: all 0.5s cubic-bezier(0.4, 0, 0.2, 1);
will-change: transform, z-index;
}
.card-item.no-transition {
transition: none !important;
}
.card-frame {
position: relative;
width: 100%;
height: 100%;
border-radius: 20rpx;
padding: 10rpx;
background-image: url('/static/square/cangpinkuang1.png');
background-size: cover;
}
.card-frame.no-border {
background-image: none;
}
.card-image {
width: 100%;
height: 100%;
border-radius: 14rpx;
object-fit: cover;
}
.coming-soon-badge {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 160rpx;
height: 160rpx;
}
.text-panel {
position: absolute;
right: 30rpx;
top: 50%;
transform: translateY(-50%);
width: 200rpx;
height: 392rpx;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
background: url('/static/castlove/xiahualan.png') no-repeat center;
background-size: 130%;
border-radius: 20rpx;
}
.arrow-btn {
width: 60rpx;
height: 32rpx;
display: flex;
align-items: center;
justify-content: center;
opacity: 0.8;
}
.arrow-icon {
width: 48rpx;
height: 48rpx;
}
.text-list {
display: flex;
flex-direction: column;
padding: 0 20rpx;
}
.text-item {
color: #fff;
font-size: 26rpx;
font-weight: 500;
padding: 10rpx 20rpx;
border-radius: 14rpx;
display: flex;
justify-content: center;
}
.text-item.active {
font-weight: bold;
background: url('/static/nft/dingbutubiao_liang.png') no-repeat center;
background-size: 100% 100%;
}
.font-large {
font-size: 34rpx;
}
.font-mid {
font-size: 30rpx;
}
</style>