vue 學習筆記 商品列表頁(小球動畫)

商品列表頁樣式

使用 ScrollNav組件 url:didi.github.io/cube-ui/#/z…
在tab切換的時候已經將數據的加載處理完成,直接將樣式和結構複製到組件當中就能夠加載成功vue

<div class="goods">
 <div class="scroll-nav-wrapper">
   <cube-scroll-nav :side="true" :data="goods" :options="scrollOptions" v-if="goods.length">
     <cube-scroll-nav-panel
       v-for="good in goods"
       :key="good.name"
       :label="good.name"
       :title="good.name"
     >
       <ul>
         <li v-for="food in good.foods" :key="food.item" class="food-item">
           <div class="icon">
             <img :src="food.icon" width="57" height="57" />
           </div>
           <div class="content">
             <h2 class="name">{{food.name}}</h2>
           </div>
         </li>
       </ul>
     </cube-scroll-nav-panel>
   </cube-scroll-nav>
 </div>
</div>
複製代碼

商品列表頁下面的購物信息欄

  1. 在goods組件中將shop-cart組件引入
//完成的goods組件結構
<template>
  <div class="goods">
    <div class="scroll-nav-wrapper">
      <cube-scroll-nav :side="true" :data="goods" :options="scrollOptions" v-if="goods.length">
        <cube-scroll-nav-panel
          v-for="good in goods"
          :key="good.name"
          :label="good.name"
          :title="good.name"
        >
          <ul>
            <li v-for="food in good.foods" :key="food.item" class="food-item">
              <div class="icon">
                <img :src="food.icon" width="57" height="57" />
              </div>
              <div class="content">
                <h2 class="name">{{food.name}}</h2>
              </div>
            </li>
          </ul>
        </cube-scroll-nav-panel>
      </cube-scroll-nav>
    </div>
    <div class="shop-cart-wrapper">  
      <!--購物車組件  delivery-pricep配送費  min-price=最低消費-->
      <shopCart ref="shopCart" :delivery-price="seller.deliveryPrice" :min-price="seller.minPrice"
      ></shopCart>
    </div>
  </div>
</template>
複製代碼

js部分的處理git

<script>
import { goodsAjax } from "api/";
import shopCart from "../shop-cart/shop-cart";

export default {
  name: "goods",
  components: {
    shopCart
  },
  props: {
    tabData: {
      //動態組件傳遞過來的值   經過tabs傳遞過來的值中,都是seller不一次性把全部的數據都加載
      type: Object,
      default() {
        return {};
      }
    }
  },
  data() {
    return {
      goods: [],
      selectedFood: {},
      scrollOptions: {
        click: false, //不監聽點擊事件,不影響左右滑動又不影響點擊時觸發滑動
        directionLockThreshold: 0
      }
    };
  },
  computed: {
    seller() {
      return this.tabData.seller;
    }
  },
  methods: {
    async fetch() {
      //打一個標記若是請求過一次數據就再也不進行第二次數據請求,確保組件再來回切換的時候只有一次數據加載
      if (this.fetched) return;
      this.fetched = true;
      this.goods = (await goodsAjax()).data;
    }
  }
};
</script>
複製代碼

購物車樣式結構github

<template>
  <div>
    <div class="shopcart">
      <div class="content">
        <div class="content-left">
          <div class="logo-wrapper">
            <div class="logo" :class="{'highlight':totalCount>0}">
              <i class="icon-shopping_cart" :class="{'highlight':totalCount>0}"></i>
            </div>
            <div class="num" v-show="totalCount>0">
            </div>
          </div>
          <div class="price" :class="{'highlight':totalPrice>0}">¥{{totalPrice}}</div>
          <div class="desc">另需配送費¥{{deliveryPrice}}元</div>
        </div>
        <div class="content-right">
          <div class="pay" :class="payClass">{{payDesc}}</div>
        </div>
      </div>
    </div>
  </div>
