Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

【Canvas杂谈:第一季】RAF/FPS/dt干嘛用? #1

Closed
hongru opened this issue Oct 16, 2013 · 10 comments
Closed

【Canvas杂谈:第一季】RAF/FPS/dt干嘛用? #1

hongru opened this issue Oct 16, 2013 · 10 comments

Comments

@hongru
Copy link
Owner

hongru commented Oct 16, 2013

title


写在前面的话

  1. 为什么要写【Canvas杂谈】?

    答:还是抱着对于canvas的热情和态度,即使应用场景有限,不忍放下。加之目前集团淘宝,天猫,无线都有游戏化场景的需求。有一些和技术有关无关的话想和大家分享。
  2. 【Canvas杂谈】讲什么?

    答:和canvas有关的事情,他的好,他的坏,他坑爹的地方,以及可以和他做的窝心的事情。
  3. 该用什么样的心态来阅读【Canvas杂谈】?

    答:以“玩乐技术”的态度,它讲技术,但也讲故事。

我们今天要说的故事。要从RAF开始。
首先,RAF是什么?我相信大部分正在阅读此文的同学应该正在使用chrome浏览器。打开控制台,敲出webkitRequestAnimationFrame .
小编不会赘述这个东西干嘛用的,知道的同学已经知道了,不太了解的同学随便google一下也就知道了。说的简单一点,它就是个计时器 干着和setTimeout(callback, 16) 类似的事情。

为什么偏偏是setTimeout(callback, 16)
不知道大家对上面的16ms有什么疑义没?
image

两三年前当js动画的计时器都是使用setTimeoutsetInterval来实现的时候。小编曾经有过以下的蠢事。

  • 以前在做一些带动画的组件的时候,比如幻灯片slide,或者什么淡出淡出,划入划出的时候。总是觉得这个动画的时间间隔设的越短,动画表现就会越流畅。

所以曾经做过一件事:

setInterval(function(){  
    // animation func
}, 1);

最后发现其实setInterval 这种东西是有个最小的时间间隔的。比如小编现在在用的chrome里面 setInterval 的最小时间间隔就是4ms。

但是就算给他设置4ms的interval 它也不是4ms,因为... 我们还要考虑setInterval中的function的运行时间... 说到这里,剩下的小伙伴们自己去想吧。

好吧,回到开始的 16ms的话题。为什么16ms是合适的,而不是10ms或者20ms。当然这跟我们的人体工程有关系。有医学研究发现我们人眼对于刷新率的感知。每秒60帧几乎就满足了我们的眼睛。意思就是好像你花个5k块钱买个刷新率每秒200帧的电视看起来跟别人1k块的刷新率每秒60帧的电视看起来流畅度差不多... 汗!!
image

那么16ms 和60帧又有什么关系呢? 请打开计算器 计算一下, 1秒 = 1000毫秒, 1000/60 = ??

就算群众的眼睛是雪亮的..60帧也足够了。

16ms和RAF又有什么关系呢??
image
请看图,不解释 💯

然后,我们要说的是RAF和setInterval(callback, 16)以及setTimeout(callback, 16) 的关系
在扯之前,先让大家看两个demo 【图片可点】。
1-1 2-1

这两个实现的是同一个东西,虽然不是canvas实现的东西,但是跟RAF和setInterval有关。我相信大家用浏览器打开这两个demo看到的效果是一样的。当然,浏览器需要支持RAF,然后,请按下面的操作做:

同时打开这两个demo,然后浏览器最小化,或者切到别的tab去看看新闻啊,看看美女帅哥啊,过个半分一分钟再回到这两个页面来看看。
我想会突然好像知道了什么事情。。。

setInterval 和 setTimeout 在非当前窗口,或者浏览器‘休眠’的时候,即使渲染停止了,但是计时器不会休息,仍然会顽强的跑着!
这其实是一个不好的讯息。会大大加重浏览器的负担,同时也会影响这个页面或者app本身的性能。

以上,为第一个明显的不同的地方。

第二个不同的地方,在于callback队列的不同。小编举个栗子:

我们假设这里有3个循环计数器的实现,A是利用RAF的递归调用做的,B是用setInterval + 16ms做的,C是用setTimeout + 16ms + 递归调用实现的。
然后用这3个计步器来做一个callback的循环调用。同时假设这个callback执行消耗的时间固定为100ms。
那么我们可以大致猜想一下 B在做这件事情的具体过程:
B从时间零点开始第一次执行callback,16ms之后发现应该执行第二次了,但是发现第一次还没做完,于是还是得继续第一次没做完的事情,把第二次callback排到自己的一个计划队列里面,等到第一次执行完了再执行队列里的第二次...
但是恶性循环,32ms之后,第一次的事情还没做完,第3次的任务又来了。。。
于是,这个任务队列越来越长,越来越长。。。

C来安排任务的机制会比B稍微好一点点,但终究也逃不了B的厄运,身上的负担会越来越重。

我们来看A做这件事情的过程。

每一次任务的完成仍然需要100ms,但是浏览器不会在A没有完成当前任务的时候给它下一次的任务。也就是说A总是花100ms完成一次任务,然后再花100ms完成下一次任务。

所以,A完成10次任务所花的时间应该是1000ms,但是B和C完成10次任务应该需要>=1000ms,因为他们还要分心去管理他们的任务队列。

