Skip to content

Commit e11829a

Browse files
Adding lottie playback using Babylon (#16990)
This PR adds support for Babylon to play a subset of lottie animations. - These animations will be played on an offscreen canvas and a worker thread. - The code takes a dependency only on ThinEngine and as few features as possible from Babylon to minimize package size. - Only certain features of lottie are supported currently. - This is a highly experimental feature, use it at your own risk :) To use this feature, instantiate an object of the class LottiePlayer and just set it to play the animation you want: ``` const player = new LottiePlayer(divHtmlElement, 'urltolottie.json'); player.playAnimation(); ``` The player will create a canvas and set it to offscreen inside your div HTML element. By default, the animation will just play once, but you can control multiple parameters of the playback and the rendering by using the parameter of playAnimation, which takes an object of type AnimationConfiguration. Below is the default configuration that will be merged with the user configuration. ``` /** * Default configuration for lottie animations playback. */ const DefaultConfiguration: AnimationConfiguration = { loopAnimation: false, // By default do not loop animations spriteAtlasSize: 2048, // Size of the texture atlas gapSize: 5, // Gap around the sprites in the atlas spritesCapacity: 64, // Maximum number of sprites the renderer can handle at once backgroundColor: { r: 1, g: 1, b: 1, a: 1 }, // Background color for the animation canvas scaleMultiplier: 5, // Minimum scale factor to prevent too small sprites, devicePixelRatio: 1, // Scale factor, easingSteps: 4, // Number of steps to sample easing functions for animations - Less than 4 causes issues with some interpolations ignoreOpacityAnimations: true, // Whether to ignore opacity animations for performance supportDeviceLost: false, // Whether to support device lost events for WebGL contexts, }; ```
1 parent a64fb05 commit e11829a

File tree

17 files changed

+2996
-0
lines changed

17 files changed

+2996
-0
lines changed
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
/* eslint-disable @typescript-eslint/no-restricted-imports */
2+
export { LottiePlayer, AnimationConfiguration } from "./lottiePlayer";

packages/dev/addons/src/lottie/lottie/animationParser.ts

