/** * Copyright (c) Facebook, Inc. and its affiliates. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * * @format */ 'use strict'; const keyOf = require('fbjs/lib/keyOf'); const X_DIM = keyOf({x: null}); const Y_DIM = keyOf({y: null}); const Z_DIM = keyOf({z: null}); const InitialOperationField = { transformTranslate: [0, 0, 0], transformScale: [1, 1, 1], }; const InterpolateMatrix = { transformScale: function(mat, x, y, z) { mat[0] = mat[0] * x; mat[1] = mat[1] * x; mat[2] = mat[2] * x; mat[3] = mat[3] * x; mat[4] = mat[4] * y; mat[5] = mat[5] * y; mat[6] = mat[6] * y; mat[7] = mat[7] * y; mat[8] = mat[8] * z; mat[9] = mat[9] * z; mat[10] = mat[10] * z; mat[11] = mat[11] * z; }, transformTranslate: function(mat, x, y, z) { mat[12] = mat[0] * x + mat[4] * y + mat[8] * z + mat[12]; mat[13] = mat[1] * x + mat[5] * y + mat[9] * z + mat[13]; mat[14] = mat[2] * x + mat[6] * y + mat[10] * z + mat[14]; mat[15] = mat[3] * x + mat[7] * y + mat[11] * z + mat[15]; }, }; const computeNextValLinear = function(anim, from, to, value) { const hasRoundRatio = 'round' in anim; const roundRatio = anim.round; let ratio = (value - anim.min) / (anim.max - anim.min); if (!anim.extrapolate) { ratio = ratio > 1 ? 1 : ratio < 0 ? 0 : ratio; } let nextVal = from * (1 - ratio) + to * ratio; if (hasRoundRatio) { nextVal = Math.round(roundRatio * nextVal) / roundRatio; } if (!isFinite(nextVal)) { nextVal = null; } return nextVal; }; const computeNextValLinearScalar = function(anim, value) { return computeNextValLinear(anim, anim.from, anim.to, value); }; const setNextValAndDetectChange = function(result, name, nextVal, didChange) { if (!didChange) { const prevVal = result[name]; result[name] = nextVal; didChange = didChange || nextVal !== prevVal; } else { result[name] = nextVal; } return didChange; }; const initIdentity = function(mat) { mat[0] = 1; mat[1] = 0; mat[2] = 0; mat[3] = 0; mat[4] = 0; mat[5] = 1; mat[6] = 0; mat[7] = 0; mat[8] = 0; mat[9] = 0; mat[10] = 1; mat[11] = 0; mat[12] = 0; mat[13] = 0; mat[14] = 0; mat[15] = 1; }; const computeNextMatrixOperationField = function( anim, name, dim, index, value, ) { if (anim.from[dim] !== undefined && anim.to[dim] !== undefined) { return computeNextValLinear(anim, anim.from[dim], anim.to[dim], value); } else { return InitialOperationField[name][index]; } }; const computeTransform = function( anim, name, value, result, didChange, didMatrix, ) { const transform = result.transform !== undefined ? result.transform : (result.transform = [{matrix: []}]); const mat = transform[0].matrix; const m0 = mat[0]; const m1 = mat[1]; const m2 = mat[2]; const m3 = mat[3]; const m4 = mat[4]; const m5 = mat[5]; const m6 = mat[6]; const m7 = mat[7]; const m8 = mat[8]; const m9 = mat[9]; const m10 = mat[10]; const m11 = mat[11]; const m12 = mat[12]; const m13 = mat[13]; const m14 = mat[14]; const m15 = mat[15]; if (!didMatrix) { initIdentity(mat); // This will be the first transform. } const x = computeNextMatrixOperationField(anim, name, X_DIM, 0, value); const y = computeNextMatrixOperationField(anim, name, Y_DIM, 1, value); const z = computeNextMatrixOperationField(anim, name, Z_DIM, 2, value); InterpolateMatrix[name](mat, x, y, z); if (!didChange) { didChange = m0 !== mat[0] || m1 !== mat[1] || m2 !== mat[2] || m3 !== mat[3] || m4 !== mat[4] || m5 !== mat[5] || m6 !== mat[6] || m7 !== mat[7] || m8 !== mat[8] || m9 !== mat[9] || m10 !== mat[10] || m11 !== mat[11] || m12 !== mat[12] || m13 !== mat[13] || m14 !== mat[14] || m15 !== mat[15]; } return didChange; }; /** * @param {object} anims Animation configuration by style property name. * @return {function} Function accepting style object, that mutates that style * object and returns a boolean describing if any update was actually applied. */ const buildStyleInterpolator = function(anims) { function styleInterpolator(result, value) { let didChange = false; let didMatrix = false; for (const name in anims) { const anim = anims[name]; if (anim.type === 'linear') { if (name in InterpolateMatrix) { didChange = computeTransform( anim, name, value, result, didChange, didMatrix, ); didMatrix = true; } else { const next = computeNextValLinearScalar(anim, value); didChange = setNextValAndDetectChange(result, name, next, didChange); } } else if (anim.type === 'constant') { const next = anim.value; didChange = setNextValAndDetectChange(result, name, next, didChange); } else if (anim.type === 'step') { const next = value >= anim.threshold ? anim.to : anim.from; didChange = setNextValAndDetectChange(result, name, next, didChange); } else if (anim.type === 'identity') { const next = value; didChange = setNextValAndDetectChange(result, name, next, didChange); } } return didChange; } return styleInterpolator; }; module.exports = buildStyleInterpolator;