I love the new addition of Figma Motion and it’s exactly what I’ve been looking for. In particular, the ability to export an animated SVG is fantastic and saves me so much time authoring animations “by hand”!
Problem
I do however have a problem with the exported files. When I set up my animation in Figma, everything looks great. Exporting it as SVG adds additional transform attribute to the group, which (if it has animations) completely displaces it by said transform. When I remove the transform attribute from the group, the layout is fixed and resembles what I can see in Figma.
I suppose the source of the problem is somewhere in the code that parses the animation set up in Figma and converts it to the SVG animation keyframes. As the keyframes already specify the position / translation, the additional transform on the group moves the entire group an breaks the offsets specified by the keyframes.
Example
My example uses two rectangles with rounded corners, both of which are within a mask group (alpha) that provides the shared gradient fill. The entire thing is overlayed on top of device frame mockup.




<svg xmlns="http://www.w3.org/2000/svg" width="640" height="480" viewBox="0 0 640 480" fill="none">
<style>
@keyframes kf_Button_transform_0 {
0% {
transform: translateX(17px) translateY(259.009px) translateX(0px) translateY(0px) translate(84px, 13.075px) scaleX(0) scaleY(0) translate(-84px, -13.075px);
}
1.67% {
transform: translateX(17px) translateY(259.009px) translateX(0px) translateY(0px) translate(84px, 13.075px) scaleX(0) scaleY(0) translate(-84px, -13.075px);
}
3.33% {
transform: translateX(17px) translateY(259.009px) translateX(0px) translateY(0px) translate(84px, 13.075px) scaleX(0) scaleY(0) translate(-84px, -13.075px);
}
5% {
transform: translateX(17px) translateY(259.009px) translateX(0px) translateY(0px) translate(84px, 13.075px) scaleX(0) scaleY(0) translate(-84px, -13.075px);
}
6.67% {
transform: translateX(17px) translateY(259.009px) translateX(0px) translateY(0px) translate(84px, 13.075px) scaleX(0) scaleY(0) translate(-84px, -13.075px);
}
8.33% {
transform: translateX(17px) translateY(259.009px) translateX(0px) translateY(0px) translate(84px, 13.075px) scaleX(0) scaleY(0) translate(-84px, -13.075px);
}
10% {
transform: translateX(17px) translateY(259.009px) translateX(0px) translateY(0px) translate(84px, 13.075px) scaleX(0) scaleY(0) translate(-84px, -13.075px);
}
11.67% {
transform: translateX(17px) translateY(259.009px) translateX(0px) translateY(0px) translate(84px, 13.075px) scaleX(0) scaleY(0) translate(-84px, -13.075px);
}
13.33% {
transform: translateX(17px) translateY(259.009px) translateX(0px) translateY(0px) translate(84px, 13.075px) scaleX(0) scaleY(0) translate(-84px, -13.075px);
}
15% {
transform: translateX(17px) translateY(259.009px) translateX(0px) translateY(0px) translate(84px, 13.075px) scaleX(0) scaleY(0) translate(-84px, -13.075px);
}
16.67% {
transform: translateX(17px) translateY(259.009px) translateX(0px) translateY(0px) translate(84px, 13.075px) scaleX(0) scaleY(0) translate(-84px, -13.075px);
}
18.33% {
transform: translateX(17px) translateY(259.009px) translateX(0px) translateY(0px) translate(84px, 13.075px) scaleX(0) scaleY(0) translate(-84px, -13.075px);
}
20% {
transform: translateX(17px) translateY(259.009px) translateX(-0.001px) translateY(0px) translate(84px, 13.075px) scaleX(0) scaleY(0) translate(-84px, -13.075px);
}
21.67% {
transform: translateX(17px) translateY(259.009px) translateX(-0.001px) translateY(0px) translate(84px, 13.075px) scaleX(0) scaleY(0) translate(-84px, -13.075px);
}
23.33% {
transform: translateX(17px) translateY(259.009px) translateX(-0.001px) translateY(0px) translate(84px, 13.075px) scaleX(0) scaleY(0) translate(-84px, -13.075px);
}
25% {
transform: translateX(17px) translateY(259.009px) translateX(-0.001px) translateY(0px) translate(84px, 13.075px) scaleX(0) scaleY(0) translate(-84px, -13.075px);
}
26.67% {
transform: translateX(17px) translateY(259.009px) translateX(-0.001px) translateY(0px) translate(84px, 13.075px) scaleX(0) scaleY(0) translate(-84px, -13.075px);
}
28.33% {
transform: translateX(17px) translateY(259.009px) translateX(-0.001px) translateY(0px) translate(84px, 13.075px) scaleX(0) scaleY(0) translate(-84px, -13.075px);
}
30% {
transform: translateX(17px) translateY(259.009px) translateX(-0.001px) translateY(0px) translate(84px, 13.075px) scaleX(0) scaleY(0) translate(-84px, -13.075px);
}
31.67% {
transform: translateX(17px) translateY(259.009px) translateX(-0.001px) translateY(0px) translate(84px, 13.075px) scaleX(0) scaleY(0) translate(-84px, -13.075px);
}
33.33% {
transform: translateX(17px) translateY(259.009px) translateX(-0.001px) translateY(0px) translate(84px, 13.075px) scaleX(0) scaleY(0) translate(-84px, -13.075px);
}
35% {
transform: translateX(17px) translateY(259.009px) translateX(-0.001px) translateY(0px) translate(84px, 13.075px) scaleX(0) scaleY(0) translate(-84px, -13.075px);
}
36.67% {
transform: translateX(17px) translateY(259.009px) translateX(-0.001px) translateY(0px) translate(84px, 13.075px) scaleX(0) scaleY(0) translate(-84px, -13.075px);
}
38.33% {
transform: translateX(17px) translateY(259.009px) translateX(-0.001px) translateY(0px) translate(84px, 13.075px) scaleX(0) scaleY(0) translate(-84px, -13.075px);
}
40% {
transform: translateX(17px) translateY(259.009px) translateX(-0.001px) translateY(0px) translate(84px, 13.075px) scaleX(0) scaleY(0) translate(-84px, -13.075px);
}
41.67% {
transform: translateX(17px) translateY(259.009px) translateX(-0.001px) translateY(0px) translate(84px, 13.075px) scaleX(0) scaleY(0) translate(-84px, -13.075px);
}
43.33% {
transform: translateX(17px) translateY(259.009px) translateX(-0.001px) translateY(0px) translate(84px, 13.075px) scaleX(0) scaleY(0) translate(-84px, -13.075px);
}
45% {
transform: translateX(17px) translateY(259.009px) translateX(-0.001px) translateY(0px) translate(84px, 13.075px) scaleX(0) scaleY(0) translate(-84px, -13.075px);
}
46.67% {
transform: translateX(17px) translateY(259.009px) translateX(-0.001px) translateY(0px) translate(84px, 13.075px) scaleX(0) scaleY(0) translate(-84px, -13.075px);
}
48.33% {
transform: translateX(17px) translateY(259.009px) translateX(-0.001px) translateY(0px) translate(84px, 13.075px) scaleX(0) scaleY(0) translate(-84px, -13.075px);
}
50% {
transform: translateX(17px) translateY(259.009px) translateX(-0.001px) translateY(0px) translate(84px, 13.075px) scaleX(0) scaleY(0) translate(-84px, -13.075px);
}
51.67% {
transform: translateX(17px) translateY(259.009px) translateX(-0.001px) translateY(0px) translate(84px, 13.075px) scaleX(0) scaleY(0) translate(-84px, -13.075px);
}
53.33% {
transform: translateX(17px) translateY(259.009px) translateX(-0.001px) translateY(0px) translate(84px, 13.075px) scaleX(0) scaleY(0) translate(-84px, -13.075px);
}
55% {
transform: translateX(17px) translateY(259.009px) translateX(-0.001px) translateY(0px) translate(84px, 13.075px) scaleX(0) scaleY(0) translate(-84px, -13.075px);
}
56.67% {
transform: translateX(17px) translateY(259.009px) translateX(-0.001px) translateY(0px) translate(84px, 13.075px) scaleX(0) scaleY(0) translate(-84px, -13.075px);
}
58.33% {
transform: translateX(17px) translateY(259.009px) translateX(-0.001px) translateY(0px) translate(84px, 13.075px) scaleX(0) scaleY(0) translate(-84px, -13.075px);
}
60% {
transform: translateX(17px) translateY(259.009px) translateX(-0.002px) translateY(0px) translate(84px, 13.075px) scaleX(0) scaleY(0) translate(-84px, -13.075px);
}
61.67% {
transform: translateX(17px) translateY(259.009px) translateX(-0.002px) translateY(0px) translate(84px, 13.075px) scaleX(0) scaleY(0) translate(-84px, -13.075px);
}
63.33% {
transform: translateX(17px) translateY(259.009px) translateX(-0.002px) translateY(0px) translate(84px, 13.075px) scaleX(0) scaleY(0) translate(-84px, -13.075px);
}
65% {
transform: translateX(17px) translateY(259.009px) translateX(-0.002px) translateY(0px) translate(84px, 13.075px) scaleX(0) scaleY(0) translate(-84px, -13.075px);
}
66.67% {
transform: translateX(17px) translateY(259.009px) translateX(-0.002px) translateY(0px) translate(84px, 13.075px) scaleX(0) scaleY(0) translate(-84px, -13.075px);
}
68.33% {
transform: translateX(17px) translateY(259.009px) translateX(-0.002px) translateY(0px) translate(84px, 13.075px) scaleX(0) scaleY(0) translate(-84px, -13.075px);
}
70% {
transform: translateX(17px) translateY(259.009px) translateX(-0.002px) translateY(0px) translate(84px, 13.075px) scaleX(0) scaleY(0) translate(-84px, -13.075px);
}
71.67% {
transform: translateX(17px) translateY(259.009px) translateX(-0.002px) translateY(0px) translate(84px, 13.075px) scaleX(0) scaleY(0) translate(-84px, -13.075px);
}
73.33% {
transform: translateX(17px) translateY(259.009px) translateX(-0.002px) translateY(0px) translate(84px, 13.075px) scaleX(0) scaleY(0) translate(-84px, -13.075px);
}
75% {
transform: translateX(17px) translateY(259.009px) translateX(-0.002px) translateY(0px) translate(84px, 13.075px) scaleX(0) scaleY(0) translate(-84px, -13.075px);
}
75.27% {
transform: translateX(17px) translateY(259.009px) translateX(-0.002px) translateY(0px) translate(84px, 13.075px) scaleX(0) scaleY(0) translate(-84px, -13.075px);
}
76.67% {
transform: translateX(17px) translateY(259.009px) translateX(-0.002px) translateY(0px) translate(84px, 13.075px) scaleX(1.07) scaleY(1.07) translate(-84px, -13.075px);
}
78.33% {
transform: translateX(17px) translateY(259.009px) translateX(-0.002px) translateY(0px) translate(84px, 13.075px) scaleX(0.995) scaleY(0.995) translate(-84px, -13.075px);
}
79.57% {
transform: translateX(17px) translateY(259.009px) translateX(-0.002px) translateY(0px) translate(84px, 13.075px) scaleX(1) scaleY(1) translate(-84px, -13.075px);
}
80% {
transform: translateX(17px) translateY(259.009px) translateX(-0.002px) translateY(0px) translate(84px, 13.075px) scaleX(1) scaleY(1) translate(-84px, -13.075px);
}
81.67% {
transform: translateX(17px) translateY(259.009px) translateX(-0.002px) translateY(0px) translate(84px, 13.075px) scaleX(1) scaleY(1) translate(-84px, -13.075px);
}
83.33% {
transform: translateX(17px) translateY(259.009px) translateX(-0.002px) translateY(0px) translate(84px, 13.075px) scaleX(1) scaleY(1) translate(-84px, -13.075px);
}
85% {
transform: translateX(17px) translateY(259.009px) translateX(-0.002px) translateY(0px) translate(84px, 13.075px) scaleX(1) scaleY(1) translate(-84px, -13.075px);
}
86.67% {
transform: translateX(17px) translateY(259.009px) translateX(-0.002px) translateY(0px) translate(84px, 13.075px) scaleX(1) scaleY(1) translate(-84px, -13.075px);
}
88.33% {
transform: translateX(17px) translateY(259.009px) translateX(-0.002px) translateY(0px) translate(84px, 13.075px) scaleX(1) scaleY(1) translate(-84px, -13.075px);
}
90% {
transform: translateX(17px) translateY(259.009px) translateX(-0.002px) translateY(0px) translate(84px, 13.075px) scaleX(1) scaleY(1) translate(-84px, -13.075px);
}
90.32% {
transform: translateX(17px) translateY(259.009px) translateX(-0.002px) translateY(0px) translate(84px, 13.075px) scaleX(1) scaleY(1) translate(-84px, -13.075px);
}
91.67% {
transform: translateX(17px) translateY(259.009px) translateX(-0.002px) translateY(-72.346px) translate(84px, 13.075px) scaleX(1) scaleY(1) translate(-84px, -13.075px);
}
93.33% {
transform: translateX(17px) translateY(259.009px) translateX(-0.002px) translateY(-161.304px) translate(84px, 13.075px) scaleX(1) scaleY(1) translate(-84px, -13.075px);
}
95% {
transform: translateX(17px) translateY(259.009px) translateX(-0.002px) translateY(-250.798px) translate(84px, 13.075px) scaleX(1) scaleY(1) translate(-84px, -13.075px);
}
95.7% {
transform: translateX(17px) translateY(259.009px) translateX(-0.002px) translateY(-288.311px) translate(84px, 13.075px) scaleX(1) scaleY(1) translate(-84px, -13.075px);
}
96.67% {
transform: translateX(17px) translateY(259.009px) translateX(-0.002px) translateY(-288.311px) translate(84px, 13.075px) scaleX(1) scaleY(1) translate(-84px, -13.075px);
}
98.33% {
transform: translateX(17px) translateY(259.009px) translateX(-0.002px) translateY(-288.311px) translate(84px, 13.075px) scaleX(1) scaleY(1) translate(-84px, -13.075px);
}
100% {
transform: translateX(17px) translateY(259.009px) translateX(-0.002px) translateY(-288.311px) translate(84px, 13.075px) scaleX(1) scaleY(1) translate(-84px, -13.075px);
}
}
@keyframes kf_Button_opacity_0 {
0% {
animation-timing-function: linear;
opacity: 1;
}
90.32% {
animation-timing-function: cubic-bezier(0.5, 0, 0.5, 1);
opacity: 1;
}
94.62% {
animation-timing-function: linear;
opacity: 0;
}
100% {
opacity: 0;
}
}
#Button {
transform-origin: 0 0;
animation:
kf_Button_transform_0 9.3s linear infinite,
kf_Button_opacity_0 9.3s linear infinite;
}
@keyframes kf_One_tile_offset_distance_0 {
0% {
offset-distance: 0%;
}
5.376% {
offset-distance: 0%;
}
6.25% {
offset-distance: 7.353%;
}
7.5% {
offset-distance: 17.647%;
}
8.75% {
offset-distance: 27.942%;
}
9.677% {
offset-distance: 35.295%;
}
27.957% {
offset-distance: 35.295%;
}
28.75% {
offset-distance: 36.292%;
}
30% {
offset-distance: 37.842%;
}
30.108% {
offset-distance: 37.953%;
}
31.25% {
offset-distance: 41.246%;
}
32.5% {
offset-distance: 45.089%;
}
33.75% {
offset-distance: 48.931%;
}
34.409% {
offset-distance: 51.126%;
}
35% {
offset-distance: 51.801%;
}
36.25% {
offset-distance: 55.006%;
}
36.559% {
offset-distance: 55.175%;
}
45.161% {
offset-distance: 55.175%;
}
46.25% {
offset-distance: 56.14%;
}
47.5% {
offset-distance: 58.392%;
}
48.387% {
offset-distance: 59.035%;
}
58.602% {
offset-distance: 59.035%;
}
58.75% {
offset-distance: 59.196%;
}
60% {
offset-distance: 60.644%;
}
61.25% {
offset-distance: 62.252%;
}
61.828% {
offset-distance: 62.896%;
}
89.247% {
offset-distance: 62.896%;
}
90% {
offset-distance: 67.534%;
}
91.25% {
offset-distance: 76.81%;
}
92.5% {
offset-distance: 86.086%;
}
93.75% {
offset-distance: 93.816%;
}
94.624% {
offset-distance: 100%;
}
100% {
offset-distance: 100%;
}
}
@keyframes kf_One_tile_opacity_0 {
0% {
animation-timing-function: linear;
opacity: 0;
}
5.38% {
animation-timing-function: cubic-bezier(0.5, 0, 0.5, 1);
opacity: 0;
}
9.68% {
animation-timing-function: linear;
opacity: 1;
}
89.25% {
animation-timing-function: cubic-bezier(0.5, 0, 0.5, 1);
opacity: 1;
}
93.55% {
animation-timing-function: linear;
opacity: 0;
}
100% {
opacity: 0;
}
}
#One_tile {
translate: 17px 78.996px;
transform-origin: 0 0;
transform-box: view-box;
offset-path: path('M0 274.255 C0 182.838 0 91.421 0 0.004 C0 0.002 0 0.001 0 0 C2 6.587 4 13.175 6 19.762 C6 53.882 6 88.002 6 122.121 C4 132.415 2 142.709 0 153.003 C0 143.005 0 133.007 0 123.009 C0 113.009 0 103.009 0 93.009 C0 -3.094 0 -99.198 0 -195.302');
offset-rotate: 0deg;
offset-anchor: 0px 0px;
animation:
kf_One_tile_offset_distance_0 9.3s linear infinite,
kf_One_tile_opacity_0 9.3s linear infinite;
}
</style>
<g id="app_configure" clip-path="url(#clip0_23_3091)">
<g id="Device_mockup_3" transform="translate(219 29)">
<rect id="Contents_fill" width="202" height="422" rx="25" fill="white"/>
<g id="Device_contents">
<mask id="mask0_23_3091" style="mask-type:alpha" maskUnits="userSpaceOnUse" x="0" y="0" width="640" height="480">
<g id="Device_contents_2">
<rect id="Button" transform="translate(17 259.009)" width="168" height="26.1499" rx="8" fill="white"/>
<rect id="One_tile" transform="" width="168" height="75" rx="8" fill="white"/>
</g>
</mask>
<g mask="url(#mask0_23_3091)">
<rect id="Contents_fill_2" width="202" height="422" rx="25" fill="url(#paint0_linear_23_3091)"/>
</g>
</g>
<g id="Device_mockup">
<g id="device_frame">
<rect id="iphone_frame" x="3.5" y="3.5" width="195" height="415" rx="21.5" stroke="black" stroke-width="7"/>
<rect id="camera_cutout" transform="matrix(1 0 0 -1 73 31)" width="56" height="17" rx="8.5" fill="black"/>
<rect id="home_button" opacity="0.2" transform="matrix(1 0 0 -1 66 409)" width="70" height="3" rx="1.5" fill="black"/>
</g>
</g>
</g>
</g>
<defs>
<linearGradient id="paint0_linear_23_3091" x1="101" y1="0" x2="101" y2="422" gradientUnits="userSpaceOnUse">
<stop stop-opacity="0.4"/>
<stop offset="1" stop-opacity="0"/>
</linearGradient>
<clipPath id="clip0_23_3091">
<rect width="640" height="480" fill="white"/>
</clipPath>
</defs>
</svg>
