1. 程式人生 > >JavaScript中的該如何[更好的]做動效

JavaScript中的該如何[更好的]做動效

cli cpu get ron 是個 性能 可見 align max

在用js寫動畫的時候,無非使用 setTimeout/setInterval 或者 requestAnimationFrame 來處理動畫(在jquery的代碼裏也是這麽幹的),本文主要為了記錄下兩者的區別及使用兩者來實現動過程。

以實現一個簡單的滾動到頂部為例

setInterval

setInterval() 方法重復調用一個函數或執行一個代碼段,在每次調用之間具有固定的時間延遲。返回一個intervalID,可用於 cancelInterval 達到結束循環的效果。

setTimeout 和 setInterval 的實現基本沒區別,一個是定時執行,一個是定時循環執行,前者加個自己調用自己就是後者了,下面主要以 setInterval 為代表

實現過程:

1.寫個方法,該方法需要傳入一個代表動畫所需執行的時間的參數(如:滾動到頂部需要1000毫秒)

function doAnimate(duration){
    return function(){
        // do something
    }
}

2.取當前頁面距頂部高度、滾動速度(以勻速為例)、寫個開始動畫的函數(為了給addEventListener綁事件傳參,其實也可直接 dom.onclick = fn )

function doAnimate(duration){
    return function(){
        var start = document.documentElement.scrollTop;
        
var scrollSpeed = start/duration*(1000/60); // 以大多瀏覽器的刷新頻率60幀(60Hz)為準 1秒60次的刷新 var timer; var startTime = +new Date(); // 標記時間,僅供後面測效果用而已 function startAnimate(){ timer = setInterval(function () { // do something },1000/60) } } }

3.寫滾動動畫

function
doAnimate(duration){ return function(){ var start = document.documentElement.scrollTop; var scrollSpeed = start/duration*(1000/60); // 這裏取到平均滾動距離,以大多瀏覽器的刷新頻率60幀(60Hz)為準,1秒60次的刷新 var timer; function startAnimate(){ timer = setInterval(function () { document.documentElement.scrollTop = start < scrollSpeed ? start -= start : start -= scrollSpeed; // 當前該滾動到何處,如果距離頂部小於平均滾動距離,直接滾到scrollTop為0;如果大於,則取到的觸發時高度以平均滾動距離遞減 if(start === 0){ clearInterval(timer); } },1000/60) } } }

4.寫個很高的頁面、給個div、加個click事件觸發滾動回頂部

html

<!-- 為了更好的看到滾動效果及測滾動是否平滑,我們用某度的大圖扔頁面上 -->
<img src="./來自百度壁紙的大圖" />
<img src="./來自百度壁紙的大圖" />
<img src="./來自百度壁紙的大圖" />

<div id="scrollTop_1000" style="width: 60px;height: 20px;background: #fff;position: fixed; bottom:50px;right:50px;text-align: center">1000</div>
<div id="scrollTop_3000" style="width: 60px;height: 20px;background: #fff;position: fixed; bottom:80px;right:50px;text-align: center">3000</div>

js

function doAnimate(duration){
    return function(){
        var start = document.documentElement.scrollTop;
        var scrollSpeed = start/duration*(1000/60); // 這裏取到平均滾動距離,以大多瀏覽器的刷新頻率60幀(60Hz)為準,1秒60次的刷新
        var timer;
        function startAnimate(){
            timer = setInterval(function () {
                document.documentElement.scrollTop = start < scrollSpeed ? start -= start : start -= scrollSpeed; // 當前該滾動到何處,如果距離頂部小於平均滾動距離,直接滾到scrollTop為0;如果大於,則取到的觸發時高度以平均滾動距離遞減
                if(start === 0){
                    clearInterval(timer);
                }
            },1000/60)
        }
    }
}
document.getElementById(‘scrollTop_1000‘).addEventListener(‘click‘,doAnimate(1000),!1)
document.getElementById(‘scrollTop_3000‘).addEventListener(‘click‘,doAnimate(3000),!1)

效果如圖:

技術分享

截圖用了霸氣十足的面子果實能力者,分別測了設置 duration 為1000和3000的滾動效果 -。- 嗯...他是個好人

requestAnimationFrame

requestAnimationFrame() 方法告訴瀏覽器您希望執行動畫,並請求瀏覽器調用指定的函數在下一次重繪之前更新動畫。該方法將在重繪之前調用的回調作為參數。返回一個 requestID ,可用於 cancelAnimationFrame 達到取消 requestAnimationFrame 動畫的效果。

實現思路如上,代碼如下:

html

<!-- 為了更好的看到滾動效果及測滾動是否平滑,我們用某度的大圖扔頁面上 -->
<img src="./來自百度壁紙的大圖" />
<img src="./來自百度壁紙的大圖" />
<img src="./來自百度壁紙的大圖" />

<div id="scrollTop_1000" style="width: 60px;height: 20px;background: #fff;position: fixed; bottom:50px;right:50px;text-align: center">1000</div>
<div id="scrollTop_3000" style="width: 60px;height: 20px;background: #fff;position: fixed; bottom:80px;right:50px;text-align: center">3000</div>

js

function doAnimate(duration){
    return function(){
        var start = document.documentElement.scrollTop;
        // 獲取實時時間
        var nowTime = function(){
            return +new Date
        }
        // 開始時間 用於計算動畫運行時間和動畫規定時間的百分比
        var startTime = nowTime();
        var animateId;
        var startAnimate = function() {
            animateId = requestAnimationFrame(toTop);
        }
        var stopAnimate = function() {
            cancelAnimationFrame(animateId)
        }
        function toTop(){
            // 剩下時間
            var restTime = Math.max(0, duration - ( nowTime() - startTime))
            var percent = restTime / duration || 0;
            var changeStyle = function(value){
                document.documentElement.scrollTop = value;
            }
            // 根本比例獲取剩下的距離,也就是實時距離頂部的距離
            var distance = start * percent;
            if(!distance){
                changeStyle(distance)
                stopAnimate();
            }else{
                changeStyle(distance)
                startAnimate(toTop);
            }
        }
        startAnimate(toTop);
    }
}

document.getElementById(‘scrollTop_1000‘).addEventListener(‘click‘,doAnimate(1000),!1)
document.getElementById(‘scrollTop_3000‘).addEventListener(‘click‘,doAnimate(3000),!1)

效果如圖:
技術分享

沒區別,沒毛病,然而並沒有和上面用同一張圖...(其實打印下時間,會發現 setInterval 會是1000毫秒以內,大致在960-980毫秒之間,這個梗哪位大神可知???求解!!!)

兩者的區別

requestAnimationFrame 會請求瀏覽器調用指定的函數在下一次重繪之前更新動畫,所以開發者不用考慮頻率/丟幀問題

setInterval 中,會因為瀏覽器顯示頻率和 JavaScript 單線程可能會引發阻塞的問題而導致丟幀(視覺應為動畫不流暢)

requestAnimationFrame 會把每一幀中的所有 DOM 操作集中起來,在一次重繪或回流中就完成,性能方面更出色

對於隱藏或者不可見的元素,requestAnimationFrame 將不會進行重繪或回流,這點可減少cpu,gpu及內存的負荷

setInterval 兼容一些老版本的瀏覽器(jquery保留這個應該也是為了兼容老版本瀏覽器...)

requestAnimationFrame 兼容圖
技術分享


順便扔上jquery裏animate的部分代碼:

jQuery.fx.start = function() {
    if ( !timerId ) {
        timerId = window.requestAnimationFrame ?
            window.requestAnimationFrame( raf ) :
            window.setInterval( jQuery.fx.tick, jQuery.fx.interval );
    }
};

jQuery.fx.stop = function() {
    if ( window.cancelAnimationFrame ) {
        window.cancelAnimationFrame( timerId );
    } else {
        window.clearInterval( timerId );
    }

    timerId = null;
};

這裏的 jQuery.fx.interval 默認值為 13 ,也是因為顯示頻率的問題~

略顯尷尬... 在我windows上和mac上也保留一些老版本的瀏覽器,測效果的結果簡直蛋疼...看來兼容方面還是需要做處理的,天將降大任於 setInterval 啊 :-D

面子果實都登場了,小夥伴們是不是也該頂一頂了呢 [偷笑][偷笑][偷笑] 歡迎交流 歡迎指出各個問題~

JavaScript中的該如何[更好的]做動效