金沙棋牌官方平台

当前位置:金沙棋牌 > 金沙棋牌官方平台 > 深入理解requestAnimationFrame,一个酷炫的金沙棋牌

深入理解requestAnimationFrame,一个酷炫的金沙棋牌

来源:http://www.logblo.com 作者:金沙棋牌 时间:2019-11-05 18:52

浅析 requestAnimationFrame

2017/03/02 · JavaScript · 1 评论 · requestAnimationFrame

原文出处: 淘宝前端团队(FED)- 腾渊   

金沙棋牌官方平台 1

相信现在绝大多数人在 JavaScript 中绘制动画已经在使用 requestAnimationFrame 了,关于 requestAnimationFrame 的种种就不多说了,关于这个 API 的资料,详见 http://www.w3.org/TR/animation-timing/,https://developer.mozilla.org/en/docs/Web/API/window.requestAnimationFrame。

如果我们把时钟往前拨到引入 requestAnimationFrame 之前,如果在 JavaScript 中要实现动画效果,怎么办呢?无外乎使用 setTimeout 或 setInterval。那么问题就来了:

  • 如何确定正确的时间间隔(浏览器、机器硬件的性能各不相同)?
  • 毫秒的不精确性怎么解决?
  • 如何避免过度渲染(渲染频率太高、tab 不可见等等)?

开发者可以用很多方式来减轻这些问题的症状,但是彻底解决,这个、基本、很难。

归根到底,问题的根源在于时机。对于前端开发者来说,setTimeout 和 setInterval 提供的是一个等长的定时器循环(timer loop),但是对于浏览器内核对渲染函数的响应以及何时能够发起下一个动画帧的时机,是完全不了解的。对于浏览器内核来讲,它能够了解发起下一个渲染帧的合适时机,但是对于任何 setTimeout 和 setInterval 传入的回调函数执行,都是一视同仁的,它很难知道哪个回调函数是用于动画渲染的,因此,优化的时机非常难以掌握。悖论就在于,写 JavaScript 的人了解一帧动画在哪行代码开始,哪行代码结束,却不了解应该何时开始,应该何时结束,而在内核引擎来说,事情却恰恰相反,所以二者很难完美配合,直到 requestAnimationFrame 出现。

本人很喜欢 requestAnimationFrame 这个名字,因为起得非常直白 – request animation frame,对于这个 API 最好的解释就是名字本身了。这样一个 API,你传入的 API 不是用来渲染一帧动画,你上街都不好意思跟人打招呼。

由于本人是个喜欢阅读代码的人,为了体现自己好学的态度,特意读了下 Chrome 的代码去了解它是怎么实现 requestAnimationFrame 的(代码基于 Android 4.4):

JavaScript

