Skip to content

Commit 8b5d7bd

Browse files
authored
Add AnimationFinished event to SKLottieView (#209)
1 parent 675d177 commit 8b5d7bd

File tree

7 files changed

+67
-9
lines changed

7 files changed

+67
-9
lines changed

docs/api/ui-forms/sklottieview.md

+5-4
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,11 @@ There are several properties that can be used to control th animation playback:
2424

2525
There are a few events that can be used to be notified of animation loading events:
2626

27-
| Event | Type | Description |
28-
| :------------------- | :-------------- | :---------- |
29-
| **AnimationLoaded** | `EventHandler` | Invoked when the animation has loaded successfully. |
30-
| **AnimationFailed** | `EventHandler` | Invoked when there was an error loading the animation. |
27+
| Event | Type | Description |
28+
| :---------------------- | :-------------- | :---------- |
29+
| **AnimationLoaded** | `EventHandler` | Invoked when the animation has loaded successfully. |
30+
| **AnimationFailed** | `EventHandler` | Invoked when there was an error loading the animation. |
31+
| **AnimationCompleted** | `EventHandler` | Invoked when the animation is finished playing (after all the repeats). Infinite animations never complete so will not trigger the event. |
3132

3233
## Parts
3334

docs/api/ui-maui/sklottieview.md

+5-4
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,11 @@ There are several properties that can be used to control th animation playback:
2424

2525
There are a few events that can be used to be notified of animation loading events:
2626

27-
| Event | Type | Description |
28-
| :------------------- | :-------------- | :---------- |
29-
| **AnimationLoaded** | `EventHandler` | Invoked when the animation has loaded successfully. |
30-
| **AnimationFailed** | `EventHandler` | Invoked when there was an error loading the animation. |
27+
| Event | Type | Description |
28+
| :---------------------- | :-------------- | :---------- |
29+
| **AnimationLoaded** | `EventHandler` | Invoked when the animation has loaded successfully. |
30+
| **AnimationFailed** | `EventHandler` | Invoked when there was an error loading the animation. |
31+
| **AnimationCompleted** | `EventHandler` | Invoked when the animation is finished playing (after all the repeats). Infinite animations never complete so will not trigger the event. |
3132

3233
## Parts
3334

samples/Maui/SkiaSharpDemo/Demos/Lottie/LottiePage.xaml

+2-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,8 @@
1313
Progress="{Binding Progress}"
1414
IsAnimationEnabled="{Binding IsBusy}"
1515
AnimationFailed="OnAnimationFailed"
16-
AnimationLoaded="OnAnimationLoaded" />
16+
AnimationLoaded="OnAnimationLoaded"
17+
AnimationCompleted="OnAnimationCompleted" />
1718

1819
<BoxView Color="Green" Opacity="0.5" CornerRadius="12"
1920
WidthRequest="25" HeightRequest="24" Margin="24"

samples/Maui/SkiaSharpDemo/Demos/Lottie/LottiePage.xaml.cs

+5
Original file line numberDiff line numberDiff line change
@@ -71,4 +71,9 @@ private void OnAnimationLoaded(object sender, EventArgs e)
7171
{
7272
Debug.WriteLine("Lottie animation loaded.");
7373
}
74+
75+
private void OnAnimationCompleted(object sender, EventArgs e)
76+
{
77+
Debug.WriteLine("Lottie animation finished playing.");
78+
}
7479
}

source/SkiaSharp.Extended.UI/Controls/Lottie/SKLottieView.shared.cs

+11
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,12 @@ public SKLottieView()
5757
ResourceLoader<Themes.SKLottieViewResources>.EnsureRegistered(this);
5858

5959
IsAnimationEnabled = true;
60+
61+
#if DEBUG
62+
AnimationCompleted += (s, e) => DebugUtils.LogEvent(nameof(AnimationCompleted));
63+
AnimationFailed += (s, e) => DebugUtils.LogEvent(nameof(AnimationFailed));
64+
AnimationLoaded += (s, e) => DebugUtils.LogEvent(nameof(AnimationLoaded));
65+
#endif
6066
}
6167

