diff --git a/src/AtomUI.Core/Animations/AnimationExtensions.cs b/src/AtomUI.Core/Animations/AnimationExtensions.cs new file mode 100644 index 000000000..132eaa534 --- /dev/null +++ b/src/AtomUI.Core/Animations/AnimationExtensions.cs @@ -0,0 +1,22 @@ +using Avalonia.Animation; + +namespace AtomUI.Animations; + +internal static class AnimationExtensions +{ + public static async Task RunInfiniteAsync(this Animation animation, Animatable control, + CancellationToken cancellationToken = default) + { + animation.IterationCount = new IterationCount(1); + try + { + while (!cancellationToken.IsCancellationRequested) + { + await animation.RunAsync(control, cancellationToken); + } + } + catch (TaskCanceledException) + { + } + } +} \ No newline at end of file diff --git a/src/AtomUI.Desktop.Controls/Skeleton/AbstractSkeleton.cs b/src/AtomUI.Desktop.Controls/Skeleton/AbstractSkeleton.cs index 271478ed2..273e3eebb 100644 --- a/src/AtomUI.Desktop.Controls/Skeleton/AbstractSkeleton.cs +++ b/src/AtomUI.Desktop.Controls/Skeleton/AbstractSkeleton.cs @@ -1,4 +1,5 @@ using System.Reactive.Disposables; +using AtomUI.Animations; using AtomUI.Controls; using AtomUI.Data; using Avalonia; @@ -7,6 +8,7 @@ using Avalonia.Animation.Easings; using Avalonia.Controls.Primitives; using Avalonia.Media; using Avalonia.Styling; +using Avalonia.Threading; using Avalonia.VisualTree; namespace AtomUI.Desktop.Controls; @@ -150,11 +152,12 @@ public abstract class AbstractSkeleton : TemplatedControl protected void StartActiveAnimation() { + _cancellationTokenSource?.Cancel(); + _cancellationTokenSource = new CancellationTokenSource(); if (_animation != null) { - _cancellationTokenSource?.Cancel(); - _cancellationTokenSource = new CancellationTokenSource(); - _animation?.RunAsync(this, _cancellationTokenSource.Token); + Dispatcher.UIThread.InvokeAsync(async () => + await _animation.RunInfiniteAsync(this, _cancellationTokenSource.Token)); } } @@ -179,7 +182,6 @@ public abstract class AbstractSkeleton : TemplatedControl _cancellationTokenSource?.Cancel(); _animation = new Animation { - IterationCount = IterationCount.Infinite, Easing = MotionEasingCurve ?? new CubicEaseOut(), Duration = MotionDuration, Children = diff --git a/src/AtomUI.Desktop.Controls/Spin/SpinIndicator.cs b/src/AtomUI.Desktop.Controls/Spin/SpinIndicator.cs index 9804e1728..ddb0025d3 100644 --- a/src/AtomUI.Desktop.Controls/Spin/SpinIndicator.cs +++ b/src/AtomUI.Desktop.Controls/Spin/SpinIndicator.cs @@ -1,4 +1,5 @@ -using AtomUI.Controls; +using AtomUI.Animations; +using AtomUI.Controls; using AtomUI.Desktop.Controls.DesignTokens; using AtomUI.Desktop.Controls.Themes; using AtomUI.Theme; @@ -15,6 +16,7 @@ using Avalonia.Media; using Avalonia.Media.Transformation; using Avalonia.Metadata; using Avalonia.Styling; +using Avalonia.Threading; namespace AtomUI.Desktop.Controls; @@ -139,7 +141,13 @@ public class SpinIndicator : TemplatedControl, BuildIndicatorAnimation(); _cancellationTokenSource?.Cancel(); _cancellationTokenSource = new CancellationTokenSource(); - _animation?.RunAsync(this, _cancellationTokenSource.Token); + if (_animation != null) + { + Dispatcher.UIThread.InvokeAsync(async () => + { + await _animation.RunInfiniteAsync(this, _cancellationTokenSource.Token); + }); + } } protected override void OnDetachedFromVisualTree(VisualTreeAttachmentEventArgs e) @@ -182,7 +190,6 @@ public class SpinIndicator : TemplatedControl, _cancellationTokenSource?.Cancel(); _animation = new Animation { - IterationCount = IterationCount.Infinite, Easing = MotionEasingCurve ?? new LinearEasing(), Duration = MotionDuration, Children = diff --git a/src/AtomUI.Desktop.Controls/Switch/SwitchKnob.cs b/src/AtomUI.Desktop.Controls/Switch/SwitchKnob.cs index a6322623b..810c89ddb 100644 --- a/src/AtomUI.Desktop.Controls/Switch/SwitchKnob.cs +++ b/src/AtomUI.Desktop.Controls/Switch/SwitchKnob.cs @@ -1,4 +1,5 @@ using System.Reactive.Disposables; +using AtomUI.Animations; using AtomUI.Controls; using AtomUI.Desktop.Controls.DesignTokens; using AtomUI.Data; @@ -12,6 +13,7 @@ using Avalonia.Interactivity; using Avalonia.LogicalTree; using Avalonia.Media; using Avalonia.Styling; +using Avalonia.Threading; using Avalonia.VisualTree; namespace AtomUI.Desktop.Controls; @@ -170,7 +172,6 @@ internal class SwitchKnob : Control _bindingDisposable?.Dispose(); _bindingDisposable = BindUtils.RelayBind(this, LoadingAnimationDurationProperty, loadingAnimation, Animation.DurationProperty); loadingAnimation.Duration = LoadingAnimationDuration; - loadingAnimation.IterationCount = IterationCount.Infinite; loadingAnimation.Easing = new LinearEasing(); loadingAnimation.Children.Add(new KeyFrame { @@ -197,7 +198,7 @@ internal class SwitchKnob : Control KeyTime = LoadingAnimationDuration }); _cancellationTokenSource = new CancellationTokenSource(); - loadingAnimation.RunAsync(this, _cancellationTokenSource!.Token); + Dispatcher.UIThread.InvokeAsync(async () => await loadingAnimation.RunInfiniteAsync(this, _cancellationTokenSource.Token)); } public void NotifyStopLoading()