requestAnimationFrame是HTML5游戏和动画必不可少的函数,相对于setTimeout或setInterval它有两个优势,一是它注册的回调函数与浏览器的渲染同步,不用担心Timer的时间间隔太长或太短。二是时间间隔相对与Timer要稳定,requestAnimationFrame注册的回调函数最高执行频率是60FPS,虽然在HTML5游戏里通常是达不到的,但是它两次调用之间时间间隔要比Timer稳定一些。前段时间我在CanTK Runtime里自己模拟过requestAnimationFrame,为了深入的理解Chrome里实现requestAnimationFrame方法,花了一点时间去读Blink的代码。

requestAnimationFrame的基本用法如下:

  1. var start = null;
  2. var element = document.getElementById("SomeElementYouWantToAnimate");
  3. function step(timestamp) {
  4. if (!start) start = timestamp;
  5. var progress = timestamp - start;
  6. element.style.left = Math.min(progress/10, 200) + "px";
  7. if (progress < 2000) {
  8. window.requestAnimationFrame(step);
  9. }
  10. }
  11. window.requestAnimationFrame(step);

我比较关注的是回调函数的注册和调用过程:

  • 1.注册回调函数的实现。

(WebKit/Source/core/dom/ScriptedAnimationController.cpp)

  1. ScriptedAnimationController::CallbackId ScriptedAnimationController::registerCallback(FrameRequestCallback* callback)
  2. {
  3. CallbackId id = m_callbackCollection.registerCallback(callback);
  4. scheduleAnimationIfNeeded();
  5. return id;
  6. }

注册之后还需要请求重绘,scheduleAnimationIfNeeded最终会调到ThreadProxy::SendCommitRequestToImplThreadIfNeeded:

(cc/trees/thread_proxy.cc)

  1. bool ThreadProxy::SendCommitRequestToImplThreadIfNeeded(
  2. CommitPipelineStage required_stage) {
  3. DCHECK(IsMainThread());
  4. DCHECK_NE(NO_PIPELINE_STAGE, required_stage);
  5. bool already_posted =
  6. main().max_requested_pipeline_stage != NO_PIPELINE_STAGE;
  7. main().max_requested_pipeline_stage =
  8. std::max(main().max_requested_pipeline_stage, required_stage);
  9. if (already_posted)
  10. return false;
  11. Proxy::ImplThreadTaskRunner()->PostTask(
  12. FROM_HERE,
  13. base::Bind(&ThreadProxy::SetNeedsCommitOnImplThread,
  14. impl_thread_weak_ptr_));
  15. return true;
  16. }
  17. void ThreadProxy::SetNeedsCommitOnImplThread() {
  18. TRACE_EVENT0("cc", "ThreadProxy::SetNeedsCommitOnImplThread");
  19. DCHECK(IsImplThread());
  20. impl().scheduler->SetNeedsBeginMainFrame();
  21. }
  22. void Scheduler::SetNeedsBeginMainFrame() {
  23. state_machine_.SetNeedsBeginMainFrame();
  24. ProcessScheduledActions();
  25. }
  • 2.执行回调函数的实现。

WebKit/Source/core/dom/ScriptedAnimationController.cpp

  1. void ScriptedAnimationController::executeCallbacks(double monotonicTimeNow)
  2. {
  3. // dispatchEvents() runs script which can cause the document to be destroyed.
  4. if (!m_document)
  5. return;
  6. double highResNowMs = 1000.0 * m_document->loader()->timing().monotonicTimeToZeroBasedDocumentTime(monotonicTimeNow);
  7. double legacyHighResNowMs = 1000.0 * m_document->loader()->timing().monotonicTimeToPseudoWallTime(monotonicTimeNow);
  8. m_callbackCollection.executeCallbacks(highResNowMs, legacyHighResNowMs);
  9. }
  10. void FrameRequestCallbackCollection::executeCallbacks(double highResNowMs, double highResNowMsLegacy)
  11. {
  12. // First, generate a list of callbacks to consider. Callbacks registered from this point
  13. // on are considered only for the "next" frame, not this one.
  14. ASSERT(m_callbacksToInvoke.isEmpty());
  15. m_callbacksToInvoke.swap(m_callbacks);
  16. for (size_t i = 0; i < m_callbacksToInvoke.size(); ++i) {
  17. FrameRequestCallback* callback = m_callbacksToInvoke[i].get();
  18. if (!callback->m_cancelled) {
  19. TRACE_EVENT1("devtools.timeline", "FireAnimationFrame", "data", InspectorAnimationFrameEvent::data(m_context, callback->m_id));
  20. InspectorInstrumentationCookie cookie = InspectorInstrumentation::willFireAnimationFrame(m_context, callback->m_id);
  21. if (callback->m_useLegacyTimeBase)
  22. callback->handleEvent(highResNowMsLegacy);
  23. else
  24. callback->handleEvent(highResNowMs);
  25. InspectorInstrumentation::didFireAnimationFrame(cookie);
  26. TRACE_EVENT_INSTANT1(TRACE_DISABLED_BY_DEFAULT("devtools.timeline"), "UpdateCounters", TRACE_EVENT_SCOPE_THREAD, "data", InspectorUpdateCountersEvent::data());
  27. }
  28. }
  29. m_callbacksToInvoke.clear();
  30. }