6268
public SKLottieImageSource? Source
@@ -99,6 +105,8 @@ public SKLottieRepeatMode RepeatMode
99105

100106
public event EventHandler? AnimationLoaded;
101107

108+
public event EventHandler? AnimationCompleted;
109+
102110
protected override void Update(TimeSpan deltaTime)
103111
{
104112
if (animation is null)
@@ -191,6 +199,9 @@ private void UpdateProgress(TimeSpan progress)
191199
IsComplete =
192200
isFinishedRun &&
193201
repeatsCompleted >= totalRepeatCount;
202+
203+
if (IsComplete)
204+
AnimationCompleted?.Invoke(this, EventArgs.Empty);
194205
}
195206

196207
if (!IsAnimationEnabled)

source/SkiaSharp.Extended.UI/Utils/DebugUtils.shared.cs

+13
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using System.ComponentModel;
22
using System.Diagnostics;
3+
using System.Linq;
34

45
namespace SkiaSharp.Extended.UI;
56

@@ -17,4 +18,16 @@ static void OnPropertyChanged(object sender, PropertyChangedEventArgs e)
1718
Debug.WriteLine($"PropertyChanged: {sender.GetType().Name}.{e.PropertyName} = {value}");
1819
}
1920
}
21+
22+
[Conditional("DEBUG")]
23+
public static void LogMessage(string message, params object[] args)
24+
{
25+
Debug.WriteLine($"{message} [{string.Join(", ", args.Select(a => a?.ToString()))}]");
26+
}
27+
28+
[Conditional("DEBUG")]
29+
public static void LogEvent(string eventName, params object[] args)
30+
{
31+
Debug.WriteLine($"Event: {eventName}({string.Join(", ", args.Select(a => a?.ToString()))})");
32+
}
2033
}

tests/SkiaSharp.Extended.UI.Tests/Controls/Lottie/SKLottieViewTest.cs

+26
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,8 @@ public async Task UpdatesMoveProgress()
4646
// create
4747
var source = new SKFileLottieImageSource { File = TrophyJson };
4848
var lottie = new WaitingLottieView { Source = source };
49+
var animationCompleted = 0;
50+
lottie.AnimationCompleted += (s, e) => animationCompleted++;
4951
await lottie.LoadedTask;
5052

5153
// update
@@ -54,6 +56,7 @@ public async Task UpdatesMoveProgress()
5456
// test
5557
Assert.Equal(TimeSpan.FromSeconds(1), lottie.Progress);
5658
Assert.False(lottie.IsComplete);
59+
Assert.Equal(0, animationCompleted);
5760
}
5861

5962
[Fact]
@@ -62,22 +65,27 @@ public async Task MultipleUpdatesMoveProgressUntilDurationMax()
6265
// create
6366
var source = new SKFileLottieImageSource { File = TrophyJson };
6467
var lottie = new WaitingLottieView { Source = source };
68+
var animationCompleted = 0;
69+
lottie.AnimationCompleted += (s, e) => animationCompleted++;
6570
await lottie.LoadedTask;
6671

6772
// update & test
6873
lottie.CallUpdate(TimeSpan.FromSeconds(1));
6974
Assert.Equal(TimeSpan.FromSeconds(1), lottie.Progress);
7075
Assert.False(lottie.IsComplete);
76+
Assert.Equal(0, animationCompleted);
7177

7278
// update & test
7379
lottie.CallUpdate(TimeSpan.FromSeconds(1));
7480
Assert.Equal(TimeSpan.FromSeconds(2), lottie.Progress);
7581
Assert.False(lottie.IsComplete);
82+
Assert.Equal(0, animationCompleted);
7683

7784
// update & test
7885
lottie.CallUpdate(TimeSpan.FromSeconds(1));
7986
Assert.Equal(TimeSpan.FromSeconds(2.3666665), lottie.Progress);
8087
Assert.True(lottie.IsComplete);
88+
Assert.Equal(1, animationCompleted);
8189
}
8290

8391
[Fact]
@@ -86,6 +94,8 @@ public async Task UpdatesLargerThanDurationHasMax()
8694
// create
8795
var source = new SKFileLottieImageSource { File = TrophyJson };
8896
var lottie = new WaitingLottieView { Source = source };
97+
var animationCompleted = 0;
98+
lottie.AnimationCompleted += (s, e) => animationCompleted++;
8999
await lottie.LoadedTask;
90100

