requestAnimationFrame在Chrome里的实现

分享一下我老师大神的人工智能教程!零基础,通俗易懂!http://blog.csdn.net/jiangjunshow

也欢迎大家转载本篇文章。分享知识,造福人民,实现我们中华民族伟大复兴!

                       

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

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
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

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

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

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

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

注册之后还需要请求重绘,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();} 
   
   
  • 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
  • 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();}
   
   
  • 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

这行代码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);}
   
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
           

给我老师的人工智能教程打call!http://blog.csdn.net/jiangjunshow

这里写图片描述

猜你喜欢

转载自blog.csdn.net/jggyff/article/details/84195667