VUE2.0 饿了么学习笔记(11)滚动小球的实现

滚动小球的实现,只需要考虑enter效果即可,用emit和on派发和监听事件,cartcontrol.vue派发事件{this.$emit('cart-add', event.target)};,goods.vue监听事件cart-add,并添加监听函数cartAdd<cartcontrol v-on:cart-add="cartAdd" :food="food"></cartcontrol>,在监听函数cartAdd中拿到shopcart的DOM对象 this.$refs.shopcart.drop(target);,返回shopcart.vue编写drop()获取下落小球的初始位置,并实现beforeEnter,enter和afterEnter方法,设置动画

1)在shopcart中定义小球,ball-container中存放小球,小球的状态存放在一个数组中,初始状态都是隐藏的

 <div class="ball-container">
                <div v-for="ball in balls" :key="ball.id">
                        <transition name="drop" @before-enter="beforeEnter" @enter="enter" @after-enter="afterEnter">
                            <div v-show="ball.show" class="ball">
                                <div class="inner inner-hook">
                                </div>
                            </div>
                        </transition>
                </div> 
            </div>
2)为小球添加样式,ball位于购物车图片的上方.ball position:fix z-index:200并设置缓动函数: transition 

  设置inner,inner代表内层的一个小球,并设置缓动函数

3)此处没有设置enter和leave的动画,是因为要动态计算cartcontrol中加号的位置(即起始点宽高的位置,left或者是top的值),首先要拿到cartcontrol中加号这个元素。因为在cartcontrol点击加号会出发事件,我们可以在cartcontral中添加一个派发事件,将它的DOM对象传出来,即在点击事件中添加分发事件addCart(event) {this.$emit('cart-add', event.target)};

 methods: {
        addCart(event) { 
            //解决PC端双点击的问题
            if (!event._constructed) { //浏览器直接return掉,去掉自带click事件的点击
                return;
            }
            //console.log('click');//点击不生效,不要忘了在foodScroll中添加click: true 
            if (!this.food.count) {
                 //food.count是原json中不存在的属性,不能直接添加
                //this.food.count = 1;
                Vue.set(this.food, 'count', 1);//给this.food增加一个count属性,并初始化为1
            } else {
                this.food.count++;
            }
//设置滚动对象时,点击加号,设置一个派发事件,将DOM对象传出去,将target(DOM)作为cart.add事件的对象传入
//$emit, $on, $off 分别来分发、监听、取消监听事件:
            this.$emit('cart-add', event.target);
        },
        decreaseCart(event) {
            //解决PC端双点击的问题
            if (!event._constructed) { //浏览器直接return掉,去掉自带click事件的点击
                return;
            }
            if (this.food.count) {
                this.food.count--;
            }
        }
    }
};

4)之后在父组件goods中对cart-add进行监听并添加处理事件cartAdd(传入target),<cartcontrol v-on:cart-add="cartAdd" :food="food"></cartcontrol>,cartAdd函数添加在methods中

 cartAdd(target) {
     //拿到traget(DOM对象)之后,将其传入shopcart组件中drop(target){}方法,
     //此处用this.$refs调用子组件,访问DOM时用的是ref="menuWrapper"
    this.$nextTick(() => { //回调函数异步执行,两个动画效果就不会卡顿了
      this.$refs.shopcart.drop(target);
    });
   }

5)此处回到shopcart.vue编写drop(el){}方法,drop方法对应name="drop"的transition,并实现1)中的beforeEnter,enter和afterEnter方法,并在css中添加transition

 methods: {
            drop(el) {
                //console.log(el); //验证是否能正确输出
                //遍历balls,拿到第一个show为false的球,做一个动画
                for (let i = 0; i < this.balls.length; i++) {
                    let ball = this.balls[i];
                    if (!ball.show) { //show为false的球
                        ball.show = true; //小球下落
                        ball.el = el;//保留当前的DOM对象,用来计算位置
                        this.dropBalls.push(ball); //dropBalls存的是已经下落的小球
                        return;
                    }
                }
            },
       //定义三个钩子函数实现动画
            beforeEnter(el) { //el为当前执行transition动画的DOM对象
            //先找到所有为true的小球(连续点击的情况)
                let count = this.balls.length;
                while (count--) {
                    let ball = this.balls[count];
                    if (ball.show) { //这个是要运动的小球true
                        let rect = ball.el.getBoundingClientRect();//获得元素相当于视口的位置
                        let x = rect.left - 32;
                        let y = -(window.innerHeight - rect.top - 22);
                        el.style.display = ''; //v-show默认display:none,设置为空,让它显示
                        //外层元素是纵向的动画,内层元素是横向的动画
                        el.style.webkitTransform = `translate3d(0,${y}px,0)`;
                        el.style.transform = `translate3d(0,${y}px,0)`;
                        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) {
                /* 触发浏览器重绘,重绘之后才可以设置transform*/
                /* 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) { //动画完成
                let ball = this.dropBalls.shift();//删除并返回第一个ball
                if (ball) {
                    ball.show = false; //重置ball.show的状态
                    el.style.display = 'none';
                }
            },
   }

CSS:

 .ball-container
            .ball
                position fixed //相对于视口做布局
                left 32px
                bottom 22px
                z-index 200
                transition: all 0.6s cubic-bezier(0.49, -0.29, 0.75, 0.41)
                .inner
                    width 16px
                    height 16px
                    border-radius 50%
                    background rgb(0,160,220)
                    transition all 0.4s linear  //x轴做一个线性的过渡即可

猜你喜欢

转载自blog.csdn.net/qq_22317389/article/details/80391336