frame.js 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171
  1. /**
  2. * @fileOverview
  3. *
  4. * 提供动画帧的基本支持
  5. */
  6. define(function(require, exports) {
  7. // 原生动画帧方法 polyfill
  8. var requestAnimationFrame =
  9. window.requestAnimationFrame ||
  10. window.mozRequestAnimationFrame ||
  11. window.webkitRequestAnimationFrame ||
  12. window.msRequestAnimationFrame ||
  13. function(fn) {
  14. return setTimeout(fn, 1000 / 60);
  15. };
  16. var cancelAnimationFrame =
  17. window.cancelAnimationFrame ||
  18. window.mozCancelAnimationFrame ||
  19. window.webkitCancelAnimationFrame ||
  20. window.msCancelAnimationFrame ||
  21. window.clearTimeout;
  22. // 上一个请求的原生动画帧 id
  23. var frameRequestId;
  24. // 等待执行的帧动作的集合,这些帧的方法将在下个原生动画帧同步执行
  25. var pendingFrames = [];
  26. /**
  27. * 添加一个帧到等待集合中
  28. *
  29. * 如果添加的帧是序列的第一个,至少有一个帧需要被执行,则会请求一个原生动画帧来执行
  30. */
  31. function pushFrame(frame) {
  32. if (pendingFrames.push(frame) === 1) {
  33. frameRequestId = requestAnimationFrame(executePendingFrames);
  34. }
  35. }
  36. /**
  37. * 执行所有等待帧
  38. */
  39. function executePendingFrames() {
  40. var frames = pendingFrames;
  41. pendingFrames = [];
  42. while (frames.length) {
  43. executeFrame(frames.pop());
  44. }
  45. frameRequestId = 0;
  46. }
  47. /**
  48. * @method kity.requestFrame
  49. * @catalog animate
  50. * @grammar kity.requestFrame(action) => {frame}
  51. * @description 请求一个帧,执行指定的动作。动作回调提供一些有用的信息
  52. *
  53. * @param {Function} action
  54. *
  55. * 要执行的动作,该动作回调有一个参数 frame,其中:
  56. *
  57. * frame.time {Number}
  58. * 动作执行时的时间戳(ms)
  59. *
  60. * frame.index {Number}
  61. * 当前执行的帧的编号(首帧为 0)
  62. *
  63. * frame.dur {Number}
  64. * 上一帧至当前帧经过的时间,单位 ms
  65. *
  66. * frame.elapsed {Number}
  67. * 从首帧开始到当前帧经过的时间,单位 ms
  68. *
  69. * frame.action {Number}
  70. * 指向当前的帧处理函数
  71. *
  72. * frame.next()
  73. * 表示下一帧继续执行。如果不调用该方法,将不会执行下一帧。
  74. *
  75. * @example
  76. *
  77. * ```js
  78. * kity.requestFrame(function(frame) {
  79. * console.log('平均帧率:' + frame.elapsed / (frame.index + 1));
  80. *
  81. * // 更新或渲染动作
  82. *
  83. * frame.next(); //继续执行下一帧
  84. * });
  85. * ```
  86. */
  87. function requestFrame(action) {
  88. var frame = initFrame(action);
  89. pushFrame(frame);
  90. return frame;
  91. }
  92. /**
  93. * @method kity.releaseFrame
  94. * @catalog animate
  95. * @grammar kity.releaseFrame(frame)
  96. * @description 释放一个已经请求过的帧,如果该帧在等待集合里,将移除,下个动画帧不会执行释放的帧
  97. *
  98. * @param {frame} frame 使用 kity.requestFrame() 返回的帧
  99. *
  100. * @example
  101. *
  102. * ```js
  103. * var frame = kity.requestFrame(function() {....});
  104. * kity.releaseFrame(frame);
  105. * ```
  106. */
  107. function releaseFrame(frame) {
  108. var index = pendingFrames.indexOf(frame);
  109. if (~index) {
  110. pendingFrames.splice(index, 1);
  111. }
  112. if (pendingFrames.length === 0) {
  113. cancelAnimationFrame(frameRequestId);
  114. }
  115. }
  116. /**
  117. * 初始化一个帧,主要用于后续计算
  118. */
  119. function initFrame(action) {
  120. var frame = {
  121. index: 0,
  122. time: +new Date(),
  123. elapsed: 0,
  124. action: action,
  125. next: function() {
  126. pushFrame(frame);
  127. }
  128. };
  129. return frame;
  130. }
  131. /**
  132. * 执行一个帧动作
  133. */
  134. function executeFrame(frame) {
  135. // 当前帧时间错
  136. var time = +new Date();
  137. // 当上一帧到当前帧经过的时间
  138. var dur = time - frame.time;
  139. //
  140. // http://stackoverflow.com/questions/13133434/requestanimationframe-detect-stop
  141. // 浏览器最小化或切换标签,requestAnimationFrame 不会执行。
  142. // 检测时间超过 200 ms(频率小于 5Hz ) 判定为计时器暂停,重置为一帧长度
  143. //
  144. if (dur > 200) {
  145. dur = 1000 / 60;
  146. }
  147. frame.dur = dur;
  148. frame.elapsed += dur;
  149. frame.time = time;
  150. frame.action.call(null, frame);
  151. frame.index++;
  152. }
  153. // 暴露
  154. exports.requestFrame = requestFrame;
  155. exports.releaseFrame = releaseFrame;
  156. });