JS 的平凡之路--學習人氣眼中的效果(上)

descire發表於2017-07-25

最近看了看人氣眼的介面,感覺到學習的地方有很多呀。這裡先帶大家看看人氣值跳動的實現。本篇程式碼基於Vue2.x.x。

一、概要

  首先看一下效果圖:

人氣值動畫

  要想實現上面的效果,我們分為這幾個部分:

  • 判斷元素是否在可視區域內;
  • 函式節流的使用;
  • 元素高度過渡動畫;
  • 數字跳動動畫;

二、判斷元素是否在可視區域之內

  首先我們要先獲取元素的位置資訊,這裡我們採用getBoundingClientRect方法,MDN上對於該方法的介紹。然後我們只要與可視區域做個比較,就OK了。

    // methods
    isElementInViewport (el, offset) {
        const h = offset || 20,
              box = el.getBoundingClientRect(),
              top = (box.top >= 0),
              left = (box.left >= 0),
              bottom = (box.bottom <= (window.innerHeight || document.documentElement.clientHeight) + h),
              right = (box.right <= (window.innerWidth || document.documentElement.clientWidth) + h);
        return (top && left && bottom && right);
    }
複製程式碼

三、函式節流的使用

  接下來我們需要監聽'scroll'事件,判斷元素是否出現在可視區域內。對於scroll事件的優化之一,我們需要使用函式節流。你可以選擇匯入underscore.js的throttle函式,但是這裡我嘗試了一下requestAnimationFrame來實現函式節流:

    //mounted: 
    document.addEventListener('scroll', this.onScroll , false);
    
    // methods:
    onScroll (e) {
        if (!this.LOCK) {
            this.LOCK = true;
            window.requestAnimationFrame(this.scrollAction);
        }
    },
    scrollAction () {
        const flag = this.isElementInViewport(this.$refs.zfbitem, 100);
        if (!flag) {
            this.LOCK = false;
        } else {
            //觸發元素高度過渡動畫
            this.active = true;
            //去除掉事件監聽
            document.removeEventListener('scroll', this.onScroll , false);
        }
    }
複製程式碼

四、元素的高度過渡動畫

  在CSS當中,實現一種動畫效果,你可以有很多種方式,這裡我也就不一一列舉了,此例子中我採用高度過渡的方式實現效果。有人就會說通過高度過渡沒有任何難度啊?實際上,你需要注意的點還是蠻多的:

  • 想讓一個元素的高度為0,並不是簡單的height:0;就能做到的,前提是你不能設定border、垂直方向的padding等;
  • 當你的元素設定height為100px時,你再設定max-height為0,它一樣只顯示0的高度;
  • 當height的值為auto時,我們是無法過渡的。所以對於auto的情況,我們可以採用max-height來模擬一下,一般情況下,效果還行。

  實現的程式碼還是很簡單的,這裡就不貼程式碼了。

五、數字跳動動畫

  首先我們需要在高度過渡動畫完成後執行數字跳動動畫,這裡我們需要監聽'transitionend'事件,對於這個事件需要特別注意的點:

  • 每個過渡屬性完成後多會觸發一次transitionend;
  • transitionend事件支援冒泡,如果子元素也有過渡效果的話,一定要阻止冒泡。
    // watch : 
    active (newValue) {
        if (newValue) {
            this.$refs.zfbitem.addEventListener('transitionend', this.transitionAction, false);
        }
    }
    
    // methods:
    transitionAction (e) {
        //不再需要監聽時,一定要移除監聽
        this.$refs.zfbitem.removeEventListener('transitionend', this.transitionAction, false);
        this.numberBounce();
    }
複製程式碼

  對於數字跳動的動畫,正好利用了Vue響應式的特性偷懶了一波,感覺實現的還是有些生硬,我主要是從這幾個方面下手的:

  • 暫且預設兩位數;
  • 個位和十位多需要先跳一輪0~9,然後再跳向最終的數字,這樣避免特殊的情況;
  • 個位和十位動畫執行的時長是一樣的,通過時長和各自需要跳動的字數,計算出每一幀需要的時間。
    //元件需要傳入的引數
    props: {
        rate: Number
    }
    
    //分割個位 和 十位
    computed: {
        numberArray () {
            return (this.rate + '').split('');
        }
    }
    
    numberBounce () {
        let arr = this.numberArray,
            totalTime = 200,
            a = arr[1],
            aLen = Number.parseInt(a),
            aTime = Math.floor(totalTime / (aLen + 9)),
            i = 0,
            b = arr[0],
            bLen = Number.parseInt(b),
            bTime = Math.floor(totalTime / (bLen + 9)),
            j = 0;
        this.bit = 0;
        this.ten = 0;
        this.bitTimeId = setInterval(_ => {
            i++;
            this.bit = i % 10; // 計數
            if (i - 10 === aLen) {
                //千萬不要忘記清除定時器哦
                clearInterval(this.bitTimeId);
                this.bitTimeId = null;
            }
        }, aTime);

        this.tenTimeId = setInterval(_ => {
            j++;
            this.ten = j % 10;
            if (j - 10 === bLen) {
                clearInterval(this.tenTimeId);
                this.tenTimeId = null;
            }
        }, bTime);
    }
複製程式碼

六、總結

  這雖然是一個簡單的效果,但是包含的知識點很多,這裡又要強調了:基礎很是重要,千萬不要浮躁。(^_^)


  喜歡本文的小夥伴們,歡迎關注我的訂閱號超愛敲程式碼,檢視更多內容.

JS 的平凡之路--學習人氣眼中的效果(上)

相關文章