</template>
<script>
export default {
  name: "shop-cart",
  props: {
    deliveryPrice: {
      type: Number,
      default: 0
    },
    minPrice: {
      type: Number,
      default: 0
    },
    selectedFoods: {
      type: Array,
      default() {
        return [];
      }
    }
  },
  computed: {
    totalPrice() {
      //計算總價
      let total = 0;
      this.selectedFoods.forEach(food => {
         total += food.price * food.count;
      });
      return total;
    },
    totalCount() {
      //計算總和
      let count = 0;
        this.selectedFoods.forEach(food => {
          count += food.count;
        });
      return count;
    },
    payDesc() {
      //支出的描述
      if (this.totalPrice === 0) {
        return `¥${this.minPrice}元起送`;
      } else if (this.totalPrice < this.minPrice) {
        let diff = this.minPrice - this.totalPrice;
        return `還差¥${diff}元起送`;
      } else {
        return "去結算";
      }
    },
    payClass() {
      //結算按鈕根據不一樣的狀態,不一樣的樣式
      if (!this.totalCount || this.totalPrice < this.minPrice) {
        return "not-enough";
      } else {
        return "enough";
      }
    }
  }
</script>
複製代碼

上面的代碼能夠正常的顯示完整商品列表頁面,沒有相關的數據處理web

  1. 每件商品選擇的小按鈕,加減以及顯示的數量,組件處理


小球組件的結構 cart-control:

<template>
  <div class="cartcontrol">
    <transition name="move">
      <div class="cart-decrease" v-show="food.count>0" @click.stop="decrease">
        <span class="inner icon-remove_circle_outline"></span>
      </div>
    </transition>
    <div class="cart-count" v-show="food.count>0">{{food.count}}</div>
    <div class="cart-add icon-add_circle" @click.stop="add"></div>
  </div>
</template>
複製代碼

js部分的內容:api

<script>
  const EVENT_ADD = 'add'

  export default {
    name: 'cart-control',
    props: {
      food: {  //從goods組件傳過來的值
        type: Object
      }
    },
    methods: {
      add(event) {
        if (!this.food.count) {
          this.$set(this.food, 'count', 1)
        } else {
          this.food.count++
        }
        this.$emit(EVENT_ADD, event.target)
      },
      decrease() {
        if (this.food.count) {
          this.food.count--
        }
      }
    }
  }
</script>
複製代碼

將小球組件引入goods組件中:bash

<template>
  <div class="goods">
    <div class="scroll-nav-wrapper">
      <cube-scroll-nav :side="true" :data="goods" :options="scrollOptions" v-if="goods.length">
        <cube-scroll-nav-panel
          v-for="good in goods"
          :key="good.name"
          :label="good.name"
          :title="good.name"
        >
          <ul>
            <li
              @click="selectFood(food)"
              v-for="food in good.foods"
              :key="food.name"
              class="food-item"
            >
              <div class="icon">
                <img width="57" height="57" :src="food.icon">
              </div>
              <div class="content">
                <h2 class="name">{{food.name}}</h2>
                <p class="desc">{{food.description}}</p>
                <div class="extra">
                  <span class="count">月售{{food.sellCount}}份</span><span>好評率{{food.rating}}%</span>
                </div>
                <div class="price">
                  <span class="now">¥{{food.price}}</span>
                  <span class="old" v-show="food.oldPrice">¥{{food.oldPrice}}</span>
                </div>
                <div class="cart-control-wrapper">
                  <!--引入 添加減小商品組件 -->
                  <cart-control @add="onAdd" :food="food"></cart-control>
                </div>
              </div>
            </li>
          </ul>
        </cube-scroll-nav-panel>
      </cube-scroll-nav>
    </div>
    <div class="shop-cart-wrapper">
      <shopCart ref="shopCart" :delivery-price="seller.deliveryPrice" :min-price="seller.minPrice"
:selectedFoods="selectedFoods"
      ></shopCart>
    </div>
  </div>
</template>

<script>
import { goodsAjax } from "api/";
import shopCart from "../shop-cart/shop-cart";
import cartControl from "../cart-control/cart-control"

export default {
  name: "goods",
  components: {
    shopCart,
    cartControl
  },
  props: {
    tabData: {
      //動態組件傳遞過來的值
      type: Object,
      default() {
        return {};
      }
    }
  },
  data() {
    return {
      goods: [],
      selectedFood: {},
      scrollOptions: {
        click: false, //不監聽點擊事件,不影響左右滑動又不影響點擊時觸發滑動
        directionLockThreshold: 0
      }
    };
  },
  computed: {
    seller() {
      return this.tabData.seller;
    },
    selectedFoods(){ //選擇商品
        let foods = []
        this.goods.forEach((good) => {
          good.foods.forEach((food) => {
            if (food.count) {
              foods.push(food)
            }
          })
        })
        return foods
    }
  },
  methods: {
    async fetch() {
      //打一個標記若是請求過一次數據就再也不進行第二次數據請求,確保組件再來回切換的時候只有一次數據加載
      if (this.fetched) return;
      this.fetched = true;
      this.goods = (await goodsAjax()).data;
    },
    onAdd(){
        
    }
  }
};
</script>

<style scoped lang="stylus">
@import "./goods.styl"
</style>


複製代碼
  1. 小球的動畫效果的實現app

    1/ 在cart-control.vue組件中,派發一個事件async

<div class="cart-add icon-add_circle" @click.stop="add"></div>
 
 js部分
 
  add(event) {
        if (!this.food.count) {
          this.$set(this.food, 'count', 1)
        } else {
          this.food.count++
        }
        //派發一個事件   在goods子組件當中監聽這個事件
        this.$emit(EVENT_ADD, event.target)
      }

複製代碼

2/ 在goods組件當中監聽add這個事件ide

<cart-control @add="onAdd :food="food"></cart-control> <script> methods:{ onAdd(el){ //驅動shop-cart組件中 的drop方法 this.$refs.shopCart.drop(el) } } </script> 複製代碼

3/ 在shop-cart組件中接收一個參數fetch

methods: {
     drop(el){ //驅動的動畫
       console.log(el)
     }
 }
複製代碼

以上這三步只是找到對應的按鈕

4/ shop-cart 建立隱藏的小球

const BALL_LEN = 10;
function createBalls(){//建立隱藏的小球
    let ret = [];
    for(let i=0;i<BALL_LEN;i++){
        ret.push({
            show:false
        })
    }
    return ret
}
export default {
    data(){
     return {
         balls:createBalls()  //一開始是隱藏的
     }
  },
  
}
複製代碼

5/ shop-cart 中建立小求容器

<div class="ball-container">
          <!-- 小球的容器 -->
        <div v-for="(ball,index) in balls" :key="index">
          <transition
            @before-enter="beforeDrop"
            @enter="dropping"
            @after-enter="afterDrop ">
            <!--第一個元素是v-if v-show 進行過渡的-->
            <div class="ball" v-show="ball.show">
              <div class="inner inner-hook"></div>
            </div>
          </transition>
        </div>
      </div>
複製代碼

6/ drop方法觸發小球過渡

created(){
     this.dropBalls = [];//這是下落小球
  },
methods: {
    drop(el){ //驅動的動畫 小球過渡 找一個小球
      for(let i=0;i<this.balls.length;i++) {
           const ball = this.balls[i];
           if(!ball.show) {
               ball.show =true;
               ball.el = el;
               this.dropBalls.push(ball);//將隱藏小球裝到下落小球裏面
               return 
           }
      }
    }
  }
複製代碼

7/ 小球過渡動畫

beforeDrop(el) {
        const ball = this.dropBalls[this.dropBalls.length - 1]
        //getBoundingClientRect 獲取當前元素座標信息,原生js的語法
        const rect = ball.el.getBoundingClientRect()
        const x = rect.left - 32
        const y = -(window.innerHeight - rect.top - 22)
        el.style.display = ''
        el.style.transform = el.style.webkitTransform = `translate3d(0,${y}px,0)`
        const inner = el.getElementsByClassName(innerClsHook)[0]
        inner.style.transform = inner.style.webkitTransform = `translate3d(${x}px,0,0)`
      },
      dropping(el, done) {
          //從新獲取元素的位置信息
        this._reflow = document.body.offsetHeight
        el.style.transform = el.style.webkitTransform = `translate3d(0,0,0)`
        const inner = el.getElementsByClassName(innerClsHook)[0]
        inner.style.transform = inner.style.webkitTransform = `translate3d(0,0,0)`
        el.addEventListener('transitionend', done)
      },
      afterDrop(el) {
          //從動畫隊列裏,將動畫的元素去掉
        const ball = this.dropBalls.shift()
        if (ball) {
          ball.show = false
          el.style.display = 'none'
        }
      }
複製代碼
相關文章
相關標籤/搜索