91101
// update
@@ -94,6 +104,7 @@ public async Task UpdatesLargerThanDurationHasMax()
94104
// test
95105
Assert.Equal(TimeSpan.FromSeconds(2.3666665), lottie.Progress);
96106
Assert.True(lottie.IsComplete);
107+
Assert.Equal(1, animationCompleted);
97108
}
98109

99110
[Fact]
@@ -102,6 +113,8 @@ public async Task NegativeUpdatesDoesNothing()
102113
// create
103114
var source = new SKFileLottieImageSource { File = TrophyJson };
104115
var lottie = new WaitingLottieView { Source = source };
116+
var animationCompleted = 0;
117+
lottie.AnimationCompleted += (s, e) => animationCompleted++;
105118
await lottie.LoadedTask;
106119

107120
// update
@@ -110,6 +123,7 @@ public async Task NegativeUpdatesDoesNothing()
110123
// test
111124
Assert.Equal(TimeSpan.Zero, lottie.Progress);
112125
Assert.False(lottie.IsComplete);
126+
Assert.Equal(0, animationCompleted);
113127
}
114128

115129
[Fact]
@@ -118,6 +132,8 @@ public async Task NegativeUpdatesAfterPositiveGoesBack()
118132
// create
119133
var source = new SKFileLottieImageSource { File = TrophyJson };
120134
var lottie = new WaitingLottieView { Source = source };
135+
var animationCompleted = 0;
136+
lottie.AnimationCompleted += (s, e) => animationCompleted++;
121137
await lottie.LoadedTask;
122138

123139
// update
@@ -128,6 +144,7 @@ public async Task NegativeUpdatesAfterPositiveGoesBack()
128144
// test
129145
Assert.Equal(TimeSpan.FromSeconds(1), lottie.Progress);
130146
Assert.False(lottie.IsComplete);
147+
Assert.Equal(0, animationCompleted);
131148
}
132149

133150
[Theory]
@@ -150,6 +167,8 @@ public async Task ReachingTheEndAndThenMoreWithRepeat(SKLottieRepeatMode repeatM
150167
// create
151168
var source = new SKFileLottieImageSource { File = TrophyJson };
152169
var lottie = new WaitingLottieView { Source = source, RepeatMode = repeatMode, RepeatCount = -1 };
170+
var animationCompleted = 0;
171+
lottie.AnimationCompleted += (s, e) => animationCompleted++;
153172
await lottie.LoadedTask;
154173

155174
// update
@@ -159,6 +178,7 @@ public async Task ReachingTheEndAndThenMoreWithRepeat(SKLottieRepeatMode repeatM
159178
// test
160179
Assert.Equal(TimeSpan.FromSeconds(progress), lottie.Progress);
161180
Assert.False(lottie.IsComplete);
181+
Assert.Equal(0, animationCompleted);
162182
}
163183

164184
[Theory]
@@ -180,6 +200,8 @@ public async Task ReachingTheEndAndThenMoreWithRepeatModeButZeroCount(SKLottieRe
180200
// create
181201
var source = new SKFileLottieImageSource { File = TrophyJson };
182202
var lottie = new WaitingLottieView { Source = source, RepeatMode = repeatMode, RepeatCount = 0 };
203+
var animationCompleted = 0;
204+
lottie.AnimationCompleted += (s, e) => animationCompleted++;
183205
await lottie.LoadedTask;
184206

185207
// update
@@ -189,5 +211,9 @@ public async Task ReachingTheEndAndThenMoreWithRepeatModeButZeroCount(SKLottieRe
189211
// test
190212
Assert.Equal(TimeSpan.FromSeconds(progress), lottie.Progress);
191213
Assert.Equal(isComplete, lottie.IsComplete);
214+
if (isComplete)
215+
Assert.Equal(1, animationCompleted);
216+
else
217+
Assert.Equal(0, animationCompleted);
192218
}
193219
}

0 commit comments

Comments
 (0)