这行代码m_callbacksToInvoke.swap(m_callbacks);比较有意思,在循环执行的callbacks会有新的callback注册进来,我在实现这个功能时,没看过这段代码,当时费了点功夫才想明白怎么搞。

上面的代码是由RenderWidgetCompositor::BeginMainFrame调过来的:

(src/content/renderer/gpu/render_widget_compositor.cc)

  1. void RenderWidgetCompositor::BeginMainFrame(const cc::BeginFrameArgs& args) {
  2. double frame_time_sec = (args.frame_time - base::TimeTicks()).InSecondsF();
  3. double deadline_sec = (args.deadline - base::TimeTicks()).InSecondsF();
  4. double interval_sec = args.interval.InSecondsF();
  5. WebBeginFrameArgs web_begin_frame_args =
  6. WebBeginFrameArgs(frame_time_sec, deadline_sec, interval_sec);
  7. compositor_deps_->GetRendererScheduler()->WillBeginFrame(args);
  8. widget_->webwidget()->beginFrame(web_begin_frame_args);
  9. }

requestAnimationFrame在Chrome里的实现的更多相关文章

  1. LocalStorage在Chrome里的实现

    前段时间我们在实现CanTK-Runtime时,也曾在V8基础上模拟过浏览器的LocaleStorage功能,其实现非常简单:每个domain的数据使用的单独文件存储,因为同一时间只有一个游戏运行,所 ...

  2. 【27前端】base标签带有href属性会让chrome里的svg元素url失效

    一个chrome的问题,但具体原因不明. 触发条件:chrome浏览器base标签里href属性有值的时候 触发问题:svg里面的元素如果有用url的滤镜和模糊,则会失效,在firefox里和IE10 ...

  3. 玩爽了!直接在Chrome里抓取数据

    一个小测试发现可以自动做题,于是想通过脚本的方式看能不能获取相应的题库,刚好可以学习一下JS异步操作.花了一天时间,总算跑顺利了,遇到了不少坑.记录下来分享. 1.JS如何顺序执行 JS有强大的异步操 ...

  4. 直接在Chrome里抓取数据

    一个小测试发现可以自动做题,于是想通过脚本的方式看能不能获取相应的题库,刚好可以学习一下JS异步操作.花了一天时间,总算跑顺利了,遇到了不少坑.记录下来分享. 1.JS如何顺序执行 JS有强大的异步操 ...

  5. favicon.ico在chrome里显示正常,在ie,edge浏览器中不显示

    代码: <head> <meta charset="UTF-8"> <link href="imgs/favicon.ico" r ...

  6. 关闭在chrome里使用双指前进后退页面的功能

    defaults write com.google.Chrome AppleEnableSwipeNavigateWithScrolls -bool FALSE

  7. 在CHROME里安装 VIMIUM 插件, 方便操作

    VIMIUM 插件使用方法 VIMIUM 命令列表 网页导航 j, :向下滚动网页 k, :向上滚动网页 h : 向左滚动 l : 向右滚动 gg : 滚动到网页头部 G : 滚动到网页底部 :向上翻 ...

  8. 如何在CHROME里调试前端代码?

    以前看前端们调得很神的, 刚看书到这里,作一个记录,演练了一下,确实有点神!!! :) <!DOCTYPE html> <html lang="en"> & ...

  9. Chrome 里的请求报错 "CAUTION: Provisional headers are shown" 是什么意思?

    在调试器中看到文件显示提示为 CAUTION: Provisional headers are shown, 可是直接复制链接访问资源却可以正常访问, 最后发现是https 问题,资源采用ssl协议, ...

随机推荐

  1. infragistcs 又

    1:UltraGrid风格设置函数 public static void ColorGrid(ref Infragistics.Win.UltraWinGrid.UltraGrid dgd) { // ...

  2. percona-toolkit工具包的使用教程之开发类工具

    percona-toolkit工具包的使用教程之开发类工具 1.  pt-duplicate-key-checker l  功能介绍: 功能为从mysql表中找出重复的索引和外键,这个工具会将重复的索 ...

  3. Python3基础 random 产生置顶区间的随机整数

    镇场诗:---大梦谁觉,水月中建博客.百千磨难,才知世事无常.---今持佛语,技术无量愿学.愿尽所学,铸一良心博客.------------------------------------------ ...

  4. .Net操作注册表--un

    C#操作注册表 导入命名空间 Using MicroSoft.Win32;//64位系统装的64位版本

  5. 2012 #5 History repeat itself

    History repeat itself Time Limit:1000MS     Memory Limit:32768KB     64bit IO Format:%I64d & %I6 ...

  6. 关于directX最近的学习方案

    目标:弄清楚如何渲染一个地形,以及人物坐标以及摄像机方向,弄清四大矩阵原理 实践: 1.再次学习矩阵相关知识 2.实现红龙本书的地形 3.搜寻这方面资料书籍

  7. Java EE 锚、表格用法

    <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding= ...

  8. error: command 'gcc' failed with exit status 1 的解决办法

    yum install gcc python-devel 之前yum install gcc* 了 所以没成功. wget http://prdownloads.sourceforge.net/doc ...

  9. 在Spring中使用脚本

    Spring支持3中不同的脚本语言(看来支持地还挺多的嘛):JRuby.Groovy和BeanShell. 这三个都是java社区的脚本语言(反正到目前为止我一个都没用过,可见我有多挫). JRuby ...

  10. HDU 3065 病毒侵袭持续中

    HDU 3065 病毒侵袭持续中 Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) ...