本文最后更新于:2023年11月5日 晚上
封装需求
支持控制每秒刷新帧数(FPS),节省性能消耗
支持控制持续时间
支持获取实时进度
支持暂停和继续
使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 import rAFWithFPS from "./rAFWithFPS" const { pause, next } = rAFWithFPS (progress => { console .group ('执行统计' ) console .count ('执行次数:' ) console .log ('执行进度:' , progress) console .groupEnd ('执行统计' ) }, 2000 , 60 )setTimeout (() => { pause () setTimeout (() => { next () }, 1000 ) }, 500 )
使用rAFWithFPS后可以传递三个参数
执行的回调函数
参数一:进度,范围 0 - 1
参数二:rAF对象,可使用cancelAnimationFrame手动结束
执行时间(毫秒),可传递 -1 表示无限时间,默认 -1
每秒刷新帧数(FPS),默认 30
如上,就是共执行120(2 * 60)次回调,执行30次后暂停,一秒后继续执行90次
rAFWithFPS.js 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 const rAFWithFPS = (callback, duration = -1 , fps = 30 ) => { let rAFInstance, then = performance.now (), diff, now, runDuration = 0 , pauseDiff, runDurationStart = performance.now () const fpsInterval = 1000 / fps const run = ( ) => { rAFInstance = requestAnimationFrame (run) now = performance.now () diff = now - then runDuration += (now - runDurationStart) runDurationStart = performance.now () if (diff > fpsInterval) { then = now - (diff % fpsInterval) callback (duration === -1 ? 0 : Math .min (runDuration / duration, 1 ), rAFInstance) } if (runDuration > duration && duration !== -1 ) cancelAnimationFrame (rAFInstance) } const pause = ( ) => { pauseDiff = performance.now () - then rAFInstance = (cancelAnimationFrame (rAFInstance), undefined ) } const next = ( ) => { if (rAFInstance) return const pauseWithNow = performance.now () - pauseDiff then = pauseWithNow runDurationStart = pauseWithNow run () } run () return { pause, next } }export default rAFWithFPS
tips:
使用performance.now()代替Date.now()能获取精度高达微秒的浮点数,更多介绍MDN:https://developer.mozilla.org/en-US/docs/Web/API/Performance/now
使用now - (diff % fpsInterval)代替start = now,外国友人详细的回答:
All you can control is when you’re going to skip a frame. A 60 fps monitor always draws at 16ms intervals. For example if you want your game to run at 50fps, you want to skip every 6th frame. You check if 20ms (1000/50) has elapsed, and it hasn’t (only 16ms has elapsed) so you skip a frame, then the next frame 32ms has elapsed since you drew, so you draw and reset. But then you’ll skip half the frames and run at 30fps. So when you reset you remember you waited 12ms too long last time. So next frame another 16ms passes but you count it as 16+12=28ms so you draw again and you waited 8ms too long
未知原因:暂停后执行概率出现少执行一次问题,如最后进度在0.99,执行次数例子中为119次的情况