requestAnimationFrame在Chrome里的实现
2015-10-02 07:19
656 查看
requestAnimationFrame是HTML5游戏和动画必不可少的函数,相对于setTimeout或setInterval它有两个优势,一是它注册的回调函数与浏览器的渲染同步,不用担心Timer的时间间隔太长或太短。二是时间间隔相对与Timer要稳定,requestAnimationFrame注册的回调函数最高执行频率是60FPS,虽然在HTML5游戏里通常是达不到的,但是它两次调用之间时间间隔要比Timer稳定一些。前段时间我在CanTK Runtime里自己模拟过requestAnimationFrame,为了深入的理解Chrome里实现requestAnimationFrame方法,花了一点时间去读Blink的代码。
requestAnimationFrame的基本用法如下:
我比较关注的是回调函数的注册和调用过程:
1.注册回调函数的实现。
(WebKit/Source/core/dom/ScriptedAnimationController.cpp)
注册之后还需要请求重绘,scheduleAnimationIfNeeded最终会调到ThreadProxy::SendCommitRequestToImplThreadIfNeeded:
(cc/trees/thread_proxy.cc)
2.执行回调函数的实现。
WebKit/Source/core/dom/ScriptedAnimationController.cpp
这行代码m_callbacksToInvoke.swap(m_callbacks);比较有意思,在循环执行的callbacks会有新的callback注册进来,我在实现这个功能时,没看过这段代码,当时费了点功夫才想明白怎么搞。
上面的代码是由RenderWidgetCompositor::BeginMainFrame调过来的:
(src/content/renderer/gpu/render_widget_compositor.cc)
requestAnimationFrame的基本用法如下:
var start = null; var element = document.getElementById("SomeElementYouWantToAnimate"); function step(timestamp) { if (!start) start = timestamp; var progress = timestamp - start; element.style.left = Math.min(progress/10, 200) + "px"; if (progress < 2000) { window.requestAnimationFrame(step); } } window.requestAnimationFrame(step);
我比较关注的是回调函数的注册和调用过程:
1.注册回调函数的实现。
(WebKit/Source/core/dom/ScriptedAnimationController.cpp)
ScriptedAnimationController::CallbackId ScriptedAnimationController::registerCallback(FrameRequestCallback* callback) { CallbackId id = m_callbackCollection.registerCallback(callback); scheduleAnimationIfNeeded(); return id; }
注册之后还需要请求重绘,scheduleAnimationIfNeeded最终会调到ThreadProxy::SendCommitRequestToImplThreadIfNeeded:
(cc/trees/thread_proxy.cc)
bool ThreadProxy::SendCommitRequestToImplThreadIfNeeded( CommitPipelineStage required_stage) { DCHECK(IsMainThread()); DCHECK_NE(NO_PIPELINE_STAGE, required_stage); bool already_posted = main().max_requested_pipeline_stage != NO_PIPELINE_STAGE; main().max_requested_pipeline_stage = std::max(main().max_requested_pipeline_stage, required_stage); if (already_posted) return false; Proxy::ImplThreadTaskRunner()->PostTask( FROM_HERE, base::Bind(&ThreadProxy::SetNeedsCommitOnImplThread, impl_thread_weak_ptr_)); return true; } void ThreadProxy::SetNeedsCommitOnImplThread() { TRACE_EVENT0("cc", "ThreadProxy::SetNeedsCommitOnImplThread"); DCHECK(IsImplThread()); impl().scheduler->SetNeedsBeginMainFrame(); } void Scheduler::SetNeedsBeginMainFrame() { state_machine_.SetNeedsBeginMainFrame(); ProcessScheduledActions(); }
2.执行回调函数的实现。
WebKit/Source/core/dom/ScriptedAnimationController.cpp
void ScriptedAnimationController::executeCallbacks(double monotonicTimeNow) { // dispatchEvents() runs script which can cause the document to be destroyed. if (!m_document) return; double highResNowMs = 1000.0 * m_document->loader()->timing().monotonicTimeToZeroBasedDocumentTime(monotonicTimeNow); double legacyHighResNowMs = 1000.0 * m_document->loader()->timing().monotonicTimeToPseudoWallTime(monotonicTimeNow); m_callbackCollection.executeCallbacks(highResNowMs, legacyHighResNowMs); } void FrameRequestCallbackCollection::executeCallbacks(double highResNowMs, double highResNowMsLegacy) { // First, generate a list of callbacks to consider. Callbacks registered from this point // on are considered only for the "next" frame, not this one. ASSERT(m_callbacksToInvoke.isEmpty()); m_callbacksToInvoke.swap(m_callbacks); for (size_t i = 0; i < m_callbacksToInvoke.size(); ++i) { FrameRequestCallback* callback = m_callbacksToInvoke[i].get(); if (!callback->m_cancelled) { TRACE_EVENT1("devtools.timeline", "FireAnimationFrame", "data", InspectorAnimationFrameEvent::data(m_context, callback->m_id)); InspectorInstrumentationCookie cookie = InspectorInstrumentation::willFireAnimationFrame(m_context, callback->m_id); if (callback->m_useLegacyTimeBase) callback->handleEvent(highResNowMsLegacy); else callback->handleEvent(highResNowMs); InspectorInstrumentation::didFireAnimationFrame(cookie); TRACE_EVENT_INSTANT1(TRACE_DISABLED_BY_DEFAULT("devtools.timeline"), "UpdateCounters", TRACE_EVENT_SCOPE_THREAD, "data", InspectorUpdateCountersEvent::data()); } } m_callbacksToInvoke.clear(); }
这行代码m_callbacksToInvoke.swap(m_callbacks);比较有意思,在循环执行的callbacks会有新的callback注册进来,我在实现这个功能时,没看过这段代码,当时费了点功夫才想明白怎么搞。
上面的代码是由RenderWidgetCompositor::BeginMainFrame调过来的:
(src/content/renderer/gpu/render_widget_compositor.cc)
void RenderWidgetCompositor::BeginMainFrame(const cc::BeginFrameArgs& args) { double frame_time_sec = (args.frame_time - base::TimeTicks()).InSecondsF(); double deadline_sec = (args.deadline - base::TimeTicks()).InSecondsF(); double interval_sec = args.interval.InSecondsF(); WebBeginFrameArgs web_begin_frame_args = WebBeginFrameArgs(frame_time_sec, deadline_sec, interval_sec); compositor_deps_->GetRendererScheduler()->WillBeginFrame(args); widget_->webwidget()->beginFrame(web_begin_frame_args); }
相关文章推荐
- 把Chrome OS安装在VMWare下及Chrome OS的关键特性和一些截图
- Chrome的hack写法以及CSS的支持程度图示
- Chrome Visual Studio 2005下的编译过程
- 在firefox和Chrome下关闭浏览器窗口无效的解决方法
- chrome下jq width()方法取值为0的解决方法
- chrome不支持form.submit的解决方案
- chrome扩展学习 右键菜单实现代码
- chrome浏览器不支持onmouseleave事件的解决技巧
- javascript判断chrome浏览器的方法
- Ubuntu、Linux Mint一键安装Chrome浏览器的Shell脚本分享
- Linux下的Chrome快捷键详细整理
- Chrome内核下由ashx输出的js代码不起作用的解决方法
- 使用Chrome浏览器调试Android App详解
- Chrome Web App开发小结
- FCKeditor 在chrome中不显示问题
- 使用Chrome调试JavaScript的断点设置和调试技巧
- 兼容最新firefox、chrome和IE的javascript图片预览实现代码
- [转]chrome涉及开源项目
- 下载chrome插件和离线安装CRX文件的方法
- 3ff8 javascript 获取Flash下载进度兼容IE, FireFox,Chrome浏览器