Vue 2.0 实现购物车小球抛物线动画详解

作者:雾松

时间:2026-10-23

浏览:403

本文基于 Vue 2.0 详解购物车小球抛物线动画的实现过程。内容涵盖布局与样式编写,重点解析子组件与父组件间的数据同步问题,包括利用 Vue.set 更新属性及通过事件总线传递 DOM 对象。同时深入讲解 Transition 钩子函数的使用,确保小球动画流畅执行并正确重置状态。

文章目录

Vue 2.0 购物车小球抛物线动画实现

本文分享了基于 Vue 2.0 实现购物车小球抛物线动画的具体代码示例。

注:本项目参考了饿了么的设计,采用最新版本的 Vue 开发。视频中部分旧有写法已不再适用。

布局与样式代码

页面布局代码


<div class="ball-container">
 <transition name="drop"
       v-for="ball in balls"
       @before-enter="beforeDrop"
       @enter="dropping"
       @after-enter="afterDrop">
  <div v-show="ball.show" class="ball" v-bind:css="false">
   <div class="inner inner-hook" ></div>
  </div>
 </transition>
</div>

样式代码(采用 Stylus 预处理器编写)


.ball-container
 .ball
  position fixed
  left 32px
  bottom 22px
  z-index 200
  transition all 0.4s cubic-bezier(0.49,-0.29,0.75,0.41)
  .inner
   width 16px
   height 16px
   border-radius 50%
   background-color rgb(0,160,220)
   transition all 0.4s linear

核心逻辑代码

逻辑代码


data() {
  return {
   balls : [
    {
     show: false
    },
    {
     show: false
    },
    {
     show: false
    },
    {
     show: false
    },
    {
     show: false
    }
   ],
   dropBalls: []
  };
},   
methods: {
  drop(el) {
   for(let i = 0; i < this.balls.length; i++) {
    let ball = this.balls[i];
    if(!ball.show) {
     ball.show = true;
     ball.el = el;
     this.dropBalls.push(ball);
     return ;
    }
   }
  }
  beforeDrop(el) {
   let count = this.balls.length;
   while (count--) {
    let ball = this.balls[count];
    if(ball.show) {
     let rect = ball.el.getBoundingClientRect();
     let x = rect.left - 32;
     let y = -(window.innerHeight - rect.top - 22);
     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)`;
    }
   }
  },
  dropping(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)';
   });
  },
  afterDrop(el){
   let ball = this.dropBalls.shift();
   if(ball) {
    ball.show = false;
    el.style.display = 'none';
   }
  }
}

关于 getBoundingClientRect() 方法,请参阅此文章:https://www.jb51.net/article/134208.htm

说明:

组件结构与通信机制

goods 组件内部包含 menu(菜单 div)、foods(商品列表 div)以及 shopcart(购物车组件)。其中,foods 组件内嵌有 cartcontrol(即小球组件)。

组件间通信机制:主要涉及菜单与商品数据交互。

数据同步与状态管理

第一个问题:小球动画需要获取被点击商品的数量信息。

虽然可以通过 Vue 的 props 将 foods 数据传递给 cartcontrol,但这存在局限:子组件状态更新无法直接同步回父组件。此外,在子组件中为 food 注册 count 属性后,该属性也无法同步回 goods 组件。

解决方案:

引入全局 Vue 实例。

使用 Vue.set(target, key, value) 方法对 target 对象注册 count 属性。

第二个问题:小球点击后,需将累计商品数量传递给 shopcart 组件。

在 goods 组件的 computed 中定义计算方法,并通过 props 将该值传递给 shopcart。

由于 shopcart 仅对传入数据进行运算而不修改源数据,因此无需将结果同步回父组件。

抛物线运动与过渡钩子

第三个问题:实现购物车小球的抛物线运动效果。

要实现抛物线运动,首先确定落点均为购物车位置,而小球起始位置随机。因此需获取点击“+”号按钮时的 x, y 坐标。此外,抛物线动画仅在 enter 到 enter-to 阶段生效,leave 到 leave-to 阶段不执行,故需利用 Vue 提供的钩子函数来控制。

获取“+”号按钮的 x, y 坐标:

小球(cartcontrol)作为子组件,需将数据传递给 goods 父组件。可选方案包括使用 Vuex 或直接使用事件总线。在本饿了么 Demo 中,直接采用了事件总线方式。

创建一个空的 Vue 实例作为 Bus。在 cartcontrol 中,通过 Bus.$emit(key, ...arg) 触发监听,并在父组件中通过 Bus.$on(key, function(...arg)) 接收事件。将操作涉及的 DOM 对象作为参数传递即可。

Vue 提供的过渡钩子函数说明:

需注意,Vue 官网指出,若仅使用 JS 过渡,done 参数是必须的。若未正确添加 done,after-enter 方法可能无法执行。另外,Vue 官网建议在带有过渡效果的元素上添加 v-bind:class='false'。此前未添加时,发现小球仅在第一次点击位置执行过渡效果,添加后问题解决。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持本站。

实用工具推荐

电子包浆

绿化、压缩、噪点和虚拟水印,自由调节做旧程度。

使用 ›
图片压缩

选择画质和输出格式,预览压缩前后的大小。

使用 ›
相关文章