Skip to content

Commit

Permalink
add support for transform effect
Browse files Browse the repository at this point in the history
  • Loading branch information
bodymovin committed May 26, 2023
1 parent a349c4e commit efeb109
Show file tree
Hide file tree
Showing 18 changed files with 233 additions and 18 deletions.
23 changes: 23 additions & 0 deletions player/js/3rd_party/transformation-matrix.js
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,28 @@ const Matrix = (function () {
return this;
}

function multiply(matrix) {
var matrixProps = matrix.props;
return this.transform(
matrixProps[0],
matrixProps[1],
matrixProps[2],
matrixProps[3],
matrixProps[4],
matrixProps[5],
matrixProps[6],
matrixProps[7],
matrixProps[8],
matrixProps[9],
matrixProps[10],
matrixProps[11],
matrixProps[12],
matrixProps[13],
matrixProps[14],
matrixProps[15]
);
}

function isIdentity() {
if (!this._identityCalculated) {
this._identity = !(this.props[0] !== 1 || this.props[1] !== 0 || this.props[2] !== 0 || this.props[3] !== 0 || this.props[4] !== 0 || this.props[5] !== 1 || this.props[6] !== 0 || this.props[7] !== 0 || this.props[8] !== 0 || this.props[9] !== 0 || this.props[10] !== 1 || this.props[11] !== 0 || this.props[12] !== 0 || this.props[13] !== 0 || this.props[14] !== 0 || this.props[15] !== 1);
Expand Down Expand Up @@ -398,6 +420,7 @@ const Matrix = (function () {
this.setTransform = setTransform;
this.translate = translate;
this.transform = transform;
this.multiply = multiply;
this.applyToPoint = applyToPoint;
this.applyToX = applyToX;
this.applyToY = applyToY;
Expand Down
43 changes: 43 additions & 0 deletions player/js/effects/TransformEffect.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import effectTypes from '../utils/helpers/effectTypes';
import Matrix from '../3rd_party/transformation-matrix';
import { degToRads } from '../utils/common';

function TransformEffect() {
}

TransformEffect.prototype.init = function (effectsManager) {
this.effectsManager = effectsManager;
this.type = effectTypes.TRANSFORM_EFFECT;
this.matrix = new Matrix();
this.opacity = -1;
this._mdf = false;
this._opMdf = false;
};

TransformEffect.prototype.renderFrame = function (forceFrame) {
this._opMdf = false;
this._mdf = false;
if (forceFrame || this.effectsManager._mdf) {
var effectElements = this.effectsManager.effectElements;
var anchor = effectElements[0].p.v;
var position = effectElements[1].p.v;
var scaleHeight = effectElements[3].p.v;
var scaleWidth = effectElements[4].p.v;
var skew = effectElements[5].p.v;
var skewAxis = effectElements[6].p.v;
var rotation = effectElements[7].p.v;
this.matrix.reset();
this.matrix.translate(-anchor[0], -anchor[1], anchor[2]);
this.matrix.scale(scaleWidth * 0.01, scaleHeight * 0.01, 1);
this.matrix.rotate(-rotation * degToRads);
this.matrix.skewFromAxis(-skew * degToRads, (skewAxis + 90) * degToRads);
this.matrix.translate(position[0], position[1], 0);
this._mdf = true;
if (this.opacity !== effectElements[8].p.v) {
this.opacity = effectElements[8].p.v;
this._opMdf = true;
}
}
};

export default TransformEffect;
8 changes: 6 additions & 2 deletions player/js/elements/canvasElements/CVBaseElement.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import getBlendMode from '../../utils/helpers/blendModes';
import Matrix from '../../3rd_party/transformation-matrix';
import CVEffects from './CVEffects';
import CVMaskElement from './CVMaskElement';
import effectTypes from '../../utils/helpers/effectTypes';

function CVBaseElement() {
}
Expand Down Expand Up @@ -37,6 +38,7 @@ CVBaseElement.prototype = {
this.canvasContext = this.globalData.canvasContext;
this.transformCanvas = this.globalData.transformCanvas;
this.renderableEffectsManager = new CVEffects(this);
this.searchEffectTransforms();
},
createContent: function () {},
setBlendMode: function () {
Expand All @@ -49,6 +51,7 @@ CVBaseElement.prototype = {
},
createRenderableComponents: function () {
this.maskManager = new CVMaskElement(this.data, this);
this.transformEffects = this.renderableEffectsManager.getEffects(effectTypes.TRANSFORM_EFFECT);
},
hideElement: function () {
if (!this.hidden && (!this.isInRange || this.isTransparent)) {
Expand Down Expand Up @@ -136,12 +139,13 @@ CVBaseElement.prototype = {
}
this.renderTransform();
this.renderRenderable();
this.renderLocalTransform();
this.setBlendMode();
var forceRealStack = this.data.ty === 0;
this.prepareLayer();
this.globalData.renderer.save(forceRealStack);
this.globalData.renderer.ctxTransform(this.finalTransform.mat.props);
this.globalData.renderer.ctxOpacity(this.finalTransform.mProp.o.v);
this.globalData.renderer.ctxTransform(this.finalTransform.localMat.props);
this.globalData.renderer.ctxOpacity(this.finalTransform.localOpacity);
this.renderInnerContent();
this.globalData.renderer.restore(forceRealStack);
this.exitLayer();
Expand Down
48 changes: 46 additions & 2 deletions player/js/elements/canvasElements/CVEffects.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,50 @@
function CVEffects() {
var registeredEffects = {};

function CVEffects(elem) {
var i;
var len = elem.data.ef ? elem.data.ef.length : 0;
this.filters = [];
var filterManager;
for (i = 0; i < len; i += 1) {
filterManager = null;
var type = elem.data.ef[i].ty;
if (registeredEffects[type]) {
var Effect = registeredEffects[type].effect;
filterManager = new Effect(elem.effectsManager.effectElements[i], elem);
}
if (filterManager) {
this.filters.push(filterManager);
}
}
if (this.filters.length) {
elem.addRenderableComponent(this);
}
}

CVEffects.prototype.renderFrame = function (_isFirstFrame) {
var i;
var len = this.filters.length;
for (i = 0; i < len; i += 1) {
this.filters[i].renderFrame(_isFirstFrame);
}
};

CVEffects.prototype.getEffects = function (type) {
var i;
var len = this.filters.length;
var effects = [];
for (i = 0; i < len; i += 1) {
if (this.filters[i].type === type) {
effects.push(this.filters[i]);
}
}
return effects;
};

export function registerEffect(id, effect) {
registeredEffects[id] = {
effect,
};
}
CVEffects.prototype.renderFrame = function () {};

export default CVEffects;
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import TransformEffect from '../../../effects/TransformEffect';
import { extendPrototype } from '../../../utils/functionExtensions';

function CVTransformEffect(effectsManager) {
this.init(effectsManager);
}
extendPrototype([TransformEffect], CVTransformEffect);

export default CVTransformEffect;
1 change: 1 addition & 0 deletions player/js/elements/helpers/RenderableDOMElement.js
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ function RenderableDOMElement() {}
}
this.renderTransform();
this.renderRenderable();
this.renderLocalTransform();
this.renderElement();
this.renderInnerContent();
if (this._isFirstFrame) {
Expand Down
64 changes: 61 additions & 3 deletions player/js/elements/helpers/TransformElement.js
Original file line number Diff line number Diff line change
@@ -1,15 +1,20 @@
import Matrix from '../../3rd_party/transformation-matrix';
import TransformPropertyFactory from '../../utils/TransformProperty';
import effectTypes from '../../utils/helpers/effectTypes';

function TransformElement() {}

TransformElement.prototype = {
initTransform: function () {
var mat = new Matrix();
this.finalTransform = {
mProp: this.data.ks ? TransformPropertyFactory.getTransformProperty(this, this.data.ks, this) : { o: 0 },
_matMdf: false,
_localMatMdf: false,
_opMdf: false,
mat: new Matrix(),
mat: mat,
localMat: mat,
localOpacity: 1,
};
if (this.data.ao) {
this.finalTransform.mProp.autoOriented = true;
Expand Down Expand Up @@ -44,8 +49,61 @@ TransformElement.prototype = {
mat = this.finalTransform.mProp.v.props;
finalMat.cloneFromProps(mat);
for (i = 0; i < len; i += 1) {
mat = this.hierarchy[i].finalTransform.mProp.v.props;
finalMat.transform(mat[0], mat[1], mat[2], mat[3], mat[4], mat[5], mat[6], mat[7], mat[8], mat[9], mat[10], mat[11], mat[12], mat[13], mat[14], mat[15]);
finalMat.multiply(this.hierarchy[i].finalTransform.mProp.v);
}
}
}
if (this.finalTransform._matMdf) {
this.finalTransform._localMatMdf = this.finalTransform._matMdf;
}
if (this.finalTransform._opMdf) {
this.finalTransform.localOpacity = this.finalTransform.mProp.o.v;
}
},
renderLocalTransform: function () {
if (this.localTransforms) {
var i = 0;
var len = this.localTransforms.length;
this.finalTransform._localMatMdf = this.finalTransform._matMdf;
if (!this.finalTransform._localMatMdf || !this.finalTransform._opMdf) {
while (i < len) {
if (this.localTransforms[i]._mdf) {
this.finalTransform._localMatMdf = true;
}
if (this.localTransforms[i]._opMdf) {
this.finalTransform._opMdf = true;
}
i += 1;
}
}
if (this.finalTransform._localMatMdf) {
var localMat = this.finalTransform.localMat;
this.localTransforms[0].matrix.clone(localMat);
for (i = 1; i < len; i += 1) {
var lmat = this.localTransforms[i].matrix;
localMat.multiply(lmat);
}
localMat.multiply(this.finalTransform.mat);
}
if (this.finalTransform._opMdf) {
var localOp = this.finalTransform.localOpacity;
for (i = 0; i < len; i += 1) {
localOp *= this.localTransforms[i].opacity * 0.01;
}
this.finalTransform.localOpacity = localOp;
}
}
},
searchEffectTransforms: function () {
if (this.renderableEffectsManager) {
var transformEffects = this.renderableEffectsManager.getEffects(effectTypes.TRANSFORM_EFFECT);
if (transformEffects.length) {
this.localTransforms = [];
this.finalTransform.localMat = new Matrix();
var i = 0;
var len = transformEffects.length;
for (i = 0; i < len; i += 1) {
this.localTransforms.push(transformEffects[i]);
}
}
}
Expand Down
4 changes: 1 addition & 3 deletions player/js/elements/helpers/shapes/SVGElementsRenderer.js
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,6 @@ const SVGElementsRenderer = (function () {
var lvl = itemData.lvl;
var paths;
var mat;
var props;
var iterations;
var k;
for (l = 0; l < lLen; l += 1) {
Expand All @@ -76,8 +75,7 @@ const SVGElementsRenderer = (function () {
iterations = lvl - itemData.styles[l].lvl;
k = itemData.transformers.length - 1;
while (iterations > 0) {
props = itemData.transformers[k].mProps.v.props;
mat.transform(props[0], props[1], props[2], props[3], props[4], props[5], props[6], props[7], props[8], props[9], props[10], props[11], props[12], props[13], props[14], props[15]);
mat.multiply(itemData.transformers[k].mProps.v);
iterations -= 1;
k -= 1;
}
Expand Down
4 changes: 1 addition & 3 deletions player/js/elements/helpers/shapes/ShapeTransformManager.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,11 +38,9 @@ ShapeTransformManager.prototype = {
i += 1;
}
if (_mdf) {
var props;
sequence.finalTransform.reset();
for (i = len - 1; i >= 0; i -= 1) {
props = sequence.transforms[i].transform.mProps.v.props;
sequence.finalTransform.transform(props[0], props[1], props[2], props[3], props[4], props[5], props[6], props[7], props[8], props[9], props[10], props[11], props[12], props[13], props[14], props[15]);
sequence.finalTransform.multiply(sequence.transforms[i].transform.mProps.v);
}
}
sequence._mdf = _mdf;
Expand Down
7 changes: 4 additions & 3 deletions player/js/elements/svgElements/SVGBaseElement.js
Original file line number Diff line number Diff line change
Expand Up @@ -71,11 +71,11 @@ SVGBaseElement.prototype = {
}
},
renderElement: function () {
if (this.finalTransform._matMdf) {
this.transformedElement.setAttribute('transform', this.finalTransform.mat.to2dCSS());
if (this.finalTransform._localMatMdf) {
this.transformedElement.setAttribute('transform', this.finalTransform.localMat.to2dCSS());
}
if (this.finalTransform._opMdf) {
this.transformedElement.setAttribute('opacity', this.finalTransform.mProp.o.v);
this.transformedElement.setAttribute('opacity', this.finalTransform.localOpacity);
}
},
destroyBaseElement: function () {
Expand All @@ -92,6 +92,7 @@ SVGBaseElement.prototype = {
createRenderableComponents: function () {
this.maskManager = new MaskElement(this.data, this, this.globalData);
this.renderableEffectsManager = new SVGEffects(this);
this.searchEffectTransforms();
},
getMatte: function (matteType) {
// This should not be a common case. But for backward compatibility, we'll create the matte object.
Expand Down
12 changes: 12 additions & 0 deletions player/js/elements/svgElements/SVGEffects.js
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,18 @@ SVGEffects.prototype.renderFrame = function (_isFirstFrame) {
}
};

SVGEffects.prototype.getEffects = function (type) {
var i;
var len = this.filters.length;
var effects = [];
for (i = 0; i < len; i += 1) {
if (this.filters[i].type === type) {
effects.push(this.filters[i]);
}
}
return effects;
};

export function registerEffect(id, effect, countsAsEffect) {
registeredEffects[id] = {
effect,
Expand Down
10 changes: 10 additions & 0 deletions player/js/elements/svgElements/effects/SVGTransformEffect.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import TransformEffect from '../../../effects/TransformEffect';
import { extendPrototype } from '../../../utils/functionExtensions';

function SVGTransformEffect(_, filterManager) {
this.init(filterManager);
}

extendPrototype([TransformEffect], SVGTransformEffect);

export default SVGTransformEffect;
3 changes: 3 additions & 0 deletions player/js/modules/canvas.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,14 @@ import Expressions from '../utils/expressions/Expressions';
import interfacesProvider from '../utils/expressions/InterfacesProvider';
import expressionPropertyDecorator from '../utils/expressions/ExpressionPropertyDecorator';
import expressionTextPropertyDecorator from '../utils/expressions/ExpressionTextPropertyDecorator';
import CVTransformEffect from '../elements/canvasElements/effects/CVTransformEffect';
import { registerEffect } from '../elements/canvasElements/CVEffects';

// Registering expression plugin
setExpressionsPlugin(Expressions);
setExpressionInterfaces(interfacesProvider);
expressionPropertyDecorator();
expressionTextPropertyDecorator();
registerEffect(35, CVTransformEffect);

export default lottie;
5 changes: 5 additions & 0 deletions player/js/modules/full.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,9 @@ import SVGProLevelsFilter from '../elements/svgElements/effects/SVGProLevelsFilt
import SVGDropShadowEffect from '../elements/svgElements/effects/SVGDropShadowEffect';
import SVGMatte3Effect from '../elements/svgElements/effects/SVGMatte3Effect';
import SVGGaussianBlurEffect from '../elements/svgElements/effects/SVGGaussianBlurEffect';
import SVGTransformEffect from '../elements/svgElements/effects/SVGTransformEffect';
import CVTransformEffect from '../elements/canvasElements/effects/CVTransformEffect';
import { registerEffect as canvasRegisterEffect } from '../elements/canvasElements/CVEffects';

// Registering renderers
registerRenderer('canvas', CanvasRenderer);
Expand Down Expand Up @@ -59,5 +62,7 @@ registerEffect(24, SVGProLevelsFilter, true);
registerEffect(25, SVGDropShadowEffect, true);
registerEffect(28, SVGMatte3Effect, false);
registerEffect(29, SVGGaussianBlurEffect, true);
registerEffect(35, SVGTransformEffect, false);
canvasRegisterEffect(35, CVTransformEffect);

export default lottie;
Loading

0 comments on commit efeb109

Please sign in to comment.