小知识,大挑战!本文正在参与“程序员必备小知识”创作活动。
开头
某拼夕夕轮播效果APP上原效果大致如上图(将就着看吧),整体来说也挺简单的一个小效果的。
我们先分析一波:
- 同时展示多个头像进行轮播,这个微信小程序的swiper组件的
display-multiple-items属性就能轻松解决。 - 左边出去一个淡出效果,但是swiper组件轮播出去直接就是溢出隐藏
overflow: hidden,所以这里还要想个办法。 - 右边进入一个放大效果及外层一个简单的圆环效果。
大致实现这三步就行啦?那肯定不行了,你太小看产品经理了,加,我们抄也要抄得不一样,有个性:
- 先轮播反个方向,从左向右,可惜Swiper组件没有定义方向的属性,所以我们只能去手动反转180度了。
- 不固定死6个用户信息,当四个用户信息就开始轮播三个,五个就轮播四个,最多就展示六个。(听不懂??? 继续看下面咯)
正戏开始
一、先创建项目,写好基本的结构、样式。
index.wxml 文件
// index.wxml 完整代码
<view class="container">
<!-- 轮播框 -->
<view wx:if="{{photosData.length>=4}}" style="width: {{swiperBoxWidth + 'rpx'}}" class="photos__swiper-box">
<swiper bindanimationfinish="bindanimationfinish" bindchange="bindchange" circular="{{true}}"
duration="{{1000}}" interval="{{interval}}" autoplay="{{true}}" display-multiple-items="{{showNumberItem}}"
class="photos__swiper">
<block wx:for="{{photosData}}" wx:key="index">
<swiper-item catchtouchmove="stopTouchMove">
<view class="photos__item">
<view class="photos__avatar">
<image class="photos__avatar-img" src="https://p1-jj.byteimg.com/tos-cn-i-t2oaga2asx/gold-user-assets/2020/1/6/16f786fbd44167e9~tplv-t2oaga2asx-image.image"></image>
<view style="position: absolute;color: red;z-index: 10;bottom: 0;transform:rotate(180deg);font-size: 36rpx;font-weight: bold;">{{index + 1}}</view>
<view class="photo__circle"></view>
</view>
</view>
</swiper-item>
</block>
</swiper>
<!-- 更多按钮, 遮住第一个 -->
<view class="photos__more">
<image class="photos__more-img" src="../../assets/images/more.png"></image>
<view class="photos__more-block"></view>
</view>
</view>
</view>
index.wxss 文件
.container{
width: 100%;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
padding: 100rpx 0;
}
.photos__swiper-box{
transform: rotate(180deg);
position: relative;
width: 360rpx;
height: 84rpx;
}
.photos__swiper{
width: 100%;
height: 100%;
position: relative;
}
.photos__item{
position: relative;
width: 90rpx;
height: 80rpx;
display: flex;
align-items: center;
justify-content: center;
}
.photos__avatar{
width: 68rpx;
height: 68rpx;
position: relative;
opacity: 1;
}
.photos__avatar--scale{
transform: scale(0);
}
.photos__avatar-img{
width: 100%;
height: 100%;
border-radius: 50%;
transform: rotate(180deg);
}
/*更多按钮*/
.photos__more{
width: 90rpx;
height: 100%;
position: absolute;
top: 0;
left: 0;
z-index: 1;
transform: rotate(180deg);
display: flex;
justify-content: center;
align-items: center;
}
.photos__more-img{
width: 68rpx;
height: 68rpx;
position: relative;
z-index: 1;
border-radius: 50%;
}
.photos__more-block{
position: absolute;
z-index: 0;
right: 0;
width: 40rpx;
height: 100%;
background-color: #fff;
}
/*红色圆环*/
.photo__circle{
position: absolute;
width: 80rpx;
height: 80rpx;
border-radius: 50%;
border: 2rpx dashed #FF6464;
top: -6rpx;
left: -6rpx;
box-sizing: border-box;
opacity: 0;
transform: rotate(0deg);
}
index.js
const app = getApp()
Page({
data: {
swiperBoxWidth: 360, // 一个swiper-item是90rpx. 假设展示3个,则4*90; 展示4个,则5*90 ...
showNumberItem: 4, // 展示的个数, 4/5/6/6+四种情况, 为了效果默认会加上一个; 注意这个数不能大于数组长度
interval: 5000, // 多久滑动一次
swiperIndex: 0, // 当前swiper所在的索引
photosData: [{}, {}, {}, {}, {}],
}
})
- 大致上面的代码,能实现一个同时展示多个头像的自动轮播,并且我通过
rotate(180deg)给整个盒子旋转了180度,让轮播方向从左到右。 - 我们需要注意的是swiper组件的
display-multiple-items属性,当你同时展示了4个元素,那么你的<swiper-item/>必须要有五个才行,这样才能自动轮播。 - 为了实现上面 分析第二步骤 的淡出轮播效果,我想了个办法,就是我们让每次轮播出去的那个元素的下一个元素加个淡出效果,再把固定在右边的“更多按钮”元素定位到swiper组件上面,这样子就能实现了。(看不懂的自己想,哈哈~)
二、出去与进来的动画效果
动画效果这里主要是用到了CSS3的动画(animation)来实现,主要的想法就是在对应的时机添加相应的类名来完成,就是怎么简单 easy~ 。
/* 动画 */
.fade-out{ /*淡出*/
transition: 1000ms linear;
transition-property: transform, opacity;
transform-origin: 50% 50% 0;
opacity: 0;
}
.scale-in{ /*放大进入*/
transition: 500ms ease;
transition-property: transform;
transform-origin: 50% 50% 0;
transform: scale(1);
}
.rotate-in{ /*旋转消失*/
animation: rotateOpacity 1800ms linear 700ms; /*放大进入的动画大概300ms,但中间停留个200ms做准备*/
}
.opacity-in{
animation: opacity 6000ms ease;
}
@keyframes rotateOpacity{
0% {
opacity: 0;
transform: rotate(0deg);
}
10% {
opacity: 1;
}
80% {
opacity: 0.8;
}
100% {
opacity: 0;
transform: rotate(120deg);
}
}
@keyframes opacity{
0%{
opacity: 0.2;
}
50%{
opacity: 1;
}
100%{
opacity: 0;
}
}
动画效果这里没啥好说的,因为是高仿也拿不到拼多多的动画效果具体数值,所以基本都是靠感觉啦。
补:
animation: name duration timing-function delay iteration-count direction;
我们找到对应的元素添加相关的动画类名
<view class="{{item.outAni ? 'fade-out' : ''}} {{item.inAni ? 'scale-in' : ''}} {{item.scale ? 'photos__avatar--scale' : ''}} photos__avatar">
...
<view class="{{item.inAni ? 'rotate-in' : ''}} photo__circle"></view>
</view>
因为很多时候信息都是通过接口请求回来的,所以我们先简单模拟一下请求数据。
onLoad: function() {
setTimeout(() => {
let result = [{}, {}, {}, {}];
// 因为有一个被遮挡, 所以要复制一个元素
if(result.length > 3 && result.length <= 7) {
result.push(JSON.parse(JSON.stringify(result[0])))
}
this.setData({
photosData: result,
showNumberItem: this.getShowItemNumber(result.length),
swiperBoxWidth: this.pxToRpx(this.rpxToPx(90) * this.getShowItemNumber(result.length)), // swiper需求要一个宽度
})
}, 800)
},
// 获取swiper应该同时展示多少个
getShowItemNumber(num) {
switch(num) {
case 5: return 4; break;
case 6: return 5; break;
case 7: return 6; break;
default: return 7;
}
},
// rpx转px
rpxToPx(rpx) {
return rpx / 750 * wx.getSystemInfoSync().windowWidth;
},
// px转rpx
pxToRpx(px) {
return px * 750 / wx.getSystemInfoSync().windowWidth;
},
以上JS逻辑只是做了一件事情就是计算一个这个Swiper组件的宽度,因为我们上面 分析第五步骤 说过,我们展示的个数是不确定的,所以我们的个数由返回的数据来决定。
然后我们要做的就是何时让这些类名生效,何时添加这些类名? 这个时机是个重点,主要我们会用到Swiper组件的bindchange()和bindanimationfinish()事件,下面我们实现这个事件的逻辑就大功告成了。
const app = getApp()
Page({
...,
bindchange(e) {
let current = e.detail.current;
let swiperIndex = this.data.swiperIndex;
let swiperData = this.data.photosData;
// 1.整体初始化
swiperData.forEach(item => {
item.outAni = false;
item.inAni = false;
item.scale = false; // 进入时要放大, 所以要先将其缩放为0
});
// 2.给需要动画的元素打标识
if(swiperIndex >= swiperData.length - 1) {
swiperData[0].outAni = true;
}else {
swiperData[swiperIndex+1].outAni = true;
}
swiperData[this.getNewIndex(swiperIndex, this.data.showNumberItem, swiperData.length)].scale = true;
// 3. 最后赋值
this.setData({
swiperIndex: current,
photosData: swiperData,
})
},
bindanimationfinish(e) {
let swiperIndex = this.data.swiperIndex;
let swiperData = this.data.photosData;
let targetIndex = this.getNewIndex(swiperIndex - 1, this.data.showNumberItem, swiperData.length);
// 放大进入
swiperData[targetIndex].inAni = true;
this.setData({
photosData: swiperData,
})
},
// 由一个索引值添加一定增量后获取另一个索引值
getNewIndex(I, R, L) {
/**
* @param {*} I: 取的索引值
* @param {*} R: 增量
* @param {*} L: 添加增量后的索引值
*/
return (I + R) >= L ? (I + R - L) : (I + R)
}
})
主要添加类名的时机是重点,自己细细品吧,哈哈~
源码
以上源码请点它。传送门
至此,本篇文章就写完啦,撒花撒花。
希望本文对你有所帮助,如有任何疑问,期待你的留言哦。
老样子,点赞+评论=你会了,收藏=你精通了。