Skip to content

Commit 56f986c

Browse files
authored
[LottieGen] Added new AnimationController API support for LottieGen (#488)
1 parent a3ee250 commit 56f986c

19 files changed

+283
-55
lines changed

Lottie-Windows/Lottie-Windows-Uwp/Lottie-Windows-Uwp.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
<PropertyGroup>
44
<TargetFramework>uap10.0.16299</TargetFramework>
55
<OutputType>winmdobj</OutputType>
6-
6+
77

88
<PackageId>CommunityToolkit.Uwp.Lottie</PackageId>
99
<PackageTags>UWP Toolkit Windows Animations Lottie XAML</PackageTags>

build/Install-WindowsSdkISO.ps1

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -242,6 +242,11 @@ if ($InstallWindowsSDK)
242242
# Note: there is a delay from Windows SDK announcements to availability via the static link
243243
$uri = "https://go.microsoft.com/fwlink/?prd=11966&pver=1.0&plcid=0x409&clcid=0x409&ar=Flight&sar=Sdsurl&o1=$buildNumber"
244244

245+
if ($buildNumber -eq "22621")
246+
{
247+
$uri = "https://go.microsoft.com/fwlink/p/?linkid=2196240"
248+
}
249+
245250
if ($env:TEMP -eq $null)
246251
{
247252
$env:TEMP = Join-Path $env:SystemDrive 'temp'

source/Lottie/AnimatedVisualFactory.cs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ sealed class AnimatedVisualFactory
3333
Loader? _loader;
3434
WinCompData.Visual? _wincompDataRootVisual;
3535
WinCompData.CompositionPropertySet? _wincompDataThemingPropertySet;
36+
IEnumerable<WinCompData.AnimationController>? _wincompDataAnimationControllers;
3637
CompositionPropertySet? _themingPropertySet;
3738
double _width;
3839
double _height;
@@ -62,6 +63,7 @@ internal void SetRootVisual(WinCompData.Visual rootVisual)
6263
CompositionObjectNodes.
6364
Where(n => n.Object is WinCompData.CompositionPropertySet cps && cps.Owner is null).
6465
Select(n => (WinCompData.CompositionPropertySet)n.Object).FirstOrDefault();
66+
_wincompDataAnimationControllers = graph.CompositionObjectNodes.Where(n => (n.Object is WinCompData.AnimationController) && ((WinCompData.AnimationController)n.Object).IsCustom).Select(n => (WinCompData.AnimationController)n.Object);
6567
}
6668

6769
internal bool CanInstantiate => _wincompDataRootVisual is not null;
@@ -84,7 +86,10 @@ internal void SetRootVisual(WinCompData.Visual rootVisual)
8486
var instantiator = new Instantiator(compositor, surfaceResolver: LoadImageFromUri);
8587

8688
// _wincompDataRootVisual is not null is implied by CanInstantiate.
87-
var result = new DisposableAnimatedVisual((Visual)instantiator.GetInstance(_wincompDataRootVisual!))
89+
Visual rootVisual = (Visual)instantiator.GetInstance(_wincompDataRootVisual!);
90+
IEnumerable<AnimationController> animationControllers = _wincompDataAnimationControllers!.Select(o => (AnimationController)instantiator.GetInstance(o));
91+
92+
var result = new DisposableAnimatedVisual(rootVisual, animationControllers)
8893
{
8994
Size = new System.Numerics.Vector2((float)_width, (float)_height),
9095
Duration = _duration,

source/Lottie/DisposableAnimatedVisual.cs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
#nullable enable
66

77
using System;
8+
using System.Collections.Generic;
89
using System.Numerics;
910
using Microsoft.UI.Xaml.Controls;
1011

@@ -18,13 +19,20 @@ namespace CommunityToolkit.WinUI.Lottie
1819
{
1920
sealed class DisposableAnimatedVisual : IAnimatedVisual, IDisposable
2021
{
21-
internal DisposableAnimatedVisual(Visual rootVisual)
22+
internal DisposableAnimatedVisual(Visual rootVisual, IEnumerable<AnimationController> customAnimationControllers)
2223
{
2324
RootVisual = rootVisual;
25+
CustomAnimationControllers = customAnimationControllers;
2426
}
2527

2628
public Visual RootVisual { get; }
2729

30+
/// <summary>
31+
/// Keeps references to all custom AnimationController objects.
32+
/// We need references because otherwise they will be destroyed from dwm.
33+
/// </summary>
34+
public IEnumerable<AnimationController> CustomAnimationControllers { get; }
35+
2836
public TimeSpan Duration { get; set; }
2937

3038
public Vector2 Size { get; set; }

source/Lottie/Instantiator.cs

Lines changed: 35 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -402,16 +402,27 @@ void StartAnimations(Wd.CompositionObject source, Wc.CompositionObject target)
402402
foreach (var animator in source.Animators)
403403
{
404404
var animation = GetCompositionAnimation(animator.Animation);
405-
target.StartAnimation(animator.AnimatedProperty, animation);
406-
var controller = animator.Controller;
407-
if (controller is not null)
405+
if (animator.Controller is null || !animator.Controller.IsCustom)
408406
{
409-
var animationController = GetAnimationController(controller);
410-
if (controller.IsPaused)
407+
target.StartAnimation(animator.AnimatedProperty, animation);
408+
var controller = animator.Controller;
409+
if (controller is not null)
411410
{
412-
animationController.Pause();
411+
var animationController = GetAnimationController(controller);
412+
if (controller.IsPaused)
413+
{
414+
animationController.Pause();
415+
}
413416
}
414417
}
418+
else
419+
{
420+
throw new InvalidOperationException("LottieViewer and Instantiator does not support custom AnimationControllers yet");
421+
/*
422+
We should retarget to SDK 22621 to support this
423+
target.StartAnimation(animator.AnimatedProperty, animation, GetAnimationController(animator.Controller));
424+
*/
425+
}
415426
}
416427
}
417428

@@ -422,9 +433,25 @@ Wc.AnimationController GetAnimationController(Wd.AnimationController obj)
422433
return result;
423434
}
424435

425-
var targetObject = GetCompositionObject(obj.TargetObject);
436+
if (obj.IsCustom)
437+
{
438+
throw new InvalidOperationException("LottieViewer and Instantiator does not support custom AnimationControllers yet");
439+
/*
440+
We should retarget to SDK 22621 to support this
441+
result = CacheAndInitializeCompositionObject(obj, _c.CreateAnimationController());
442+
443+
if (obj.IsPaused)
444+
{
445+
result.Pause();
446+
}
447+
*/
448+
}
449+
else
450+
{
451+
var targetObject = GetCompositionObject(obj.TargetObject!);
452+
result = CacheAndInitializeCompositionObject(obj, targetObject.TryGetAnimationController(obj.TargetProperty));
453+
}
426454

427-
result = CacheAndInitializeCompositionObject(obj, targetObject.TryGetAnimationController(obj.TargetProperty));
428455
StartAnimations(obj, result);
429456
return result;
430457
}

source/Lottie/Loader.cs

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -219,17 +219,20 @@ static uint GetCurrentUapVersion()
219219
{
220220
// Start testing on version 2. We know that at least version 1 is supported because
221221
// we are running in UAP code.
222-
var versionToTest = 2u;
222+
var versionToTest = 1u;
223223

224224
// Keep querying until IsApiContractPresent fails to find the version.
225-
while (ApiInformation.IsApiContractPresent("Windows.Foundation.UniversalApiContract", (ushort)versionToTest))
225+
while (ApiInformation.IsApiContractPresent("Windows.Foundation.UniversalApiContract", (ushort)(versionToTest + 1)))
226226
{
227227
// Keep looking ...
228228
versionToTest++;
229229
}
230230

231+
// TODO: we do not support UAP above 14 in Lottie-Windows yet, only in LottieGen.
232+
versionToTest = Math.Min(versionToTest, 14);
233+
231234
// Query failed on versionToTest. Return the previous version.
232-
return versionToTest - 1;
235+
return versionToTest;
233236
}
234237

235238
// Specializes the Stopwatch to do just the one thing we need of it - get the time

source/Lottie/LottieVisualSource.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
using System;
88
using System.Collections.Generic;
9+
using System.Linq;
910
using System.Runtime.InteropServices.WindowsRuntime;
1011
using System.Threading.Tasks;
1112
using CommunityToolkit.WinUI.Lottie;

source/LottieToWinComp/Animate.cs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,13 @@ public static void WithKeyFrame(
4343
Debug.Assert(scale <= 1, "Precondition");
4444
Debug.Assert(animation.KeyFrameCount > 0, "Precondition");
4545

46+
if (scale == 1.0 && offset == 0.0 && context.ObjectFactory.IsUapApiAvailable(nameof(AnimationController)) && context.RootProgressController is not null)
47+
{
48+
// Special case when we can use root AnimationController (no time stretch, no time offset)
49+
compObject.StartAnimation(target, animation, context.RootProgressController!);
50+
return;
51+
}
52+
4653
var state = context.GetStateCache<StateCache>();
4754

4855
// Start the animation ...

source/LottieToWinComp/CompositionObjectFactory.cs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,12 @@ internal uint GetUapVersionForApi(string apiName)
8686
case nameof(PathKeyFrameAnimation):
8787
return 11;
8888

89+
// AnimationController class was introduced in version 6, but
90+
// it became possible to create it explicitly only after verstion 15
91+
// with compositor.CreateAnimationController() method
92+
case nameof(AnimationController):
93+
return 15;
94+
8995
default:
9096
throw new InvalidOperationException();
9197
}
@@ -304,5 +310,11 @@ void ConsumeVersionFeature(uint uapVersion)
304310

305311
HighestUapVersionUsed = Math.Max(HighestUapVersionUsed, uapVersion);
306312
}
313+
314+
internal AnimationController CreateAnimationControllerList()
315+
{
316+
ConsumeVersionFeature(GetUapVersionForApi(nameof(AnimationController)));
317+
return _compositor.CreateAnimationController();
318+
}
307319
}
308320
}

