Skip to content

Commit 4198236

Browse files
LottieWeb: Remove expressions eval, harden SSR check.
1 parent 142144a commit 4198236

File tree

1 file changed

+17
-350
lines changed

1 file changed

+17
-350
lines changed

examples/jsm/libs/lottie_canvas.module.js

+17-350
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,14 @@
11
const lottie = {};
22

3-
if (typeof document !== 'undefined') {
3+
// Checks for SSR including partial DOM implementation and emulation.
4+
// In these environments, Lottie should not be loaded or it can crash.
5+
// https://github.com/pmndrs/three-stdlib/pull/398#issuecomment-2600778156
6+
const isSSR =
7+
typeof navigator === 'undefined' ||
8+
typeof document === 'undefined' ||
9+
typeof CanvasRenderingContext2D === 'undefined'
10+
11+
if (isSSR === false) {
412

513
const svgNS = 'http://www.w3.org/2000/svg';
614

@@ -7433,7 +7441,6 @@ const TextExpressionInterface = (function () {
74337441
if (stringValue !== _prevValue) {
74347442
elem.textProperty.currentData.t = _prevValue;
74357443
_sourceText = new String(stringValue); // eslint-disable-line no-new-wrappers
7436-
// If stringValue is an empty string, eval returns undefined, so it has to be returned as a String primitive
74377444
_sourceText.value = stringValue || new String(stringValue); // eslint-disable-line no-new-wrappers
74387445
}
74397446
return _sourceText;
@@ -13925,354 +13932,14 @@ const ExpressionManager = (function () {
1392513932
return path;
1392613933
}
1392713934

13928-
function initiateExpression(elem, data, property) {
13929-
var val = data.x;
13930-
var needsVelocity = /velocity(?![\w\d])/.test(val);
13931-
var _needsRandom = val.indexOf('random') !== -1;
13932-
var elemType = elem.data.ty;
13933-
var transform;
13934-
var $bm_transform;
13935-
var content;
13936-
var effect;
13937-
var thisProperty = property;
13938-
thisProperty.valueAtTime = thisProperty.getValueAtTime;
13939-
Object.defineProperty(thisProperty, 'value', {
13940-
get: function () {
13941-
return thisProperty.v;
13942-
},
13943-
});
13944-
elem.comp.frameDuration = 1 / elem.comp.globalData.frameRate;
13945-
elem.comp.displayStartTime = 0;
13946-
var inPoint = elem.data.ip / elem.comp.globalData.frameRate;
13947-
var outPoint = elem.data.op / elem.comp.globalData.frameRate;
13948-
var width = elem.data.sw ? elem.data.sw : 0;
13949-
var height = elem.data.sh ? elem.data.sh : 0;
13950-
var name = elem.data.nm;
13951-
var loopIn;
13952-
var loop_in;
13953-
var loopOut;
13954-
var loop_out;
13955-
var smooth;
13956-
var toWorld;
13957-
var fromWorld;
13958-
var fromComp;
13959-
var toComp;
13960-
var fromCompToSurface;
13961-
var position;
13962-
var rotation;
13963-
var anchorPoint;
13964-
var scale;
13965-
var thisLayer;
13966-
var thisComp;
13967-
var mask;
13968-
var valueAtTime;
13969-
var velocityAtTime;
13970-
13971-
var scoped_bm_rt;
13972-
// val = val.replace(/(\\?"|')((http)(s)?(:\/))?\/.*?(\\?"|')/g, "\"\""); // deter potential network calls
13973-
var expression_function = eval('[function _expression_function(){' + val + ';scoped_bm_rt=$bm_rt}]')[0]; // eslint-disable-line no-eval
13974-
var numKeys = property.kf ? data.k.length : 0;
13975-
13976-
var active = !this.data || this.data.hd !== true;
13977-
13978-
var wiggle = function wiggle(freq, amp) {
13979-
var iWiggle;
13980-
var j;
13981-
var lenWiggle = this.pv.length ? this.pv.length : 1;
13982-
var addedAmps = createTypedArray('float32', lenWiggle);
13983-
freq = 5;
13984-
var iterations = Math.floor(time * freq);
13985-
iWiggle = 0;
13986-
j = 0;
13987-
while (iWiggle < iterations) {
13988-
// var rnd = BMMath.random();
13989-
for (j = 0; j < lenWiggle; j += 1) {
13990-
addedAmps[j] += -amp + amp * 2 * BMMath.random();
13991-
// addedAmps[j] += -amp + amp*2*rnd;
13992-
}
13993-
iWiggle += 1;
13994-
}
13995-
// var rnd2 = BMMath.random();
13996-
var periods = time * freq;
13997-
var perc = periods - Math.floor(periods);
13998-
var arr = createTypedArray('float32', lenWiggle);
13999-
if (lenWiggle > 1) {
14000-
for (j = 0; j < lenWiggle; j += 1) {
14001-
arr[j] = this.pv[j] + addedAmps[j] + (-amp + amp * 2 * BMMath.random()) * perc;
14002-
// arr[j] = this.pv[j] + addedAmps[j] + (-amp + amp*2*rnd)*perc;
14003-
// arr[i] = this.pv[i] + addedAmp + amp1*perc + amp2*(1-perc);
14004-
}
14005-
return arr;
14006-
}
14007-
return this.pv + addedAmps[0] + (-amp + amp * 2 * BMMath.random()) * perc;
14008-
}.bind(this);
14009-
14010-
if (thisProperty.loopIn) {
14011-
loopIn = thisProperty.loopIn.bind(thisProperty);
14012-
loop_in = loopIn;
14013-
}
14014-
14015-
if (thisProperty.loopOut) {
14016-
loopOut = thisProperty.loopOut.bind(thisProperty);
14017-
loop_out = loopOut;
14018-
}
14019-
14020-
if (thisProperty.smooth) {
14021-
smooth = thisProperty.smooth.bind(thisProperty);
14022-
}
14023-
14024-
function loopInDuration(type, duration) {
14025-
return loopIn(type, duration, true);
14026-
}
14027-
14028-
function loopOutDuration(type, duration) {
14029-
return loopOut(type, duration, true);
14030-
}
14031-
14032-
if (this.getValueAtTime) {
14033-
valueAtTime = this.getValueAtTime.bind(this);
14034-
}
14035-
14036-
if (this.getVelocityAtTime) {
14037-
velocityAtTime = this.getVelocityAtTime.bind(this);
14038-
}
14039-
14040-
var comp = elem.comp.globalData.projectInterface.bind(elem.comp.globalData.projectInterface);
14041-
14042-
function lookAt(elem1, elem2) {
14043-
var fVec = [elem2[0] - elem1[0], elem2[1] - elem1[1], elem2[2] - elem1[2]];
14044-
var pitch = Math.atan2(fVec[0], Math.sqrt(fVec[1] * fVec[1] + fVec[2] * fVec[2])) / degToRads;
14045-
var yaw = -Math.atan2(fVec[1], fVec[2]) / degToRads;
14046-
return [yaw, pitch, 0];
14047-
}
14048-
14049-
function easeOut(t, tMin, tMax, val1, val2) {
14050-
return applyEase(easeOutBez, t, tMin, tMax, val1, val2);
14051-
}
14052-
14053-
function easeIn(t, tMin, tMax, val1, val2) {
14054-
return applyEase(easeInBez, t, tMin, tMax, val1, val2);
14055-
}
14056-
14057-
function ease(t, tMin, tMax, val1, val2) {
14058-
return applyEase(easeInOutBez, t, tMin, tMax, val1, val2);
14059-
}
14060-
14061-
function applyEase(fn, t, tMin, tMax, val1, val2) {
14062-
if (val1 === undefined) {
14063-
val1 = tMin;
14064-
val2 = tMax;
14065-
} else {
14066-
t = (t - tMin) / (tMax - tMin);
14067-
}
14068-
if (t > 1) {
14069-
t = 1;
14070-
} else if (t < 0) {
14071-
t = 0;
14072-
}
14073-
var mult = fn(t);
14074-
if ($bm_isInstanceOfArray(val1)) {
14075-
var iKey;
14076-
var lenKey = val1.length;
14077-
var arr = createTypedArray('float32', lenKey);
14078-
for (iKey = 0; iKey < lenKey; iKey += 1) {
14079-
arr[iKey] = (val2[iKey] - val1[iKey]) * mult + val1[iKey];
14080-
}
14081-
return arr;
14082-
}
14083-
return (val2 - val1) * mult + val1;
14084-
}
14085-
14086-
function nearestKey(time) {
14087-
var iKey;
14088-
var lenKey = data.k.length;
14089-
var index;
14090-
var keyTime;
14091-
if (!data.k.length || typeof (data.k[0]) === 'number') {
14092-
index = 0;
14093-
keyTime = 0;
14094-
} else {
14095-
index = -1;
14096-
time *= elem.comp.globalData.frameRate;
14097-
if (time < data.k[0].t) {
14098-
index = 1;
14099-
keyTime = data.k[0].t;
14100-
} else {
14101-
for (iKey = 0; iKey < lenKey - 1; iKey += 1) {
14102-
if (time === data.k[iKey].t) {
14103-
index = iKey + 1;
14104-
keyTime = data.k[iKey].t;
14105-
break;
14106-
} else if (time > data.k[iKey].t && time < data.k[iKey + 1].t) {
14107-
if (time - data.k[iKey].t > data.k[iKey + 1].t - time) {
14108-
index = iKey + 2;
14109-
keyTime = data.k[iKey + 1].t;
14110-
} else {
14111-
index = iKey + 1;
14112-
keyTime = data.k[iKey].t;
14113-
}
14114-
break;
14115-
}
14116-
}
14117-
if (index === -1) {
14118-
index = iKey + 1;
14119-
keyTime = data.k[iKey].t;
14120-
}
14121-
}
14122-
}
14123-
var obKey = {};
14124-
obKey.index = index;
14125-
obKey.time = keyTime / elem.comp.globalData.frameRate;
14126-
return obKey;
14127-
}
14128-
14129-
function key(ind) {
14130-
var obKey;
14131-
var iKey;
14132-
var lenKey;
14133-
if (!data.k.length || typeof (data.k[0]) === 'number') {
14134-
throw new Error('The property has no keyframe at index ' + ind);
14135-
}
14136-
ind -= 1;
14137-
obKey = {
14138-
time: data.k[ind].t / elem.comp.globalData.frameRate,
14139-
value: [],
14140-
};
14141-
var arr = Object.prototype.hasOwnProperty.call(data.k[ind], 's') ? data.k[ind].s : data.k[ind - 1].e;
14142-
14143-
lenKey = arr.length;
14144-
for (iKey = 0; iKey < lenKey; iKey += 1) {
14145-
obKey[iKey] = arr[iKey];
14146-
obKey.value[iKey] = arr[iKey];
14147-
}
14148-
return obKey;
14149-
}
14150-
14151-
function framesToTime(fr, fps) {
14152-
if (!fps) {
14153-
fps = elem.comp.globalData.frameRate;
14154-
}
14155-
return fr / fps;
14156-
}
14157-
14158-
function timeToFrames(t, fps) {
14159-
if (!t && t !== 0) {
14160-
t = time;
14161-
}
14162-
if (!fps) {
14163-
fps = elem.comp.globalData.frameRate;
14164-
}
14165-
return t * fps;
14166-
}
14167-
14168-
function seedRandom(seed) {
14169-
BMMath.seedrandom(randSeed + seed);
14170-
}
14171-
14172-
function sourceRectAtTime() {
14173-
return elem.sourceRectAtTime();
14174-
}
14175-
14176-
function substring(init, end) {
14177-
if (typeof value === 'string') {
14178-
if (end === undefined) {
14179-
return value.substring(init);
14180-
}
14181-
return value.substring(init, end);
14182-
}
14183-
return '';
14184-
}
14185-
14186-
function substr(init, end) {
14187-
if (typeof value === 'string') {
14188-
if (end === undefined) {
14189-
return value.substr(init);
14190-
}
14191-
return value.substr(init, end);
14192-
}
14193-
return '';
14194-
}
14195-
14196-
function posterizeTime(framesPerSecond) {
14197-
time = framesPerSecond === 0 ? 0 : Math.floor(time * framesPerSecond) / framesPerSecond;
14198-
value = valueAtTime(time);
14199-
}
14200-
14201-
var time;
14202-
var velocity;
14203-
var value;
14204-
var text;
14205-
var textIndex;
14206-
var textTotal;
14207-
var selectorValue;
14208-
var index = elem.data.ind;
14209-
var hasParent = !!(elem.hierarchy && elem.hierarchy.length);
14210-
var parent;
14211-
var randSeed = Math.floor(Math.random() * 1000000);
14212-
var globalData = elem.globalData;
14213-
function executeExpression(_value) {
14214-
// globalData.pushExpression();
14215-
value = _value;
14216-
if (this.frameExpressionId === elem.globalData.frameId && this.propType !== 'textSelector') {
14217-
return value;
14218-
}
14219-
if (this.propType === 'textSelector') {
14220-
textIndex = this.textIndex;
14221-
textTotal = this.textTotal;
14222-
selectorValue = this.selectorValue;
14223-
}
14224-
if (!thisLayer) {
14225-
text = elem.layerInterface.text;
14226-
thisLayer = elem.layerInterface;
14227-
thisComp = elem.comp.compInterface;
14228-
toWorld = thisLayer.toWorld.bind(thisLayer);
14229-
fromWorld = thisLayer.fromWorld.bind(thisLayer);
14230-
fromComp = thisLayer.fromComp.bind(thisLayer);
14231-
toComp = thisLayer.toComp.bind(thisLayer);
14232-
mask = thisLayer.mask ? thisLayer.mask.bind(thisLayer) : null;
14233-
fromCompToSurface = fromComp;
14234-
}
14235-
if (!transform) {
14236-
transform = elem.layerInterface('ADBE Transform Group');
14237-
$bm_transform = transform;
14238-
if (transform) {
14239-
anchorPoint = transform.anchorPoint;
14240-
/* position = transform.position;
14241-
rotation = transform.rotation;
14242-
scale = transform.scale; */
14243-
}
14244-
}
14245-
14246-
if (elemType === 4 && !content) {
14247-
content = thisLayer('ADBE Root Vectors Group');
14248-
}
14249-
if (!effect) {
14250-
effect = thisLayer(4);
14251-
}
14252-
hasParent = !!(elem.hierarchy && elem.hierarchy.length);
14253-
if (hasParent && !parent) {
14254-
parent = elem.hierarchy[0].layerInterface;
14255-
}
14256-
time = this.comp.renderedFrame / this.comp.globalData.frameRate;
14257-
if (_needsRandom) {
14258-
seedRandom(randSeed + time);
14259-
}
14260-
if (needsVelocity) {
14261-
velocity = velocityAtTime(time);
14262-
}
14263-
expression_function();
14264-
this.frameExpressionId = elem.globalData.frameId;
14265-
14266-
// TODO: Check if it's possible to return on ShapeInterface the .v value
14267-
// Changed this to a ternary operation because Rollup failed compiling it correctly
14268-
scoped_bm_rt = scoped_bm_rt.propType === propTypes.SHAPE
14269-
? scoped_bm_rt.v
14270-
: scoped_bm_rt;
14271-
return scoped_bm_rt;
14272-
}
14273-
// Bundlers will see these as dead code and unless we reference them
14274-
executeExpression.__preventDeadCodeRemoval = [$bm_transform, anchorPoint, time, velocity, inPoint, outPoint, width, height, name, loop_in, loop_out, smooth, toComp, fromCompToSurface, toWorld, fromWorld, mask, position, rotation, scale, thisComp, numKeys, active, wiggle, loopInDuration, loopOutDuration, comp, lookAt, easeOut, easeIn, ease, nearestKey, key, text, textIndex, textTotal, selectorValue, framesToTime, timeToFrames, sourceRectAtTime, substring, substr, posterizeTime, index, globalData];
14275-
return executeExpression;
13935+
// https://github.com/airbnb/lottie-web/issues/2828
13936+
// https://github.com/airbnb/lottie-web/pull/2833
13937+
// Bail out if we don't want expressions
13938+
function noOp(_value) {
13939+
return _value;
13940+
}
13941+
function initiateExpression(_elem, _data, _property) {
13942+
return noOp;
1427613943
}
1427713944

1427813945
ob.initiateExpression = initiateExpression;

0 commit comments

Comments
 (0)