218 lines
4.7 KiB
Vue
218 lines
4.7 KiB
Vue
<template>
|
||
<view class="banner-container">
|
||
<swiper
|
||
class="banner-swiper"
|
||
:circular="true"
|
||
:autoplay="true"
|
||
:interval="4000"
|
||
:duration="500"
|
||
:indicator-dots="false"
|
||
@change="onSwiperChange"
|
||
>
|
||
<swiper-item v-for="(banner, index) in banners" :key="index">
|
||
<view class="banner-content" @click="previewImage(index)">
|
||
<!-- 添加 lazy-load 属性实现懒加载 -->
|
||
<image
|
||
:src="banner.image"
|
||
mode="aspectFill"
|
||
class="banner-image"
|
||
lazy-load
|
||
:show-menu-by-longpress="false"
|
||
@load="onImageLoad(index)"
|
||
@error="onImageError(index)"
|
||
/>
|
||
<view class="banner-overlay"></view>
|
||
<view class="banner-text">
|
||
<text class="banner-title">{{ banner.title }}</text>
|
||
<text class="banner-subtitle">{{ banner.subtitle }}</text>
|
||
<text class="banner-desc">{{ banner.desc }}</text>
|
||
</view>
|
||
<!-- 可选:添加加载占位符 -->
|
||
<view v-if="!imageLoaded[index]" class="image-placeholder">
|
||
<view class="loading-spinner"></view>
|
||
</view>
|
||
</view>
|
||
</swiper-item>
|
||
</swiper>
|
||
<view class="banner-dots">
|
||
<view
|
||
v-for="(_, index) in banners"
|
||
:key="index"
|
||
:class="['dot', { active: currentIndex === index }]"
|
||
></view>
|
||
</view>
|
||
</view>
|
||
</template>
|
||
|
||
<script setup>
|
||
import { ref } from 'vue'
|
||
|
||
const banners = [
|
||
{
|
||
image: 'https://images.unsplash.com/photo-1534438327276-14e5300c3a48?w=800&q=80',
|
||
title: '突破自我',
|
||
subtitle: '超越极限',
|
||
desc: '科学训练 · 遇见更好的自己'
|
||
},
|
||
{
|
||
image: 'https://images.unsplash.com/photo-1517836357463-d25dfeac3438?w=800&q=80',
|
||
title: '专业指导',
|
||
subtitle: '高效训练',
|
||
desc: '私人定制 · 专属健身方案'
|
||
},
|
||
{
|
||
image: 'https://images.unsplash.com/photo-1571019614242-c5c5dee9f50b?w=800&q=80',
|
||
title: '健康生活',
|
||
subtitle: '从这里开始',
|
||
desc: '全方位 · 打造完美体态'
|
||
}
|
||
]
|
||
|
||
const currentIndex = ref(0)
|
||
// 记录每张图片的加载状态
|
||
const imageLoaded = ref(banners.map(() => false))
|
||
|
||
const onSwiperChange = (e) => {
|
||
currentIndex.value = e.detail.current
|
||
}
|
||
|
||
const previewImage = (index) => {
|
||
const urls = banners.map(banner => banner.image)
|
||
uni.previewImage({
|
||
urls: urls,
|
||
current: urls[index],
|
||
indicator: 'default',
|
||
loop: true
|
||
})
|
||
}
|
||
|
||
// 图片加载成功回调
|
||
const onImageLoad = (index) => {
|
||
imageLoaded.value[index] = true
|
||
console.log(`图片 ${index} 加载完成`)
|
||
}
|
||
|
||
// 图片加载失败回调
|
||
const onImageError = (index) => {
|
||
console.error(`图片 ${index} 加载失败`)
|
||
// 可选:设置默认占位图
|
||
// banners[index].image = '默认图片URL'
|
||
}
|
||
</script>
|
||
|
||
<style lang="scss" scoped>
|
||
.banner-container {
|
||
position: relative;
|
||
z-index: 2;
|
||
width: 100%;
|
||
}
|
||
|
||
.banner-swiper {
|
||
width: 100%;
|
||
height: 480rpx;
|
||
}
|
||
|
||
.banner-content {
|
||
width: 100%;
|
||
height: 100%;
|
||
position: relative;
|
||
overflow: hidden;
|
||
}
|
||
|
||
.banner-image {
|
||
width: 100%;
|
||
height: 100%;
|
||
}
|
||
|
||
/* 添加图片占位符样式 */
|
||
.image-placeholder {
|
||
position: absolute;
|
||
top: 0;
|
||
left: 0;
|
||
right: 0;
|
||
bottom: 0;
|
||
background: linear-gradient(135deg, #f5f7fa 0%, #e8ecf1 100%);
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
z-index: 1;
|
||
}
|
||
|
||
.loading-spinner {
|
||
width: 60rpx;
|
||
height: 60rpx;
|
||
border: 4rpx solid rgba(255, 255, 255, 0.3);
|
||
border-top: 4rpx solid #7AB5CC;
|
||
border-radius: 50%;
|
||
animation: spin 1s linear infinite;
|
||
}
|
||
|
||
@keyframes spin {
|
||
0% { transform: rotate(0deg); }
|
||
100% { transform: rotate(360deg); }
|
||
}
|
||
|
||
.banner-overlay {
|
||
position: absolute;
|
||
left: 0;
|
||
right: 0;
|
||
top: 0;
|
||
bottom: 0;
|
||
z-index: 2; /* 确保遮罩层在占位符上面 */
|
||
}
|
||
|
||
/* 其余样式保持不变 */
|
||
.banner-text {
|
||
position: absolute;
|
||
left: 36rpx;
|
||
top: 50%;
|
||
transform: translateY(-50%);
|
||
z-index: 3;
|
||
}
|
||
|
||
.banner-title {
|
||
display: block;
|
||
font-size: 48rpx;
|
||
font-weight: 800;
|
||
color: #ffffff;
|
||
margin-bottom: 8rpx;
|
||
text-shadow: 0 4rpx 16rpx rgba(80, 150, 190, 0.4);
|
||
}
|
||
|
||
.banner-subtitle {
|
||
display: block;
|
||
font-size: 56rpx;
|
||
font-weight: 800;
|
||
color: #E0F0FA;
|
||
margin-bottom: 16rpx;
|
||
text-shadow: 0 4rpx 16rpx rgba(80, 150, 190, 0.4);
|
||
}
|
||
|
||
.banner-desc {
|
||
display: block;
|
||
font-size: 26rpx;
|
||
color: rgba(255, 255, 255, 0.85);
|
||
}
|
||
|
||
.banner-dots {
|
||
display: flex;
|
||
justify-content: center;
|
||
gap: 16rpx;
|
||
margin-top: -100rpx;
|
||
position: relative;
|
||
z-index: 3;
|
||
}
|
||
|
||
.dot {
|
||
width: 48rpx;
|
||
height: 8rpx;
|
||
border-radius: 9999rpx;
|
||
background: #D0E4EE;
|
||
transition: all 0.3s ease;
|
||
}
|
||
|
||
.dot.active {
|
||
width: 64rpx;
|
||
background: linear-gradient(90deg, #7AB5CC, #9CCFDF);
|
||
}
|
||
</style> |