source/LottieToWinComp/TranslationContext.cs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,11 @@ internal static TranslationResult TryTranslateLottieComposition(
150150
/// </summary>
151151
public ContainerVisual? RootVisual { get; private set; }
152152

153+
/// <summary>
154+
/// AnimationController that has Progress property bound to RootVisual.Progress property.
155+
/// </summary>
156+
public AnimationController? RootProgressController { get; private set; }
157+
153158
/// <summary>
154159
/// True iff theme property bindings are enabled.
155160
/// </summary>
@@ -223,6 +228,16 @@ void Translate()
223228
// Add the master progress property to the visual.
224229
RootVisual.Properties.InsertScalar(ProgressPropertyName, 0);
225230

231+
// AnimationController that has Progress value bound to RootVisual.Progress
232+
if (ObjectFactory.IsUapApiAvailable(nameof(AnimationController)))
233+
{
234+
RootProgressController = ObjectFactory.CreateAnimationControllerList();
235+
var rootProgressAnimation = context.ObjectFactory.CreateExpressionAnimation(ExpressionFactory.RootProgress);
236+
rootProgressAnimation.SetReferenceParameter(ExpressionFactory.RootName, RootVisual);
237+
RootProgressController.Pause();
238+
RootProgressController.StartAnimation("Progress", rootProgressAnimation);
239+
}
240+
226241
// Add the translations of each layer to the root visual. This will recursively
227242
// add the tranlation of the layers in precomps.
228243
var contentsChildren = RootVisual.Children;

0 commit comments

Comments
 (0)