欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

【总结】微信小程序 - 用动画实现自定义轮播图

程序员文章站 2022-07-12 08:13:29
...

最近小程序有个需求要做一个类似轮播的自定义样式,搜了一下插件们,一般都用到了jQuery,但是小程序又不支持操作DOM,所以直接自己卷起袖子干吧。
如果有任何有问题的地方,欢迎各位看官指出,大家一起讨论才能进步地更快XD

文章基本流程:
1.效果图
2.实现代码
3.被放弃的提案及理由(TAT)
4.总结

1.首先放实现的效果图

因为需求里只有中间的三张图,所以之后会把旁边两张的opacity调成0,这里为了方便看就留着啦。

【总结】微信小程序 - 用动画实现自定义轮播图

2. 代码

a. wxml

这里之前考虑过要使用template,毕竟比较方便。但是每个图片又会再绑定不同的动画效果,所以就直接这样摊大饼展开写了,如果谁有更好的方法,答应我,一定要告诉我,好嘛 :)

<view style="white-space: nowrap;" class="box" bindtouchstart="touchstart" bindtouchmove="touchmove" bindtouchend="touchend">
    <view class="club" animation="{{animation1}}" bindtap="scrollLeft">
      <image src="{{clubs[0].image}}"/>
      <text>{{clubs[0].name}}</text>
    </view>
    <view class="club" animation="{{animation2}}" bindtap="scrollLeft">
      <image src="{{clubs[1].image}}"/>
      <text>{{clubs[1].name}}</text>
    </view>
    <view class="club" animation="{{animation3}}">
      <image src="{{clubs[2].image}}"/>
      <text>{{clubs[2].name}}</text>
    </view>
    <view class="club" animation="{{animation4}}" bindtap="scrollRight">
      <image src="{{clubs[3].image}}"/>
      <text>{{clubs[3].name}}</text>
    </view>
    <view class="club" animation="{{animation5}}" bindtap="scrollRight">
      <image src="{{clubs[4].image}}"/>
      <text>{{clubs[4].name}}</text>
    </view>
  </view>

b. wxss

这里因为没有涉及到图片互相遮盖的问题,所以大胆地使用了opacity,但是如果做成了叠加效果的话,我的方法是使用伪类:after或者:before在图片上加一个白色背景色带透明度的一层。

有关css的transform3d可以参考【这篇简单易懂的文章】
transform2d可以参考【这篇附带演示的文章】

.box {
  height: 340rpx;
  z-index: 0;
  margin: 50rpx 0;
}
.box .club {
  height: 140rpx;
  width: 140rpx;
  position: relative;
  display: inline-block;
}
.club image {
  height: 140rpx;
  width: 140rpx;
}
.club text {
  display: block;
  width: 100%;
  font-size: 24rpx;
  line-height: 40rpx;
  text-align: center;
}
.box .club:nth-child(1) {
  transform: scale(0.8, 0.8) translateX(-120rpx);
  opacity: 0.2;
  z-index: 10;
}
.box .club:nth-child(2) {
  transform: scale(1,1) translateX(-80rpx);
  opacity: 0.5;
  z-index: 100;
}
.box .club:nth-child(3) {
  transform: scale(1.4,1.4);
  z-index: 1000;
}
.box .club:nth-child(4) {
  transform: scale(1,1) translateX(80rpx);
  opacity: 0.5;
  z-index: 100;
}
.box .club:nth-child(5) {
  transform: scale(0.8, 0.8) translateX(120rpx);
  opacity: 0.2;
  z-index: 10;
}
.box .club:nth-child(1) text,
.box .club:nth-child(2) text,
.box .club:nth-child(4) text,
.box .club:nth-child(5) text{
  visibility: hidden;
}

c. js

①首先,要通过js的touchstart,touchmove和touchend事件判断手指的左右滑动。

此处参考了【这篇文章】

附送【小程序事件文档传送门】

//触摸开始事件
  touchstart: function(e) {
    console.log(e.touches[0].pageX);
    this.data.touchDot = e.touches[0].pageX;
    var that = this;
    this.data.interval = setInterval(function(){
      that.data.time+=1;
    },100);
  },
  //触摸移动事件
  touchmove: function(e) {
    let touchMove = e.touches[0].pageX;
    let touchDot = this.data.touchDot;
    let time = this.data.time;
    console.log("touchMove: " + touchMove + ", touchDot: " + touchDot + ", diff: " + (touchMove - touchDot));
    //向左滑动
    if(touchMove - touchDot <= -40 && time<10 &&!this.data.done) {
      console.log("向左滑动");
      this.data.done = true;
      this.scrollLeft();
    }
    //向右滑动
    if (touchMove - touchDot >= 40 && time < 10 && !this.data.done) {
      console.log("向右滑动");
      this.data.done = true;
      this.scrollRight();
    }
  },
  //触摸结束事件
  touchend: function(e) {
    clearInterval(this.data.interval);
    this.data.time = 0;
    this.data.done = false;
  }

