Vue shopCart 组件开发详解


Posted in Javascript onJanuary 26, 2018

一、shopCart组件

(1) goods 父组件和 子组件 shopCart 传参

deliveryPrice:{ // 单价 从json seller 对象数据中获取
 type:Number,
 default:0
},
minPrice:{ // 最低起送价 从json seller 对象数据中获取
 type:Number,
 default:20
}

其中 deliveryPrice 和 minPrice 的数据都是从 data.json数据 中 seller 对象下 获得。所以在goods 组件中还要 获取到 seller对象 的数据,否则会报错:

[Vue warn]: Error in render: "TypeError: Cannot read property 'deliveryPrice' of undefined"

解决方法:根组件 App.vue 中 router-view 组件获取seller 数据,传到 goods 组件中

1-1.app.vue (根组件 也是 goods 的父组件)

<keep-alive>
 <router-view :sell="sellerObj"></router-view>
</keep-alive>

注意:sellerObj 是data 定义 的 对象里用来接收 data.json 数据,相当于 实参

1-2.goods.vue (相对于跟组件的子组件 且 shopCart 的父组件)

通过props 属性 进行组件之间的通信

props: {
  sell: Object // 相当于 形参
 },

1-3.shopCart.vue ( goods 的子组件)

<shopCart :delivery-price="sell.deliveryPrice" :min-price="sell.minPrice"></shopCart>

(2) 选中商品 的 计算功能

1-1. 传入用户选中商品的集合

说明:从父组件会 传入一个用户选中商品的 数组,数组里会存放着 n 个对象,每个对象里存放着该 商品的 价格 和 数量。

