requestAnimationFrame詳解
為什麼要說它,源於看到的一道面試題:問題是用js實現一個無限迴圈的動畫。
首先想到的是定時器
<!doctype html>
<html lang="en">
<head>
<title>Document</title>
<style>
#e{
width: 100px;
height: 100px;
background: red;
position: absolute;
left: 0;
top: 0;
zoom: 1;
}
</style>
</head>
<body>
<div id="e"></div>
<script>
var e = document.getElementById("e");
var flag = true;
var left = 0;
function render() {
if(flag == true){
if(left>=100){
flag = false
}
e.style.left = ` ${left++}px`
}else{
if(left<=0){
flag = true
}
e.style.left = ` ${left--}px`
}
}
setInterval(function(){
render()
},1000/60)
</script>
</body>
</html>
可以說是完美實現!
至於時間間隔為什麼是1000/60,這是因為大多數螢幕渲染的時間間隔是每秒60幀。
既然setInterval可以搞定為啥還要用requestAnimationFrame呢?最直觀的感覺就是,新增api的人是個大神級牛人,我只能懷疑自己。
所以搜尋相關問題發現以下兩點
requestAnimationFrame 比起 setTimeout、setInterval的優勢主要有兩點:
1、requestAnimationFrame 會把每一幀中的所有DOM操作集中起來,在一次重繪或迴流中就完成,並且重繪或迴流的時間間隔緊緊跟隨瀏覽器的重新整理頻率,一般來說,這個頻率為每秒60幀。
2、在隱藏或不可見的元素中,requestAnimationFrame將不會進行重繪或迴流,這當然就意味著更少的的cpu,gpu和記憶體使用量。
直接上程式碼:
<!doctype html>
<html lang="en">
<head>
<title>Document</title>
<style>
#e{
width: 100px;
height: 100px;
background: red;
position: absolute;
left: 0;
top: 0;
zoom: 1;
}
</style>
</head>
<body>
<div id="e"></div>
<script>
var e = document.getElementById("e");
var flag = true;
var left = 0;
function render() {
if(flag == true){
if(left>=100){
flag = false
}
e.style.left = ` ${left++}px`
}else{
if(left<=0){
flag = true
}
e.style.left = ` ${left--}px`
}
}
//requestAnimationFrame效果
(function animloop() {
render();
window.requestAnimationFrame(animloop);
})();
</script>
</body>
</html>
我沒有新增各個瀏覽器的相容寫法,這裡只說用法。
效果是實現了,不過我想到兩個問題。
1、怎麼停止requestAnimationFrame?是否有類似clearInterval這樣的類似方法?
第一個問題:答案是確定的 必須有:cancelAnimationFrame()接收一個引數 requestAnimationFrame預設返回一個id,cancelAnimationFrame只需要傳入這個id就可以停止了。
<!doctype html>
<html lang="en">
<head>
<title>Document</title>
<style>
#e{
width: 100px;
height: 100px;
background: red;
position: absolute;
left: 0;
top: 0;
zoom: 1;
}
</style>
</head>
<body>
<div id="e"></div>
<script>
var e = document.getElementById("e");
var flag = true;
var left = 0;
var rafId = null
function render() {
if(flag == true){
if(left>=100){
flag = false
}
e.style.left = ` ${left++}px`
}else{
if(left<=0){
flag = true
}
e.style.left = ` ${left--}px`
}
}
//requestAnimationFrame效果
(function animloop(time) {
console.log(time,Date.now())
render();
rafId = requestAnimationFrame(animloop);
//如果left等於50 停止動畫
if(left == 50){
cancelAnimationFrame(rafId)
}
})();
//setInterval效果
// setInterval(function(){
// render()
// },1000/60)
</script>
</body>
</html>
2019-10-10 at 10.19.07.gif
附上一個效果圖。也可直接capy程式碼測試。
2、如果我想動畫頻率降低怎麼做,為什麼不考慮加快呵呵 當前重新整理頻率已經是螢幕的重新整理頻率了再快也沒有意義了
這個略微麻煩點
預設情況下,requestAnimationFrame執行頻率是1000/60,大概是16ms多執一次。
如果我們想每50ms執行一次怎麼辦呢?
requestAnimationFrame執行條件類似遞迴呼叫 (說的是類似)別咬我,既然這樣的話我們能否自定一個時間間隔再執行呢?當然定時器這麼low的東西我們就不考慮了,都已經拋棄它用rAF了(都快結束了我才想起寫簡寫太他媽長了),
這個思路來源於我幾年前搞IM的一個專案,服務端推送訊息為了減小包的大小不給時間戳,這個我們做前端的都知道,我們雖然很牛逼 不過使用者更牛逼,萬一改了時間就不好玩了。
解決方案是 當和服務端通訊時 記錄下一個時間差,(時間差等於服務端時間-本地時間)不管正負我們只要這個時間差。這樣每當我們接受到訊息 或者傳送訊息的時候我們就拿本地時間和是價差相加。這樣就可以保證和服務端時間是一致的了,思路是不是很牛逼哈哈。
撤了半天我們通過以上思路來解決下rAF改變間隔的問題
上程式碼
<!doctype html>
<html lang="en">
<head>
<title>Document</title>
<style>
#e{
width: 100px;
height: 100px;
background: red;
position: absolute;
left: 0;
top: 0;
zoom: 1;
}
</style>
</head>
<body>
<div id="e"></div>
<script>
var e = document.getElementById("e");
var flag = true;
var left = 0;
//當前執行時間
var nowTime = 0;
//記錄每次動畫執行結束的時間
var lastTime = Date.now();
//我們自己定義的動畫時間差值
var diffTime = 40;
function render() {
if(flag == true){
if(left>=100){
flag = false
}
e.style.left = ` ${left++}px`
}else{
if(left<=0){
flag = true
}
e.style.left = ` ${left--}px`
}
}
//requestAnimationFrame效果
(function animloop() {
//記錄當前時間
nowTime = Date.now()
// 當前時間-上次執行時間如果大於diffTime,那麼執行動畫,並更新上次執行時間
if(nowTime-lastTime > diffTime){
lastTime = nowTime
render();
}
requestAnimationFrame(animloop);
})()
</script>
</body>
</html>
附上一個效果:
2019-10-10 at 10.58.30.gif
作者:我是一個前端
連結:http://www.jianshu.com/p/fa5512dfb4f5
來源:簡書
著作權歸作者所有。商業轉載請聯絡作者獲得授權,非商業轉載請註明出處。
- Highcharts使用HTML表中的資料建立互動式圖表教程
- bzoj1529: [POI2005]ska Piggy banks(並查集)
- 深入剖析Java中的斷言assert
- Nacos 1.4.1 之前存在鑑權漏洞,建議修復到最新版
- 記憶體操作函式:memcpy函式,memove函式
- 微軟開源 Python 自動化神器 Playwright
- Web前端之HTML
- java class檔案安全加密工具
- 5G QoS和DNN以及網路切片技術
- 有獎問答獲獎名單出爐,快來看看有沒有你!
- IDEA Groovy指令碼一鍵生成實體類,用法舒服,高效!
- 微信api呼叫限制,45009 reach max api daily quota limit 解決方法
- OpenStack Placement元件
- 【Linux伺服器開發系列】手寫使用者態協議棧,udpipeth資料包的封裝,零拷貝的實現,柔性陣列
- requestAnimationFrame詳解
- k8s叢集多容器Pod和資源共享
- 梯度提升樹(GBDT)詳解之二:分類舉例
- Automatic Model Evaluation - 知乎
- Linux下IPMI iBMC遠端管理配置查詢及密碼重置
- 京東/淘寶的手機銷售榜(前4名 -- 手機品牌 --手機型號*3 --手機解析度 -- 手機作業系統 --安卓版本號)(android / IOS)