②然后写scrollLeft和scrollRight事件

这里参考了【这篇文章】中设置动画的方式。

主要思想:在一定的时间(duration)之内,让图片向左(/右)移动,并伴以适当的scale以及opacity变换。然后再在0s之内完成两件事:一个是瞬间使刚才移动的图片回归原位(scale和opacity自然也不必说),还有一个是在图片的队列中shift(/pop)出一个来再push(/unshift)进去,并且在0s内setData。这样从视觉上看起来就是图片向左(/右)滑动了。

此处需要注意的是:图像动画的duration最好大于瞬间还原以及set数组的定时时间。如果他们的时间都设置为同样的话(如300),在滑动中肉眼能够看到明显的闪动(由于前后的图片切换)。

【小程序动画文档传送门】

//向左滑动事件
  scrollLeft(){
    var animation1 = wx.createAnimation({
      duration: 300,
      timingFunction: "linear",
      delay: 0
    })
    var animation2 = wx.createAnimation({
      duration: 300,
      timingFunction: "linear",
      delay: 0
    })
    var animation3 = wx.createAnimation({
      duration: 300,
      timingFunction: "linear",
      delay: 0
    })
    var animation4 = wx.createAnimation({
      duration: 300,
      timingFunction: "linear",
      delay: 0
    })
    var animation5 = wx.createAnimation({
      duration: 300,
      timingFunction: "linear",
      delay: 0
    })

    this.animation1 = animation1;
    this.animation2 = animation2;
    this.animation3 = animation3;
    this.animation4 = animation4;
    this.animation5 = animation5;

    this.animation1.translateX(-60).opacity(0).step();
    this.animation2.translateX(-140).opacity(0.5).scale(0.8,0.8).step();
    this.animation3.translateX(-110).opacity(0.5).scale(1,1).step();
    this.animation4.translateX(-70).opacity(1).scale(1.4,1.4).step();
    this.animation5.translateX(-30).opacity(0.5).scale(1,1).step();


    this.setData({
      animation1: animation1.export(),
      animation2: animation2.export(),
      animation3: animation3.export(),
      animation4: animation4.export(),
      animation5: animation5.export()
    })

    var that = this;
    setTimeout(function () {
      that.animation1.translateX(-50).opacity(0.2).scale(0.8, 0.8).step({ duration: 0, timingFunction: 'linear'});
      that.animation2.translateX(-40).opacity(0.5).scale(1, 1).step({ duration: 0, timingFunction: 'linear' });
      that.animation3.translateX(0).opacity(1).scale(1.4, 1.4).step({ duration: 0, timingFunction: 'linear' });
      that.animation4.translateX(40).opacity(0.5).scale(1, 1).step({ duration: 0, timingFunction: 'linear' });
      that.animation5.translateX(50).opacity(0.2).scale(0.8, 0.8).step({ duration: 0, timingFunction: 'linear' });
      that.setData({
        animation1: animation1.export(),
        animation2: animation2.export(),
        animation3: animation3.export(),
        animation4: animation4.export(),
        animation5: animation5.export()
      })
    }.bind(this), 195)

    let array = this.data.clubs;
    let shift = array.shift();
    array.push(shift);

    setTimeout(function () {
      this.setData({
        clubs: array
      })
    }.bind(this), 195)
  },

  //向右滑动事件
  scrollRight() {
    var animation1 = wx.createAnimation({
      duration: 300,
      timingFunction: "linear",
      delay: 0
    })
    var animation2 = wx.createAnimation({
      duration: 300,
      timingFunction: "linear",
      delay: 0
    })
    var animation3 = wx.createAnimation({
      duration: 300,
      timingFunction: "linear",
      delay: 0
    })
    var animation4 = wx.createAnimation({
      duration: 300,
      timingFunction: "linear",
      delay: 0
    })
    var animation5 = wx.createAnimation({
      duration: 300,
      timingFunction: "linear",
      delay: 0
    })

    this.animation1 = animation1;
    this.animation2 = animation2;
    this.animation3 = animation3;
    this.animation4 = animation4;
    this.animation5 = animation5;

    this.animation1.translateX(30).opacity(0.5).scale(1,1).step();
    this.animation2.translateX(70).opacity(1).scale(1.4, 1.4).step();
    this.animation3.translateX(110).opacity(0.5).scale(1, 1).step();
    this.animation4.translateX(120).opacity(0.2).scale(0.8, 0.8).step();
    this.animation5.translateX(130).opacity(0).step();


    this.setData({
      animation1: animation1.export(),
      animation2: animation2.export(),
      animation3: animation3.export(),
      animation4: animation4.export(),
      animation5: animation5.export()
    })

    var that = this;
    setTimeout(function () {
      that.animation1.translateX(-50).opacity(0.2).scale(0.8, 0.8).step({ duration: 0, timingFunction: 'linear' });
      that.animation2.translateX(-40).opacity(0.5).scale(1, 1).step({ duration: 0, timingFunction: 'linear' });
      that.animation3.translateX(0).opacity(1).scale(1.4, 1.4).step({ duration: 0, timingFunction: 'linear' });
      that.animation4.translateX(40).opacity(0.5).scale(1, 1).step({ duration: 0, timingFunction: 'linear' });
      that.animation5.translateX(50).opacity(0.2).scale(0.8, 0.8).step({ duration: 0, timingFunction: 'linear' });
      that.setData({
        animation1: animation1.export(),
        animation2: animation2.export(),
        animation3: animation3.export(),
        animation4: animation4.export(),
        animation5: animation5.export()
      })
    }.bind(this), 195)

    let array = this.data.clubs;
    let pop = array.pop();
    array.unshift(pop);

    setTimeout(function () {
      this.setData({
        clubs: array
      })
    }.bind(this), 195)
  }

