前言

抛物线动画应用场景较多,在购物车页面比较常见,那么,怎么来实现这样一个动画?

先看一下Demo图。

图片名称

实现原理及步骤

  1. 动态计算每一个加号到购物车的距离;
图片名称
      // 加号到顶部的距离
      let iconTop = this.$refs.banner.$el.clientHeight + this.$refs['goods-' + id][0].offsetTop + this.$refs['goods-' + id][0].clientHeight - 6
      // 商品列表滚动的距离
      const scrollTop = this.$refs.list.scroll.wrapperOffset.top - this.$refs.list.scroll.y + RESERVED_HEIGHT
      // 向上滚动
      if (scrollTop > 0) {
        iconTop = iconTop - scrollTop
      }
      // 加号到左边的距离
      const iconLeft = window.innerWidth - this.$refs['add-' + id][0].clientWidth - 12
      const x = iconLeft - this.$refs.cart.offsetLeft - this.$refs.cart.clientWidth / 2
      const y = window.innerHeight - (this.$refs.cartView.clientHeight - this.$refs.cart.offsetTop - this.$refs.cart.clientHeight / 2) - iconTop + 10
  1. 利用css3 动画。translate来做位移,从加号图标按钮到购物车按钮的x、y方向发生的位移距离。rotate做图标旋转,让动画更加真实;
  2. 根据抛物线方程式y=a*x2,计算出每一个阶段加号图标应该出现的位置;
// 计算a的值
const a = y / (x * x)
  1. 利用create-keyframe-animation,把计算好的js代码转义成CSS动画代码,并绑定到对应document的node(加号图标)上。
  2. 动画结束后,进行相关操作。购物车放大缩小一下,价格以及数量增加。
    // 取消选择商品
    unselectGoods (id) {
      this.goodsList.forEach(goods => {
        if (goods.id === id && goods.selectCount > 0) {
          if (goods.iconDisabled) {
            return
          }
          goods.selectCount--
          return 0
        }
      })
    },
    // 选择商品
    selectGoods (id) {
      this.goodsList.forEach(goods => {
        if (goods.id === id && goods.selectCount < goods.surplus) {
          if (goods.iconDisabled === true) {
            return
          }
          goods.iconDisabled = true
          const _self = this
          this.paoAnimation(id, function () {
            // 抛物线动画结束事件
            animations.runAnimation(_self.$refs.cart, 'cartScale')
            goods.selectCount++
            goods.iconDisabled = false
          })
          return 0
        }
      })
    },
    // 抛物线动画
    paoAnimation (id, callback) {
      // 加号到顶部的距离
      let iconTop = this.$refs.banner.$el.clientHeight + this.$refs['goods-' + id][0].offsetTop + this.$refs['goods-' + id][0].clientHeight - 6
      // 商品列表滚动的距离
      const scrollTop = this.$refs.list.scroll.wrapperOffset.top - this.$refs.list.scroll.y + RESERVED_HEIGHT
      // 向上滚动
      if (scrollTop > 0) {
        iconTop = iconTop - scrollTop
      }
      // 加号到左边的距离
      const iconLeft = window.innerWidth - this.$refs['add-' + id][0].clientWidth - 12
      const x = iconLeft - this.$refs.cart.offsetLeft - this.$refs.cart.clientWidth / 2
      const y = window.innerHeight - (this.$refs.cartView.clientHeight - this.$refs.cart.offsetTop - this.$refs.cart.clientHeight / 2) - iconTop + 10
      const a = y / (x * x)
      let animation = {}
      for (let i = 0; i <= 10; i++) {
        animation[(i * 10) + '%'] = {transform: `translate(${-i / 10 * x}px,  ${(-i / 10 * x) * (-i / 10 * x) * a}px) rotateZ(${360 * (1 - i / 10)}deg)`}
      }
      animations.registerAnimation({
        name: 'move-' + id,
        animation,
        presets: {
          duration: 250,
          easing: 'linear',
          fillMode: 'forwards',
          resetWhenDone: true
        }
      })
      animations.runAnimation(this.$refs['add-' + id][0], 'move-' + id, callback)
    },
    // 缩放动画
    registerScalAnimation () {
      const animation = {
        '0%': {transform: `scale(1)`},
        '25%': {transform: `scale(1.1)`},
        '50%': {transform: `scale(1.2)`},
        '75%': {transform: `scale(1.1)`},
        '100%': {transform: `scale(1)`}
      }
      animations.registerAnimation({
        name: 'cartScale',
        animation,
        presets: {
          duration: 300,
          resetWhenDone: true
        }
      })
    },

注意事项

  1. 点我[具体代码实现]
  2. copy一个加号图标用来做动画,原来的按钮还是放在原来的位置。
  3. 动画过程中禁止再次点击
  4. 在使用create-keyframe-animation时,为了使动画可以重复使用,需要加resetWhenDone: true。
  5. 推荐使用CSS3动画生成器
Logo

前往低代码交流专区

更多推荐