request-frame.es.js 7.0 KB

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