props:{       // 通过父组件传过来的 ( 相当于形参 )
 selefoodsArr:{   // 用户选中的商品存放在一个数组里  接收的是 data.json数据的 goods(数组)
 type:Array, // 当父组件传过来的 类型是对象或者 是数组时, default 就是一个函数
 default (){
 return []  // 返回数组 存放着选中 商品 对应的 goods下的 foods 数组(由 父组件 的 实参 决定的返回值)
 }
}

1-2. 利用计算属性 选中商品数量的变化,商品总价,动态改变描述等功能

computed:{
 totalPrice (){     //计算总价,超过起送额度后提示可付款
 let total=0   // 定义一个返回值
 this.selefoodsArr.forEach((rfoods) =>{ // 遍历 这个 goods 数组 取到 价格 和 数量 (当然在这里数据库没有count 这个属性,稍后 我们会利用 vue.set() 新建一个count 属性)
  total += rfoods.price * rfoods.count // 形参 rfoods 实参 是 foods
 });
 return total;
 },
 totalCount (){   // //计算选中的food数量,在购物车图标处显示,采用绝对定位,top:0;right:0;显示在购物车图标右上角  
 let count=0
 this.selefoodsArr.forEach((rfoods) =>{ // 形参 rfoods 实参 是 foods
  count += rfoods.count
 });
 return count;
 },
 payDesc (){    //控制底部右边内容随food的变化而变化,payDesc()控制显示内容,enough 添加类调整显示样式
 let diff = this.minPrice - this.totalPrice
    if (!this.totalPrice) {
     return `¥${this.minPrice}起送`
    } else if (diff > 0) {
     return `还差¥${diff}元`
    } else {
     return '去结算'
    }
 }  
}

这样就渲染到 template 里了

<div class="shopCart">
 <div class="content">
  <div class="content-left">
 <div class="logo-wrapper"> 
 <!--徽章 展示选中商品的个数-->
 <div class="badge" v-show="totalCount">
 {{totalCount}}
 </div>
 <!--购物车 图标 选择商品和未选择商品 时 动态改变 样式 条件:只要选择了商品即总价不为0 ,样式变--> 
  <div class="logo" :class="{'active':totalCount}">
   <i class="icon-shopping_cart"></i>
  </div>
 </div>
 <!--同理: 总价 不为0 字体高亮-->
 <div class="price" :class="{'active':totalPrice}">
  ¥{{totalPrice}}
 </div>
 <!--配送费 data.json 提供-->
 <div class="desc">
  另需要配送费¥{{deliveryPrice}}元
 </div>
  </div>
  <!--根据条件  动态 改变样式-->
  <div class="content-right" :class="{'enough':totalPrice>=minPrice}">  
 {{payDesc}}  
 </div>
 </div>
</div>

相关样式

&.active
  color white
  
&.enough
  background #00b43c
  color white

总结:通过以上学习我们能发现,selectFoods()的变化起着关键作用,它的变化会引起DOM的变化,并最终体现到界面上,而我们不用关注DOM内部的具体实现,这就是vue的一大好处。如果采用jQuery完成这些功能会略显繁杂。

二、cartControl 组件

说明:这个组件是控制购物车小球的。其中涉及到小球的动画

(1) 新增属性 count

说明:

在goods 下的 foods 添加一个属性 count,用来存储用户选中的商品个数,计算商品总价 以及 关联徽章(显示用户选择商品的个数)的变化

方法:通过import Vue from 'vue';使用set接口,通过vue.set()添加属性,当它变化时就能被检测到,从而父组件能获取到count值(遍历选中的商品时使用)

methods:{
 addCart(event){ // 点击count 加,
  //console.log(event.target);
 if (!event._constructed) { // 去掉自带click事件的点击
    return;
   }
 if(!this.foodsele.count){
 Vue.set(this.foodsele, 'count', 1)
 }else{
 this.foodsele.count++
 }  
 },
 decreaseCart (event){ // 点击减少
 if (!event._constructed) { // 去掉自带click事件的点击
    return;
    }
 if(this.foodsele.count){
 this.foodsele.count --
  } 
  }
}

(2)添加按钮 实现transtion 过渡

我们要实现的效果是:当点击添加按钮时,减少按钮出现 并伴随着 旋转、平移以及透明度变化的 一些 动画效果

<transition name='move'> <!--平移动画-->  
 <div class="cart-decrease" v-show="foodsele.count" @click='decreaseCart($event)'>
  <span class="icon-remove_circle_outline inner"></span><!--旋转、透明度动画--> 
  </div>
</transition>
.cart-decrease
  display inline-block
  padding 6px
  transition: all .4s linear  /*过渡效果的 CSS 属性的名称、过渡效果需要多少时间、速度效果的速度曲线*/  
  .inner
   line-height 24px
   font-size 24px
   color rgb(0,160,220)
   transition all 0.4s linear
  &.move-enter-active, &.move-leave-active
   transform translate3d(0,0,0) /* 这样可以开启硬件加速,动画更流畅,3D旋转,X轴位移24px */
   .inner   
    display inline-block  /* 设置成inline-block才有高度,才能有动画 */
    transform rotate(0)
  &.move-enter, &.move-leave-active
   opacity: 0
   transform translate3d(24px,0,0)
   .inner
    transform rotate(180deg)

三、抛物线小球动画

通过两个层来控制小球,外层控制一个方向的变化,内层控制另外一个方向的变化(写两层才会有抛物线的效果),采用fixed布局(是相对于视口的动画)

事件发射和接收

扩展

Vue1.0组件间传递

  1. 使用$on()监听事件;
  2. 使用$emit()在它上面触发事件;
  3. 使用$dispatch()派发事件,事件沿着父链冒泡;
  4. 使用$broadcast()广播事件,事件向下传导给所有的后代

(1) Vue2.0 组件之间传递数据

1-1. 当点击 添加数量时 在 cartControl 组件里的 addCount 方法里 通过 $emit 属性 派发一个事件 , 传入点击的对象

addCart(event){ // 点击count 加,
//  console.log(event.target);
 if (!event._constructed) { // 去掉自带click事件的点击
    return;
   }
 if(!this.foodsele.count){
 Vue.set(this.foodsele, 'count', 1)
 }else{
 this.foodsele.count++
 }
// 当点击 添加数量时 通过 $emit 属性 提交一个名为 add 给父组件
// 子组件通过 $emit触发 add事件 ,将参数传递给父组件
 this.$emit('add', event.target);
}

1-2. 操作 goods 组件

购物车组件如果提交了addCart事件就调用add函数

<cart-control :foodsele='food' @add="addFood"></cart-control>

父组件使用 @add="addFood"监听由子组件vm.$emit触发的事件,通过addFood()接受从子组件传递过来的数据,通知父组件数据改变了。

addFood(target) {
  this._drop(target);
}

1-3. 父组件访问子组件 vue 提供了接口 ref

<shopCart ref="shopCart" :delivery-price="sell.deliveryPrice" :min-price="sell.minPrice" :selefoods-arr='selectfoods'  ></shopCart>
_drop(target) {
  // 体验优化,异步执行下落动画
  this.$nextTick(() => {
   this.$refs.shopCart.balldrop(target);// 将target传入shopCart子组件中的balldrop方法,所以drop方法能获得用户点击按钮的元素,即能获取点击按钮的位置
  });
}

区别 访问DOM 变量

1-3. 操作 shopCart 组件

data (){ // 定义一个数组 来 控制小球的状态  定义多个对象,表示页面中做多同时运动的小球
 return{ // 定义 5 个 小球  
 balls:[{show:false},{show:false},{show:false},{show:false},{show:false}],
 dropBalls:[] // 接收下落小球
  }
}
methods:{
 balldrop(ele) {
// console.log(el) 取到点击 对象
   for(var i=0;i<this.balls.length;i++){
    let ball=this.balls[i]
    if(!ball.show){
     ball.show=true
     ball.ele=ele
     this.dropBalls.push(ball)
     return;
    }
   }        
 }
}

动画过程开始,利用vue 提供的钩子函数

beforeEnter (el){ //找到所以设为true的小球
 let count=this.balls.length
 while(count--){
 let ball = this.balls[count];
 if(ball.show){
  let pos=ball.el.getBoundingClientRect() //返回元素相对于视口偏移的位置
  let x=pos.left-32  // 点击的按钮与小球(fixed)之间x方向的差值
  let y=-(window.innerHeight-pos.top-22)
  el.style.display = '';  //设置初始位置前,手动置空,覆盖之前的display:none,使其显示
       el.style.webkitTransform = `translate3d(0,${y}px,0)`; //外层元素做纵向的动画,y是变量
       el.style.transform = `translate3d(0,${y}px,0)`;
       let inner = el.getElementsByClassName('inner_hook')[0];//内层元素做横向动画,inner-hook(用于js选择的样式名加上-hook,表明只是用                                   //于js选择的,没有真实的样式含义)
       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) {
  let ball = this.dropBalls.shift(); //取到做完动画的球,再置为false,即重置,它还可以接着被利用
  if (ball) {
   ball.show = false;
   el.style.display = 'none';
  }
 }
<div class="ball-container">
  <div v-for="ball in balls">
   <transition 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>
</div>
&.drop-enter,&.drop-enter-active
    transition all 0.4s 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

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

Javascript 相关文章推荐
JScript中的undefined和&quot;undefined&quot;的区别
Mar 08 Javascript
javascript+iframe 实现无刷新载入整页的代码
Mar 17 Javascript
jQuery Validation插件remote验证方式的Bug解决
Jul 01 Javascript
网页广告中JS代码的信息监听示例
Apr 02 Javascript
javascript控制在光标位置插入文字适合表情的插入
Jun 09 Javascript
JS实现响应鼠标点击动画渐变弹出层效果代码
Mar 25 Javascript
移动端基础事件总结与应用
Jan 12 Javascript
完美解决spring websocket自动断开连接再创建引发的问题
Mar 02 Javascript
记React connect的几种写法(小结)
Sep 18 Javascript
JavaScript对象拷贝与赋值操作实例分析
Dec 10 Javascript
JavaScript内置对象之Array的使用小结
May 12 Javascript
在vue-cli3.0 中使用预处理器 (Sass/Less/Stylus) 配置全局变量操作
Aug 10 Javascript
jquery写出PC端轮播图实例
Jan 26 #jQuery
深入理解vue中slot与slot-scope的具体使用
Jan 26 #Javascript
从零开始最小实现react服务器渲染详解
Jan 26 #Javascript
微信小程序模版渲染详解
Jan 26 #Javascript
微信小程序如何获取用户信息
Jan 26 #Javascript
vue实现前进刷新后退不刷新效果
Jan 26 #Javascript
Vue2.5 结合 Element UI 之 Table 和 Pagination 组件实现分页功能
Jan 26 #Javascript
You might like
十天学会php之第十天
2006/10/09 PHP
php获取mysql版本的几种方法小结
2008/03/25 PHP
坏狼php学习 计数器实例代码
2008/06/15 PHP
PHP 验证身份证是否合法的函数
2017/02/09 PHP
PHP解析url并得到url参数方法总结
2018/10/11 PHP
laravel 解决多库下的DB::transaction()事务失效问题
2019/10/21 PHP
PHP编程一定要改掉的5个不良习惯
2020/09/18 PHP
javascript new后的constructor属性
2010/08/05 Javascript
jQuery LigerUI 使用教程表格篇(1)
2012/01/18 Javascript
jquery判断浏览器类型的代码
2012/11/05 Javascript
jquery在项目中做复选框时遇到的一些问题笔记
2013/11/17 Javascript
js控制页面的全屏展示和退出全屏显示的方法
2015/03/10 Javascript
编写高质量JavaScript代码的基本要点
2016/03/02 Javascript
js中 计算两个日期间的工作日的简单实例
2016/08/08 Javascript
javascript动画之磁性吸附效果篇
2016/12/09 Javascript
Vue 2中ref属性的使用方法及注意事项
2017/06/12 Javascript
Angular5中调用第三方库及jQuery的添加的方法
2018/06/07 jQuery
基于Vue2实现简易的省市区县三级联动组件效果
2018/11/05 Javascript
JS正则表达式验证密码强度
2020/03/18 Javascript
vue实现简单跑马灯效果
2020/05/25 Javascript
javascript开发实现贪吃蛇游戏
2020/07/31 Javascript
Vue实现导航栏菜单
2020/08/19 Javascript
Ant Design的Table组件去除
2020/10/24 Javascript
vant-ui AddressEdit地址编辑和van-area的用法说明
2020/11/03 Javascript
nuxt.js添加环境变量,区分项目打包环境操作
2020/11/06 Javascript
python获取图片颜色信息的方法
2015/03/18 Python
Python中的单下划线和双下划线使用场景详解
2019/09/09 Python
Python3.5 win10环境下导入kera/tensorflow报错的解决方法
2019/12/19 Python
Python类反射机制使用实例解析
2019/12/30 Python
python切割图片的示例
2020/11/12 Python
css3绘制百度的小度熊
2018/10/29 HTML / CSS
HTML5 canvas画图并保存成图片的jcanvas插件
2014/01/17 HTML / CSS
2014年关于两会精神的心得体会
2014/03/17 职场文书
《酸的和甜的》教学反思
2016/02/18 职场文书
MySQL kill不掉线程的原因
2021/05/07 MySQL
vue3.0 数字翻牌组件的使用方法详解
2022/04/20 Vue.js