Skip to content

Commit 3c1e744

Browse files
authored
[LottieGen] Added workaround to make path animations playable even with different number of segments in keyframes (#495)
1 parent 109463c commit 3c1e744

File tree

5 files changed

+54
-1
lines changed

5 files changed

+54
-1
lines changed

Lottie-Windows.sln

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Issues", "Issues", "{07D6DF
133133
source\Issues\LT0042.md = source\Issues\LT0042.md
134134
source\Issues\LT0043.md = source\Issues\LT0043.md
135135
source\Issues\LT0044.md = source\Issues\LT0044.md
136+
source\Issues\LT0045.md = source\Issues\LT0045.md
136137
source\Issues\LV0001.md = source\Issues\LV0001.md
137138
source\Issues\LV0002.md = source\Issues\LV0002.md
138139
source\Issues\LV0003.md = source\Issues\LV0003.md

LottieGen/LottieGen.sln

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Issues", "Issues", "{BDB88D
8888
..\source\Issues\LT0042.md = ..\source\Issues\LT0042.md
8989
..\source\Issues\LT0043.md = ..\source\Issues\LT0043.md
9090
..\source\Issues\LT0044.md = ..\source\Issues\LT0044.md
91+
..\source\Issues\LT0045.md = ..\source\Issues\LT0045.md
9192
..\source\Issues\LV0001.md = ..\source\Issues\LV0001.md
9293
..\source\Issues\LV0002.md = ..\source\Issues\LV0002.md
9394
..\source\Issues\LV0003.md = ..\source\Issues\LV0003.md

source/Issues/LT0045.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
[comment]: # (name:PathAnimationHasDifferentNumberOfSegments)
2+
[comment]: # (text:Path animation has different number of segments.)
3+
4+
# Lottie-Windows Warning LT0045
5+
6+
One of the path animations has different number of segments for different keyframes. Windows Composition API
7+
can't animate in this case, so Lottie-Windows added zero-length segments to some keyframs to make number of segments
8+
the same among all keyframes.
9+
10+
## Resources
11+
12+
* [Lottie-Windows repository](https://aka.ms/lottie)
13+
* [Questions and feedback via Github](https://github.com/windows-toolkit/Lottie-Windows/issues)

source/LottieToWinComp/Paths.cs

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ public static CompositionSpriteShape TranslatePath(
4545
else if (context.ObjectFactory.IsUapApiAvailable(nameof(PathKeyFrameAnimation), versionDependentFeatureDescription: "Path animation"))
4646
{
4747
// PathKeyFrameAnimation was introduced in 6 but was unreliable until 11.
48-
Animate.Path(context, path, fillType, geometry, nameof(geometry.Path), nameof(geometry.Path));
48+
Animate.Path(context, PopulatePathGeometriesToTheSameSize(path), fillType, geometry, nameof(geometry.Path), nameof(geometry.Path));
4949
isPathApplied = true;
5050
}
5151
}
@@ -63,6 +63,42 @@ public static CompositionSpriteShape TranslatePath(
6363
return result;
6464
}
6565

66+
static TrimmedAnimatable<PathGeometry> PopulatePathGeometriesToTheSameSize(TrimmedAnimatable<PathGeometry> original)
67+
{
68+
int maxSegments = Math.Max(original.KeyFrames.Select(x => x.Value.BezierSegments.Count).Max(), original.InitialValue.BezierSegments.Count);
69+
int minSegments = Math.Min(original.KeyFrames.Select(x => x.Value.BezierSegments.Count).Min(), original.InitialValue.BezierSegments.Count);
70+
71+
// If all PathGeometry objects have the same number of segments -> Composition API can animate this.
72+
if (minSegments == maxSegments)
73+
{
74+
return original;
75+
}
76+
77+
original.Context.Issues.PathAnimationHasDifferentNumberOfSegments();
78+
79+
Func<PathGeometry, PathGeometry> populatePathGeometry = (PathGeometry pathGeometry) => {
80+
List<BezierSegment> segments = pathGeometry.BezierSegments.ToList();
81+
82+
while (segments.Count < maxSegments)
83+
{
84+
var p = segments.Last().ControlPoint1;
85+
segments.Add(new BezierSegment(p, p, p, p));
86+
}
87+
88+
return new PathGeometry(new Sequence<BezierSegment>(segments.AsEnumerable()), pathGeometry.IsClosed);
89+
};
90+
91+
// If some PathGeometry ogjects have different number of segments -> Composition API can't animate this,
92+
// we are using a workaround: populate path geometries to the same size, with the 0-length segments at the end.
93+
var newKeyFrames = new List<KeyFrame<PathGeometry>>();
94+
foreach (var keyframe in original.KeyFrames)
95+
{
96+
newKeyFrames.Add(new KeyFrame<PathGeometry>(keyframe.Frame, populatePathGeometry(keyframe.Value), keyframe.SpatialBezier, keyframe.Easing));
97+
}
98+
99+
return new TrimmedAnimatable<PathGeometry>(original.Context, populatePathGeometry(original.InitialValue), newKeyFrames);
100+
}
101+
66102
// If the given path is equivalent to a static path with an animated offset, convert
67103
// the path to that form and apply it to the given geometry and shape.
68104
static bool TryApplyPathAsStaticPathWithAnimatedOffset(

source/LottieToWinComp/TranslationIssues.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,8 @@ internal TranslationIssues(bool throwOnIssue)
126126

127127
internal void MatteLayerIsNeverVisible() => Report("LT0044", "One of the matte layers is never visible. It may be unintentional.");
128128

129+
internal void PathAnimationHasDifferentNumberOfSegments() => Report("LT0045", "Path animation has different number of segments for different keyframes.");
130+
129131
void Report(string code, string description)
130132
{
131133
_issues.Add((code, description));

0 commit comments

Comments
 (0)