评论

微信小程序中实现丝滑的加购动画

通过css变量、css animation以及动画监听等技术点,实现丝滑的加购动画

前言

大家在商城类小程序中经常会看到如下图的加购动画,丝滑的抛物线动画进入购物车的过程给用户带来了更好的购物体验~ 这样的动画其实需要考虑到很多情况,要不然就很容易出现问题。

在接下来的文章中,我们将手把手教会大家实现这样的功能。

二、技术分析

2.1 现象剖析

首先简单的看下整个加购动画的过程,小球的出现是从用户点击处产生的,随即完成一个抛物线后,进入金额处(购物车),最后购物车产生放大效果。

我们通常实现动画的方式一般就是两种,第一种就是js 控制运行的轨迹,第二种就是css实现。js实现的话势必会出现性能问题,在某些边界情况下会出现卡顿,作为前端人员想必大家都知道~

如果使用css实现的话,我们需要解决以下几个问题:

  1. 如何确认和设置小球的初始位置?
  2. 如何实现一个抛物线的动画?
  3. 如何知道抛物线动画结束了?并通知购物车发生抖动和放大效果?

2.2 分步实现

1.加购小球

我们知道在小程序中是没有办法动态创建一个节点的,所以小球是需要在当前页面事先准备好,以一个组件身份存在~

小球组件的初始位置由用户点击位置进行决定,所以小球的position必须是个变量,让我们想到了css变量~

组件接收外部两个属性

  • showBall控制显隐
  • position,通过style去定义css var变量从而设置小球初始位置

如下是简单的小球组件代码:

// animationBall.wxml

// animationBall.wxss
.ball-box {
  width: 20rpx;
  height: 20rpx;
  border-radius: 100%;
  position: fixed;
  z-index: 10;
  left: var(--startX);
  top: var(--startY);
}
// animationBall.js
Component({
  properties: {
    showBall: {
      type: Boolean,
      value: false
    },
    position: {
      type: Object,
      value: {
      }
    }
  },
  observers: {
    "position.startX, position.startY": function(startX, startY) {
      let style = `--startX:${startX}px;--startY:${startY}px;--endX: 15vw;--endY: 92vh`;
      this.setData({
        style
      });
    }
  }
});


2.页面点击事件

我们在页面中添加小球组件,并且给加购点击动作添加事件~

这样当我们点击时候就可以初始化小球的位置以及让小球展示出来

// 购买页面点击事件 buy.js
buy(event){
  this.setData({
    "position.startX": event.touches[0].clientX,
    "position.startY": event.touches[0].clientY,
    showBall: true
  });
}
// buy.wxml

    


3.animation动画拆解

我们确定了小球的初始位置和终点位置(终点位置是固定的,这里不赘述了),接下来就是需要去剖析抛物线动画~

一个抛物线整体去看,会比较没有头目,但是我们可以从某个单一维度去看整个小球运动的过程,然后再将这几个维度的动画组合起来,就可以完成整个动画的设计。我们分成三个维度,分别是y轴的位移、x轴的位移、小球大小、小球透明度。

1.y轴位移:小球先有一小段的上升,上升到最高点,这一段y轴位移一直在增加,我们给它动画叫做throwTopY,接下来抛物线下降,这个过程y轴一直在减少,一直减少到我们终点为止,我们叫做throwDropY。

// animationBall.wxss
@keyframes throwTopY {
  0% {
    top: var(--startY);
  }
  100% {
    top: calc(var(--startY) - 120rpx);
  }
}
@keyframes throwDropY {
  0% {
    top: calc(var(--startY) - 120rpx);
  }
  100% {
    top: var(--endY);
  }
}

2.x轴位移:小球x轴的位移在整个运动过程中都是从右向左变化的,我们认为一直是在线性变化的,我们动画称为throwX。

// animationBall.wxss
@keyframes throwX {
  0% {
    left: var(--startX);
  }
  100% {
    left: var(--endX);
  }
}

3.小球大小:小球大小在整个过程中,上升阶段是变大的,我们称为scaleTop, 当下降的时候小球一直在变小,称为scaleDrop。

