Avoid eval in buildStyleInterpolator
Reviewed By: vjeux Differential Revision: D5950405 fbshipit-source-id: ee794317f820e8fbb87752b88539171115a8e00e
This commit is contained in:
parent
3e31038301
commit
915ac20c76
|
@ -9,547 +9,137 @@
|
|||
* @providesModule buildStyleInterpolator
|
||||
*/
|
||||
|
||||
/**
|
||||
* Cannot "use strict" because we must use eval in this file.
|
||||
*/
|
||||
/* eslint-disable global-strict */
|
||||
'use strict';
|
||||
|
||||
var keyOf = require('fbjs/lib/keyOf');
|
||||
|
||||
var X_DIM = keyOf({x: null});
|
||||
var Y_DIM = keyOf({y: null});
|
||||
var Z_DIM = keyOf({z: null});
|
||||
var W_DIM = keyOf({w: null});
|
||||
|
||||
var TRANSFORM_ROTATE_NAME = keyOf({transformRotateRadians: null});
|
||||
|
||||
var ShouldAllocateReusableOperationVars = {
|
||||
transformRotateRadians: true,
|
||||
transformScale: true,
|
||||
transformTranslate: true,
|
||||
};
|
||||
|
||||
var InitialOperationField = {
|
||||
transformRotateRadians: [0, 0, 0, 1],
|
||||
transformTranslate: [0, 0, 0],
|
||||
transformScale: [1, 1, 1],
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Creates a highly specialized animation function that may be evaluated every
|
||||
* frame. For example:
|
||||
*
|
||||
* var ToTheLeft = {
|
||||
* opacity: {
|
||||
* from: 1,
|
||||
* to: 0.7,
|
||||
* min: 0,
|
||||
* max: 1,
|
||||
* type: 'linear',
|
||||
* extrapolate: false,
|
||||
* round: 100,
|
||||
* },
|
||||
* left: {
|
||||
* from: 0,
|
||||
* to: -SCREEN_WIDTH * 0.3,
|
||||
* min: 0,
|
||||
* max: 1,
|
||||
* type: 'linear',
|
||||
* extrapolate: true,
|
||||
* round: PixelRatio.get(),
|
||||
* },
|
||||
* };
|
||||
*
|
||||
* var toTheLeft = buildStyleInterpolator(ToTheLeft);
|
||||
*
|
||||
* Would returns a specialized function of the form:
|
||||
*
|
||||
* function(result, value) {
|
||||
* var didChange = false;
|
||||
* var nextScalarVal;
|
||||
* var ratio;
|
||||
* ratio = (value - 0) / 1;
|
||||
* ratio = ratio > 1 ? 1 : (ratio < 0 ? 0 : ratio);
|
||||
* nextScalarVal = Math.round(100 * (1 * (1 - ratio) + 0.7 * ratio)) / 100;
|
||||
* if (!didChange) {
|
||||
* var prevVal = result.opacity;
|
||||
* result.opacity = nextScalarVal;
|
||||
* didChange = didChange || (nextScalarVal !== prevVal);
|
||||
* } else {
|
||||
* result.opacity = nextScalarVal;
|
||||
* }
|
||||
* ratio = (value - 0) / 1;
|
||||
* nextScalarVal = Math.round(2 * (0 * (1 - ratio) + -30 * ratio)) / 2;
|
||||
* if (!didChange) {
|
||||
* var prevVal = result.left;
|
||||
* result.left = nextScalarVal;
|
||||
* didChange = didChange || (nextScalarVal !== prevVal);
|
||||
* } else {
|
||||
* result.left = nextScalarVal;
|
||||
* }
|
||||
* return didChange;
|
||||
* }
|
||||
*/
|
||||
|
||||
var ARGUMENT_NAMES_RE = /([^\s,]+)/g;
|
||||
/**
|
||||
* This is obviously a huge hack. Proper tooling would allow actual inlining.
|
||||
* This only works in a few limited cases (where there is no function return
|
||||
* value, and the function operates mutatively on parameters).
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
*
|
||||
* var inlineMe(a, b) {
|
||||
* a = b + b;
|
||||
* };
|
||||
*
|
||||
* inline(inlineMe, ['hi', 'bye']); // "hi = bye + bye;"
|
||||
*
|
||||
* @param {string} fnStr Source of any simple function who's arguments can be
|
||||
* replaced via a regex.
|
||||
* @param {array<string>} replaceWithArgs Corresponding names of variables
|
||||
* within an environment, to replace `func` args with.
|
||||
* @return {string} Resulting function body string.
|
||||
*/
|
||||
var inline = function(fnStr, replaceWithArgs) {
|
||||
var parameterNames = fnStr.slice(fnStr.indexOf('(') + 1, fnStr.indexOf(')'))
|
||||
.match(ARGUMENT_NAMES_RE) ||
|
||||
[];
|
||||
var replaceRegexStr = parameterNames.map(function(paramName) {
|
||||
return '\\b' + paramName + '\\b';
|
||||
}).join('|');
|
||||
var replaceRegex = new RegExp(replaceRegexStr, 'g');
|
||||
var fnBody = fnStr.substring(fnStr.indexOf('{') + 1, fnStr.lastIndexOf('}'));
|
||||
var newFnBody = fnBody.replace(replaceRegex, function(parameterName) {
|
||||
var indexInParameterNames = parameterNames.indexOf(parameterName);
|
||||
var replacementName = replaceWithArgs[indexInParameterNames];
|
||||
return replacementName;
|
||||
});
|
||||
return newFnBody.split('\n');
|
||||
var 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];
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Simply a convenient way to inline functions using the inline function.
|
||||
*/
|
||||
var MatrixOps = {
|
||||
unroll: `function(matVar, m0, m1, m2, m3, m4, m5, m6, m7, m8, m9, m10, m11, m12, m13, m14, m15) {
|
||||
m0 = matVar[0];
|
||||
m1 = matVar[1];
|
||||
m2 = matVar[2];
|
||||
m3 = matVar[3];
|
||||
m4 = matVar[4];
|
||||
m5 = matVar[5];
|
||||
m6 = matVar[6];
|
||||
m7 = matVar[7];
|
||||
m8 = matVar[8];
|
||||
m9 = matVar[9];
|
||||
m10 = matVar[10];
|
||||
m11 = matVar[11];
|
||||
m12 = matVar[12];
|
||||
m13 = matVar[13];
|
||||
m14 = matVar[14];
|
||||
m15 = matVar[15];
|
||||
}`,
|
||||
|
||||
matrixDiffers: `function(retVar, matVar, m0, m1, m2, m3, m4, m5, m6, m7, m8, m9, m10, m11, m12, m13, m14, m15) {
|
||||
retVar = retVar ||
|
||||
m0 !== matVar[0] ||
|
||||
m1 !== matVar[1] ||
|
||||
m2 !== matVar[2] ||
|
||||
m3 !== matVar[3] ||
|
||||
m4 !== matVar[4] ||
|
||||
m5 !== matVar[5] ||
|
||||
m6 !== matVar[6] ||
|
||||
m7 !== matVar[7] ||
|
||||
m8 !== matVar[8] ||
|
||||
m9 !== matVar[9] ||
|
||||
m10 !== matVar[10] ||
|
||||
m11 !== matVar[11] ||
|
||||
m12 !== matVar[12] ||
|
||||
m13 !== matVar[13] ||
|
||||
m14 !== matVar[14] ||
|
||||
m15 !== matVar[15];
|
||||
}`,
|
||||
|
||||
transformScale: `function(matVar, opVar) {
|
||||
// Scaling matVar by opVar
|
||||
var x = opVar[0];
|
||||
var y = opVar[1];
|
||||
var z = opVar[2];
|
||||
matVar[0] = matVar[0] * x;
|
||||
matVar[1] = matVar[1] * x;
|
||||
matVar[2] = matVar[2] * x;
|
||||
matVar[3] = matVar[3] * x;
|
||||
matVar[4] = matVar[4] * y;
|
||||
matVar[5] = matVar[5] * y;
|
||||
matVar[6] = matVar[6] * y;
|
||||
matVar[7] = matVar[7] * y;
|
||||
matVar[8] = matVar[8] * z;
|
||||
matVar[9] = matVar[9] * z;
|
||||
matVar[10] = matVar[10] * z;
|
||||
matVar[11] = matVar[11] * z;
|
||||
matVar[12] = matVar[12];
|
||||
matVar[13] = matVar[13];
|
||||
matVar[14] = matVar[14];
|
||||
matVar[15] = matVar[15];
|
||||
}`,
|
||||
|
||||
/**
|
||||
* All of these matrix transforms are not general purpose utilities, and are
|
||||
* only suitable for being inlined for the use of building up interpolators.
|
||||
*/
|
||||
transformTranslate: `function(matVar, opVar) {
|
||||
// Translating matVar by opVar
|
||||
var x = opVar[0];
|
||||
var y = opVar[1];
|
||||
var z = opVar[2];
|
||||
matVar[12] = matVar[0] * x + matVar[4] * y + matVar[8] * z + matVar[12];
|
||||
matVar[13] = matVar[1] * x + matVar[5] * y + matVar[9] * z + matVar[13];
|
||||
matVar[14] = matVar[2] * x + matVar[6] * y + matVar[10] * z + matVar[14];
|
||||
matVar[15] = matVar[3] * x + matVar[7] * y + matVar[11] * z + matVar[15];
|
||||
}`,
|
||||
|
||||
/**
|
||||
* @param {array} matVar Both the input, and the output matrix.
|
||||
* @param {quaternion specification} q Four element array describing rotation.
|
||||
*/
|
||||
transformRotateRadians: `function(matVar, q) {
|
||||
// Rotating matVar by q
|
||||
var xQuat = q[0], yQuat = q[1], zQuat = q[2], wQuat = q[3];
|
||||
var x2Quat = xQuat + xQuat;
|
||||
var y2Quat = yQuat + yQuat;
|
||||
var z2Quat = zQuat + zQuat;
|
||||
var xxQuat = xQuat * x2Quat;
|
||||
var xyQuat = xQuat * y2Quat;
|
||||
var xzQuat = xQuat * z2Quat;
|
||||
var yyQuat = yQuat * y2Quat;
|
||||
var yzQuat = yQuat * z2Quat;
|
||||
var zzQuat = zQuat * z2Quat;
|
||||
var wxQuat = wQuat * x2Quat;
|
||||
var wyQuat = wQuat * y2Quat;
|
||||
var wzQuat = wQuat * z2Quat;
|
||||
// Step 1: Inlines the construction of a quaternion matrix ('quatMat')
|
||||
var quatMat0 = 1 - (yyQuat + zzQuat);
|
||||
var quatMat1 = xyQuat + wzQuat;
|
||||
var quatMat2 = xzQuat - wyQuat;
|
||||
var quatMat4 = xyQuat - wzQuat;
|
||||
var quatMat5 = 1 - (xxQuat + zzQuat);
|
||||
var quatMat6 = yzQuat + wxQuat;
|
||||
var quatMat8 = xzQuat + wyQuat;
|
||||
var quatMat9 = yzQuat - wxQuat;
|
||||
var quatMat10 = 1 - (xxQuat + yyQuat);
|
||||
// quatMat3/7/11/12/13/14 = 0, quatMat15 = 1
|
||||
|
||||
// Step 2: Inlines multiplication, takes advantage of constant quatMat cells
|
||||
var a00 = matVar[0];
|
||||
var a01 = matVar[1];
|
||||
var a02 = matVar[2];
|
||||
var a03 = matVar[3];
|
||||
var a10 = matVar[4];
|
||||
var a11 = matVar[5];
|
||||
var a12 = matVar[6];
|
||||
var a13 = matVar[7];
|
||||
var a20 = matVar[8];
|
||||
var a21 = matVar[9];
|
||||
var a22 = matVar[10];
|
||||
var a23 = matVar[11];
|
||||
|
||||
var b0 = quatMat0, b1 = quatMat1, b2 = quatMat2;
|
||||
matVar[0] = b0 * a00 + b1 * a10 + b2 * a20;
|
||||
matVar[1] = b0 * a01 + b1 * a11 + b2 * a21;
|
||||
matVar[2] = b0 * a02 + b1 * a12 + b2 * a22;
|
||||
matVar[3] = b0 * a03 + b1 * a13 + b2 * a23;
|
||||
b0 = quatMat4; b1 = quatMat5; b2 = quatMat6;
|
||||
matVar[4] = b0 * a00 + b1 * a10 + b2 * a20;
|
||||
matVar[5] = b0 * a01 + b1 * a11 + b2 * a21;
|
||||
matVar[6] = b0 * a02 + b1 * a12 + b2 * a22;
|
||||
matVar[7] = b0 * a03 + b1 * a13 + b2 * a23;
|
||||
b0 = quatMat8; b1 = quatMat9; b2 = quatMat10;
|
||||
matVar[8] = b0 * a00 + b1 * a10 + b2 * a20;
|
||||
matVar[9] = b0 * a01 + b1 * a11 + b2 * a21;
|
||||
matVar[10] = b0 * a02 + b1 * a12 + b2 * a22;
|
||||
matVar[11] = b0 * a03 + b1 * a13 + b2 * a23;
|
||||
}`
|
||||
};
|
||||
|
||||
// Optimized version of general operation applications that can be used when
|
||||
// the target matrix is known to be the identity matrix.
|
||||
var MatrixOpsInitial = {
|
||||
transformScale: `function(matVar, opVar) {
|
||||
// Scaling matVar known to be identity by opVar
|
||||
matVar[0] = opVar[0];
|
||||
matVar[1] = 0;
|
||||
matVar[2] = 0;
|
||||
matVar[3] = 0;
|
||||
matVar[4] = 0;
|
||||
matVar[5] = opVar[1];
|
||||
matVar[6] = 0;
|
||||
matVar[7] = 0;
|
||||
matVar[8] = 0;
|
||||
matVar[9] = 0;
|
||||
matVar[10] = opVar[2];
|
||||
matVar[11] = 0;
|
||||
matVar[12] = 0;
|
||||
matVar[13] = 0;
|
||||
matVar[14] = 0;
|
||||
matVar[15] = 1;
|
||||
}`,
|
||||
|
||||
transformTranslate: `function(matVar, opVar) {
|
||||
// Translating matVar known to be identity by opVar;
|
||||
matVar[0] = 1;
|
||||
matVar[1] = 0;
|
||||
matVar[2] = 0;
|
||||
matVar[3] = 0;
|
||||
matVar[4] = 0;
|
||||
matVar[5] = 1;
|
||||
matVar[6] = 0;
|
||||
matVar[7] = 0;
|
||||
matVar[8] = 0;
|
||||
matVar[9] = 0;
|
||||
matVar[10] = 1;
|
||||
matVar[11] = 0;
|
||||
matVar[12] = opVar[0];
|
||||
matVar[13] = opVar[1];
|
||||
matVar[14] = opVar[2];
|
||||
matVar[15] = 1;
|
||||
}`,
|
||||
|
||||
/**
|
||||
* @param {array} matVar Both the input, and the output matrix - assumed to be
|
||||
* identity.
|
||||
* @param {quaternion specification} q Four element array describing rotation.
|
||||
*/
|
||||
transformRotateRadians: `function(matVar, q) {
|
||||
|
||||
// Rotating matVar which is known to be identity by q
|
||||
var xQuat = q[0], yQuat = q[1], zQuat = q[2], wQuat = q[3];
|
||||
var x2Quat = xQuat + xQuat;
|
||||
var y2Quat = yQuat + yQuat;
|
||||
var z2Quat = zQuat + zQuat;
|
||||
var xxQuat = xQuat * x2Quat;
|
||||
var xyQuat = xQuat * y2Quat;
|
||||
var xzQuat = xQuat * z2Quat;
|
||||
var yyQuat = yQuat * y2Quat;
|
||||
var yzQuat = yQuat * z2Quat;
|
||||
var zzQuat = zQuat * z2Quat;
|
||||
var wxQuat = wQuat * x2Quat;
|
||||
var wyQuat = wQuat * y2Quat;
|
||||
var wzQuat = wQuat * z2Quat;
|
||||
// Step 1: Inlines the construction of a quaternion matrix ('quatMat')
|
||||
var quatMat0 = 1 - (yyQuat + zzQuat);
|
||||
var quatMat1 = xyQuat + wzQuat;
|
||||
var quatMat2 = xzQuat - wyQuat;
|
||||
var quatMat4 = xyQuat - wzQuat;
|
||||
var quatMat5 = 1 - (xxQuat + zzQuat);
|
||||
var quatMat6 = yzQuat + wxQuat;
|
||||
var quatMat8 = xzQuat + wyQuat;
|
||||
var quatMat9 = yzQuat - wxQuat;
|
||||
var quatMat10 = 1 - (xxQuat + yyQuat);
|
||||
// quatMat3/7/11/12/13/14 = 0, quatMat15 = 1
|
||||
|
||||
// Step 2: Inlines the multiplication with identity matrix.
|
||||
var b0 = quatMat0, b1 = quatMat1, b2 = quatMat2;
|
||||
matVar[0] = b0;
|
||||
matVar[1] = b1;
|
||||
matVar[2] = b2;
|
||||
matVar[3] = 0;
|
||||
b0 = quatMat4; b1 = quatMat5; b2 = quatMat6;
|
||||
matVar[4] = b0;
|
||||
matVar[5] = b1;
|
||||
matVar[6] = b2;
|
||||
matVar[7] = 0;
|
||||
b0 = quatMat8; b1 = quatMat9; b2 = quatMat10;
|
||||
matVar[8] = b0;
|
||||
matVar[9] = b1;
|
||||
matVar[10] = b2;
|
||||
matVar[11] = 0;
|
||||
matVar[12] = 0;
|
||||
matVar[13] = 0;
|
||||
matVar[14] = 0;
|
||||
matVar[15] = 1;
|
||||
}`
|
||||
};
|
||||
|
||||
|
||||
var setNextValAndDetectChange = function(name, tmpVarName) {
|
||||
return (
|
||||
' if (!didChange) {\n' +
|
||||
' var prevVal = result.' + name + ';\n' +
|
||||
' result.' + name + ' = ' + tmpVarName + ';\n' +
|
||||
' didChange = didChange || (' + tmpVarName + ' !== prevVal);\n' +
|
||||
' } else {\n' +
|
||||
' result.' + name + ' = ' + tmpVarName + ';\n' +
|
||||
' }\n'
|
||||
);
|
||||
};
|
||||
|
||||
var computeNextValLinear = function(anim, from, to, tmpVarName) {
|
||||
var computeNextValLinear = function(anim, from, to, value) {
|
||||
var hasRoundRatio = 'round' in anim;
|
||||
var roundRatio = anim.round;
|
||||
var fn = ' ratio = (value - ' + anim.min + ') / ' + (anim.max - anim.min) + ';\n';
|
||||
var ratio = (value - anim.min) / (anim.max - anim.min);
|
||||
if (!anim.extrapolate) {
|
||||
fn += ' ratio = ratio > 1 ? 1 : (ratio < 0 ? 0 : ratio);\n';
|
||||
ratio = ratio > 1 ? 1 : (ratio < 0 ? 0 : ratio);
|
||||
}
|
||||
|
||||
var roundOpen = (hasRoundRatio ? 'Math.round(' + roundRatio + ' * ' : '' );
|
||||
var roundClose = (hasRoundRatio ? ') / ' + roundRatio : '' );
|
||||
fn +=
|
||||
' ' + tmpVarName + ' = ' +
|
||||
roundOpen +
|
||||
'(' + from + ' * (1 - ratio) + ' + to + ' * ratio)' +
|
||||
roundClose + ';\n';
|
||||
return fn;
|
||||
};
|
||||
|
||||
var computeNextValLinearScalar = function(anim) {
|
||||
return computeNextValLinear(anim, anim.from, anim.to, 'nextScalarVal');
|
||||
};
|
||||
|
||||
var computeNextValConstant = function(anim) {
|
||||
var constantExpression = JSON.stringify(anim.value);
|
||||
return ' nextScalarVal = ' + constantExpression + ';\n';
|
||||
};
|
||||
|
||||
var computeNextValStep = function(anim) {
|
||||
return (
|
||||
' nextScalarVal = value >= ' +
|
||||
(anim.threshold + ' ? ' + anim.to + ' : ' + anim.from) + ';\n'
|
||||
);
|
||||
};
|
||||
|
||||
var computeNextValIdentity = function(anim) {
|
||||
return ' nextScalarVal = value;\n';
|
||||
};
|
||||
|
||||
var operationVar = function(name) {
|
||||
return name + 'ReuseOp';
|
||||
};
|
||||
|
||||
var createReusableOperationVars = function(anims) {
|
||||
var ret = '';
|
||||
for (var name in anims) {
|
||||
if (ShouldAllocateReusableOperationVars[name]) {
|
||||
ret += 'var ' + operationVar(name) + ' = [];\n';
|
||||
}
|
||||
var nextVal = from * (1 - ratio) + to * ratio;
|
||||
if (hasRoundRatio) {
|
||||
nextVal = Math.round(roundRatio * nextVal) / roundRatio;
|
||||
}
|
||||
return ret;
|
||||
return nextVal;
|
||||
};
|
||||
|
||||
var newlines = function(statements) {
|
||||
return '\n' + statements.join('\n') + '\n';
|
||||
var computeNextValLinearScalar = function(anim, value) {
|
||||
return computeNextValLinear(anim, anim.from, anim.to, value);
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {Animation} anim Configuration entry.
|
||||
* @param {key} dimension Key to examine in `from`/`to`.
|
||||
* @param {number} index Field in operationVar to set.
|
||||
* @return {string} Code that sets the operation variable's field.
|
||||
*/
|
||||
var computeNextMatrixOperationField = function(anim, name, dimension, index) {
|
||||
var fieldAccess = operationVar(name) + '[' + index + ']';
|
||||
if (anim.from[dimension] !== undefined && anim.to[dimension] !== undefined) {
|
||||
return ' ' + anim.from[dimension] !== anim.to[dimension] ?
|
||||
computeNextValLinear(anim, anim.from[dimension], anim.to[dimension], fieldAccess) :
|
||||
fieldAccess + ' = ' + anim.from[dimension] + ';';
|
||||
var setNextValAndDetectChange = function(result, name, nextVal, didChange) {
|
||||
if (!didChange) {
|
||||
var prevVal = result[name];
|
||||
result[name] = nextVal;
|
||||
didChange = didChange || (nextVal !== prevVal);
|
||||
} else {
|
||||
return ' ' + fieldAccess + ' = ' + InitialOperationField[name][index] + ';';
|
||||
result[name] = nextVal;
|
||||
}
|
||||
return didChange;
|
||||
};
|
||||
|
||||
var 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;
|
||||
};
|
||||
|
||||
var 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];
|
||||
}
|
||||
};
|
||||
|
||||
var unrolledVars = [];
|
||||
for (var varIndex = 0; varIndex < 16; varIndex++) {
|
||||
unrolledVars.push('m' + varIndex);
|
||||
}
|
||||
var setNextMatrixAndDetectChange = function(orderedMatrixOperations) {
|
||||
var fn = [
|
||||
' var transform = result.transform !== undefined ? ' +
|
||||
'result.transform : (result.transform = [{ matrix: [] }]);' +
|
||||
' var transformMatrix = transform[0].matrix;'
|
||||
];
|
||||
fn.push.apply(
|
||||
fn,
|
||||
inline(MatrixOps.unroll, ['transformMatrix'].concat(unrolledVars))
|
||||
);
|
||||
for (var i = 0; i < orderedMatrixOperations.length; i++) {
|
||||
var opName = orderedMatrixOperations[i];
|
||||
if (i === 0) {
|
||||
fn.push.apply(
|
||||
fn,
|
||||
inline(MatrixOpsInitial[opName], ['transformMatrix', operationVar(opName)])
|
||||
);
|
||||
} else {
|
||||
fn.push.apply(
|
||||
fn,
|
||||
inline(MatrixOps[opName], ['transformMatrix', operationVar(opName)])
|
||||
);
|
||||
}
|
||||
var computeTransform = function(anim, name, value, result,
|
||||
didChange, didMatrix) {
|
||||
var transform = result.transform !== undefined ?
|
||||
result.transform : (result.transform = [{ matrix: [] }]);
|
||||
var mat = transform[0].matrix;
|
||||
var m0 = mat[0];
|
||||
var m1 = mat[1];
|
||||
var m2 = mat[2];
|
||||
var m3 = mat[3];
|
||||
var m4 = mat[4];
|
||||
var m5 = mat[5];
|
||||
var m6 = mat[6];
|
||||
var m7 = mat[7];
|
||||
var m8 = mat[8];
|
||||
var m9 = mat[9];
|
||||
var m10 = mat[10];
|
||||
var m11 = mat[11];
|
||||
var m12 = mat[12];
|
||||
var m13 = mat[13];
|
||||
var m14 = mat[14];
|
||||
var m15 = mat[15];
|
||||
if (!didMatrix) {
|
||||
initIdentity(mat); // This will be the first transform.
|
||||
}
|
||||
fn.push.apply(
|
||||
fn,
|
||||
inline(MatrixOps.matrixDiffers, ['didChange', 'transformMatrix'].concat(unrolledVars))
|
||||
);
|
||||
return fn;
|
||||
};
|
||||
|
||||
var InterpolateMatrix = {
|
||||
transformTranslate: true,
|
||||
transformRotateRadians: true,
|
||||
transformScale: true,
|
||||
};
|
||||
|
||||
var createFunctionString = function(anims) {
|
||||
// We must track the order they appear in so transforms are applied in the
|
||||
// correct order.
|
||||
var orderedMatrixOperations = [];
|
||||
|
||||
// Wrapping function allows the final function to contain state (for
|
||||
// caching).
|
||||
var fn = 'return (function() {\n';
|
||||
fn += createReusableOperationVars(anims);
|
||||
fn += 'return function(result, value) {\n';
|
||||
fn += ' var didChange = false;\n';
|
||||
fn += ' var nextScalarVal;\n';
|
||||
fn += ' var ratio;\n';
|
||||
|
||||
for (var name in anims) {
|
||||
var anim = anims[name];
|
||||
if (anim.type === 'linear') {
|
||||
if (InterpolateMatrix[name]) {
|
||||
orderedMatrixOperations.push(name);
|
||||
var setOperations = [
|
||||
computeNextMatrixOperationField(anim, name, X_DIM, 0),
|
||||
computeNextMatrixOperationField(anim, name, Y_DIM, 1),
|
||||
computeNextMatrixOperationField(anim, name, Z_DIM, 2)
|
||||
];
|
||||
if (name === TRANSFORM_ROTATE_NAME) {
|
||||
setOperations.push(computeNextMatrixOperationField(anim, name, W_DIM, 3));
|
||||
}
|
||||
fn += newlines(setOperations);
|
||||
} else {
|
||||
fn += computeNextValLinearScalar(anim, 'nextScalarVal');
|
||||
fn += setNextValAndDetectChange(name, 'nextScalarVal');
|
||||
}
|
||||
} else if (anim.type === 'constant') {
|
||||
fn += computeNextValConstant(anim);
|
||||
fn += setNextValAndDetectChange(name, 'nextScalarVal');
|
||||
} else if (anim.type === 'step') {
|
||||
fn += computeNextValStep(anim);
|
||||
fn += setNextValAndDetectChange(name, 'nextScalarVal');
|
||||
} else if (anim.type === 'identity') {
|
||||
fn += computeNextValIdentity(anim);
|
||||
fn += setNextValAndDetectChange(name, 'nextScalarVal');
|
||||
}
|
||||
var x = computeNextMatrixOperationField(anim, name, X_DIM, 0, value);
|
||||
var y = computeNextMatrixOperationField(anim, name, Y_DIM, 1, value);
|
||||
var 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];
|
||||
}
|
||||
if (orderedMatrixOperations.length) {
|
||||
fn += newlines(setNextMatrixAndDetectChange(orderedMatrixOperations));
|
||||
}
|
||||
fn += ' return didChange;\n';
|
||||
fn += '};\n';
|
||||
fn += '})()';
|
||||
return fn;
|
||||
return didChange;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -558,15 +148,34 @@ var createFunctionString = function(anims) {
|
|||
* object and returns a boolean describing if any update was actually applied.
|
||||
*/
|
||||
var buildStyleInterpolator = function(anims) {
|
||||
// Defer compiling this method until we really need it.
|
||||
var interpolator = null;
|
||||
function lazyStyleInterpolator(result, value) {
|
||||
if (interpolator === null) {
|
||||
interpolator = Function(createFunctionString(anims))();
|
||||
function styleInterpolator(result, value) {
|
||||
var didChange = false;
|
||||
var didMatrix = false;
|
||||
for (var name in anims) {
|
||||
var anim = anims[name];
|
||||
if (anim.type === 'linear') {
|
||||
if (name in InterpolateMatrix) {
|
||||
didChange = computeTransform(anim, name, value, result,
|
||||
didChange, didMatrix);
|
||||
didMatrix = true;
|
||||
} else {
|
||||
var next = computeNextValLinearScalar(anim, value);
|
||||
didChange = setNextValAndDetectChange(result, name, next, didChange);
|
||||
}
|
||||
} else if (anim.type === 'constant') {
|
||||
var next = anim.value;
|
||||
didChange = setNextValAndDetectChange(result, name, next, didChange);
|
||||
} else if (anim.type === 'step') {
|
||||
var next = value >= anim.threshold ? anim.to : anim.from;
|
||||
didChange = setNextValAndDetectChange(result, name, next, didChange);
|
||||
} else if (anim.type === 'identity') {
|
||||
var next = value;
|
||||
didChange = setNextValAndDetectChange(result, name, next, didChange);
|
||||
}
|
||||
}
|
||||
return interpolator(result, value);
|
||||
return didChange;
|
||||
}
|
||||
return lazyStyleInterpolator;
|
||||
return styleInterpolator;
|
||||
};
|
||||
|
||||
module.exports = buildStyleInterpolator;
|
||||
|
|
Loading…
Reference in New Issue