request-frame.js 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229
  1. /**
  2. * request-frame - requestAnimationFrame & cancelAnimationFrame polyfill for optimal cross-browser development.
  3. * @version v1.5.3
  4. * @license MIT
  5. * Copyright Julien Etienne 2015 All Rights Reserved.
  6. */
  7. (function (global, factory) {
  8. typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
  9. typeof define === 'function' && define.amd ? define(factory) :
  10. (global.requestFrame = factory());
  11. }(this, (function () { 'use strict';
  12. /**
  13. * @param {String} type - request | cancel | native.
  14. * @return {Function} Timing function.
  15. */
  16. function requestFrame(type) {
  17. // The only vendor prefixes required.
  18. var vendors = ['moz', 'webkit'];
  19. // Disassembled timing function abbreviations.
  20. var aF = 'AnimationFrame';
  21. var rqAF = 'Request' + aF;
  22. // Checks for firefox 4 - 10 function pair mismatch.
  23. var mozRAF = window.mozRequestAnimationFrame;
  24. var mozCAF = window.mozCancelAnimationFrame;
  25. var hasMozMismatch = mozRAF && !mozCAF;
  26. // Final assigned functions.
  27. var assignedRequestAnimationFrame;
  28. var assignedCancelAnimationFrame;
  29. // Initial time of the timing lapse.
  30. var previousTime = 0;
  31. var requestFrameMain;
  32. // Date.now polyfill, mainly for legacy IE versions.
  33. if (!Date.now) {
  34. Date.now = function () {
  35. return new Date().getTime();
  36. };
  37. }
  38. /**
  39. * hasIOS6RequestAnimationFrameBug.
  40. * @See {@Link https://gist.github.com/julienetie/86ac394ec41f1271ff0a}
  41. * - for Commentary.
  42. * @Copyright 2015 - Julien Etienne.
  43. * @License: MIT.
  44. */
  45. function hasIOS6RequestAnimationFrameBug() {
  46. var webkitRAF = window.webkitRequestAnimationFrame;
  47. var rAF = window.requestAnimationFrame;
  48. // CSS/ Device with max for iOS6 Devices.
  49. var hasMobileDeviceWidth = screen.width <= 768 ? true : false;
  50. // Only supports webkit prefixed requestAnimtionFrane.
  51. var requiresWebkitprefix = !(webkitRAF && rAF);
  52. // iOS6 webkit browsers don't support performance now.
  53. var hasNoNavigationTiming = window.performance ? false : true;
  54. var iOS6Notice = 'setTimeout is being used as a substitiue for \n requestAnimationFrame due to a bug within iOS 6 builds';
  55. var hasIOS6Bug = requiresWebkitprefix && hasMobileDeviceWidth && hasNoNavigationTiming;
  56. var bugCheckresults = function bugCheckresults(timingFnA, timingFnB, notice) {
  57. if (timingFnA || timingFnB) {
  58. console.warn(notice);
  59. return true;
  60. } else {
  61. return false;
  62. }
  63. };
  64. var displayResults = function displayResults(hasBug, hasBugNotice, webkitFn, nativeFn) {
  65. if (hasBug) {
  66. return bugCheckresults(webkitFn, nativeFn, hasBugNotice);
  67. } else {
  68. return false;
  69. }
  70. };
  71. return displayResults(hasIOS6Bug, iOS6Notice, webkitRAF, rAF);
  72. }
  73. /**
  74. * Native clearTimeout function.
  75. * @return {Function}
  76. */
  77. function clearTimeoutWithId(id) {
  78. clearTimeout(id);
  79. }
  80. /**
  81. * Based on a polyfill by Erik, introduced by Paul Irish &
  82. * further improved by Darius Bacon.
  83. * @see {@link http://www.paulirish.com/2011/
  84. * requestanimationframe-for-smart-animating}
  85. * @see {@link https://github.com/darius/requestAnimationFrame/blob/
  86. * master/requestAnimationFrame.js}
  87. * @callback {Number} Timestamp.
  88. * @return {Function} setTimeout Function.
  89. */
  90. function setTimeoutWithTimestamp(callback) {
  91. var immediateTime = Date.now();
  92. var lapsedTime = Math.max(previousTime + 16, immediateTime);
  93. return setTimeout(function () {
  94. callback(previousTime = lapsedTime);
  95. }, lapsedTime - immediateTime);
  96. }
  97. /**
  98. * Queries the native function, prefixed function
  99. * or use the setTimeoutWithTimestamp function.
  100. * @return {Function}
  101. */
  102. function queryRequestAnimationFrame() {
  103. if (Array.prototype.filter) {
  104. assignedRequestAnimationFrame = window['request' + aF] || window[vendors.filter(function (vendor) {
  105. if (window[vendor + rqAF] !== undefined) return vendor;
  106. }) + rqAF] || setTimeoutWithTimestamp;
  107. } else {
  108. return setTimeoutWithTimestamp;
  109. }
  110. if (!hasIOS6RequestAnimationFrameBug()) {
  111. return assignedRequestAnimationFrame;
  112. } else {
  113. return setTimeoutWithTimestamp;
  114. }
  115. }
  116. /**
  117. * Queries the native function, prefixed function
  118. * or use the clearTimeoutWithId function.
  119. * @return {Function}
  120. */
  121. function queryCancelAnimationFrame() {
  122. var cancellationNames = [];
  123. if (Array.prototype.map) {
  124. vendors.map(function (vendor) {
  125. return ['Cancel', 'CancelRequest'].map(function (cancellationNamePrefix) {
  126. cancellationNames.push(vendor + cancellationNamePrefix + aF);
  127. });
  128. });
  129. } else {
  130. return clearTimeoutWithId;
  131. }
  132. /**
  133. * Checks for the prefixed cancelAnimationFrame implementation.
  134. * @param {Array} prefixedNames - An array of the prefixed names.
  135. * @param {Number} i - Iteration start point.
  136. * @return {Function} prefixed cancelAnimationFrame function.
  137. */
  138. function prefixedCancelAnimationFrame(prefixedNames, i) {
  139. var cancellationFunction = void 0;
  140. for (; i < prefixedNames.length; i++) {
  141. if (window[prefixedNames[i]]) {
  142. cancellationFunction = window[prefixedNames[i]];
  143. break;
  144. }
  145. }
  146. return cancellationFunction;
  147. }
  148. // Use truthly function
  149. assignedCancelAnimationFrame = window['cancel' + aF] || prefixedCancelAnimationFrame(cancellationNames, 0) || clearTimeoutWithId;
  150. // Check for iOS 6 bug
  151. if (!hasIOS6RequestAnimationFrameBug()) {
  152. return assignedCancelAnimationFrame;
  153. } else {
  154. return clearTimeoutWithId;
  155. }
  156. }
  157. function getRequestFn() {
  158. if (hasMozMismatch) {
  159. return setTimeoutWithTimestamp;
  160. } else {
  161. return queryRequestAnimationFrame();
  162. }
  163. }
  164. function getCancelFn() {
  165. return queryCancelAnimationFrame();
  166. }
  167. function setNativeFn() {
  168. if (hasMozMismatch) {
  169. window.requestAnimationFrame = setTimeoutWithTimestamp;
  170. window.cancelAnimationFrame = clearTimeoutWithId;
  171. } else {
  172. window.requestAnimationFrame = queryRequestAnimationFrame();
  173. window.cancelAnimationFrame = queryCancelAnimationFrame();
  174. }
  175. }
  176. /**
  177. * The type value "request" singles out firefox 4 - 10 and
  178. * assigns the setTimeout function if plausible.
  179. */
  180. switch (type) {
  181. case 'request':
  182. case '':
  183. requestFrameMain = getRequestFn();
  184. break;
  185. case 'cancel':
  186. requestFrameMain = getCancelFn();
  187. break;
  188. case 'native':
  189. setNativeFn();
  190. break;
  191. default:
  192. throw new Error('RequestFrame parameter is not a type.');
  193. }
  194. return requestFrameMain;
  195. }
  196. return requestFrame;
  197. })));