原生小程序写卡片堆叠式轮播图
首先看下最终的效果,三张卡片堆叠式swiper,居中的为展示,左右两边为前一个和后一个,如果是第一长,或者最后一张,对应的前后无阴影堆叠,可进行跟手切换
实现思路
一共渲染出3个卡片,然后根据显示位置设置active,prev,next等属性,监听用户的滑动行为,对3个卡片的位置进行调整,然后只在touchMove时进行切换
代码如下
WXML:
<!-- pages/homeTest/homeTest.wxml -->
<view class="container" bindtouchstart="touchStart" bindtouchmove="touchMove" bindtouchend="touchEnd">
<view class="swiper-item {{currentIndex == item.id ? 'activeItem' : ''}} {{prevIndex == item.id ? 'prevItem' : ''}} {{nextIndex == item.id ? 'nextItem' : ''}}"
style="{{currentIndex == item.id ? activeAnimation : prevIndex == item.id ? prevAnimation : nextIndex == item.id ? nextAnimation : ''}}"
wx:for="{{imgUrls}}" wx:index="{{index}}" wx:key="id">
<image mode="aspectFill" src="{{item.image}}" class="slide-image" />
</view>
</view>
<view class="dots-box own-class">
<view class="dots {{currentIndex == index ? 'bg-333' : ''}}" wx:for="{{imgUrls}}" wx:key="index"></view>
</view>
WXSS:
page {
background-color: #fff;
}
.container {
display: flex;
flex-direction: row;
justify-content: center;
position: relative;
margin-top: 200rpx;
}
.swiper-item {
position: absolute;
display: flex;
box-sizing: border-box;
z-index: 1;
display: none;
}
.slide-image {
width: 617rpx;
height: 636rpx;
border-radius: 16rpx;
z-index: 1;
box-shadow: 10rpx 5rpx 40rpx rgba(0, 0, 0, 0.5);
}
/* 上一个 */
.prevItem {
left: 17rpx;
transform: scale(1) ;
display: block;
opacity: 0.4;
}
.nextItem {
right: 17rpx;
transform:scale(1) ;
display: block;
opacity: 0.4;
}
.activeItem {
position: absolute;
transform: scale(1.1);
display: block;
z-index: 3;
}
.dots-box {
display: flex;
justify-content: start;
align-items: center;
padding-left: 32rpx;
}
.dots {
width: 20rpx;
height: 8rpx;
margin: 0 4rpx;
background: #D9D9D9;
border-radius: 4px;
margin-top: 170rpx;
}
.bg-333 {
background-color: #A9B3FA;
}
JS:
// pages/homeTest/homeTest.js
Page({
/**
* 页面的初始数据
*/
data: {
// 图片数组
prevIndex: 0,
currentIndex: -1,
nextIndex: 0,
activeAnimation: '',
prevAnimation: '',
nextAnimation: '',
imgUrls: [ // 卡片数据
{
id: 0,
title: "Card 1",
image: "https://qiniusleep.25youpin.com/bgImages/202304211420346150.png"
},
{
id: 1,
title: "Card 2",
image: "https://qiniusleep.25youpin.com/bgImages/202304211420304869.png"
},
{
id: 2,
title: "Card 3",
image: "https://qiniusleep.25youpin.com/bgImages/202304211419587459.png"
},
// {
// id: 3,
// title: "Card 4",
// image: "https://qiniusleep.25youpin.com/bgImages/202304211420329910.png"
// },
// {
// id: 4,
// title: "Card 5",
// image: "https://qiniusleep.25youpin.com/bgImages/202304141656539615.png"
// },
],
startX: 0,
// 移动方向
direction: '',
//开始动画
startAnimate:false
},
/**
* 生命周期函数--监听页面加载
*/
onLoad(options) {
this.initIndex()
},
initIndex() {
this.swiperChange({
detail: {
current: 0
}
})
},
async swiperChange(e) {
let currentIndex = e.detail.current; // 获取当前swiper-item的index
let prevIndex = currentIndex > 0 ? currentIndex - 1 : this.data.imgUrls.length - 1; // 获取前一个swiper-item的index
let nextIndex = currentIndex < this.data.imgUrls.length - 1 ? currentIndex + 1 : 0; // 获取后一个swiper-item的index
this.setData({
currentIndex: e.detail.current,
prevIndex: prevIndex,
nextIndex: nextIndex,
});
},
//touchstart 事件处理函数
touchStart(e) {
// 记录当前手指触摸屏幕时的坐标
this.setData({
startX: e.touches[0].pageX
});
},
// touchmove 事件处理函数
touchMove(e) {
if(this.data.startAnimate)return
// 获取当前手指的坐标
let currentX = e.touches[0].pageX;
// 计算手指的滑动方向
let progress = ((Math.abs(currentX - this.data.startX)) / 350).toFixed(2);
let direction = currentX - this.data.startX > 0 ? 'right' : 'left';
if (currentX - this.data.startX == 0) return
if (progress > 1) progress = 1
// 根据滑动方向执行相应的操作
// 设置元素的位置和大小
let scale = progress * 0.1;
let threshold = 0.15 //切换的阈值
let opacity = 0.6 * (progress * (1 / threshold) > 1 ? "1" : progress * (1 / threshold));
if (direction === 'right') {
let activeStyle = `transform:scale(${1.1-scale}) translateX(${progress*50}rpx);opacity:${1-opacity};z-index:${progress>threshold?2:3};transition:all 0.1s`
let prevStyle = `transform:scale(${1+scale}) translateX(${progress*50}rpx);opacity:${0.4+opacity};z-index:${progress>threshold?3:2};transition:all 0.1s`
let nextStyle = `transform: translateX(${-progress*100}rpx);z-index:1;transition:all 0.1s`
this.setData({
activeAnimation: activeStyle,
prevAnimation: prevStyle,
nextAnimation: nextStyle,
direction: 'right',
progress: progress,
})
// 执行向右滑动的操作
} else if (direction === 'left') {
// 执行向左滑动的操作
let activeStyle = `transform:scale(${1.1-scale}) translateX(${-progress*50}rpx);opacity:${1-opacity};z-index:${progress>threshold?2:3};transition:all 0.1s`
let nextStyle = `transform:scale(${1+scale}) translateX(${-progress*50}rpx);opacity:${0.4+opacity};z-index:${progress>threshold?3:2};transition:all 0.1s`
let prevStyle = `transform: translateX(${progress*100}rpx);z-index:1;transition:all 0.1s`
this.setData({
activeAnimation: activeStyle,
prevAnimation: prevStyle,
nextAnimation: nextStyle,
direction: 'left',
progress: progress,
})
}
},
touchEnd() {
let threshold = 0.15 //切换的阈值
if (this.data.progress < threshold) {
this.setData({
activeAnimation: 'transition:all 0.4s;',
prevAnimation: 'transition:all 0.4s;',
nextAnimation: 'transition:all 0.4s;',
direction: '',
startAnimate:true,
})
} else {
if (this.data.direction == "left") {
let activeStyle = `transform:scale(1) translateX(-50rpx);opacity:0.4;z-index:2;transition:all 0.3s;`
let nextStyle = `transform:scale(1.1) translateX(-47rpx);opacity:1;z-index:3;transition:all 0.3s;`
let prevStyle = `transform:scale(1) translateX(100rpx);opacity:0.4;z-index:1;transition:all 0.3s;`
this.setData({
activeAnimation: activeStyle,
prevAnimation: prevStyle,
nextAnimation: nextStyle,
direction: 'left',
startAnimate:true,
})
} else if (this.data.direction === 'right') {
let activeStyle = `transform:scale(1) translateX(50rpx);opacity:0.4;z-index:2;transition:all 0.3s;`
let prevStyle = `transform:scale(1.1) translateX(47rpx);opacity:1;z-index:3;transition:all 0.3s;`
let nextStyle = `transform:scale(1) translateX(-100rpx);opacity:0.4;z-index:1;transition:all 0.3s;`
this.setData({
activeAnimation: activeStyle,
prevAnimation: prevStyle,
nextAnimation: nextStyle,
direction: 'right',
startAnimate:true,
})
}
}
setTimeout(() => {
this.setData({
activeAnimation: '',
prevAnimation: "",
nextAnimation: "",
progress: 0,
startAnimate:false,
})
this.animateEndchangeSwiper()
}, 300);
},
animateEndchangeSwiper() {
if (this.data.direction == 'right') {
this.swiperChange({
detail: {
current: this.data.prevIndex
}
})
} else if (this.data.direction == 'left') {
this.swiperChange({
detail: {
current: this.data.nextIndex
}
})
}
this.setData({
direction: '',
})
},
/**
* 生命周期函数--监听页面初次渲染完成
*/
onReady() {
},
/**
* 生命周期函数--监听页面显示
*/
onShow() {
},
/**
* 生命周期函数--监听页面隐藏
*/
onHide() {
},
/**
* 生命周期函数--监听页面卸载
*/
onUnload() {
},
/**
* 页面相关事件处理函数--监听用户下拉动作
*/
onPullDownRefresh() {
},
/**
* 页面上拉触底事件的处理函数
*/
onReachBottom() {
},
/**
* 用户点击右上角分享
*/
onShareAppMessage() {
}
})