int Document::requestAnimationFrame(PassRefPtr<RequestAnimationFrameCallback> callback) { if (!m_scriptedAnimationController) { m_scriptedAnimationController = ScriptedAnimationController::create(this); // We need to make sure that we don't start up the animation controller on a background tab, for example. if (!page()) m_scriptedAnimationController->suspend(); } return m_scriptedAnimationController->registerCallback(callback); }

1
2
3
4
5
6
7
8
9
10
11
int Document::requestAnimationFrame(PassRefPtr<RequestAnimationFrameCallback> callback)
{
  if (!m_scriptedAnimationController) {
    m_scriptedAnimationController = ScriptedAnimationController::create(this);
    // We need to make sure that we don't start up the animation controller on a background tab, for example.
      if (!page())
        m_scriptedAnimationController->suspend();
  }
 
  return m_scriptedAnimationController->registerCallback(callback);
}

仔细看看就觉得底层实现意外地简单,生成一个 ScriptedAnimationController 的实例,然后注册这个 callback。那我们就看看 ScriptAnimationController 里面做了些什么:

JavaScript

void ScriptedAnimationController::serviceScriptedAnimations(double monotonicTimeNow) { if (!m_callbacks.size() || m_suspendCount) return; double highResNowMs = 1000.0 * m_document->loader()->timing()->monotonicTimeToZeroBasedDocumentTime(monotonicTimeNow); double legacyHighResNowMs = 1000.0 * m_document->loader()->timing()->monotonicTimeToPseudoWallTime(monotonicTimeNow); // First, generate a list of callbacks to consider. Callbacks registered from this point // on are considered only for the "next" frame, not this one. CallbackList callbacks(m_callbacks); // Invoking callbacks may detach elements from our document, which clears the document's // reference to us, so take a defensive reference. RefPtr<ScriptedAnimationController> protector(this); for (size_t i = 0; i < callbacks.size(); ++i) { RequestAnimationFrameCallback* callback = callbacks[i].get(); if (!callback->m_firedOrCancelled) { callback->m_firedOrCancelled = true; InspectorInstrumentationCookie cookie = InspectorInstrumentation::willFireAnimationFrame(m_document, callback->m_id); if (callback->m_useLegacyTimeBase) callback->handleEvent(legacyHighResNowMs); else callback->handleEvent(highResNowMs); InspectorInstrumentation::didFireAnimationFrame(cookie); } } // Remove any callbacks we fired from the list of pending callbacks. for (size_t i = 0; i < m_callbacks.size();) { if (m_callbacks[i]->m_firedOrCancelled) m_callbacks.remove(i); else ++i; } if (m_callbacks.size()) scheduleAnimation(); }

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
void ScriptedAnimationController::serviceScriptedAnimations(double monotonicTimeNow)
{
  if (!m_callbacks.size() || m_suspendCount)
    return;
 
    double highResNowMs = 1000.0 * m_document->loader()->timing()->monotonicTimeToZeroBasedDocumentTime(monotonicTimeNow);
    double legacyHighResNowMs = 1000.0 * m_document->loader()->timing()->monotonicTimeToPseudoWallTime(monotonicTimeNow);
 
    // First, generate a list of callbacks to consider.  Callbacks registered from this point
    // on are considered only for the "next" frame, not this one.
    CallbackList callbacks(m_callbacks);
 
    // Invoking callbacks may detach elements from our document, which clears the document's
    // reference to us, so take a defensive reference.
    RefPtr<ScriptedAnimationController> protector(this);
 
    for (size_t i = 0; i < callbacks.size(); ++i) {
        RequestAnimationFrameCallback* callback = callbacks[i].get();
      if (!callback->m_firedOrCancelled) {
        callback->m_firedOrCancelled = true;
        InspectorInstrumentationCookie cookie = InspectorInstrumentation::willFireAnimationFrame(m_document, callback->m_id);
        if (callback->m_useLegacyTimeBase)
          callback->handleEvent(legacyHighResNowMs);
        else
          callback->handleEvent(highResNowMs);
        InspectorInstrumentation::didFireAnimationFrame(cookie);
      }
    }
 
    // Remove any callbacks we fired from the list of pending callbacks.
    for (size_t i = 0; i < m_callbacks.size();) {
      if (m_callbacks[i]->m_firedOrCancelled)
        m_callbacks.remove(i);
      else
        ++i;
    }
 
    if (m_callbacks.size())
      scheduleAnimation();
}

这个函数自然就是执行回调函数的地方了。那么动画是如何被触发的呢?我们需要快速地看一串函数(一个从下往上的 call stack):

JavaScript

void PageWidgetDelegate::animate(Page* page, double monotonicFrameBeginTime) { FrameView* view = mainFrameView(page); if (!view) return; view->serviceScriptedAnimations(monotonicFrameBeginTime); }

1
2
3
4
5
6
7
void PageWidgetDelegate::animate(Page* page, double monotonicFrameBeginTime)
{
  FrameView* view = mainFrameView(page);
  if (!view)
    return;
  view->serviceScriptedAnimations(monotonicFrameBeginTime);
}

JavaScript

void WebViewImpl::animate(double monotonicFrameBeginTime) { TRACE_EVENT0("webkit", "WebViewImpl::animate"); if (!monotonicFrameBeginTime) monotonicFrameBeginTime = monotonicallyIncreasingTime(); // Create synthetic wheel events as necessary for fling. if (m_gestureAnimation) { if (m_gestureAnimation->animate(monotonicFrameBeginTime)) scheduleAnimation(); else { m_gestureAnimation.clear(); if (m_layerTreeView) m_layerTreeView->didStopFlinging(); PlatformGestureEvent endScrollEvent(PlatformEvent::GestureScrollEnd, m_positionOnFlingStart, m_globalPositionOnFlingStart, 0, 0, 0, false, false, false, false); mainFrameImpl()->frame()->eventHandler()->handleGestureScrollEnd(endScrollEvent); } } if (!m_page) return; PageWidgetDelegate::animate(m_page.get(), monotonicFrameBeginTime); if (m_continuousPaintingEnabled) { ContinuousPainter::setNeedsDisplayRecursive(m_rootGraphicsLayer, m_pageOverlays.get()); m_client->scheduleAnimation(); } }

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
void WebViewImpl::animate(double monotonicFrameBeginTime)
{
  TRACE_EVENT0("webkit", "WebViewImpl::animate");
 
  if (!monotonicFrameBeginTime)
      monotonicFrameBeginTime = monotonicallyIncreasingTime();
 
  // Create synthetic wheel events as necessary for fling.
  if (m_gestureAnimation) {
    if (m_gestureAnimation->animate(monotonicFrameBeginTime))
      scheduleAnimation();
    else {
      m_gestureAnimation.clear();
      if (m_layerTreeView)
        m_layerTreeView->didStopFlinging();
 
      PlatformGestureEvent endScrollEvent(PlatformEvent::GestureScrollEnd,
          m_positionOnFlingStart, m_globalPositionOnFlingStart, 0, 0, 0,
          false, false, false, false);
 
      mainFrameImpl()->frame()->eventHandler()->handleGestureScrollEnd(endScrollEvent);
    }
  }
 
  if (!m_page)
    return;
 
  PageWidgetDelegate::animate(m_page.get(), monotonicFrameBeginTime);
 
  if (m_continuousPaintingEnabled) {
    ContinuousPainter::setNeedsDisplayRecursive(m_rootGraphicsLayer, m_pageOverlays.get());
    m_client->scheduleAnimation();
  }
}

JavaScript

void RenderWidget::AnimateIfNeeded() { if (!animation_update_pending_) return; // Target 60FPS if vsync is on. Go as fast as we can if vsync is off. base::TimeDelta animationInterval = IsRenderingVSynced() ? base::TimeDelta::FromMilliseconds(16) : base::TimeDelta(); base::Time now = base::Time::Now(); // animation_floor_time_ is the earliest time that we should animate when // using the dead reckoning software scheduler. If we're using swapbuffers // complete callbacks to rate limit, we can ignore this floor. if (now >= animation_floor_time_ || num_swapbuffers_complete_pending_ > 0) { TRACE_EVENT0("renderer", "RenderWidget::AnimateIfNeeded") animation_floor_time_ = now + animationInterval; // Set a timer to call us back after animationInterval before // running animation callbacks so that if a callback requests another // we'll be sure to run it at the proper time. animation_timer_.Stop(); animation_timer_.Start(FROM_HERE, animationInterval, this, &RenderWidget::AnimationCallback); animation_update_pending_ = false; if (is_accelerated_compositing_active_ && compositor_) { compositor_->Animate(base::TimeTicks::Now()); } else { double frame_begin_time = (base::TimeTicks::Now() - base::TimeTicks()).InSecondsF(); webwidget_->animate(frame_begin_time); } return; } TRACE_EVENT0("renderer", "EarlyOut_AnimatedTooRecently"); if (!animation_timer_.IsRunning()) { // This code uses base::Time::Now() to calculate the floor and next fire // time because javascript's Date object uses base::Time::Now(). The // message loop uses base::TimeTicks, which on windows can have a // different granularity than base::Time. // The upshot of all this is that this function might be called before // base::Time::Now() has advanced past the animation_floor_time_. To // avoid exposing this delay to javascript, we keep posting delayed // tasks until base::Time::Now() has advanced far enough. base::TimeDelta delay = animation_floor_time_ - now; animation_timer_.Start(FROM_HERE, delay, this, &RenderWidget::AnimationCallback); } }

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
void RenderWidget::AnimateIfNeeded() {
  if (!animation_update_pending_)
    return;
 
  // Target 60FPS if vsync is on. Go as fast as we can if vsync is off.
  base::TimeDelta animationInterval = IsRenderingVSynced() ? base::TimeDelta::FromMilliseconds(16) : base::TimeDelta();
 
  base::Time now = base::Time::Now();
 
  // animation_floor_time_ is the earliest time that we should animate when
  // using the dead reckoning software scheduler. If we're using swapbuffers
  // complete callbacks to rate limit, we can ignore this floor.
  if (now >= animation_floor_time_ || num_swapbuffers_complete_pending_ > 0) {
    TRACE_EVENT0("renderer", "RenderWidget::AnimateIfNeeded")
    animation_floor_time_ = now + animationInterval;
    // Set a timer to call us back after animationInterval before
    // running animation callbacks so that if a callback requests another
    // we'll be sure to run it at the proper time.
    animation_timer_.Stop();
    animation_timer_.Start(FROM_HERE, animationInterval, this, &RenderWidget::AnimationCallback);
    animation_update_pending_ = false;
    if (is_accelerated_compositing_active_ && compositor_) {
      compositor_->Animate(base::TimeTicks::Now());
    } else {
      double frame_begin_time = (base::TimeTicks::Now() - base::TimeTicks()).InSecondsF();
      webwidget_->animate(frame_begin_time);
    }
    return;
  }
  TRACE_EVENT0("renderer", "EarlyOut_AnimatedTooRecently");
  if (!animation_timer_.IsRunning()) {
    // This code uses base::Time::Now() to calculate the floor and next fire
    // time because javascript's Date object uses base::Time::Now().  The
    // message loop uses base::TimeTicks, which on windows can have a
    // different granularity than base::Time.
    // The upshot of all this is that this function might be called before
    // base::Time::Now() has advanced past the animation_floor_time_.  To
    // avoid exposing this delay to javascript, we keep posting delayed
    // tasks until base::Time::Now() has advanced far enough.
    base::TimeDelta delay = animation_floor_time_ - now;
    animation_timer_.Start(FROM_HERE, delay, this, &RenderWidget::AnimationCallback);
  }
}

特别说明:RenderWidget 是在 ./content/renderer/render_widget.cc 中(content::RenderWidget)而非在 ./core/rendering/RenderWidget.cpp 中。笔者最早读 RenderWidget.cpp 还因为其中没有任何关于 animation 的代码而困惑了很久。

看到这里其实 requestAnimationFrame 的实现原理就很明显了:

  • 注册回调函数
  • 浏览器更新时触发 animate
  • animate 会触发所有注册过的 callback

这里的工作机制可以理解为所有权的转移,把触发帧更新的时间所有权交给浏览器内核,与浏览器的更新保持同步。这样做既可以避免浏览器更新与动画帧更新的不同步,又可以给予浏览器足够大的优化空间。
在往上的调用入口就很多了,很多函数(RenderWidget::didInvalidateRect,RenderWidget::CompleteInit等)会触发动画检查,从而要求一次动画帧的更新。

这里一张图说明 requestAnimationFrame 的实现机制(来自官方):
金沙棋牌官方平台 2

题图: By Kai Oberhäuser

1 赞 1 收藏 1 评论

金沙棋牌官方平台 3

例如下面的代码在执行“id1 = window.requestAnimationFrame(animate);”和“id2

window.requestAnimationFrame(animate);”时会将两个元组(handle分别为id1、id2,回调函数callback都为animate)插入到Document的动画帧请求回调函数列表末尾。 因为“采样所有动画”任务会遍历执行动画帧请求回调函数列表的每个回调函数,所以在“采样所有动画”任务中会执行两次animate。

//下面代码会打印两次"animation"

var id1 = null,

id2 = null;

function animate(time) {

console.log("animation");

}

id1 = window.requestAnimationFrame(animate);

id2 = window.requestAnimationFrame(animate);  //id1和id2值不同,指向列表中不同的元组,这两个元组中的callback都为同一个animate

兼容性方法

下面为《HTML5 Canvas 核心技术》给出的兼容主流浏览器的requestNextAnimationFrame 和cancelNextRequestAnimationFrame方法,大家可直接拿去用:

window.requestNextAnimationFrame = (function () {

var originalWebkitRequestAnimationFrame = undefined,

wrapper = undefined,

callback = undefined,

geckoVersion = 0,

userAgent = navigator.userAgent,

index = 0,

self = this;

// Workaround for Chrome 10 bug where Chrome

// does not pass the time to the animation function

if (window.webkitRequestAnimationFrame) {

// Define the wrapper

wrapper = function (time) {

if (time === undefined) {

time = +new Date();

}

self.callback(time);

};

// Make the switch

originalWebkitRequestAnimationFrame = window.webkitRequestAnimationFrame;

window.webkitRequestAnimationFrame = function (callback, element) {

self.callback = callback;

// Browser calls the wrapper and wrapper calls the callback

originalWebkitRequestAnimationFrame(wrapper, element);

}

}

// Workaround for Gecko 2.0, which has a bug in

// mozRequestAnimationFrame() that restricts animations

// to 30-40 fps.

if (window.mozRequestAnimationFrame) {

// Check the Gecko version. Gecko is used by browsers

// other than Firefox. Gecko 2.0 corresponds to

// Firefox 4.0.

index = userAgent.indexOf('rv:');

if (userAgent.indexOf('Gecko') != -1) {

geckoVersion = userAgent.substr(index + 3, 3);

if (geckoVersion === '2.0') {

// Forces the return statement to fall through

// to the setTimeout() function.

window.mozRequestAnimationFrame = undefined;

}

}

}

return  window.requestAnimationFrame ||

window.webkitRequestAnimationFrame ||

window.mozRequestAnimationFrame ||

window.oRequestAnimationFrame ||

window.msRequestAnimationFrame ||

function (callback, element) {

var start,

finish;

window.setTimeout(function () {

start = +new Date();

callback(start);

finish = +new Date();

self.timeout = 1000 / 60 - (finish - start);

}, self.timeout);

};

}());

window.cancelNextRequestAnimationFrame = window.cancelRequestAnimationFrame

|| window.webkitCancelAnimationFrame

|| window.webkitCancelRequestAnimationFrame

|| window.mozCancelRequestAnimationFrame

|| window.oCancelRequestAnimationFrame

|| window.msCancelRequestAnimationFrame

|| clearTimeout;


参考资料

Timing control for script-based animations

Browsing contexts

The Document object

《HTML5 Canvas核心技术》

理解DOM

Page Visibility

Page Visibility(页面可见性) API介绍、微拓展

HOW BROWSERS WORK: BEHIND THE SCENES OF MODERN WEB BROWSERS

4,同样为了计算帧速率,修改OnDraw函数如下,在其中用glPushMatrix 和 glPopMatrix将RenderScene函数包裹起来,从而确保动画会正确运行。在SwapBuffers调用后我们调用PostRenderScene来显示帧速率信息到窗口标题。

演示图

金沙棋牌官方平台 4

本文介绍

浏览器中动画有两种实现形式:通过申明元素实现(如SVG中的

元素)和脚本实现。

可以通过setTimeout和setInterval方法来在脚本中实现动画,但是这样效果可能不够流畅,且会占用额外的资源。可参考《Html5 Canvas核心技术》中的论述:

它们有如下的特征:

1、即使向其传递毫秒为单位的参数,它们也不能达到ms的准确性。这是因为javascript是单线程的,可能会发生阻塞。

2、没有对调用动画的循环机制进行优化。

3、没有考虑到绘制动画的最佳时机,只是一味地以某个大致的事件间隔来调用循环。

其实,使用setInterval或setTimeout来实现主循环,根本错误就在于它们抽象等级不符合要求。我们想让浏览器执行的是一套可以控制各种细节的api,实现如“最优帧速率”、“选择绘制下一帧的最佳时机”等功能。但是如果使用它们的话,这些具体的细节就必须由开发者自己来完成。

requestAnimationFrame不需要使用者指定循环间隔时间,浏览器会基于当前页面是否可见、CPU的负荷情况等来自行决定最佳的帧速率,从而更合理地使用CPU。


5,在OnTimer函数中,通过增加变量DayOfYear 和 HourOfDay的值来控制地球和月球的位置,并且调用InvalidateRect来刷新界面。

一个酷炫的,基于HTML5,Jquery和Css的全屏焦点图特效,兼容各种浏览器,html5jquery

 基于HTML5和CSS的焦点图特效,梅花图案的背景很有中国特色,而且还会动哦,效果超炫,推荐下载!

cancelAnimationFrame

cancelAnimationFrame 方法用于取消先前安排的一个动画帧更新的请求。

当调用cancelAnimationFrame(handle)时,浏览器会设置该handle指向的回调函数的cancelled为true。

无论该回调函数是否在动画帧请求回调函数列表中,它的cancelled都会被设置为true。

如果该handle没有指向任何回调函数,则调用cancelAnimationFrame 不会发生任何事情。

1,我们需要调用timeGetTime()函数,因此在stdafx.h中加入:

 JS代码

 

金沙棋牌官方平台 5 1 /** 2 * @author Alexander Farkas 3 * v. 1.22 4 */ 5 (function ($) 6 { 7 if (!document.defaultView || !document.defaultView.getComputedStyle) 8 { // IE6-IE8 9 var oldCurCSS = $.curCSS; 10 $.curCSS = function (elem, name, force) 11 { 12 if (name === 'background-position') 13 { 14 name = 'backgroundPosition'; 15 } 16 if (name !== 'backgroundPosition' || !elem.currentStyle || elem.currentStyle[name]) 17 { 18 return oldCurCSS.apply(this, arguments); 19 } 20 var style = elem.style; 21 if (!force && style && style[name]) 22 { 23 return style[name]; 24 } 25 return oldCurCSS(elem, 'backgroundPositionX', force) + ' ' + oldCurCSS(elem, 'backgroundPositionY', force); 26 }; 27 } 28 29 var oldAnim = $.fn.animate; 30 $.fn.animate = function (prop) 31 { 32 if ('background-position' in prop) 33 { 34 prop.backgroundPosition = prop['background-position']; 35 delete prop['background-position']; 36 } 37 if ('backgroundPosition' in prop) 38 { 39 prop.backgroundPosition = '(' + prop.backgroundPosition; 40 } 41 return oldAnim.apply(this, arguments); 42 }; 43 44 function toArray(strg) 45 { 46 strg = strg.replace(/left|top/g, '0px'); 47 strg = strg.replace(/right|bottom/g, '100%'); 48 strg = strg.replace(/([0-9.]+)(s|)|$)/g, "$1px$2"); 49 var res = strg.match(/(-?[0-9.]+)(px|%|em|pt)s(-?[0-9.]+)(px|%|em|pt)/); 50 return [parseFloat(res[1], 10), res[2], parseFloat(res[3], 10), res[3]]; 51 } 52 53 $.fx.step.backgroundPosition = function (fx) 54 { 55 if (!fx.bgPosReady) 56 { 57 var start = $.curCSS(fx.elem, 'backgroundPosition'); 58 if (!start) 59 {//FF2 no inline-style fallback 60 start = '0px 0px'; 61 } 62 63 start = toArray(start); 64 fx.start = [start[0], start[2]]; 65 var end = toArray(fx.end); 66 fx.end = [end[0], end[2]]; 67 68 fx.unit = [end[1], end[3]]; 69 fx.bgPosReady = true; 70 } 71 //return; 72 var nowPosX = []; 73 nowPosX[0] = ((fx.end[0] - fx.start[0]) * fx.pos) + fx.start[0]

  • fx.unit[0]; 74 nowPosX[1] = ((fx.end[1] - fx.start[1]) * fx.pos) + fx.start[1] + fx.unit[1]; 75 fx.elem.style.backgroundPosition = nowPosX[0] + ' ' + nowPosX[1]; 76 77 }; 78 })(jQuery); 79 80 81 82 /* 83 tlrkSlider 84 85 example usage: 86 87 $("#slider").tlrkSlider({ 88 autoStart: false, 89 elements: { 90 "img": {delay: 10}, 91 "h2": {delay: 500}, 92 ".copy": {delay: 800}, 93 ".button": {delay: 1000} 94 } 95 }); 96 97 to go to a specific frame: 98 $("#slider").tlrkSlider("go", position); 99 "position" can have one of the following values: 100 "next", "prev", "first", "last", "+1", "-1" or a numeric value 101 102 to start/stop the automatic loop: 103 $("#slider").tlrkSlider("start"); 104 $("#slider").tlrkSlider("stop"); 105 106 to change the delay between automatic transitions: 107 $("#slider").tlrkSlider("option", "delayAnimation", 1000); 108 109 to change any option: 110 $("#slider").tlrkSlider("option", option_name, option_value); 111 112 Changing the "elements" object is not tested. 113 114 Changing the following options: "navigation", "navigationClass", "framesSelector", "autoStart" won't have any effect for now. 115 They are used only during the initialization. 116 117 $("#slider").data("tlrkSlider") will return the plugin instance and the methods/properties can be accessed from there. 118 119 The plugin contructor defaults are accessable through TlrkSlider.defaults 120 121 The function that actually sweep the elements in/out can be overriden from 122 TlrkSlider.prototype._animationIn and TlrkSlider.prototype._animationOut 123 124 See sweepIn/sweepOut 125 126 */ 127 128 129 130 ;(function( $, window, document, undefined ){ 131 132 // utility function that generates the "dots" navigation 133 function generateNavigation($el, count, config) { 134 var i, html = "", 135 width = count * 24; 136 137 html += "<ol class='" + config.navigationClass + "' style='margin-left: -" + width/2 + "px; width: " + width + "px'>"; 138 for (i = 0; i < count; i++) { 139 html += "<li><a " + (i === 0 ? "class='selected'" : "" ) + " href='#" + (i) + "'>slide</a></li>"; 140 } 141 html += "</ol>"; 142 143 $el.append(html); 144 } 145 146 function sweepOut($el, windowWidth) { 147 var dfr = $.Deferred(), 148 pos = $el.position(), 149 width = $el.width(), 150 delta, final, 151 options = $el.data("tlrkAnimOptions"); 152 153 windowWidth = windowWidth || $(window).width(); // check if the windowWidth is passed, if not - get it 154 155 delta = windowWidth - pos.left; 156 final = -(delta); 157 158 setTimeout(function(){ 159 $el.animate({left: final, opacity: "toggle"}, options.speed, options.easing, function(){ 160 dfr.resolve(); 161 }); 162 }, options.delay); 163 164 return dfr.promise(); 165 } 166 167 function sweepIn($el, windowWidth, frameLeft) { 168 var dfr = $.Deferred(), 169 options = $el.data("tlrkAnimOptions"), 170 positionData = $el.data("tlrkOriginalPos"), 171 final = positionData.position.left, 172 rightEdge; 173 174 windowWidth = windowWidth || $(window).width(); // check if the windowWidth is passed, if not - get it 175 176 $el.css({opacity: 0, display: "block"}); // move it outside the right edge of the screen 177 $el.css("left", function(current){ 178 return current + windowWidth - frameLeft; 179 }); 180 181 setTimeout(function(){ 182 $el.animate({left: final, opacity: 1}, options.speed, options.easing, function(){ 183 dfr.resolve(); 184 }); 185 }, options.delay); 186 187 return dfr.promise(); 188 } 189 190 191 // two pass function that first iterates all the elements and gets their position/width/height 192 // and then sets their position to absolute 193 function absolutize($elements) { 194 195 // todo - move it to separate function and do it just once 196 // gather the original position/dimension data for all elements 197 $elements.each(function(){ 198 var $t = $(this); 199 200 if ($t.data("tlrkOriginalPos")) return 201 202 $t.data("tlrkOriginalPos", { 203 position: $t.position(), 204 width: $t.width(), 205 height: $t.height(), 206 css_pos: $t.css("position"), 207 css_left: $t.css("left"), 208 css_top: $t.css("top"), 209 css_width: $t.css("width") || "auto", 210 css_height: $t.css("height") || "auto" 211 }); 212 213 }); 214 215 // set the absolute position 216 $elements.each(function(){ 217 var $t = $(this), 218 opos = $t.data("tlrkOriginalPos"); 219 220 $t.css({ 221 position: "absolute", 222 left: opos.position.left, 223 top: opos.position.top, 224 width: opos.width, 225 height: opos.height 226 }); 227 }); 228 } 229 230 function restoreFrameElements($elements) { 231 $elements.each(function(){ 232 var $t = $(this), 233 opos = $t.data("tlrkOriginalPos"); 234 235 if (!opos) return 236 237 $t.css({ 238 position: opos.css_pos, 239 left: opos.css_left, 240 top: opos.css_top, 241 width: opos.css_width, 242 height: opos.css_height 243 }); 244 }); 245 246 } 247 248 var TlrkSlider = function( elem, options ){ 249 this.elem = elem; 250 this.$elem = $(elem); 251 this.options = options; 252 }; 253 254 // the plugin prototype 255 TlrkSlider.prototype = { 256 defaults: { 257 258 259 defaultElementOptions: { 260 speed: 1200, 261 easing: "easeInOutBack", 262 // interval before the element starts moving when the fadeIn/Out functions are called 263 // it's a good idea to give different delays for the different elements 264 // if all have the same delay they'll start moving all together 265 delay: 100 266 }, 267 268 // dispose elements are these that are not included in the elements object 269 // but affect the document flow and will be fadedIn/Out 270 disposeDelay: 100, // delay for the dispose elements 271 disposeSpeed: 1000, // how quickly they'll fadeOut/In 272 273 delayBetweenTransition: 1000, // time between starting fadeOut and fadeIn 274 delayAnimation: 7000, // time between auto changing the current frame 275 276 loop: true, // if true when clicking next on the last frame the slider jumps to the first one 277 278 autoStart: true, // start the automatic looping through the frames on init 279 280 framesSelector: "section", // selector for the frames inside the slider 281 282 elements: { 283 "p": {delay: 100, speed: 1000, easing: "easeInOutBack"} 284 }, 285 286 navigation: true, // the dots navigation on the bottom 287 navigationClass: "slider-nav", 288 289 // callbacks 290 // another way to "catch" these events is with 291 // $(-slider-element-).bind("animationStart") 292 animationStart: null, 293 animationEnd: null 294 }, 295 296 init: function() { 297 var c, e, element, $element, 298 that = this, 299 $firstFrame; 300 301 c = this.config = $.extend({}, this.defaults, this.options); 302 303 this.elem.style.position = "relative"; // make the wrapping element relative 304 305 // basics 306 this.$frames = this.$elem.find(c.framesSelector); 307 this.framesCount = this.$frames.length; 308 this.currentFrame = 0; 309 this.queue = []; 310 311 this._$elementsByFrame = {}; 312 this._$disposeElementsByFrame = {}; 313 314 for (i = 0; i < this.framesCount; i++) { 315 this._$elementsByFrame[i] = this._getFrameElements(i); // cache the $elements by frame 316 this._$disposeElementsByFrame[i] = this._getDisposeFrameElements(i); // cache the rest of the tree for each frame 317 } 318 319 if (c.navigation) { 320 generateNavigation(this.$elem, this.framesCount, c); 321 this.$navigation = this.$elem.find("."+c.navigationClass); 322 } 323 324 // bindings 325 this.$elem.find(".slider-nav").delegate("a", "click", function(e){ 326 var frame = this.getAttribute("href").split("#")[1]; 327 that.go.call(that, frame); 328 return false; 329 }); 330 331 this.$elem // internal bindings for the callbacks 332 .bind("animationStart", function(){ 333 if ($.isFunction(c.animationStart)) {c.animationStart.apply(that, arguments);} 334 }) 335 .bind("animationEnd", function(){ 336 if ($.isFunction(c.animationEnd)) {c.animationEnd.apply(that, arguments);} 337 }) 338 ; 339 340 // start animation? 341 if (c.autoStart) { 342 this.start(); 343 } else { 344 this.running = false; 345 } 346 347 return this; 348 }, 349 350 start: function(instant) { 351 var that = this; 352 353 if (this.timer) { // we'll clear the current timer 354 window.clearTimeout(this.timer); 355 } 356 357 this.running = true; 358 359 if (instant) { 360 that.nextFrame(); 361 } else { 362 this.timer = window.setTimeout(function(){ that.nextFrame.call(that) }, that.config.delayAnimation); 363 } 364 }, 365 366 stop: function() { 367 if (!this.running) return; // we are not running 368 369 this.running = false; 370 window.clearTimeout(this.timer); 371 }, 372 373 // main function for changing frames 374 selectFrame: function(frame, dfr) { 375 var c = this.config, // shorthand for the config 376 that = this, 377 dfr = dfr || $.Deferred(), 378 dFadeIn = $.Deferred(), 379 dFadeOut = $.Deferred(); 380 381 if (isNaN(frame) || frame < 0 || frame > this.framesCount || frame === this.currentFrame) { 382 dfr.reject(); 383 return dfr.promise(); 384 } 385 386 // clear the animation loop interval if the animation is running 387 if (this.running && this.timer) { 388 window.clearTimeout(this.timer); 389 } 390 391 // check if we are currenly running an animation. 392 if (this.animated && this.queue.length > 0) { 393 // wait for the last item in the queue to finish 394 this.queue[this.queue.length-1].done(function(){ 395 that.selectFrame(frame, dfr); // and call again the selectFrame 396 }) 397 return dfr.promise(); 398 } 399 400 this.animated = true; 401 this.$elem.trigger("animationStart", [this, frame]); 402 403 this.queue.push(dfr); 404 405 // fade the frames 406 dFadeOut = this._fadeOutFrame(this.currentFrame); 407 408 // hide the fadetout frame 409 dFadeOut.done(function(){ 410 that.$frames.eq(that.currentFrame).hide(); 411 }); 412 413 window.setTimeout(function(){ // then wait delayBetweenTransition and fadeIn the new frame 414 dFadeIn = that._fadeInFrame.call(that, frame).done(function(){ 415 // when both the fadeIn and fadeOut are done we'll resolve the selectFrame promise 416 $.when(dFadeOut, dFadeIn).done(function(){ 417 that.animated = false; 418 that.queue.shift(); 419 that.$elem.trigger("animationEnd", [that]); 420 that.currentFrame = frame; 421 dfr.resolve(); 422 }); 423 }); 424 }, c.delayBetweenTransition); 425 426 // navigation html change 427 if (this.config.navigation) { 428 this.$navigation.find(".selected").removeClass("selected").end() 429 .find("a").eq(frame).addClass("selected"); 430 } 431 432 dfr.done(function(){ // we'll resume the loop animation after the transitions are done 433 if (that.running) { 434 that.start(); 435 } 436 }); 437 438 return dfr.promise(); 439 }, 440 441 _fadeFrame: function(frame, callback, direction) { 442 var dfr = $.Deferred(), 443 $frame = this.$frames.eq(frame), 444 $elements = this._$elementsByFrame[frame], 445 windowWidth = $(window).width(), // cache it before the animations, so we don't have to check it for each element 446 i, len, 447 that = this, 448 elementAnimations = [], 449 $disposeElements = this._$disposeElementsByFrame[frame], 450 $affectedElements, 451 frameLeft = $frame.offset().left; 452 453 direction = direction || "out"; 454 455 if (!$.isFunction(callback)) return; // do nothing if there's no callback passed 456 457 $affectedElements = $elements.add($disposeElements); 458 459 // position absolute the animation and dispose elements 460 absolutize($affectedElements); 461 462 // toggle the dispose elements 463 if ($disposeElements.length > 0) { 464 window.setTimeout(function(){ 465 $disposeElements[direction === "out" ? "fadeOut" : "fadeIn"](that.config.disposeSpeed); 466 }, this.config.disposeDelay); 467 } 468 469 // invoke the callback for each element 470 // the callback must return a promise 471 $elements.each(function(){ 472 elementAnimations.push( callback.call(that, $(this), windowWidth, frameLeft) ); 473 }); 474 475 // wait for all the elements to finish their animation 476 $.when.apply(this, elementAnimations).done(function(){ 477 //restoreFrameElements($affectedElements); // and restore the elements' position 478 dfr.resolve(); // resolve the fade function 479 }); 480 481 return dfr.promise(); 482 }, 483 484 _fadeOutFrame: function(frame) { 485 var dfr = $.Deferred(), 486 $frame = this.$frames.eq(frame), 487 $disposeElements = this._$disposeElementsByFrame[frame]; 488 489 this._fadeFrame(frame, this._animationOut, "out").done(function(){ 490 dfr.resolve(); 491 }) 492 493 return dfr.promise(); 494 }, 495 496 _fadeInFrame: function(frame) { 497 var dfr = $.Deferred(), 498 $frame = this.$frames.eq(frame), 499 $elements = this._$elementsByFrame[frame]; 500 501 this._restoreFrame(frame); 502 503 $frame.show(); 504 505 this._fadeFrame(frame, this._animationIn, "in").done(function(){ 506 dfr.resolve(); 507 }); 508 509 return dfr.promise(); 510 }, 511 512 _restoreFrame: function(frame){ 513 if (!frame) return 514 restoreFrameElements( this._$elementsByFrame[frame].add(this._$disposeElementsByFrame[frame]) ); 515 }, 516 517 nextFrame: function() { 518 var frame = this.currentFrame+1, 519 dfr = $.Deferred(); 520 521 if (frame > this.framesCount-1) { 522 if (this.config.loop) { 523 frame = 0; 524 } else { 525 dfr.reject(); 526 } 527 }; 528 529 this.selectFrame(frame).done(function(){ 530 dfr.resolve(); 531 }); 532 533 return dfr.promise(); 534 }, 535 536 prevFrame: function() { 537 var frame = this.currentFrame-1, 538 dfr = $.Deferred(); 539 540 if (frame < 0) { 541 if (this.config.loop) { 542 frame = this.framesCount-1; 543 } else { 544 dfr.reject(); 545 return dfr.promise(); 546 } 547 } 548 549 this.selectFrame(frame).done(function(){ 550 dfr.resolve(); 551 }); 552 553 return dfr.promise(); 554 }, 555 556 go: function(str) { // shorthand 557 switch (str) { 558 case "next": 559 case "+1": 560 this.nextFrame(); 561 break; 562 563 case "prev": 564 case "-1": 565 this.prevFrame(); 566 break; 567 568 case "first": 569 this.selectFrame(0); 570 break; 571 572 case "last": 573 this.selectFrame(this.framesCount-1); 574 break; 575 576 default: 577 if (isNaN(str)) return; 578 this.selectFrame(Number(str)); 579 } 580 }, 581 582 // returns jquery collection of animation elements 583 _getFrameElements: function(frame) { 584 var $frame = this.$frames.eq(frame), 585 elements = this.config.elements, 586 e, elementOptions, 587 $found, $frameElements = $([]); 588 589 for (e in elements) { 590 elementOptions = elements[e]; 591 $found = $frame.find(e); 592 $found.addClass("t-frame-element").data("tlrkAnimOptions", $.extend({}, this.defaults.defaultElementOptions, elementOptions )); 593 $frameElements = $frameElements.add($found); 594 } 595 596 return $frameElements; 597 }, 598 599 // returns jquery collection of elements that have to be faded out 600 // i.e. elements on the same level as the animation elements 601 // that doesn't contain other animation elements 602 _getDisposeFrameElements: function(frame) { 603 var $disposeElements = $([]), 604 $frame = this.$frames.eq(frame), 605 $elements = this._$elementsByFrame[frame]; 606 607 $elements.each(function(){ 608 var $t = $(this), 609 $siblings = $t.siblings().not(".t-frame-element"); 610 611 $siblings.each(function(){ 612 var $t = $(this); 613 // check if the node is not already marked and doesn't contains other frame elements 614 if (!$t.hasClass("t-frame-dispose") && $t.find(".t-frame-element").length === 0) { 615 $t.addClass("t-frame-dispose"); 616 $disposeElements = $disposeElements.add($t); 617 } 618 }); 619 620 }); 621 return $disposeElements; 622 }, 623 624 625 // expose the internal animationIn/Out functions that are called for each element in the frame 626 // two arguments are passed - the $element which have to be animated and the window width 627 _animationIn: sweepIn, 628 _animationOut: sweepOut 629 630 } 631 632 TlrkSlider.defaults = TlrkSlider.prototype.defaults; 633 634 $.fn.tlrkSlider = function(options) { 635 var otherArgs = Array.prototype.slice.call(arguments, 1); 636 637 return this.each(function() { 638 var $el = $(this), 639 pluginData = $el.data("tlrkSlider"); 640 641 if (!pluginData) { // check if the slider is already attached 642 pluginData = new TlrkSlider(this, options).init(); 643 $el.data("tlrkSlider", pluginData); 644 return; 645 } 646 647 //change the options or call a method 648 if (typeof options === "string") { 649 650 // setting / getting option(s) 651 if (options === "option") { 652 653 if (typeof otherArgs[0] === "string" && typeof otherArgs[1] !== "undefined") { // set an option value 654 pluginData.config[otherArgs[0]] = otherArgs[1]; 655 } 656 657 if (typeof otherArgs[0] === "object") { // extend the config with new options 658 pluginData.config = $.extend(pluginData.config, otherArgs[0]); 659 } 660 661 } else { // call a method? 662 try { 663 pluginData[options].apply(pluginData, otherArgs); 664 } catch(ex) { 665 throw "Error calling a plugin method (" + ex + ")"; 666 } 667 } 668 } 669 }); 670 }; 671 672 window.TlrkSlider = TlrkSlider; 673 674 })( jQuery, window , document ); 675 676 $(document).ready(function(){ 677 var $backgrounds = $(".header-content").find(".parallax-bg"), 678 LAYER_OFFSET = 30, 679 PRLX_SPEED = 1500, 680 $slider; 681 682 $slider = $("#slider").tlrkSlider({ 683 autoStart: true, 684 animationStart: function(ev, slider, step){ 685 var max_steps = this.framesCount; 686 $backgrounds.each(function(idx, el){ 687 var pos = (step * (100 / max_steps)) + (LAYER_OFFSET * idx); 688 $(this).animate({"backgroundPosition": pos + "% 0"}, PRLX_SPEED); 689 }); 690 }, 691 elements: { 692 "img": {delay: 10}, 693 "h2": {delay: 500}, 694 ".copy": {delay: 800}, 695 ".button": {delay: 1000} 696 } 697 }); 698 699 $(".header-content") 700 .hover( 701 function(){$(this).find(".slider-prev, .slider-next").show();}, 702 function(){$(this).find(".slider-prev, .slider-next").hide();} 703 ) 704 .find(".slider-prev").click(function(){$slider.tlrkSlider("go", "prev"); return false; }).end() 705 .find(".slider-next").click(function(){$slider.tlrkSlider("go", "next"); return false; }); 706 707 }); View Code

 

喜欢的朋友,可以下载 

下载地址

基于HTML5和CSS的焦点图特效,梅花图案的背景很有中国特色...

已解决的问题

为什么在callback内部执行cancelAnimationFrame不能取消动画?

问题描述

如下面的代码会一直执行a:

var id = null;

function a(time) {

console.log("animation");

window.cancelAnimationFrame(id); //不起作用

id = window.requestAnimationFrame(a);

}

a();

原因分析

我们来分析下这段代码是如何执行的:

1、执行a

(1)执行“a();”,执行函数a;

(2)执行“console.log("animation");”,打印“animation”;

(3)执行“window.cancelAnimationFrame(id);”,因为id为null,浏览器在动画帧请求回调函数列表中找不到对应的callback,所以不发生任何事情;

(4)执行“id = window.requestAnimationFrame(a);”,浏览器会将一个元组< handle, a>插入到Document的动画帧请求回调函数列表末尾,将id赋值为该元组的handle值;

2、a执行完毕后,执行第一个“采样所有动画”的任务

假设当前页面一直可见,因为动画帧请求回调函数列表不为空,所以浏览器会定期地加入一个“采样所有动画”的任务到线程队列中。

a执行完毕后的第一个“采样所有动画”的任务执行时会进行以下步骤:

(1)拷贝Document的动画帧请求回调函数列表到list变量中,清空Document的动画帧请求回调函数列表;

(2)遍历list的列表,列表有1个元组,该元组的callback为a;

(3)判断a的cancelled,为默认值false,所以执行a;

(4)执行“console.log("animation");”,打印“animation”;

(5)执行“window.cancelAnimationFrame(id);”,此时id指向当前元组的a(即当前正在执行的a),浏览器将

当前元组

的a的cancelled设为true。

(6)执行“id = window.requestAnimationFrame(a);”,浏览器会将

新的元组< handle, a>

插入到Document的动画帧请求回调函数列表末尾(新元组的a的cancelled为默认值false),将id赋值为该元组的handle值。

3、执行下一个“采样所有动画”的任务

当下一个“采样所有动画”的任务执行时,会判断动画帧请求回调函数列表的元组的a的cancelled,因为该元组为新插入的元组,所以值为默认值false,因此会继续执行a。

如此类推,浏览器会一直循环执行a。

解决方案

有下面两个方案:

1、执行requestAnimationFrame之后再执行cancelAnimationFrame。

下面代码只会执行一次a:

var id = null;

function a(time) {

console.log("animation");

id = window.requestAnimationFrame(a);

window.cancelAnimationFrame(id);

}

a();

2、在callback外部执行cancelAnimationFrame。 下面代码只会执行一次a:

function a(time) {

console.log("animation");

id = window.requestAnimationFrame(a);

}

a();

window.cancelAnimationFrame(id);

因为执行“window.cancelAnimationFrame(id);”时,id指向了新插入到动画帧请求回调函数列表中的元组的a,所以 “采样所有动画”任务判断元组的a的cancelled时,该值为true,从而不再执行a。

注意事项

1、在处理模型 中我们已经看到,在遍历执行拷贝的动画帧请求回调函数列表中的回调函数之前,Document的动画帧请求回调函数列表已经被清空了。因此如果要多次执行回调函数,需要在回调函数中再次调用requestAnimationFrame将包含回调函数的元组加入到Document的动画帧请求回调函数列表中,从而浏览器才会再次定期加入“采样所有动画”的任务(当页面可见并且动画帧请求回调函数列表不为空时,浏览器才会加入该任务),执行回调函数。

例如下面代码只执行1次animate函数:

var id = null;

function animate(time) {

console.log("animation");

}

window.requestAnimationFrame(animate);

下面代码会一直执行animate函数:

var id = null;

function animate(time) {

console.log("animation");

window.requestAnimationFrame(animate);

}

animate();

2、如果在执行回调函数或者Document的动画帧请求回调函数列表被清空之前多次调用requestAnimationFrame插入同一个回调函数,那么列表中会有多个元组指向该回调函数(它们的handle不同,但callback都为该回调函数),“采集所有动画”任务会执行多次该回调函数。

void CCY457OpenGLView::OnDraw(CDC* pDC)
{
    CCY457OpenGLDoc* pDoc = GetDocument();
    ASSERT_VALID(pDoc);
    // Get the system time, in milliseconds.
    m_ElapsedTime = ::timeGetTime(); // get current time
    if ( ElapsedTimeinMSSinceLastRender() < 30 )
        return
    // Clear out the color & depth buffers
    ::glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
    glPushMatrix();
        RenderScene();
    glPopMatrix();
    // Tell OpenGL to flush its pipeline
    ::glFinish();
    // Now Swap the buffers
    ::SwapBuffers( m_pDC->GetSafeHdc() );
    //Perform Post Display Processing
    // Only update the title every 15 redraws (this is about
    // every 1/2 second)
    PostRenderScene();
    // the very last thing we do is to save
    // the elapsed time, this is used with the
    // next elapsed time to calculate the
    // elapsed time since a render and the frame rate
    m_previousElapsedTime = m_ElapsedTime;
}

html代码

 1 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
 2 <html xmlns="http://www.w3.org/1999/xhtml">
 3 <head>
 4 <meta http-equiv="Content-Type" content="text/html; charset=gb2312" />
 5 <meta name="keywords" content="一个酷炫的,基于HTML5和Css的焦点图特效,兼容各种浏览器" />
 6 <meta name="description" content="一个酷炫的,基于HTML5和Css的焦点图特效,兼容各种浏览器" />
 7 <title>一个酷炫的,基于HTML5和Css的焦点图特效,兼容各种浏览器</title>
 8 <link href="css/slider.css" rel="stylesheet" type="text/css" />
 9 <!--[if lt IE 9]><script src="js/html5.js"></script><![endif]-->
10 <!--[if IE 6]> 
11 <script type="text/javascript" src="js/DD_belatedPNG.js"></script> 
12 <script>DD_belatedPNG.fix('*');</script> 
13 <![endif]-->
14 <script type='text/javascript' src='js/jquery-1.7.2.min.js'></script>
15 <script type='text/javascript' src='js/common.js'></script>
16 <script type='text/javascript' src='js/slider.js'></script>
17 </head>
18 <body>
19 <header>
20   <div class="header-content home">
21     <div class="parallax-bg" id="slider-wrap">
22       <div class="slider parallax-bg" id="slider">
23         <div class="slider-sections sandbox">
24           <section class="first"> <img alt="Kendo UI" src="images/home-banner-1.png"/>
25             <div class="text">
26               <h2>SmartSite Ver 2.2<br />
27                 智能网站管理系统 </h2>
28               <p class="copy">采用前后台完全分离技术,通过标签(支持标签循环嵌套、判断标签、自定义标签、文件循环嵌套等)加模板技术.全站生成纯静态页。</p>
29               <p class="button"><a href="http://www.cnblogs.com/">Download</a> <a class="dimmed" href="http://www.cnblogs.com/">Learn More</a></p>
30             </div>
31           </section>
32           <section> <img src="images/dataviz-home-image-q2.png" alt="Kendo UI" />
33             <div class="text" style="padding-top: 10px;">
34               <h2>企业网站管理系统</h2>
35               <p class="copy">单页面、单页面索引、新闻、产品展示、下载、友情链接、网上商城,在线支付、配送、支付方式管理、广告等模块。</p>
36               <p class="button"><a href="http://www.cnblogs.com/">Download</a> <a class="dimmed" href="http://www.cnblogs.com/">Learn More</a></p>
37             </div>
38           </section>
39           <section> <img src="images/home_banner_web-q2.png" alt="Kendo UI" />
40             <div class="text">
41               <h2>智能移动网站管理系统</h2>
42               <p class="copy">基于jquery.Mobile、HTML5技术框架,前后台完全分离,采用标签加模板技术,全站生成纯静态页。</p>
43               <p class="button"><a href="http://www.cnblogs.com/" >Download</a> <a class="dimmed" href="http://www.cnblogs.com/">Learn More</a></p>
44             </div>
45           </section>
46         </div>
47       </div>
48       <a class="slider-prev" href="javascript: void(0)">&lt;</a> <a class="slider-next" href="javascript: void(0)">&gt;</a> </div>
49   </div>
50 </header>
51 </body>
52 </html>

requestAnimationFrame

requestAnimationFrame方法用于通知浏览器重采样动画。

当requestAnimationFrame(callback)被调用时不会执行callback,而是会将元组< handle,callback>插入到动画帧请求回调函数列表末尾(其中元组的callback就是传入requestAnimationFrame的回调函数),并且返回handle值,该值为浏览器定义的、大于0的整数,唯一标识了该回调函数在列表中位置。

每个回调函数都有一个布尔标识cancelled,该标识初始值为false,并且对外不可见。

在后面的“处理模型” 中我们会看到,浏览器在执行“采样所有动画”的任务时会遍历动画帧请求回调函数列表,判断每个元组的callback的cancelled,如果为false,则执行callback。

void CCY457OpenGLView::RenderScene ()
{//绘制函数
    glTranslatef(0.0f,0.0f,-5.0f);
    //Draw the Sun
    glutWireSphere(1.0f,20,20);
    //Rotate the Planet in its orbit
    glRotatef((GLfloat) (360.0*DayOfYear)/365.0, 0.0f, 1.0f, 0.0f);
    glTranslatef(4.0f,0.0f,0.0f);
    glPushMatrix();
        //Rotate the Planet in its orbit
        glRotatef((GLfloat)(360*HourOfDay)/24.0, 0.0f,1.0f,0.0f);
        //Draw the Planet
        glutWireSphere(0.2f,20,20);
    glPopMatrix();
    glRotatef((GLfloat) (360.0*12.5*DayOfYear)/365.0, 0.0f, 1.0f, 0.0f);
    glTranslatef(0.5f,0.0f,0.0f);
    //Draw the Moon
    glutWireSphere(0.01f,20,20);
}

名词说明

6,在RenderScene中加入绘制代码:

前言

本文主要参考w3c资料,从底层实现原理的角度介绍了requestAnimationFrame、cancelAnimationFrame,给出了相关的示例代码以及我对实现原理的理解和讨论。


Frame rate is nothing but the number of frames that can be rendered per second. The higher this rate, the smoother the animation. In order to calculate the frame rate we retrieve the system time (using the Windows multimedia API function timeGetTime()) before the rendering is performed and after the buffer is swapped. The difference between the two values is the elapsed time to render one frame. Thus we can calculate the frame rate for a given application.

动画帧请求回调函数列表

每个Document都有一个动画帧请求回调函数列表,该列表可以看成是由< handle, callback>元组组成的集合。其中handle是一个整数,唯一地标识了元组在列表中的位置;callback是一个无返回值的、形参为一个时间值的函数(该时间值为由浏览器传入的从1970年1月1日到当前所经过的毫秒数)。 刚开始该列表为空。

Document

Dom模型中定义的Document节点。

Active document

浏览器上下文browsingContext中的Document被指定为active document。

browsingContext

浏览器上下文。

浏览器上下文是呈现document对象给用户的环境。 浏览器中的1个tab或一个窗口包含一个顶级浏览器上下文,如果该页面有iframe,则iframe中也会有自己的浏览器上下文,称为嵌套的浏览器上下文。

DOM模型

详见我的理解DOM。

document对象

当html文档加载完成后,浏览器会创建一个document对象。它对应于Document节点,实现了HTML的Document接口。 通过该对象可获得整个html文档的信息,从而对HTML页面中的所有元素进行访问和操作。

HTML的Document接口

该接口对DOM定义的Document接口进行了扩展,定义了 HTML 专用的属性和方法。

详见The Document object

页面可见

当页面被最小化或者被切换成后台标签页时,页面为不可见,浏览器会触发一个 visibilitychange事件,并设置document.hidden属性为true;切换到显示状态时,页面为可见,也同样触发一个 visibilitychange事件,设置document.hidden属性为false。

详见Page Visibility、Page Visibility(页面可见性) API介绍、微拓展

队列

浏览器让一个单线程共用于执行javascrip和更新用户界面。这个线程通常被称为“浏览器UI线程”。 浏览器UI线程的工作基于一个简单的队列系统,任务会被保存到队列中直到进程空闲。一旦空闲,队列中的下一个任务就被重新提取出来并运行。这些任务要么是运行javascript代码,要么执行UI更新,包括重绘和重排。

API接口

Window对象定义了以下两个接口:

partial interface Window {

long requestAnimationFrame(FrameRequestCallback callback);

void cancelAnimationFrame(long handle);

};


本文中将对第5篇文章的太阳系模型进行修改,加入一些动画效果。此外还会加入显示帧速率的代码。 

处理模型

当页面可见并且动画帧请求回调函数列表不为空时,浏览器会定期地加入一个“采样所有动画”的任务到UI线程的队列中。

此处使用伪代码来说明“采样所有动画”任务的执行步骤:

var list = {};

var browsingContexts = 浏览器顶级上下文及其下属的浏览器上下文;

for (var browsingContext in browsingContexts) {

var time = 从1970年1月1日到当前所经过的毫秒数;

var d = browsingContext的active document;  //即当前浏览器上下文中的Document节点

//如果该active document可见

if (d.hidden !== true) {

//拷贝active document的动画帧请求回调函数列表到list中,并清空该列表

var doclist = d的动画帧请求回调函数列表

doclist.appendTo(list);

clear(doclist);

}

//遍历动画帧请求回调函数列表的元组中的回调函数

for (var callback in list) {

if (callback.cancelled !== true) {

try {

//每个browsingContext都有一个对应的WindowProxy对象,WindowProxy对象会将callback指向active document关联的window对象。

//传入时间值time

callback.call(window, time);

}

//忽略异常

catch (e) {

}

}

}

}

2,为了计算绘制用时,在CCY457OpenGLView.h中加入如下变量:

并且Link—>Object/library modules中加入winmm.lib

#include <mmsystem.h>        // for MM timers (you'll need WINMM.LIB)

CCY457OpenGLView::CCY457OpenGLView()
{
    DayOfYear = 1;
    HourOfDay = 1;
}

      加入动画效果最容易的方法是响应WM_TIMER消息,在其消息处理函数中改变一些参数值,比如每过多少毫秒就旋转一定的角度,并且重绘场景。

//////////////////////////////////////////////////////////////////////////////
// PostRenderScene
// perform post display processing
// The default PostRenderScene places the framerate in the
// view's title. Replace this with your own title if you like.
void CCY457OpenGLView::PostRenderScene( void )
{
    // Only update the title every 15 redraws (this is about
    // every 1/2 second)
    static int updateFrame = 15;
    if (16 > ++updateFrame )
        return;
    updateFrame = 0;
    char string[256];
    _snprintf( string, 200, "%s ( %d Frames/sec )",
        (const char*)m_WindowTitle, FramesPerSecond() );
    GetParentFrame()->SetWindowText( string );
}
//////////////////////////////////////////////////////////////////////////////
// FramesPerSecond
// fetch frame rate calculations
int CCY457OpenGLView::FramesPerSecond( void )
{
    double eTime = ElapsedTimeinMSSinceLastRender();
    if ( 0 == (int)eTime )
        return 0;
    return (int)(1000/(int)eTime);
}
DWORD ElapsedTimeinMSSinceLastStartup()
{
    return(m_ElapsedTime - m_StartTime);
}
DWORD ElapsedTimeinMSSinceLastRender()
{
    return(m_ElapsedTime - m_previousElapsedTime);
}

Frame Rate

4,在CCY457OpenGLView类中加入下述成员函数,用来显示帧速率信息到窗口标题

    //For elapsed timing calculations
    DWORD m_StartTime, m_ElapsedTime, m_previousElapsedTime;    
    CString m_WindowTitle;    //Window Title
    int DayOfYear;
    int HourOfDay;

3,为了计算帧速率,修改OnCreate函数,在其中获取窗口标题,从标题中去掉”Untitled”字样,并启动定时器;

void CCY457OpenGLView::OnTimer(UINT nIDEvent) 
{
    if(DayOfYear < 365)
        DayOfYear++;
    else
        DayOfYear = 1;
    if(HourOfDay < 365)
        HourOfDay++;
    else
        HourOfDay = 1;
    InvalidateRect(NULL, FALSE);    
    CView::OnTimer(nIDEvent);
}

并在构造函数中进行初始化:

本文由金沙棋牌发布于金沙棋牌官方平台,转载请注明出处:深入理解requestAnimationFrame,一个酷炫的金沙棋牌

关键词:

上一篇:是你不懂我,重新认识Box

下一篇:没有了