// animationBall.wxss
@keyframes scaleSize {
  0% {
    width: 20rpx;
    height: 20rpx;
  }
  100% {
    width: 10rpx;
    height: 10rpx;
  }
}

4.小球透明度:小球的透明度在初始阶段时候是能看到的,然后再接下来的过程是慢慢变成透明,直到完全透明,我们称showAndHide

@keyframes showAndHide {
  0% {
    opacity: 1;
  }
  90% {
    opacity: 0.9;
  }
  100% {
    opacity: 0;
  }
}

按照以上的分析,单个维度的动画都已经写出来了,剩下来就是将他们组合在一起,组合的核心就是动画时间、动画曲线(贝塞尔曲线)以及动画停留在哪里~

假设整个动画的过程是需要0.5秒,上升过程我们假定0.2秒,下降的过程0.3秒

我们先用伪代码写出思路来,思考0.2秒的动画中发生了什么事情?

  • y轴位移变大
  • 小球变大

所以这个0.2秒发生了 = throwTopY + scaleTop

0.3秒发生了什么事情?

  • y轴位移变小
  • 小球变小

所以这个0.3秒发生了 = throwDropY + scaleDrop

那么整体的0.5秒钟x轴和透明度其实也发生了变化,0.5秒的throwX,0.5秒的showAndHide

思路有了之后,代码其实就已经出来了

// animationBall.wxss
.animationBall {
  animation-fill-mode: forwards;
  animation: throwTopY 0.2s cubic-bezier(0, 0.3, 0.3, 1) forwards, 
             scaleTop 0.2s cubic-bezier(0.48, 0.33, 0.24, 1.18) forwards, 
             throwDropY 0.3s cubic-bezier(0.7, 0, 1, 0.7) 0.2s forwards,
             scaleDrop 0.3s cubic-bezier(0.48, 0.33, 0.24, 1.18) 0.2s forwards, 
             throwX 0.46s linear forwards, 
             showAndHide 0.5s linear forwards;
}
// animationBall.wxml
<view class="ball-box animationBall" wx:if="{{showBall}}" style="{{style}}"></view>


上诉中涉及到贝赛尔曲线问题,这块大家自行了解下,不是本篇文章重点。

至此我们完成了小球抛物线动画的实现,不过还有个问题,我们如何知道小球完成了抛物线,并且通知到当前页面的购物车,让购物车发生抖动或者变大的效果呢?重点就在于如何通知动画完成~

我们可以对小球绑定一个监听动画的事件bindanimationend,这个事件代表最后一个动画结束,我们最后一个动画是showAndHide,所以我们通过此去进行判断即可,抛出事件后,页面获取到再做后续购物车的变化(这块也不是重点,本篇文章忽略)

// animationBall.wxml
<view bindanimationend="observeAnimation" class="ball-box animationBall" wx:if="{{showBall}}" style="{{style}}">
</view>
// animationBall.js
watchAnimation(res) {
 // 最后一组动画结束 抛出事件通知外部
 if (res?.detail?.animationName === "showAndHide") {
    this.triggerEvent("animationHasDone");
 }
}


以上就是所有的内容了,按照这样操作,你也可以实现丝滑的加购动画啦~

最后一次编辑于  2023-10-25  
点赞 2
收藏
评论

3 个评论

  • 龍
    2024-03-27

    H5这个动画很好实现,微信端我真的是麻了,不能动态创建元素,意味着只能等待上一个动画结束才能开始下一个动画,作者示例中也没有快速点击吧,都是等待动画完成再点,H5中疯狂点击都不会出现问题.因为每个动画都是异步的,看起来会更加丝滑,目前有没有更好的解决方案呢?还是说只能限制动画频次或者点击频次

    2024-03-27
    赞同
    回复
  • 九九&点单星®
    九九&点单星®
    2024-01-27

    完整的代码有不?git地址来一个哇。

    2024-01-27
    赞同
    回复
  • Rung
    Rung
    2023-10-27

    nice

    2023-10-27
    赞同
    回复
登录 后发表内容