How to Turn Lottie Animations into CSS and React Native Code
This article explains the challenges of converting designers' Lottie animation descriptions into reusable front‑end code, compares frame‑by‑frame, keyframe, and CSS variable approaches, and provides practical examples for generating CSS, CSS‑variables, and React Native Animated implementations.
1. Project Background
Front‑end developers often receive animation specifications from designers in various formats—natural language, timeline data, or AE‑exported JSON. Manually translating these descriptions into CSS, React Native Animated code is repetitive, time‑consuming, and mentally taxing.
2. Lottie Format Overview
Lottie stores animation data as JSON. The root object contains global settings (width, height, frame rate, etc.) and two crucial arrays:
layers : each entry describes a single AE layer, including its type, transform keyframes ( ks ), timing ( ip , op ), and optional parent relationships.
assets : reusable resources such as images or pre‑compositions referenced by layers.
Keyframe objects inside ks define start values ( s ), end values ( e ), frame index ( t ) and cubic‑bezier control points for timing and path interpolation.
3. Exporting Lottie to Code
Three main strategies are discussed:
3.1 Frame‑by‑frame CSS
Record the transform and opacity of every animation frame and emit a dense @keyframes rule. While it reproduces the visual effect accurately, the generated CSS is large and hard to maintain.
<code>// Example of a dense keyframe block
.hash5edafe06{transform-origin:50% 50%;animation:hash5edafe06_kf 0.667s linear;}
@keyframes hash5edafe06_kf{0%{opacity:1;transform:matrix3d(...);}…100%{opacity:0.01;transform:matrix3d(...);}}</code>3.2 Semantic Keyframe CSS
Sample the animation at 5‑10 frames per second, keep the original easing curves, and generate concise keyframes. This reduces file size and improves readability.
<code>.hash5edafe06_0{animation:hash5edafe06_0_keyframe_0 0.4s 0.267s cubic-bezier(0.333,0,0.667,1) forwards;}
@keyframes hash5edafe06_0_keyframe_0{0%{transform:scale(1,1);}66.667%{opacity:1;}100%{opacity:0.01;transform:scale(0.15,0.15);}}</code>3.3 CSS Variable Approach
Using the experimental @property rule, custom properties (e.g., --translateX , --scaleX ) are animated separately, then composed in a single transform . This decouples transform components, allowing multiple animations on the same element without DOM nesting.
<code>@property --translateX {syntax:'<number>';inherits:false;initial-value:0;}
@keyframes translateX_anim{0%{--translateX:0;}100%{--translateX:-162.4;}}
.element{transform:translateX(calc(1px*var(--translateX))) scaleX(var(--scaleX));animation:translateX_anim 0.4s forwards;}</code>Pros: smaller CSS, better readability. Cons: limited browser support because @property is still experimental.
4. React Native Animated Code Generation
React Native’s Animated API maps Lottie’s independent transform channels directly, preserving per‑segment easing curves. The article provides a hook that creates Animated.Value objects for opacity, translation, and scale, then composes them with Animated.timing and Animated.parallel .
<code>function useLayerAnimated(){
const opacityVal = useRef(new Animated.Value(1)).current;
const translateXVal = useRef(new Animated.Value(0)).current;
const translateYVal = useRef(new Animated.Value(0)).current;
const scaleXVal = useRef(new Animated.Value(1)).current;
const scaleYVal = useRef(new Animated.Value(1)).current;
const getCompositeAnimation = useCallback(()=>{
const opacityAnim = Animated.timing(opacityVal,{toValue:0.01,duration:133.333,delay:533.333,easing:Easing.bezier(0.333,0,0.667,1),useNativeDriver:true});
const translateXAnim = Animated.timing(translateXVal,{toValue:-162.4,duration:400,delay:266.667,easing:Easing.bezier(0.869,0.774,0.874,0.951),useNativeDriver:true});
const translateYAnim = Animated.timing(translateYVal,{toValue:303.456,duration:400,delay:266.667,easing:Easing.bezier(0.869,-0.64,0.874,0.445),useNativeDriver:true});
const scaleXAnim = Animated.timing(scaleXVal,{toValue:0.15,duration:400,delay:266.667,easing:Easing.bezier(0.333,0,0.667,1),useNativeDriver:true});
const scaleYAnim = Animated.timing(scaleYVal,{toValue:0.15,duration:400,delay:266.667,easing:Easing.bezier(0.333,0,0.667,1),useNativeDriver:true});
return Animated.parallel([opacityAnim,translateXAnim,translateYAnim,scaleXAnim,scaleYAnim]);
},[]);
const style = {transform:[{translateX:translateXVal},{translateY:translateYVal},{scaleX:scaleXVal},{scaleY:scaleYVal}],opacity:opacityVal};
return {animatedStyle:style,resetAnim:()=>{opacityVal.setValue(1);translateXVal.setValue(0);translateYVal.setValue(0);scaleXVal.setValue(1);scaleYVal.setValue(1);},getAnim:getCompositeAnimation};
}</code>5. Platform Integration
The conversion pipeline has been integrated into the internal Vision animation platform, providing a one‑click export of CSS, CSS‑variable, and React Native Animated code for any Lottie asset. The article links to a detailed usage guide and teases a forthcoming deep‑dive on sequence‑frame format conversion.
3.2 Summary
For short, simple animations, the frame‑by‑frame method works as a quick fallback. For most production use cases, the semantic keyframe approach offers a good balance of size and maintainability. The CSS‑variable technique yields the smallest and most flexible output but requires modern browser support. React Native Animated provides a direct mapping with full easing control.
Kuaishou Frontend Engineering
Explore the cutting‑edge tech behind Kuaishou's front‑end ecosystem
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.