1. 程式人生 > >餓了麼學習(六)shopcart購物車小球動畫

餓了麼學習(六)shopcart購物車小球動畫

HTML程式碼

生成一個動畫小球的div,並且生成五個小球,五個是為了生成一定數量的小球來作為操作使用,按照小球動畫的速度,一般來說五個也可以保證有足夠的小球數量來執行動畫

動畫的內容分別是外層和內層,外層控制動畫小球的軌道和方向,內層控制動畫小球的執行狀態
動畫使用vue的js鉤子實現

因為小球動畫只有一個方向(只執行單方向從上到下滾落),所以只用了before-enter,enter,after-enter
用v-show控制小球的可見性,在動畫執行期間可見,其餘時候隱藏

 <div class="ball-container">
      <div v-for
="ball in balls"> //用了兩種方式的動畫,css和js鉤子 <transition name="drop" @before-enter="beforeDrop" @enter="dropping" @after-enter="afterDrop"> //外層動畫 <div class="ball" v-show="ball.show"> //內層動畫 <div class="inner inner-hook"></div> </div
> </transition> </div> </div>

JS程式碼

設定了balls陣列來代表五個小球
設定了dropBalls陣列正在執行的小球

data(){
      return {
        balls: [
          {show: false},
          {show: false},
          {show: false},
          {show: false},
          {show: false}
        ],
        dropBalls: 
[] } },
只要觸發了drop事件,不止是drop事件裡面的程式碼會執行,另外幾個vue的js監聽鉤子也會一起按順序執行
    觸發了drop事件
    beforeDrop開始執行
    dropping開始執行
    afterDrop開始執行


drop事件的觸發可以通過點選cartcontrol元件的新增小球按鈕addCart事件觸發使用$emit,
也可以父元件 this.$refs.shopcart.drop(target);直接觸發


這麼做的目的是實現,在子元件cartcontrol點選之後,可以將該dom傳給父元件goods,
然後再傳給子元件shopcart,(因為目前他們之間的通道就是這樣,
shopcart子元件並沒有匯入cartcontrol子元件,所以沒有直接通訊)這樣就實現了多個元件之間的通訊,
從而可以實現需求,例如這裡就是實現點選子元件cartcontrol後新增一個動畫.
將小球滑落到另外一個元件shopcart,
$emit是觸發當前例項上的事件。附加引數都會傳給監聽器回撥。

JS 程式碼

methods: {
      drop(el) { 
      //觸發一次事件就會將所有小球進行遍歷
        for (let i = 0; i < this.balls.length; i++) {
          let ball = this.balls[i];
          if (!ball.show) { //將false的小球放到dropBalls
            ball.show = true;
            ball.el = el; //設定小球的el屬性為一個dom物件
            this.dropBalls.push(ball); 
            return;
          }
        }
      },

      beforeDrop(el){ //這個方法的執行是因為這是一個vue的監聽事件
        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.display = ''; //清空display
            el.style.webkitTransform = `translate3d(0,${y}px,0)`; 
            el.style.transform = `translate3d(0,${y}px,0)`;
            //處理內層動畫
            let inner = el.getElementsByClassName('inner-hook')[0]; //使用inner-hook類來單純被js操作
            inner.style.webkitTransform = `translate3d(${x}px,0,0)`;
            inner.style.transform = `translate3d(${x}px,0,0)`;
          }
        }
      },

      dropping(el, done) { //這個方法的執行是因為這是一個vue的監聽事件
        /* eslint-disable no-unused-vars */
        let rf = el.offsetHeight; //觸發重繪html
        this.$nextTick(() => { //讓動畫效果非同步執行,提高效能
          el.style.webkitTransform = 'translate3d(0,0,0)';
          el.style.transform = 'translate3d(0,0,0)';
          //處理內層動畫
          let inner = el.getElementsByClassName('inner-hook')[0]; //使用inner-hook類來單純被js操作
          inner.style.webkitTransform = 'translate3d(0,0,0)';
          inner.style.transform = 'translate3d(0,0,0)';
          el.addEventListener('transitionend', done); //Vue為了知道過渡的完成,必須設定相應的事件監聽器。
        });
      },

      afterDrop(el) { //這個方法的執行是因為這是一個vue的監聽事件
        let ball = this.dropBalls.shift(); //完成一次動畫就刪除一個dropBalls的小球
        if (ball) {
          ball.show = false;
          el.style.display = 'none'; //隱藏小球
        }
      }
    }

關於drop方法,是實現每一個ball的show屬性和el屬性處理,並且點選一次會自動將一個小球放到dropBalls數組裡面,放到裡面就代表的是一個小球已經被開始執行動畫,但是由於動畫是非同步的,所以先主動設定.

關於getBoundingClientRect(位移的計算是從左上角開始)

使用getBoundingClientRect獲取到當前元素的座標,然後需要位移的left減去元素的寬獲取真正的最終位移x座標

使用getBoundingClientRect獲取到當前元素的座標,然後需要當前螢幕的高度減去元素的top再減去元素本身的高度獲取到真正的最終位移y座標,並且這個是負數,因為是從左上角往下的方向

  • 關於html重繪

因為瀏覽器對於重繪是有要求並且是有佇列完成的,這是主要為了效能,雖然動畫隱藏了小球display none,但沒有觸發html重繪,或者說沒有立即觸發html重繪,所以需要手動
let rf = el.offsetHeight; 這是一個手動觸發html重繪的方法

CSS程式碼

.ball-container
            .ball
                position: fixed
                left 64px
                bottom 44px
                z-index 200
                // y軸 貝塞爾曲線
                transition all 0.4s cubic-bezier(.49, -.29, .75, .41)
                .inner
                    width 32px
                    height 32px
                    border-radius 50%
                    background: rgb(0, 160, 220)
                    transition all 0.4s linear// x軸只需要線性緩動

關於cubic-bezier(0.49, -0.29, 0.75, 0.41),是動畫拋物曲線(貝塞爾曲線)的配置,基於css3實現,至於拋物線放在外層就是為了控制內層的元素的軌道和方向的.
貝塞爾曲線與CSS3動畫、SVG和canvas的基情