topfans/frontend/pages/castlove/craft-select.vue
2026-05-13 16:34:50 +08:00

320 lines
9.1 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">
<!-- 主体内容 -->
<view class="main-container">
<!-- 左侧图片卡片区域 - 半圆弧形布局 -->
<view class="cards-container">
<view v-for="(card, index) in cardList" :key="index" class="card-item"
:class="{ 'card-selected': selectedIndex === index }" :style="getCardStyle(index)"
@click="selectCard(index)">
<view class="card-frame" :class="{ 'no-border': card.comingSoon }">
<image class="card-image" :src="card.image" mode="aspectFill" />
<image v-if="card.comingSoon" class="coming-soon-badge" src="/static/castlove/jinqingqidai.png" mode="aspectFit" />
</view>
</view>
</view>
<!-- 右侧文字列表 -->
<view class="text-panel">
<!-- 向上按钮 -->
<view class="arrow-btn arrow-up" @click="scrollUp">
<image class="arrow-icon" src="/static/castlove/jiantou.png" mode="aspectFit" style="transform: rotate(180deg);" />
</view>
<!-- 文字列表 -->
<view class="text-list">
<view v-for="(card, index) in cardList" :key="index" class="text-item"
:class="{ active: selectedIndex === index, 'font-large': index === 2, 'font-mid': index === 1 || index === 3, 'font-small': index === 0 || index === 4 }"
@click="selectCard(index)">
<text>{{ card.name }}</text>
</view>
</view>
<!-- 向下按钮 -->
<view class="arrow-btn arrow-down" @click="scrollDown">
<image class="arrow-icon" src="/static/castlove/jiantou.png" mode="aspectFit" />
</view>
</view>
</view>
<!-- 自定义底部导航 -->
<view class="nav-bar">
<button class="btn-secondary" @click="handleBack">返回</button>
<button class="btn-skip" @click="handleSkip">跳过</button>
</view>
</view>
</template>
<script>
export default {
data() {
return {
selectedIndex: 2, // 初始选中光栅卡
cardList: [
{ name: '镭射卡', image: '/static/castlove/leisheka.png', comingSoon: false },
{ name: '拍立得', image: '/static/castlove/pailide.png', comingSoon: false },
{ name: '光栅卡', image: '/static/castlove/guangshanka.png', comingSoon: false },
{ name: '撕拉片', image: '/static/castlove/silapian.png', comingSoon: false },
{ name: '开发中', image: '/static/castlove/daikaifa.png', comingSoon: true }
]
}
},
methods: {
// 获取卡片样式 - 循环滚动布局
// positions 定义了5个位置的固定样式位置0最上位置2中间位置4最下
// 当选中某个卡片时该卡片显示在位置2中间其他卡片循环填充
getCardStyle(index) {
const positions = [
{ left: 9 * 32, top: 2 * 32, rotate: 25, scale: 0.75 }, // 位置0 - 最上
{ left: 3.75 * 32, top: 9 * 32, rotate: 12, scale: 0.95 }, // 位置1 - 中上
{ left: 60, top: 580, rotate: 0, scale: 1 }, // 位置2 - 中间
{ left: 3.75 * 32, top: 27.75 * 32, rotate: -12, scale: 0.95 }, // 位置3 - 中下
{ left: 7 * 32, top: 34.25 * 32, rotate: -25, scale: 0.75 } // 位置4 - 最下
];
// 计算当前卡片应该显示在哪个位置
// 循环移位selectedIndex 的卡片显示在位置2中间
const cardPos = (index - this.selectedIndex + 2 + 5) % 5;
const pos = positions[cardPos];
// 选中卡片在中间位置时放大
if (cardPos === 2) {
return {
left: `${pos.left}rpx`,
top: `${pos.top}rpx`,
transform: `scale(${pos.scale * 1.15}) rotate(0deg)`,
zIndex: 100
};
}
return {
left: `${pos.left}rpx`,
top: `${pos.top}rpx`,
transform: `scale(${pos.scale}) rotate(${pos.rotate}deg)`,
zIndex: 30 - Math.abs(cardPos - 2) * 10
};
},
// 选择卡片
selectCard(index) {
this.selectedIndex = index;
},
handleBack() {
uni.navigateBack()
},
scrollUp() {
let newIndex = this.selectedIndex - 1;
if (newIndex >= 0) {
this.selectCard(newIndex);
}
},
scrollDown() {
let newIndex = this.selectedIndex + 1;
if (newIndex < this.cardList.length) {
this.selectCard(newIndex);
}
},
handleSkip() {
// 跳过,选择默认卡片或第一张
const defaultIndex = this.cardList.findIndex(card => !card.comingSoon);
if (defaultIndex !== -1) {
uni.navigateTo({
url: `/pages/castlove/create?name=${encodeURIComponent(this.cardList[defaultIndex].name)}`
});
}
}
}
}
</script>
<style lang="scss" scoped>
.page {
height: 100vh;
background: url('/static/castlove/beijingban.png') no-repeat center center;
background-size: cover;
position: relative;
overflow: hidden;
}
.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.6s cubic-bezier(0.4, 0, 0.2, 1);
cursor: pointer;
}
.card-frame {
position: relative;
width: 100%;
height: 100%;
border-radius: 20rpx;
padding: 10rpx;
overflow: visible;
background-image: url('/static/square/cangpinkuang1.png');
background-size: cover;
box-shadow: 0 0 0 rgba(0, 0, 0, 0.5);
&.no-border {
background-image: none;
}
}
.card-image {
width: 100%;
height: 100%;
border-radius: 14rpx;
display: block;
object-fit: cover;
}
.coming-soon-badge {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 160rpx;
height: 160rpx;
}
// .card-selected .card-frame {
// border-color: #ffd700;
// box-shadow:
// 0 0 60rpx rgba(255, 215, 0, 0.8),
// 0 20rpx 60rpx rgba(0, 0, 0, 0.4),
// inset 0 2rpx 10rpx rgba(255, 255, 255, 0.6);
// }
.text-panel {
position: absolute;
right: 30rpx;
top: 50%;
transform: translateY(-50%);
width: 200rpx;
height: 392rpx; // 18rem = 360rpx
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
background: url('/static/castlove/xiahualan.png') no-repeat center center;
background-size: 130%;
border-radius: 20rpx;
}
.arrow-btn {
width: 60rpx;
height: 32rpx;
display: flex;
align-items: center;
justify-content: center;
opacity: 0.8;
&:active {
opacity: 0.6;
}
}
.arrow-icon {
width: 48rpx;
height: 48rpx;
}
.text-list {
display: flex;
flex-direction: column;
justify-content: center;
padding: 0 20rpx;
}
.text-item {
color: rgba(255, 255, 255);
font-size: 26rpx;
font-weight: 500;
transition: all 0.3s ease;
position: relative;
padding: 10rpx 20rpx;
border-radius: 14rpx;
display: flex;
justify-content: center;
&.active {
color: #fff;
font-weight: bold;
text-shadow: 0 0 10rpx rgba(0, 0, 0, 0.8);
background: url('/static/nft/dingbutubiao_liang.png') no-repeat center center;
background-size: 100% 100%;
}
&.font-large {
font-size: 34rpx;
}
&.font-mid {
font-size: 30rpx;
}
&.font-small {
font-size: 26rpx;
}
}
.nav-bar {
position: absolute;
bottom: 0;
left: 0;
right: 0;
display: flex;
padding: 30rpx 40rpx;
padding-bottom: calc(30rpx + env(safe-area-inset-bottom));
z-index: 20;
}
.btn-secondary,
.btn-skip {
flex: 1;
height: 88rpx;
line-height: 88rpx;
border-radius: 44rpx;
font-size: 36rpx;
font-family: 'yt', sans-serif;
font-weight: 600;
border: none;
display: flex;
align-items: center;
justify-content: center;
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.3);
text-shadow: 0 4rpx 4rpx rgba(0, 0, 0, 0.3);
}
.btn-secondary {
background: rgba(255, 255, 255, 0.2);
backdrop-filter: blur(10rpx);
color: #e6e6e6;
border: 2rpx solid rgba(255, 255, 255, 0.4);
margin-right: 20rpx;
}
.btn-secondary::after {
border: none;
}
.btn-skip {
background: linear-gradient(165deg, #F0E4B1 0%, #F08399 50%, #B94E73 90%, #834B9E 100%);
color: #e6e6e6;
}
</style>