• 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是根据参考黄轶老师的仿饿了吗课程改编而来~

  • 相关阅读:
    python 模拟(简易)音乐播放器
    Python中的多态如何理解?(转)
    mysql踩得坑
    python简单模拟博客园系统
    04 信号量
    02 事件
    01 管道
    32 管道 事件 信号量 进程池 线程的创建
    02 验证进程之间是空间隔离的
    01 进程的其他方法
  • 原文地址:https://www.cnblogs.com/qiezuimh/p/9173299.html
Copyright © 2020-2023  润新知