做个更形象的比喻:

  • 你做一个任务需要一天,但是你的老板每个小时都回来给你一个新任务。
  • 你做一个任务需要一天,等你做完今天的任务,你的老板会给你下一次的任务。

结果是你都花10天做完了10个任务,请问你觉得你更喜欢哪种方式??


RAF说完了,我们来说说FPSdt
我们都知道要让canvas里面的东西动起来,本质上你在位置1画了一个矩形,然后擦掉,然后在位置二画了一个同样的矩形。所以,这个矩形从位置一运动到了位置二。
我们把这个过程叫做“一帧”。

FPS: Frame Per Second, 翻译过来是每秒的帧数。
dt: Delta Time, 翻译过来是时间差,或者每帧的时间间隔。

理论上,fps和dt的关系就是 FPS = 1s/dt 。他们都是用来表示一个系统每秒能够运行的帧数的。简单来说,这两个东西可以反应动画的流畅程度。

那么,FPS通常是怎么获取到的呢?
我们看一个demo:

这是小编闲暇时用粒子的双密度松弛算法做的简单的流体模拟实验。我们来说说右上角的fps是怎么得到的呢?
根据 FPS= 1/dt. 所以首先我们先去拿到这个帧的时间间隔dt。在你的动画循环计数器开始的时候记录下来时间,和上一次记录下来的时间相减,就能简单得到一个当前帧的时间差dt。

代码就不多说,我想大家应该都知道怎么做。拿到dt之后,换算成fps,按合适的时间抛出来就可以了。

如果你觉得麻烦,推荐大家使用 mrdoob/stats.js

最后一个问题:dt 除了可以反应动画流畅度之外,还有什么用??
小编猜测,应该好多的同学没有意识到dt这个时间差在动画,或者游戏里面还有一个莫大的用处。
举一个场景:

假设我们在性能好的机器和性能差一些的机器上同时测试一个游戏。好的机器的FPS稳定在60,差的机器FPS稳定在30。
又假设我们给游戏主角的设定为“按住方向右键,主角在游戏场景里向右走动”。在这种设定下,假设A同学的实现为

//假设person为主角的一个实例,person.x 表示主角在游戏场景的位置。在每帧的update里如果这样写。
person.x += 1

那么在好的机器上,用户按下方向键1s,游戏执行了60帧,每帧人物的位置+1,那么人物移动了60的距离。
但是在坏的机器上,用户按下方向键1s,游戏只会执行30帧,同样每帧人物位置+1,人物只会移动30的距离。

这。。。显然不是我们期望的。

接下来,每帧的时间差dt就派上用场了。如果我们在每帧的update里面这样写:

person.x += dt*60;

好的机器上,用户按下方向键1s,游戏执行60帧,每帧的间隔dt是1/60, dt_60 就是1,每帧人物移动1的距离。1s移动60的距离。
坏的机器上,游戏执行30帧,dt=1/30, dt_60=2; 也就是每帧人物会移动2的距离,那么1s下来,人物移动总距离还是60.

说到这里,小编觉得应该忽然明白了什么。。。
image


本期的【Canvas杂谈】到此为止,总结一下:

  • 如果你在做动画,不管是不是基于canvas的,不妨尝试一下requestAnimationFrame 。至于浏览器的兼容问题,小编认为你自有方法搞定。
  • 如果你在做动画,不妨把FPS 暴露出来,对性能的检测和优化一定有用的。
  • 如果你在做动画,帧的时间差dt 可以发挥更大的用处。

【完】


写在后面的话,

  • 这里是【Canvas杂谈】第一期,文字有点多,看官们不要厌烦。
  • 【Canvas杂谈】以季度为单位,每周一期,希望能对你有所帮助。 第一季话题草案 issues
  • 知之为知之,不知为不知,如果叙述有不准确,欢迎来 issues 里讨论。
  • 如果您有想听的话题,请来**New Issue** 里提。小编会有规律的随机选取有价值的话题撰文。
  • 每期【Canvas杂谈】会同步更新到ATA canvas小组圈子 http://www.atatech.org/gprofile/928
@lianqin7
Copy link

顶!!!非常赞!!!

@hongru
Copy link
Owner Author

hongru commented Oct 25, 2013

@lianqin7 多谢支持 ,可以提交感兴趣的话题,或者下次一起来完成某个话题 :)

@ksky521
Copy link

ksky521 commented Oct 26, 2013

很赞~

@hongru
Copy link
Owner Author

hongru commented Oct 26, 2013

@ksky521 三水清 来支持。多谢 :) 貌似我们有一面之缘在深圳。在一桌上吃过盒饭??

@hongru hongru closed this as completed Oct 26, 2013
@ksky521
Copy link

ksky521 commented Oct 27, 2013

没有啊,深圳的确去过,是腾讯圣诞晚会,不过没吃过盒饭,哈哈

@MicroConan
Copy link

加油!

@c9h10n2o
Copy link

c9h10n2o commented Dec 1, 2013

人眼视觉暂留导致无法感知闪烁的帧速率下限是24fps(电影)
电视领域有60fps,不过那是隔行扫描的60fields(场),等效于30fps

@hongru
Copy link
Owner Author

hongru commented Dec 2, 2013

@c9h10n2o 哇,这么精确,24fps。 人眼 ,找个机会测试一下。

@Johnbug
Copy link

Johnbug commented Jul 17, 2014

@Zwei-Eric
Copy link

开始不理解dt什么意思,看到delta time秒懂

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

7 participants