3. 被放弃的提案

原本的需求是实现一个类似这样的效果(不要笑我用了opacity,解决办法我已经写在了上边的css部分里):
【总结】微信小程序 - 用动画实现自定义轮播图
但是当我满心欢喜,实现了静态效果,准备做动画的时候,发现小程序竟然不支持perspective!!!!!!!
啥?你问什么是perspective?来看【这篇】或者【上边wxss中提到的3d的这篇】

简单说perspective就是透视,如果没有透视,那么无论你怎么翻转怎么rotate,他都是一个矩形,或者一个平行四边形(skew),而不会变成一个梯形,就是没有近大远小的效果。perspective就相当于z轴的大小,越大透视效果越强。

如图(左边的就是小程序的rotateY):
【总结】微信小程序 - 用动画实现自定义轮播图

//实现透视状的css代码
.box .club:nth-child(1){
  transform-origin: 100% 50%;
  transform: perspective(600rpx) rotateY(45deg) translateX(420rpx) scale(1.5, 1.3);
  z-index: 0;
  opacity: 0.5;
}
.box .club:nth-child(2){
  transform-origin: 100% 50%;
  transform: perspective(600rpx) rotateY(45deg) translateX(200rpx) scale(1.3, 1);
  opacity: 0.5;
  z-index: 10;
}
.box .club:nth-child(3) {
  transform-origin: 100% 50%;
  transform: perspective(600rpx) rotateY(45deg) translateX(60rpx) scale(1.1, 0.9);
  opacity: 0.5;
  z-index: 100;
}
.box .club:nth-child(4) {
  transform: perspective(600rpx) rotateY(0deg);
  z-index: 1000;
}
.box .club:nth-child(5){
  transform-origin: 0 50%;
  transform: perspective(600rpx) rotateY(-45deg) translateX(-60rpx) scale(1.1, 0.9);
  opacity: 0.5;
  z-index: 100;
}
.box .club:nth-child(6){
  transform-origin: 0 50%;
  transform: perspective(600rpx) rotateY(-45deg) translateX(-200rpx) scale(1.3, 1);
  opacity: 0.5;
  z-index: 10;
}
.box .club:nth-child(7) {
  transform-origin: 0 50%;
  transform: perspective(600rpx) rotateY(-45deg) translateX(-420rpx) scale(1.5, 1.3);
  opacity: 0.5;
  z-index: 0;
}

无动画实现效果:
【总结】微信小程序 - 用动画实现自定义轮播图

4. 总结

通过这次补了一下css的transform的知识,还接触了下小程序的动画感觉还蛮不错的。虽然折腾了好几天,但是感觉很有意义!
感谢阅读
祝各位都有美好的一天 have a nice day~~~