Lines changed: 532 additions & 0 deletions
Large diffs are not rendered by default.
Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
import type { IVector2Like } from "core/Maths/math.like";
2+
3+
import type { BezierCurve } from "../maths/bezier";
4+
5+
import type { Node } from "../rendering/node";
6+
7+
/**
8+
* Represents a Babylon.js thin version of a Lottie animation.
9+
*/
10+
export type AnimationInfo = {
11+
/**
12+
* Frame number where the animation starts.
13+
*/
14+
startFrame: number;
15+
/**
16+
* Frame number where the animation ends.
17+
*/
18+
endFrame: number;
19+
/**
20+
* Frame rate of the animation.
21+
*/
22+
frameRate: number;
23+
/**
24+
* Width of the animation in pixels
25+
*/
26+
widthPx: number;
27+
/**
28+
* Height of the animation in pixels
29+
*/
30+
heightPx: number;
31+
/**
32+
* Nodes representing the animation
33+
*/
34+
nodes: Node[];
35+
};
36+
37+
/**
38+
* Transform properties for a Lottie animation.
39+
* Any of these properties may be animated.
40+
*/
41+
export type Transform = {
42+
/**
43+
* The anchor point of the layer, which is the point around which transformations are applied.
44+
*/
45+
anchorPoint: Vector2Property;
46+
/**
47+
* The position of the layer in the animation.
48+
*/
49+
position: Vector2Property;
50+
/**
51+
* The rotation of the layer in degrees.
52+
*/
53+
rotation: ScalarProperty;
54+
/**
55+
* The scale of the layer in the X and Y axis.
56+
*/
57+
scale: Vector2Property;
58+
/**
59+
* The opacity of the layer, represented as a scalar value.
60+
*/
61+
opacity: ScalarProperty;
62+
};
63+
64+
/**
65+
* Represents a scalar that can be animated.
66+
*/
67+
export type ScalarProperty = {
68+
/**
69+
* The initial value of the property at the start of the animation.
70+
*/
71+
startValue: number;
72+
/**
73+
* The current value of the property during the animation.
74+
*/
75+
currentValue: number;
76+
/**
77+
* An array of keyframes for the property.
78+
*/
79+
keyframes?: ScalarKeyframe[];
80+
/**
81+
* The index of the current keyframe being processed in the animation.
82+
*/
83+
currentKeyframeIndex: number;
84+
};
85+
86+
/**
87+
* Represents a keyframe for a scalar property.
88+
*/
89+
export type ScalarKeyframe = {
90+
/**
91+
* The value at this keyframe.
92+
*/
93+
value: number;
94+
/**
95+
* The time at which this keyframe occurs in the animation, in frames.
96+
*/
97+
time: number;
98+
/**
99+
* The easing function applied to the transition from this keyframe to the next one.
100+
*/
101+
easeFunction: BezierCurve;
102+
};
103+
104+
/**
105+
* Represents a 2D vector that can be animated.
106+
*/
107+
export type Vector2Property = {
108+
/**
109+
* The initial value at the start of the animation.
110+
*/
111+
startValue: IVector2Like;
112+
/**
113+
* The current value during the animation.
114+
*/
115+
currentValue: IVector2Like;
116+
/**
117+
* An array of keyframes for the property.
118+
*/
119+
keyframes?: Vector2Keyframe[];
120+
/**
121+
* The index of the current keyframe being processed in the animation.
122+
*/
123+
currentKeyframeIndex: number;
124+
};
125+
126+
/**
127+
* Represents a keyframe for a 2D vector property.
128+
*/
129+
export type Vector2Keyframe = {
130+
/**
131+
* The value at this keyframe.
132+
*/
133+
value: IVector2Like;
134+
/**
135+
* The time at which this keyframe occurs in the animation, in frames.
136+
*/
137+
time: number;
138+
/**
139+
* The easing function applied to the transition from this keyframe to the next one.
140+
* This is used for the first dimension of the vector.
141+
*/
142+
easeFunction1: BezierCurve;
143+
/**
144+
* The easing function applied to the transition from this keyframe to the next one.
145+
* This is used for the second dimension of the vector.
146+
*/
147+
easeFunction2: BezierCurve;
148+
};
Lines changed: 182 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,182 @@
1+
/* eslint-disable jsdoc/require-jsdoc */
2+
/* eslint-disable @typescript-eslint/no-unused-vars */
3+
4+
// Types for the raw Lottie .json data
5+
6+
// General animation data
7+
export type RawLottieAnimation = {
8+
v: string; // Version
9+
fr: number; // Framerate in frames per second
10+
ip: number; // Frame the animation starts at (usually 0)
11+
op: number; // Frame the animation stops/loops at, which makes this the duration in frames when ip is 0
12+
w: number; // Width
13+
h: number; // Height
14+
nm?: string; // Human readable name
15+
layers: RawLottieLayer[]; // Layers
16+
};
17+
18+
// Layer data
19+
export type RawLottieLayer = {
20+
ind?: number; // Index that can be used for parenting and referenced in expressions
21+
ty: RawLayerType; // Layer type (0: precomp, 1: solid, 2: image, 3: null, 4: shape, 5: text)
22+
nm?: string; // Human readable name
23+
parent?: number; // Must be the ind property of another layer
24+
hd?: boolean; // Hidden
25+
sr?: number; // Time Stretch
26+
ao?: number; // Auto-Orient (0: false, 1: true), if 1, the layer will rotate itself to match its animated position path
27+
ip?: number; // Frame when the layer becomes visible
28+
op?: number; // Frame when the layer becomes invisible
29+
st?: number; // Start time
30+
ct?: number; // Collapse Transform (0: false, 1: true), marks that transforms should be applied before masks
31+
ks: RawTransform; // Layer transform
32+
shapes?: RawGraphicElement[];
33+
};
34+
35+
export type RawGraphicElement = {
36+
nm?: string; // Human readable name
37+
hd?: boolean; // Hidden
38+
ty: RawShapeType; // Type ('gr' for group, 'rc' for rectangle, 'el' for ellipse, 'sh' for path, etc.)
39+
bm?: number; // Blend mode
40+
ix?: number; // Index
41+
};
42+
43+
export type RawGroupShape = RawGraphicElement & {
44+
it?: RawGraphicElement[]; // shapes
45+
};
46+
47+
export type RawRectangleShape = RawGraphicElement & {
48+
d: RawShapeDirection; // direction the shape is drawn as, mostly relevant when using trim path
49+
p: RawPositionProperty; // center of the rectangle
50+
s: RawVectorProperty; // size of the rectangle
51+
r: RawScalarProperty; // rounded corners radius
52+
};
53+
54+
export type RawPathShape = RawGraphicElement & {
55+
d: RawShapeDirection; // direction the shape is drawn as, mostly relevant when using trim path
56+
ks: RawBezierShapeProperty; // bezier path
57+
};
58+
59+
export type RawFillShape = RawGraphicElement & {
60+
o: RawScalarProperty; // Opacity, 100 means fully opaque
61+
c: RawColorProperty; // Color
62+
r: RawFillRule; // fill rule
63+
};
64+
65+
export type RawGradientFillShape = RawGraphicElement & {
66+
o: RawScalarProperty; // Opacity, 100 means fully opaque
67+
g: RawGradientsProperty; // Gradient colors
68+
s: RawPositionProperty; // Starting point of the gradient
69+
e: RawPositionProperty; // End point of the gradient
70+
t: RawGradientType; // type of the gradient
71+
h: RawScalarProperty; // highlight length as a percentage between s and e
72+
a?: RawScalarProperty; // highlight angle in clockwise degrees, relative to the direction from s to e
73+
r: RawFillRule; // fill rule
74+
};
75+
76+
export type RawTransformShape = RawGraphicElement & {
77+
a: RawPositionProperty; // anchor point
78+
p: RawPositionProperty; // position/translation
79+
r: RawScalarProperty; // rotation in degrees, clockwise
80+
s: RawVectorProperty; // scale factor, [100, 100] for no scaling
81+
o: RawScalarProperty; // opacity
82+
sk: RawScalarProperty; // skew amount as an angle in degrees
83+
sa: RawScalarProperty; // skew axis, direction along which skew is applied, in degrees (0 skes along the x axis, 90 along the Y axys)
84+
};
85+
86+
export type RawTransform = {
87+
a?: RawPositionProperty; // Anchor point: a position (relative to its parent) around which transformations are applied (ie: center for rotation / scale)
88+
p?: RawPositionProperty; // Position / Translation
89+
r?: RawScalarProperty; // Rotation in degrees, clockwise
90+
s?: RawVectorProperty; // Scale factor, [100, 100] for no scaling
91+
o?: RawScalarProperty; // Opacity
92+
};
93+
94+
export type RawScalarProperty = {
95+
a: RawNumberBoolean; // Animated (0: false, 1: true)
96+
k: number | RawVectorKeyframe[]; // When it's not animated, k will contain the value directly. When animated, k will be an array of keyframes.
97+
};
98+
99+
export type RawVectorProperty = {
100+
a: RawNumberBoolean; // Animated (0: false, 1: true)
101+
k: number[] | RawVectorKeyframe[]; // When it's not animated, k will contain the value directly. When animated, k will be an array of keyframes.
102+
l: number; // Number of components in the value arrays. If present values will be truncated or expanded to match this length when accessed from expressions
103+
};
104+
105+
export type RawVectorKeyframe = {
106+
t: number; // Time, Frame number
107+
s: number[]; // Value at this keyframe
108+
h?: RawNumberBoolean; // Hold flag (0: false, 1: true)
109+
i?: RawKeyFrameEasing; // In tangent, easing tangent going into the next keyframe
110+
o?: RawKeyFrameEasing; // Out tanget, easing tangent leaving the current keyframe
111+
};
112+
113+
export type RawPositionProperty = {
114+
a: RawNumberBoolean; // Animated (0: false, 1: true)
115+
k: number[] | RawPositionKeyframe[]; // When it's not animated, k will contain the value directly. When animated, k will be an array of keyframes.
116+
l: number; // Number of components in the value arrays. If present values will be truncated or expanded to match this length when accessed from expressions
117+
};
118+
119+
export type RawPositionKeyframe = {
120+
t: number; // Time, Frame number
121+
h?: RawNumberBoolean; // Hold flag (0: false, 1: true)
122+
i?: RawKeyFrameEasing; // In tangent, easing tangent going into the next keyframe
123+
o?: RawKeyFrameEasing; // Out tanget, easing tangent leaving the current keyframe
124+
s: number[]; // Value at this keyframe
125+
ti: number[]; // Value In Tangent, tangent for values (eg: moving position around a curved path)
126+
to: number[]; // Value Out Tangent, tangent for values (eg: moving position around a curved path)
127+
};
128+
129+
export type RawBezierShapeProperty = {
130+
a: RawNumberBoolean; // Animated (0: false, 1: true)
131+
k: RawBezier | RawBezierShapeKeyframe[]; // When it's not animated, k will contain the value directly. When animated, k will be an array of keyframes
132+
};
133+
134+
export type RawBezierShapeKeyframe = {
135+
t: number; // Time, Frame number
136+
h?: RawNumberBoolean; // Hold flag (0: false, 1: true)
137+
i?: RawKeyFrameEasing; // In tangent, easing tangent going into the next keyframe
138+
o?: RawKeyFrameEasing; // Out tanget, easing tangent leaving the current keyframe
139+
s: RawBezier[]; // Value at this keyframe
140+
};
141+
142+
export type RawColorProperty = {
143+
a: RawNumberBoolean; // Animated (0: false, 1: true)
144+
k: number[] | RawColorKeyframe[]; // When it's not animated, k will contain the value directly. When animated, k will be an array of keyframes
145+
};
146+
147+
export type RawColorKeyframe = {
148+
t: number; // Time, Frame number
149+
h?: RawNumberBoolean; // Hold flag (0: false, 1: true)
150+
i?: RawKeyFrameEasing; // In tangent, easing tangent going into the next keyframe
151+
o?: RawKeyFrameEasing; // Out tanget, easing tangent leaving the current keyframe
152+
s: number[]; // Value at this keyframe
153+
};
154+
155+
export type RawGradientsProperty = {
156+
p: number; // Color stop count
157+
k: RawGradientProperty;
158+
};
159+
160+
export type RawGradientProperty = {
161+
a: RawNumberBoolean; // Animated (0: false, 1: true)
162+
k: number[]; // Gradient colors array
163+
};
164+
165+
export type RawKeyFrameEasing = {
166+
x: number | number[]; // Time component: 0 means start time of the keyframe, 1 means time of the next keyframe.
167+
y: number | number[]; // Value interpolation component: 0 means start value of the keyframe, 1 means value at the next keyframe.
168+
};
169+
170+
export type RawBezier = {
171+
c: boolean; // Closed
172+
i: number[][]; // In tangents, array of points, each point is an array of coordinates. These points are along the in tangents relative to the corresponding v
173+
o: number[][]; // Out tangents, array of points, each point is an array of coordinates. These points are along the out tangents relative to the corresponding v
174+
v: number[][]; // Vertices, array of points, each point is an array of coordinates. These points are along the bezier path
175+
};
176+
177+
export type RawNumberBoolean = 0 | 1; // 0: false, 1: true;
178+
export type RawLayerType = 0 | 1 | 2 | 3 | 4 | 5 | 6; // Layer type (0: precomposition, 1: solid, 2: image, 3: null, 4: shape, 5: text, 6: audio)
179+
export type RawShapeType = "fl" | "gf" | "gr" | "tr" | "sh" | "rc"; // Shape type (fl: fill, gf: gradient fill, gr: group, tr: transform, sh: path, rc: rectangle)
180+
export type RawShapeDirection = 1 | 3; // 1: clockwise, 3: counter-clockwise
181+
export type RawFillRule = 1 | 2; // Fill rule (1: non-zero, everything is colored (You can think of this as an OR), 2: even-odd, colored based on intersections and path direction, can be used to create "holes")
182+
export type RawGradientType = 1 | 2; // Gradient type (1: linear, 2: radial)

0 commit comments

Comments
 (0)