Vue实现购物车小球动画

思路:  

  1.因页面分组件分的比较细,由图可知是组件5到组件4的联动. 如果利用组件间通信需要 子组件5 -->组件3-->所有组件的父组件-->组件4, 层级略显复杂,所以使用了vuex状态管理管理数据.

  2.动画处理:

     利用vue的 transition 标签来处理动画.

     小球运动的轨迹是一条抛物线, 可使X轴做匀速运动, Y轴贝塞尔曲线, 可以考虑设定两个嵌套的DOM来控制运行轨迹.外层控制Y轴动画, 内层控制X轴动画.

     因小球落点是同一个位置, 可以将小球设定到落点位置, 动态获取初始值来决定小球的运动轨迹.

  3.如果连续点击小球, 则页面上可能显示多个小球, 所以需要设置多个小球保证页面上可以显示多个小球.

 结构图:

html (Ball.vue) :

      <div class="ball-container">
        <transition v-for="(ball,index) in balls" :key="index" name="drop" @before-enter="beforeEnter" @enter="enter" @after-enter="afterEnter">
          <div class="ball" v-show="ball.show" >
            <div class="inner inner-hook"></div>
          </div>
        </transition>
      </div>

css (Ball.vue):

      .ball-container
        .ball
          position fixed
          left .64rem
          bottom .44rem
          z-index 200
          width .32rem
          height .32rem
          border-radius 50%
          // background rgb(0, 160, 220)
          &.drop-enter-active
            transition: all .4s cubic-bezier(0.49, -0.29, 0.75, 0.41)
          .inner
            width .32rem
            height .32rem
            border-radius 50%
            background rgb(0, 160, 220)
            transition: all .4s linear

js (Ball.vue)

  computed: {
    ...mapState({
      balls: state => state.balls.balls,
      dropBall: state => state.balls.dropBall
    })
  },
  methods: {
    ...mapMutations(['changeShow', 'changeDropBall']),
    beforeEnter (el) {
      let count = this.balls.length
      while (count--) {
        let ball = this.balls[count]
        if (ball.show) {
          //getBoundingClientRect返回值是一个 DOMRect 对象, 此包含了一组用于描述边框的只读属性——left、top、right和bottom,单位为像素。
          //除了 width 和 height 外的属性都是相对于视口的左上角位置而言的。
          let rect = ball.el.getBoundingClientRect()
          let x = rect.left - 32
          let y = -(window.innerHeight - rect.top - 22)
          //小球外层控制y轴的运动轨迹,translate3d()可以开启硬件加速
          el.style.display = ''
          el.style.webkitTransform = `translate3d(0, ${y}px, 0)`
          el.style.transform = `translate3d(0, ${y}px, 0)`
          //内层小球控制x轴运动轨迹,内外层运动方式是不一样的, y轴贝塞尔曲线, x轴匀速.
          let inner = el.getElementsByClassName('inner-hook')[0]
          inner.style.webkitTransform = `translate3d(${x}px, 0, 0)`
          inner.style.transform = `translate3d(${x}px, 0, 0)`
        }
      }
    },
    enter (el) {
      // 触发浏览器重绘
      /* eslint-disable no-unused-vars */
      let rf = el.offsetHeight
      this.$nextTick(() => {
        el.style.webkitTransform = 'translate3d(0, 0, 0)'
        el.style.transform = 'translate3d(0, 0, 0)'
        let inner = el.getElementsByClassName('inner-hook')[0]
        inner.style.webkitTransform = `translate3d(0, 0, 0)`
        inner.style.transform = `translate3d(0, 0, 0)`
      })
    },
    afterEnter (el) {
      // 删除数组第一个元素, 并返回第一个元素,因对象都是指向地址,所以操作dropBall数组也就操作了balls数组
      let ball = this.dropBall.shift()
      if (ball) {
        ball.show = false
        el.style.display = 'none'
      }
    }
  }

store里面的moudle (balls.js):

const balls = {
  state: {
    balls: [{
      show: false
    }, {
      show: false
    }, {
      show: false
    }, {
      show: false
    }, {
      show: false
    }],
    dropBall: []
  },
  mutations: {
    changeShow (state, {index, isShow, el}) {
      state.balls[index].show = isShow
      state.balls[index].el = el
    },
    changeDropBall (state, ball) {
      state.dropBall.push(ball)
    }
  }
}

export default balls

增加商品数量(即点击+号)触发小球动画的页面 (Cartcontrol.vue):

  computed: {
    ...mapState({
      balls: state => state.balls.balls
    })
  },
  methods: {
    ...mapMutations(['changeShow', 'changeDropBall']),
    addCart (event) {
      for (let i = 0; i < this.balls.length; i++) {
        if (!this.balls[i].show) {
          this.changeShow({index: i, isShow: true, el: event.target})
          this.changeDropBall(this.balls[i])
          return
        }
      }
    }
  }

此DOME是根据参考黄轶老师的仿饿了吗课程改编而来~

原文地址:https://www.cnblogs.com/qiezuimh/p/9173299.html