Vue ( 四 ) shopCart 組件開發

1、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完成這些功能會略顯繁雜。

2、cartControl 組件

說明:這個組件是控制購物車小球的。其中涉及到小球的動畫

(1) 新增屬性 count

說明:
  • 在goods 下的 foods 添加一個屬性 count,用來存儲用戶選中的商品個數,計算商品總價 以及 關聯徽章(顯示用戶選擇商品的個數)的變化vue

  • 方法:經過import Vue from 'vue';使用set接口,經過vue.set()添加屬性,當它變化時就能被檢測到,從而父組件能獲取到count值(遍歷選中的商品時使用)web

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)

3、拋物線小球動畫

經過兩個層來控制小球,外層控制一個方向的變化,內層控制另一個方向的變化(寫兩層纔會有拋物線的效果),採用fixed佈局(是相對於視口的動畫)

事件發射和接收

組件之間傳值-1數據庫

組件之間傳值-2json

擴展

Vue1.0組件間傳遞

  • 使用$on()監聽事件;
  • 使用$emit()在它上面觸發事件;
  • 使用$dispatch()派發事件,事件沿着父鏈冒泡;
  • 使用$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
相關文章
相關標籤/搜索