buildStyleInterpolator.js 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211
  1. /**
  2. * Copyright (c) Facebook, Inc. and its affiliates.
  3. *
  4. * This source code is licensed under the MIT license found in the
  5. * LICENSE file in the root directory of this source tree.
  6. *
  7. * @format
  8. */
  9. 'use strict';
  10. const keyOf = require('fbjs/lib/keyOf');
  11. const X_DIM = keyOf({x: null});
  12. const Y_DIM = keyOf({y: null});
  13. const Z_DIM = keyOf({z: null});
  14. const InitialOperationField = {
  15. transformTranslate: [0, 0, 0],
  16. transformScale: [1, 1, 1],
  17. };
  18. const InterpolateMatrix = {
  19. transformScale: function(mat, x, y, z) {
  20. mat[0] = mat[0] * x;
  21. mat[1] = mat[1] * x;
  22. mat[2] = mat[2] * x;
  23. mat[3] = mat[3] * x;
  24. mat[4] = mat[4] * y;
  25. mat[5] = mat[5] * y;
  26. mat[6] = mat[6] * y;
  27. mat[7] = mat[7] * y;
  28. mat[8] = mat[8] * z;
  29. mat[9] = mat[9] * z;
  30. mat[10] = mat[10] * z;
  31. mat[11] = mat[11] * z;
  32. },
  33. transformTranslate: function(mat, x, y, z) {
  34. mat[12] = mat[0] * x + mat[4] * y + mat[8] * z + mat[12];
  35. mat[13] = mat[1] * x + mat[5] * y + mat[9] * z + mat[13];
  36. mat[14] = mat[2] * x + mat[6] * y + mat[10] * z + mat[14];
  37. mat[15] = mat[3] * x + mat[7] * y + mat[11] * z + mat[15];
  38. },
  39. };
  40. const computeNextValLinear = function(anim, from, to, value) {
  41. const hasRoundRatio = 'round' in anim;
  42. const roundRatio = anim.round;
  43. let ratio = (value - anim.min) / (anim.max - anim.min);
  44. if (!anim.extrapolate) {
  45. ratio = ratio > 1 ? 1 : ratio < 0 ? 0 : ratio;
  46. }
  47. let nextVal = from * (1 - ratio) + to * ratio;
  48. if (hasRoundRatio) {
  49. nextVal = Math.round(roundRatio * nextVal) / roundRatio;
  50. }
  51. if (!isFinite(nextVal)) {
  52. nextVal = null;
  53. }
  54. return nextVal;
  55. };
  56. const computeNextValLinearScalar = function(anim, value) {
  57. return computeNextValLinear(anim, anim.from, anim.to, value);
  58. };
  59. const setNextValAndDetectChange = function(result, name, nextVal, didChange) {
  60. if (!didChange) {
  61. const prevVal = result[name];
  62. result[name] = nextVal;
  63. didChange = didChange || nextVal !== prevVal;
  64. } else {
  65. result[name] = nextVal;
  66. }
  67. return didChange;
  68. };
  69. const initIdentity = function(mat) {
  70. mat[0] = 1;
  71. mat[1] = 0;
  72. mat[2] = 0;
  73. mat[3] = 0;
  74. mat[4] = 0;
  75. mat[5] = 1;
  76. mat[6] = 0;
  77. mat[7] = 0;
  78. mat[8] = 0;
  79. mat[9] = 0;
  80. mat[10] = 1;
  81. mat[11] = 0;
  82. mat[12] = 0;
  83. mat[13] = 0;
  84. mat[14] = 0;
  85. mat[15] = 1;
  86. };
  87. const computeNextMatrixOperationField = function(
  88. anim,
  89. name,
  90. dim,
  91. index,
  92. value,
  93. ) {
  94. if (anim.from[dim] !== undefined && anim.to[dim] !== undefined) {
  95. return computeNextValLinear(anim, anim.from[dim], anim.to[dim], value);
  96. } else {
  97. return InitialOperationField[name][index];
  98. }
  99. };
  100. const computeTransform = function(
  101. anim,
  102. name,
  103. value,
  104. result,
  105. didChange,
  106. didMatrix,
  107. ) {
  108. const transform =
  109. result.transform !== undefined
  110. ? result.transform
  111. : (result.transform = [{matrix: []}]);
  112. const mat = transform[0].matrix;
  113. const m0 = mat[0];
  114. const m1 = mat[1];
  115. const m2 = mat[2];
  116. const m3 = mat[3];
  117. const m4 = mat[4];
  118. const m5 = mat[5];
  119. const m6 = mat[6];
  120. const m7 = mat[7];
  121. const m8 = mat[8];
  122. const m9 = mat[9];
  123. const m10 = mat[10];
  124. const m11 = mat[11];
  125. const m12 = mat[12];
  126. const m13 = mat[13];
  127. const m14 = mat[14];
  128. const m15 = mat[15];
  129. if (!didMatrix) {
  130. initIdentity(mat); // This will be the first transform.
  131. }
  132. const x = computeNextMatrixOperationField(anim, name, X_DIM, 0, value);
  133. const y = computeNextMatrixOperationField(anim, name, Y_DIM, 1, value);
  134. const z = computeNextMatrixOperationField(anim, name, Z_DIM, 2, value);
  135. InterpolateMatrix[name](mat, x, y, z);
  136. if (!didChange) {
  137. didChange =
  138. m0 !== mat[0] ||
  139. m1 !== mat[1] ||
  140. m2 !== mat[2] ||
  141. m3 !== mat[3] ||
  142. m4 !== mat[4] ||
  143. m5 !== mat[5] ||
  144. m6 !== mat[6] ||
  145. m7 !== mat[7] ||
  146. m8 !== mat[8] ||
  147. m9 !== mat[9] ||
  148. m10 !== mat[10] ||
  149. m11 !== mat[11] ||
  150. m12 !== mat[12] ||
  151. m13 !== mat[13] ||
  152. m14 !== mat[14] ||
  153. m15 !== mat[15];
  154. }
  155. return didChange;
  156. };
  157. /**
  158. * @param {object} anims Animation configuration by style property name.
  159. * @return {function} Function accepting style object, that mutates that style
  160. * object and returns a boolean describing if any update was actually applied.
  161. */
  162. const buildStyleInterpolator = function(anims) {
  163. function styleInterpolator(result, value) {
  164. let didChange = false;
  165. let didMatrix = false;
  166. for (const name in anims) {
  167. const anim = anims[name];
  168. if (anim.type === 'linear') {
  169. if (name in InterpolateMatrix) {
  170. didChange = computeTransform(
  171. anim,
  172. name,
  173. value,
  174. result,
  175. didChange,
  176. didMatrix,
  177. );
  178. didMatrix = true;
  179. } else {
  180. const next = computeNextValLinearScalar(anim, value);
  181. didChange = setNextValAndDetectChange(result, name, next, didChange);
  182. }
  183. } else if (anim.type === 'constant') {
  184. const next = anim.value;
  185. didChange = setNextValAndDetectChange(result, name, next, didChange);
  186. } else if (anim.type === 'step') {
  187. const next = value >= anim.threshold ? anim.to : anim.from;
  188. didChange = setNextValAndDetectChange(result, name, next, didChange);
  189. } else if (anim.type === 'identity') {
  190. const next = value;
  191. didChange = setNextValAndDetectChange(result, name, next, didChange);
  192. }
  193. }
  194. return didChange;
  195. }
  196. return styleInterpolator;
  197. };
  198. module.exports = buildStyleInterpolator;