window.requestAnimationFrame()
request 請求 Animation 動畫 Frame 框架。 一個瀏覽器的方法。 用顯示器更新機制,幫忙做「動畫更新時機」最佳化,使其符合瀏覽器更新的頻率。
window.requestAnimationFrame()
方法跟 setTimeout
类似,都是推迟某个函数的执行。
不同之处在于,setTimeout
必须指定推迟的时间,
window.requestAnimationFrame()
则是推迟到浏览器「下一次重流时」执行,
重绘通常是 16ms 执行一次,不过浏览器会自动调节这个速率。
如果一個瀏覽器標籤頁面中,運行一個動畫, 當這個標籤頁不可見時,瀏覽器會暫停它,這會減少 CPU、內存的壓力,節省電池電量。
如果某个函数会改变网页的布局,一般就放在 window.requestAnimationFrame()
里面执行,
这样可以节省系统资源,使得网页效果更加平滑。
因为慢速设备会用较慢的速率重流和重绘,而速度更快的设备会有更快的速率。
window.requestAnimationFrame(callback);
上面 callback()
會接收一個 "参数",
就是系统传入的一个高精度时间戳( performance.now()
的返回值),
单位是毫秒,表示距离网页加载的时间。
var myId = window.requestAnimationFrame( mycallback )
myId 會得到一个整数,
这个整数传入 window.cancelAnimationFrame(myId)
,用来取消 mycallback 的执行。
var element = document.getElementById('animate');
element.style.position = 'absolute';
var start = null;
function step(timestamp) {
// 會接收到一個 window.requestAnimationFrame() 傳來的時間戳參數
if (!start) start = timestamp;
var progress = timestamp - start;
// 新的時間點 - 第一次執行的時間點 = 每次越來越長的時間長度。
// 希望左移元素,最大不超过 200 像素:兩個數中間取小的作為位置值。
element.style.left = Math.min(progress / 10, 200) + 'px';
// 如果距离 "第一次执行時間點" 不超过 2000 毫秒
if (progress < 2000) {
window.requestAnimationFrame(step);
// 就继续执行动画
}
}
// 優化執行 `step()` (配合螢幕更新時機)
window.requestAnimationFrame(step);
*基於 script 動畫的時間控制 script ("requestAnimationFrame") Timing control for script-based animations ("requestAnimationFrame") https://msdn.microsoft.com/zh-tw/library/hh920765(v=vs.85).aspx
requestAnimationFrame()
把能夠合併的動作,
放在一個渲染週期內完成,從而呈現出更流暢的動畫效果,
例如你原使用 setTimrout 做動畫,
改為使用 requestAnimationFrame 替換 setTimeout,
可使 JS 動畫能夠和 CSS transition 或 SVG SMIL 動畫同步發生。
目前主流浏览器 ( Firefox 23 / IE 10 / Chrome / Safari )都支持这个方法。 可以用下面的方法,检查浏览器是否支持这个 API。 如果不支持,则自行模拟部署该方法。
捲動效能優化 Scroll optimization with window.requestAnimationFrame ticking:http://jsbin.com/sipacug/edit?js,console,output
瀏覽器變換尺寸優化 optimizedResize 節流產生器 function:http://jsbin.com/vuhefes/edit?js,console,output
動畫計算要時間, 假如我們設定在 "瀏覽器尺寸變動" 時,去觸發一個動畫計算, 因為「尺寸變動」是根據瀏覽器更新,動畫因此密集地在重新計算, 但只有在最後一次,才會真正運算完,得到結果。 為了避免前面重複計算浪費效能,下面要做一個"節流產生器"。
我們將做一個自定義事件, 當觸發這個自定義事件時,才執行「動畫計算」, 而在動畫計算完成前,不能重複觸發這個自定義事件。
我們讓這個自定義事件,跟著瀏覽器的事件觸發: 例如我們監聽滾動,當滾動事件被觸發, 同時也會試著觸發這個自定義事件, 但是除非當前沒有「動畫計算」,事件才會再次被觸發、 才會執行新的「動畫計算」。
(function() {
// ### 1. 建立 window.requestAnimFrame 的後退機制用 setTimeout
window.requestAnimFrame = (function() {
return (
window.requestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.mozRequestAnimationFrame ||
window.oRequestAnimationFrame ||
window.msRequestAnimationFrame ||
function(callback) {
window.setTimeout(callback, 1000 / 60);
}
);
})();
// ### 2. throttle
var throttle = function(type, name, obj) {
obj = obj || window;
var running = false;
var func = function() {
if (running) return;
running = true;
requestAnimFrame(function() {
obj.dispatchEvent(new CustomEvent(name));
running = false;
});
};
obj.addEventListener(type, func);
};
// ### 3. 註冊事件
throttle('resize', 'optimizedResize');
throttle('scroll', 'optimizedScroll');
})();
// ### 4. 監聽到執行動作
window.addEventListener('optimizedResize', function() {
console.log('optimizedResize');
});
window.addEventListener('optimizedScroll', function() {
console.log('optimizedScroll');
});
// ### 1. window.requestAnimFrame
window.requestAnimFrame = (function() {
return (
window.requestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.mozRequestAnimationFrame ||
window.oRequestAnimationFrame ||
window.msRequestAnimationFrame ||
function(callback) {
window.setTimeout(callback, 1000 / 60)
}
)
})()
window.requestAnimFrame
window 物件中,找到 (沒有的話就建立) 一個叫 requestAnimFrame 的 key。
= (function() { ... })()
將一個立即函式指派給這個 key。立即函式,key 的值為 return 的東西
window.requestAnimationFrame ||
取得 window 物件中的 requestAnimationFrame,沒有就
window.webkitRequestAnimationFrame || ...
找 webkitRequestAnimationFrame...
是各 (webkit, ff, opera, ms) 瀏覽器中有無提供方法 (若是新瀏覽器都有提供),
function(callback) { ... }
最後沒有的話代表是就瀏覽器,那只好做一個函式:
用 setTimeout ,做一個會定時執行 callback,大多數監視器顯示頻率為 16.7ms,
(小知識: setTimeout 最低是 4ms ,再低也是 4 ms)...
下面會建立一個控制機制 throttle:
// ### 2. throttle
// 要監聽的事件 (type)、自定義事件的名稱 (name)、要安裝監聽器的對象 (obj)
var throttle = function(type, name, obj) {
obj = obj || window
// 沒有輸入 obj 參數,就指定為 window
var running = false
// 預設是未執行
// func 是一個「不會被重複執行」的機制
var func = function() {
if (running) return
// 通知此 throttle 開始
running = true
// 當符合的時機
requestAnimFrame(function() {
// 建立「一個命名為 name 的自定義事件」
// 並觸發 obj 身上的 name 事件
obj.dispatchEvent(new CustomEvent(name))
// 通知結束 throttle
running = false
})
}
// obj 監聽 type 事件,發生時,執行 func
obj.addEventListener(type, func)
}
// ### 3. 註冊事件
throttle('resize', 'optimizedResize')
throttle('scroll', 'optimizedScroll')
監聽 window 的 resize 事件,若符合更新時機, 建立 'optimizedResize' 自定義事件
window 會不斷監聽 scroll 事件, 當同時又監聽到 'optimizedResize' 自定義事件時, 才可做某些事情 而 'optimizedResize' 自定義事件不會重複被觸發。
// ### 4. 監聽到就執行動作
window.addEventListener('optimizedResize', function() {
console.log('optimizedResize')
})
window.addEventListener('optimizedScroll', function() {
console.log('optimizedScroll')
})