Browse Source

动画效果修复

refactor/namespace-and-layering
root 4 weeks ago
parent
commit
0a6dcf3551
  1. 7
      AuroraDesk.Presentation/Views/Pages/BreathePageView.axaml
  2. 171
      AuroraDesk.Presentation/Views/Pages/BreathePageView.axaml.cs
  3. 22
      ilspy_lottie/Avalonia.Skia.Lottie.csproj
  4. 397
      ilspy_lottie/Avalonia.Skia.Lottie/Lottie.cs
  5. 9
      ilspy_lottie/Avalonia.Skia.Lottie/LottieCommand.cs
  6. 368
      ilspy_lottie/Avalonia.Skia.Lottie/LottieCompositionCustomVisualHandler.cs
  7. 6
      ilspy_lottie/Avalonia.Skia.Lottie/LottiePayload.cs
  8. 17
      ilspy_lottie/Avalonia.Skia.Lottie/ServiceProviderExtensions.cs
  9. 17
      ilspy_lottie/Properties/AssemblyInfo.cs
  10. 33
      modify.md

7
AuroraDesk.Presentation/Views/Pages/BreathePageView.axaml

@ -165,12 +165,7 @@
Margin="0">
<Border Background="Transparent"
ClipToBounds="False">
<lottie:Lottie Path="{Binding AnimationPath}"
RepeatCount="{Binding RepeatCount}"
Stretch="Uniform"
HorizontalAlignment="Center"
VerticalAlignment="Center"
ClipToBounds="False" />
<ContentControl x:Name="LottieContainer" />
</Border>
</Viewbox>
</Border>

171
AuroraDesk.Presentation/Views/Pages/BreathePageView.axaml.cs

@ -2,22 +2,189 @@ using ReactiveUI.Avalonia;
using Avalonia.Markup.Xaml;
using AuroraDesk.Presentation.ViewModels.Pages;
using ReactiveUI;
using System;
using System.Reactive.Disposables;
using System.Reactive.Linq;
using Avalonia.Controls;
using Avalonia.Threading;
using Avalonia.Skia.Lottie;
using Avalonia.VisualTree;
using Avalonia.Interactivity;
namespace AuroraDesk.Presentation.Views.Pages;
public partial class BreathePageView : ReactiveUserControl<BreathePageViewModel>
{
private Lottie? _lottieControl;
private ContentControl? _lottieContainer;
private bool _isDisposed;
private IDisposable? _repeatCountSubscription;
public BreathePageView()
{
InitializeComponent();
// 若后续需要生命周期 Hook,可在此处使用 WhenActivated
this.WhenActivated(_ => { });
this.WhenActivated(disposables =>
{
// 重置停用标志
_isDisposed = false;
// 查找容器控件
_lottieContainer = this.FindControl<ContentControl>("LottieContainer");
if (_lottieContainer == null || ViewModel == null)
{
return;
}
// 创建新的 Lottie 控件实例
CreateLottieControl(disposables);
// 订阅动画路径变化
var pathSubscription = ViewModel
.WhenAnyValue(x => x.AnimationPath)
.Where(path => !string.IsNullOrEmpty(path))
.DistinctUntilChanged()
.ObserveOn(AvaloniaScheduler.Instance)
.Subscribe(path =>
{
if (_isDisposed || _lottieControl == null)
{
return;
}
try
{
Dispatcher.UIThread.Post(() =>
{
if (!_isDisposed && _lottieControl != null && !string.IsNullOrEmpty(path))
{
try
{
_lottieControl.Path = path;
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine($"[BreathePageView] Failed to set animation path: {ex.GetType().Name} - {ex.Message}");
}
}
}, DispatcherPriority.Normal);
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine($"[BreathePageView] Failed to update animation path: {ex.GetType().Name} - {ex.Message}");
}
});
disposables.Add(pathSubscription);
// 页面停用时的清理
disposables.Add(Disposable.Create(() =>
{
_isDisposed = true;
_repeatCountSubscription?.Dispose();
_repeatCountSubscription = null;
DestroyLottieControl();
}));
});
}
private void InitializeComponent()
{
AvaloniaXamlLoader.Load(this);
}
private void CreateLottieControl(CompositeDisposable disposables)
{
if (_lottieContainer == null || ViewModel == null || _isDisposed)
{
return;
}
try
{
// 获取 baseUri(用于资源加载)
// 使用程序集资源的基础 URI
var baseUri = new Uri("avares://AuroraDesk.Presentation/");
// 创建新的 Lottie 控件实例
_lottieControl = new Lottie(baseUri)
{
RepeatCount = ViewModel.RepeatCount,
Stretch = Avalonia.Media.Stretch.Uniform,
HorizontalAlignment = Avalonia.Layout.HorizontalAlignment.Center,
VerticalAlignment = Avalonia.Layout.VerticalAlignment.Center,
ClipToBounds = false
};
// 设置初始路径
if (!string.IsNullOrEmpty(ViewModel.AnimationPath))
{
_lottieControl.Path = ViewModel.AnimationPath;
}
// 订阅 RepeatCount 变化
if (ViewModel != null)
{
_repeatCountSubscription = ViewModel.WhenAnyValue(x => x.RepeatCount)
.Subscribe(count =>
{
if (_lottieControl != null && !_isDisposed)
{
Dispatcher.UIThread.Post(() =>
{
if (_lottieControl != null && !_isDisposed)
{
_lottieControl.RepeatCount = count;
}
});
}
});
if (_repeatCountSubscription != null)
{
disposables.Add(_repeatCountSubscription);
}
}
// 将控件添加到容器
_lottieContainer.Content = _lottieControl;
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine($"[BreathePageView] Failed to create Lottie control: {ex.GetType().Name} - {ex.Message}");
}
}
private void DestroyLottieControl()
{
try
{
if (_lottieContainer != null)
{
// 清空容器内容,这会触发 Lottie 控件的卸载和资源清理
Dispatcher.UIThread.Post(() =>
{
try
{
if (_lottieContainer != null)
{
_lottieContainer.Content = null;
}
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine($"[BreathePageView] Failed to clear container: {ex.GetType().Name} - {ex.Message}");
}
}, DispatcherPriority.Normal);
}
_lottieControl = null;
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine($"[BreathePageView] Failed to destroy Lottie control: {ex.GetType().Name} - {ex.Message}");
}
}
}

22
ilspy_lottie/Avalonia.Skia.Lottie.csproj

@ -0,0 +1,22 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<AssemblyName>Avalonia.Skia.Lottie</AssemblyName>
<GenerateAssemblyInfo>False</GenerateAssemblyInfo>
<TargetFramework>netcoreapp7.0</TargetFramework>
</PropertyGroup>
<PropertyGroup>
<LangVersion>12.0</LangVersion>
<AllowUnsafeBlocks>True</AllowUnsafeBlocks>
</PropertyGroup>
<PropertyGroup />
<ItemGroup />
<ItemGroup>
<Reference Include="Avalonia.Base" />
<Reference Include="Avalonia.Controls" />
<Reference Include="SkiaSharp.Skottie" />
<Reference Include="SkiaSharp" />
<Reference Include="SkiaSharp.SceneGraph" />
<Reference Include="Avalonia.Skia" />
<Reference Include="Avalonia.Markup.Xaml" />
</ItemGroup>
</Project>

397
ilspy_lottie/Avalonia.Skia.Lottie/Lottie.cs

@ -0,0 +1,397 @@
using System;
using System.IO;
using System.Numerics;
using Avalonia.Controls;
using Avalonia.Data;
using Avalonia.Interactivity;
using Avalonia.Layout;
using Avalonia.Logging;
using Avalonia.Media;
using Avalonia.Metadata;
using Avalonia.Platform;
using Avalonia.Rendering.Composition;
using SkiaSharp;
using SkiaSharp.SceneGraph;
using SkiaSharp.Skottie;
namespace Avalonia.Skia.Lottie;
public class Lottie : Control
{
private Animation? _animation;
private int _repeatCount;
private readonly Uri _baseUri;
private string? _preloadPath;
private CompositionCustomVisual? _customVisual;
public const int Infinity = -1;
public static readonly StyledProperty<string?> PathProperty = AvaloniaProperty.Register<Lottie, string>("Path", (string)null, false, (BindingMode)1, (Func<string, bool>)null, (Func<AvaloniaObject, string, string>)null, false);
public static readonly StyledProperty<Stretch> StretchProperty = AvaloniaProperty.Register<Lottie, Stretch>("Stretch", (Stretch)2, false, (BindingMode)1, (Func<Stretch, bool>)null, (Func<AvaloniaObject, Stretch, Stretch>)null, false);
public static readonly StyledProperty<StretchDirection> StretchDirectionProperty = AvaloniaProperty.Register<Lottie, StretchDirection>("StretchDirection", (StretchDirection)2, false, (BindingMode)1, (Func<StretchDirection, bool>)null, (Func<AvaloniaObject, StretchDirection, StretchDirection>)null, false);
public static readonly StyledProperty<int> RepeatCountProperty = AvaloniaProperty.Register<Lottie, int>("RepeatCount", -1, false, (BindingMode)1, (Func<int, bool>)null, (Func<AvaloniaObject, int, int>)null, false);
[Content]
public string? Path
{
get
{
return ((AvaloniaObject)this).GetValue<string>(PathProperty);
}
set
{
((AvaloniaObject)this).SetValue<string>(PathProperty, value, (BindingPriority)0);
}
}
public Stretch Stretch
{
get
{
//IL_0006: Unknown result type (might be due to invalid IL or missing references)
return ((AvaloniaObject)this).GetValue<Stretch>(StretchProperty);
}
set
{
//IL_0006: Unknown result type (might be due to invalid IL or missing references)
((AvaloniaObject)this).SetValue<Stretch>(StretchProperty, value, (BindingPriority)0);
}
}
public StretchDirection StretchDirection
{
get
{
//IL_0006: Unknown result type (might be due to invalid IL or missing references)
return ((AvaloniaObject)this).GetValue<StretchDirection>(StretchDirectionProperty);
}
set
{
//IL_0006: Unknown result type (might be due to invalid IL or missing references)
((AvaloniaObject)this).SetValue<StretchDirection>(StretchDirectionProperty, value, (BindingPriority)0);
}
}
public int RepeatCount
{
get
{
return ((AvaloniaObject)this).GetValue<int>(RepeatCountProperty);
}
set
{
((AvaloniaObject)this).SetValue<int>(RepeatCountProperty, value, (BindingPriority)0);
}
}
public Lottie(Uri baseUri)
{
_baseUri = baseUri;
}
public Lottie(IServiceProvider serviceProvider)
{
_baseUri = serviceProvider.GetContextBaseUri();
}
protected override void OnLoaded(RoutedEventArgs routedEventArgs)
{
//IL_006f: Unknown result type (might be due to invalid IL or missing references)
//IL_0074: Unknown result type (might be due to invalid IL or missing references)
//IL_0077: Unknown result type (might be due to invalid IL or missing references)
//IL_007c: Unknown result type (might be due to invalid IL or missing references)
//IL_0086: Unknown result type (might be due to invalid IL or missing references)
//IL_008b: Unknown result type (might be due to invalid IL or missing references)
//IL_008e: Unknown result type (might be due to invalid IL or missing references)
//IL_0093: Unknown result type (might be due to invalid IL or missing references)
//IL_00a1: Unknown result type (might be due to invalid IL or missing references)
//IL_00b9: Unknown result type (might be due to invalid IL or missing references)
//IL_00c4: Unknown result type (might be due to invalid IL or missing references)
((Control)this).OnLoaded(routedEventArgs);
CompositionVisual elementVisual = ElementComposition.GetElementVisual((Visual)(object)this);
Compositor val = ((elementVisual != null) ? ((CompositionObject)elementVisual).Compositor : null);
if (val != null)
{
_customVisual = val.CreateCustomVisual((CompositionCustomVisualHandler)(object)new LottieCompositionCustomVisualHandler());
ElementComposition.SetElementChildVisual((Visual)(object)this, (CompositionVisual)(object)_customVisual);
((Layoutable)this).LayoutUpdated += OnLayoutUpdated;
if (_preloadPath != null)
{
DisposeImpl();
Load(_preloadPath);
CompositionCustomVisual? customVisual = _customVisual;
Rect bounds = ((Visual)this).Bounds;
Size size = ((Rect)(ref bounds)).Size;
float x = (float)((Size)(ref size)).Width;
bounds = ((Visual)this).Bounds;
size = ((Rect)(ref bounds)).Size;
((CompositionVisual)customVisual).Size = Vector.op_Implicit(new Vector2(x, (float)((Size)(ref size)).Height));
_customVisual.SendHandlerMessage((object)new LottiePayload(LottieCommand.Update, _animation, Stretch, StretchDirection));
Start();
_preloadPath = null;
}
}
}
protected override void OnUnloaded(RoutedEventArgs routedEventArgs)
{
((Control)this).OnUnloaded(routedEventArgs);
((Layoutable)this).LayoutUpdated -= OnLayoutUpdated;
Stop();
DisposeImpl();
}
private void OnLayoutUpdated(object? sender, EventArgs e)
{
//IL_0010: Unknown result type (might be due to invalid IL or missing references)
//IL_0015: Unknown result type (might be due to invalid IL or missing references)
//IL_0018: Unknown result type (might be due to invalid IL or missing references)
//IL_001d: Unknown result type (might be due to invalid IL or missing references)
//IL_0027: Unknown result type (might be due to invalid IL or missing references)
//IL_002c: Unknown result type (might be due to invalid IL or missing references)
//IL_002f: Unknown result type (might be due to invalid IL or missing references)
//IL_0034: Unknown result type (might be due to invalid IL or missing references)
//IL_0042: Unknown result type (might be due to invalid IL or missing references)
//IL_005a: Unknown result type (might be due to invalid IL or missing references)
//IL_0065: Unknown result type (might be due to invalid IL or missing references)
if (_customVisual != null)
{
CompositionCustomVisual? customVisual = _customVisual;
Rect bounds = ((Visual)this).Bounds;
Size size = ((Rect)(ref bounds)).Size;
float x = (float)((Size)(ref size)).Width;
bounds = ((Visual)this).Bounds;
size = ((Rect)(ref bounds)).Size;
((CompositionVisual)customVisual).Size = Vector.op_Implicit(new Vector2(x, (float)((Size)(ref size)).Height));
_customVisual.SendHandlerMessage((object)new LottiePayload(LottieCommand.Update, _animation, Stretch, StretchDirection));
}
}
protected override Size MeasureOverride(Size availableSize)
{
//IL_000a: Unknown result type (might be due to invalid IL or missing references)
//IL_0010: Unknown result type (might be due to invalid IL or missing references)
//IL_002b: Unknown result type (might be due to invalid IL or missing references)
//IL_0030: Unknown result type (might be due to invalid IL or missing references)
//IL_003f: Unknown result type (might be due to invalid IL or missing references)
//IL_0044: Unknown result type (might be due to invalid IL or missing references)
//IL_004d: Unknown result type (might be due to invalid IL or missing references)
//IL_001c: Unknown result type (might be due to invalid IL or missing references)
//IL_0022: Unknown result type (might be due to invalid IL or missing references)
//IL_0052: Unknown result type (might be due to invalid IL or missing references)
//IL_0054: Unknown result type (might be due to invalid IL or missing references)
//IL_0059: Unknown result type (might be due to invalid IL or missing references)
//IL_005a: Unknown result type (might be due to invalid IL or missing references)
//IL_005c: Unknown result type (might be due to invalid IL or missing references)
//IL_0061: Unknown result type (might be due to invalid IL or missing references)
if (_animation == null)
{
return default(Size);
}
_003F val;
if (_animation == null)
{
val = default(Size);
}
else
{
SKSize size = _animation.Size;
double num = ((SKSize)(ref size)).Width;
size = _animation.Size;
val = new Size(num, (double)((SKSize)(ref size)).Height);
}
Size val2 = (Size)val;
return MediaExtensions.CalculateSize(Stretch, availableSize, val2, StretchDirection);
}
protected override Size ArrangeOverride(Size finalSize)
{
//IL_000a: Unknown result type (might be due to invalid IL or missing references)
//IL_0010: Unknown result type (might be due to invalid IL or missing references)
//IL_002b: Unknown result type (might be due to invalid IL or missing references)
//IL_0030: Unknown result type (might be due to invalid IL or missing references)
//IL_003f: Unknown result type (might be due to invalid IL or missing references)
//IL_0044: Unknown result type (might be due to invalid IL or missing references)
//IL_004d: Unknown result type (might be due to invalid IL or missing references)
//IL_001c: Unknown result type (might be due to invalid IL or missing references)
//IL_0022: Unknown result type (might be due to invalid IL or missing references)
//IL_0052: Unknown result type (might be due to invalid IL or missing references)
//IL_0054: Unknown result type (might be due to invalid IL or missing references)
//IL_0059: Unknown result type (might be due to invalid IL or missing references)
//IL_005a: Unknown result type (might be due to invalid IL or missing references)
//IL_005c: Unknown result type (might be due to invalid IL or missing references)
if (_animation == null)
{
return default(Size);
}
_003F val;
if (_animation == null)
{
val = default(Size);
}
else
{
SKSize size = _animation.Size;
double num = ((SKSize)(ref size)).Width;
size = _animation.Size;
val = new Size(num, (double)((SKSize)(ref size)).Height);
}
Size val2 = (Size)val;
return MediaExtensions.CalculateSize(Stretch, finalSize, val2, (StretchDirection)2);
}
protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
{
((Control)this).OnPropertyChanged(change);
string name = change.Property.Name;
if (!(name == "Path"))
{
if (name == "RepeatCount")
{
_repeatCount = AvaloniaPropertyChangedExtensions.GetNewValue<int>(change);
Stop();
Start();
}
}
else
{
string newValue = AvaloniaPropertyChangedExtensions.GetNewValue<string>(change);
if (_preloadPath == null && _customVisual == null)
{
_preloadPath = newValue;
}
else
{
Load(newValue);
}
}
}
private Animation? Load(Stream stream)
{
//IL_0001: Unknown result type (might be due to invalid IL or missing references)
//IL_0007: Expected O, but got Unknown
//IL_00f7: Unknown result type (might be due to invalid IL or missing references)
//IL_00fc: Unknown result type (might be due to invalid IL or missing references)
//IL_0040: Unknown result type (might be due to invalid IL or missing references)
//IL_0045: Unknown result type (might be due to invalid IL or missing references)
SKManagedStream val = new SKManagedStream(stream);
try
{
Animation val2 = default(Animation);
ParametrizedLogger valueOrDefault;
if (Animation.TryCreate((SKStream)(object)val, ref val2))
{
val2.Seek(0.0, (InvalidationController)null);
ParametrizedLogger? val3 = Logger.TryGet((LogEventLevel)2, "Control");
if (val3.HasValue)
{
valueOrDefault = val3.GetValueOrDefault();
((ParametrizedLogger)(ref valueOrDefault)).Log((object)this, $"Version: {val2.Version} Duration: {val2.Duration} Fps:{val2.Fps} InPoint: {val2.InPoint} OutPoint: {val2.OutPoint}");
}
}
else
{
ParametrizedLogger? val3 = Logger.TryGet((LogEventLevel)3, "Control");
if (val3.HasValue)
{
valueOrDefault = val3.GetValueOrDefault();
((ParametrizedLogger)(ref valueOrDefault)).Log((object)this, "Failed to load animation.");
}
}
return val2;
}
finally
{
((IDisposable)val)?.Dispose();
}
}
private Animation? Load(string path, Uri? baseUri)
{
Uri uri = (path.StartsWith("/") ? new Uri(path, UriKind.Relative) : new Uri(path, UriKind.RelativeOrAbsolute));
if (uri.IsAbsoluteUri && uri.IsFile)
{
using (FileStream stream = File.OpenRead(uri.LocalPath))
{
return Load(stream);
}
}
using Stream stream2 = AssetLoader.Open(uri, baseUri);
if (stream2 == null)
{
return null;
}
return Load(stream2);
}
private void Load(string? path)
{
//IL_006d: Unknown result type (might be due to invalid IL or missing references)
//IL_0072: Unknown result type (might be due to invalid IL or missing references)
Stop();
if (path == null)
{
DisposeImpl();
return;
}
DisposeImpl();
try
{
_repeatCount = RepeatCount;
_animation = Load(path, _baseUri);
if (_animation != null)
{
((Layoutable)this).InvalidateArrange();
((Layoutable)this).InvalidateMeasure();
Start();
}
}
catch (Exception ex)
{
ParametrizedLogger? val = Logger.TryGet((LogEventLevel)3, "Control");
if (val.HasValue)
{
ParametrizedLogger valueOrDefault = val.GetValueOrDefault();
((ParametrizedLogger)(ref valueOrDefault)).Log((object)this, "Failed to load animation: " + ex);
}
_animation = null;
}
}
private void Start()
{
//IL_0013: Unknown result type (might be due to invalid IL or missing references)
//IL_001e: Unknown result type (might be due to invalid IL or missing references)
CompositionCustomVisual? customVisual = _customVisual;
if (customVisual != null)
{
customVisual.SendHandlerMessage((object)new LottiePayload(LottieCommand.Start, _animation, Stretch, StretchDirection, _repeatCount));
}
}
private void Stop()
{
CompositionCustomVisual? customVisual = _customVisual;
if (customVisual != null)
{
customVisual.SendHandlerMessage((object)new LottiePayload(LottieCommand.Stop));
}
}
private void DisposeImpl()
{
CompositionCustomVisual? customVisual = _customVisual;
if (customVisual != null)
{
customVisual.SendHandlerMessage((object)new LottiePayload(LottieCommand.Dispose));
}
}
}

9
ilspy_lottie/Avalonia.Skia.Lottie/LottieCommand.cs

@ -0,0 +1,9 @@
namespace Avalonia.Skia.Lottie;
internal enum LottieCommand
{
Start,
Stop,
Update,
Dispose
}

368
ilspy_lottie/Avalonia.Skia.Lottie/LottieCompositionCustomVisualHandler.cs

@ -0,0 +1,368 @@
using System;
using Avalonia.Media;
using Avalonia.Platform;
using Avalonia.Rendering.Composition;
using SkiaSharp;
using SkiaSharp.SceneGraph;
using SkiaSharp.Skottie;
namespace Avalonia.Skia.Lottie;
internal class LottieCompositionCustomVisualHandler : CompositionCustomVisualHandler
{
private TimeSpan _primaryTimeElapsed;
private TimeSpan _animationElapsed;
private TimeSpan? _lastServerTime;
private bool _running;
private Animation? _animation;
private Stretch? _stretch;
private StretchDirection? _stretchDirection;
private InvalidationController? _ic;
private readonly object _sync = new object();
private int _repeatCount;
private int _count;
public override void OnMessage(object message)
{
//IL_00b2: Unknown result type (might be due to invalid IL or missing references)
//IL_00b7: Unknown result type (might be due to invalid IL or missing references)
//IL_0058: Unknown result type (might be due to invalid IL or missing references)
//IL_005d: Unknown result type (might be due to invalid IL or missing references)
//IL_00cf: Unknown result type (might be due to invalid IL or missing references)
//IL_00d4: Unknown result type (might be due to invalid IL or missing references)
//IL_0075: Unknown result type (might be due to invalid IL or missing references)
//IL_007a: Unknown result type (might be due to invalid IL or missing references)
//IL_012a: Unknown result type (might be due to invalid IL or missing references)
//IL_012b: Unknown result type (might be due to invalid IL or missing references)
//IL_012d: Unknown result type (might be due to invalid IL or missing references)
//IL_012e: Unknown result type (might be due to invalid IL or missing references)
//IL_0131: Unknown result type (might be due to invalid IL or missing references)
//IL_013e: Unknown result type (might be due to invalid IL or missing references)
//IL_00eb: Unknown result type (might be due to invalid IL or missing references)
//IL_00f7: Unknown result type (might be due to invalid IL or missing references)
if (!(message is LottiePayload lottiePayload))
{
return;
}
switch (lottiePayload.LottieCommand)
{
case LottieCommand.Start:
{
Animation animation = lottiePayload.Animation;
if (animation == null)
{
break;
}
Stretch? stretch = lottiePayload.Stretch;
if (!stretch.HasValue)
{
break;
}
Stretch valueOrDefault = stretch.GetValueOrDefault();
StretchDirection? stretchDirection = lottiePayload.StretchDirection;
if (stretchDirection.HasValue)
{
StretchDirection valueOrDefault2 = stretchDirection.GetValueOrDefault();
int? repeatCount = lottiePayload.RepeatCount;
if (repeatCount.HasValue)
{
int valueOrDefault3 = repeatCount.GetValueOrDefault();
_running = true;
_lastServerTime = null;
_stretch = valueOrDefault;
_stretchDirection = valueOrDefault2;
_animation = animation;
_repeatCount = valueOrDefault3;
_count = 0;
_animationElapsed = TimeSpan.Zero;
((CompositionCustomVisualHandler)this).RegisterForNextAnimationFrameUpdate();
}
}
break;
}
case LottieCommand.Update:
{
Stretch? stretch = lottiePayload.Stretch;
if (stretch.HasValue)
{
Stretch valueOrDefault = stretch.GetValueOrDefault();
StretchDirection? stretchDirection = lottiePayload.StretchDirection;
if (stretchDirection.HasValue)
{
StretchDirection valueOrDefault2 = stretchDirection.GetValueOrDefault();
Stretch value = valueOrDefault;
StretchDirection value2 = valueOrDefault2;
_stretch = value;
_stretchDirection = value2;
((CompositionCustomVisualHandler)this).RegisterForNextAnimationFrameUpdate();
}
}
break;
}
case LottieCommand.Stop:
_running = false;
_animationElapsed = TimeSpan.Zero;
_count = 0;
break;
case LottieCommand.Dispose:
DisposeImpl();
break;
}
}
public override void OnAnimationFrameUpdate()
{
if (_running)
{
if (_repeatCount == 0 || (_repeatCount > 0 && _count >= _repeatCount))
{
_running = false;
_animationElapsed = TimeSpan.Zero;
}
((CompositionCustomVisualHandler)this).Invalidate();
((CompositionCustomVisualHandler)this).RegisterForNextAnimationFrameUpdate();
}
}
private void DisposeImpl()
{
lock (_sync)
{
Animation? animation = _animation;
if (animation != null)
{
((SKNativeObject)animation).Dispose();
}
_animation = null;
InvalidationController? ic = _ic;
if (ic != null)
{
ic.End();
}
InvalidationController? ic2 = _ic;
if (ic2 != null)
{
((SKNativeObject)ic2).Dispose();
}
_ic = null;
}
}
private double GetFrameTime()
{
if (_animation == null)
{
return 0.0;
}
double totalSeconds = _animationElapsed.TotalSeconds;
if (_animationElapsed.TotalSeconds > _animation.Duration.TotalSeconds)
{
_animationElapsed = TimeSpan.Zero;
InvalidationController? ic = _ic;
if (ic != null)
{
ic.End();
}
InvalidationController? ic2 = _ic;
if (ic2 != null)
{
ic2.Begin();
}
_count++;
}
return totalSeconds;
}
private void Draw(SKCanvas canvas)
{
//IL_0014: Unknown result type (might be due to invalid IL or missing references)
//IL_001e: Expected O, but got Unknown
//IL_0067: Unknown result type (might be due to invalid IL or missing references)
//IL_006c: Unknown result type (might be due to invalid IL or missing references)
//IL_0076: Unknown result type (might be due to invalid IL or missing references)
//IL_007b: Unknown result type (might be due to invalid IL or missing references)
//IL_009a: Unknown result type (might be due to invalid IL or missing references)
Animation animation = _animation;
if (animation == null)
{
return;
}
if (_ic == null)
{
_ic = new InvalidationController();
_ic.Begin();
}
InvalidationController ic = _ic;
if (_repeatCount != 0)
{
double num = GetFrameTime();
if (!_running)
{
num = (float)animation.Duration.TotalSeconds;
}
SKSize size = animation.Size;
float width = ((SKSize)(ref size)).Width;
size = animation.Size;
SKRect val = default(SKRect);
((SKRect)(ref val))._002Ector(0f, 0f, width, ((SKSize)(ref size)).Height);
animation.SeekFrameTime(num, ic);
canvas.Save();
animation.Render(canvas, val);
canvas.Restore();
ic.Reset();
}
}
public unsafe override void OnRender(ImmediateDrawingContext context)
{
//IL_0092: Unknown result type (might be due to invalid IL or missing references)
//IL_0097: Unknown result type (might be due to invalid IL or missing references)
//IL_00ab: Unknown result type (might be due to invalid IL or missing references)
//IL_00b0: Unknown result type (might be due to invalid IL or missing references)
//IL_00cb: Unknown result type (might be due to invalid IL or missing references)
//IL_00d0: Unknown result type (might be due to invalid IL or missing references)
//IL_00d6: Unknown result type (might be due to invalid IL or missing references)
//IL_00e3: Unknown result type (might be due to invalid IL or missing references)
//IL_00e8: Unknown result type (might be due to invalid IL or missing references)
//IL_00f3: Unknown result type (might be due to invalid IL or missing references)
//IL_00f8: Unknown result type (might be due to invalid IL or missing references)
//IL_0130: Unknown result type (might be due to invalid IL or missing references)
//IL_0133: Unknown result type (might be due to invalid IL or missing references)
//IL_0138: Unknown result type (might be due to invalid IL or missing references)
//IL_013a: Unknown result type (might be due to invalid IL or missing references)
//IL_013c: Unknown result type (might be due to invalid IL or missing references)
//IL_0141: Unknown result type (might be due to invalid IL or missing references)
//IL_0143: Unknown result type (might be due to invalid IL or missing references)
//IL_0145: Unknown result type (might be due to invalid IL or missing references)
//IL_0147: Unknown result type (might be due to invalid IL or missing references)
//IL_014c: Unknown result type (might be due to invalid IL or missing references)
//IL_0150: Unknown result type (might be due to invalid IL or missing references)
//IL_0152: Unknown result type (might be due to invalid IL or missing references)
//IL_0157: Unknown result type (might be due to invalid IL or missing references)
//IL_015c: Unknown result type (might be due to invalid IL or missing references)
//IL_0160: Unknown result type (might be due to invalid IL or missing references)
//IL_0162: Unknown result type (might be due to invalid IL or missing references)
//IL_0167: Unknown result type (might be due to invalid IL or missing references)
//IL_0169: Unknown result type (might be due to invalid IL or missing references)
//IL_016b: Unknown result type (might be due to invalid IL or missing references)
//IL_0170: Unknown result type (might be due to invalid IL or missing references)
//IL_0176: Unknown result type (might be due to invalid IL or missing references)
//IL_017b: Unknown result type (might be due to invalid IL or missing references)
//IL_017d: Unknown result type (might be due to invalid IL or missing references)
//IL_0182: Unknown result type (might be due to invalid IL or missing references)
//IL_0187: Unknown result type (might be due to invalid IL or missing references)
//IL_018c: Unknown result type (might be due to invalid IL or missing references)
//IL_0190: Unknown result type (might be due to invalid IL or missing references)
//IL_0196: Unknown result type (might be due to invalid IL or missing references)
//IL_0199: Unknown result type (might be due to invalid IL or missing references)
//IL_019e: Unknown result type (might be due to invalid IL or missing references)
//IL_01a3: Unknown result type (might be due to invalid IL or missing references)
//IL_01c3: Unknown result type (might be due to invalid IL or missing references)
//IL_01c8: Unknown result type (might be due to invalid IL or missing references)
//IL_01fc: Unknown result type (might be due to invalid IL or missing references)
//IL_0201: Unknown result type (might be due to invalid IL or missing references)
//IL_0204: Unknown result type (might be due to invalid IL or missing references)
//IL_0206: Unknown result type (might be due to invalid IL or missing references)
//IL_020b: Unknown result type (might be due to invalid IL or missing references)
//IL_020e: Unknown result type (might be due to invalid IL or missing references)
//IL_0210: Unknown result type (might be due to invalid IL or missing references)
//IL_0212: Unknown result type (might be due to invalid IL or missing references)
//IL_0217: Unknown result type (might be due to invalid IL or missing references)
//IL_021c: Unknown result type (might be due to invalid IL or missing references)
lock (_sync)
{
if (_running)
{
if (_lastServerTime.HasValue)
{
TimeSpan timeSpan = ((CompositionCustomVisualHandler)this).CompositionNow - _lastServerTime.Value;
_primaryTimeElapsed += timeSpan;
_animationElapsed += timeSpan;
}
_lastServerTime = ((CompositionCustomVisualHandler)this).CompositionNow;
}
Animation animation = _animation;
if (animation == null)
{
return;
}
Stretch? stretch = _stretch;
if (!stretch.HasValue)
{
return;
}
Stretch valueOrDefault = stretch.GetValueOrDefault();
StretchDirection? stretchDirection = _stretchDirection;
if (!stretchDirection.HasValue)
{
return;
}
StretchDirection valueOrDefault2 = stretchDirection.GetValueOrDefault();
ISkiaSharpApiLeaseFeature val = OptionalFeatureProviderExtensions.TryGetFeature<ISkiaSharpApiLeaseFeature>((IOptionalFeatureProvider)(object)context);
if (val == null)
{
return;
}
Rect renderBounds = ((CompositionCustomVisualHandler)this).GetRenderBounds();
Rect val2 = default(Rect);
((Rect)(ref val2))._002Ector(((Rect)(ref renderBounds)).Size);
SKSize size = animation.Size;
double num = ((SKSize)(ref size)).Width;
size = animation.Size;
Size val3 = default(Size);
((Size)(ref val3))._002Ector(num, (double)((SKSize)(ref size)).Height);
if (((Size)(ref val3)).Width <= 0.0 || ((Size)(ref val3)).Height <= 0.0)
{
return;
}
Vector val4 = MediaExtensions.CalculateScaling(valueOrDefault, ((Rect)(ref renderBounds)).Size, val3, valueOrDefault2);
Size val5 = val3 * val4;
Rect val6 = ((Rect)(ref val2)).CenterRect(new Rect(val5));
Rect val7 = ((Rect)(ref val6)).Intersect(val2);
val6 = new Rect(val3);
Rect val8 = ((Rect)(ref val6)).CenterRect(new Rect(((Rect)(ref val7)).Size / val4));
SKRect val9 = SKRect.Create(default(SKPoint), animation.Size);
Matrix val10 = Matrix.CreateScale(((Rect)(ref val7)).Width / ((Rect)(ref val8)).Width, ((Rect)(ref val7)).Height / ((Rect)(ref val8)).Height);
Matrix val11 = Matrix.CreateTranslation(0.0 - ((Rect)(ref val8)).X + ((Rect)(ref val7)).X - (double)((SKRect)(ref val9)).Top, 0.0 - ((Rect)(ref val8)).Y + ((Rect)(ref val7)).Y - (double)((SKRect)(ref val9)).Left);
PushedState val12 = context.PushClip(val7);
try
{
PushedState val13 = context.PushPostTransform(val11 * val10);
try
{
ISkiaSharpApiLease val14 = val.Lease();
try
{
SKCanvas val15 = ((val14 != null) ? val14.SkCanvas : null);
if (val15 != null)
{
Draw(val15);
}
}
finally
{
((IDisposable)val14)?.Dispose();
}
}
finally
{
((IDisposable)(*(PushedState*)(&val13))/*cast due to .constrained prefix*/).Dispose();
}
}
finally
{
((IDisposable)(*(PushedState*)(&val12))/*cast due to .constrained prefix*/).Dispose();
}
}
}
}

6
ilspy_lottie/Avalonia.Skia.Lottie/LottiePayload.cs

@ -0,0 +1,6 @@
using Avalonia.Media;
using SkiaSharp.Skottie;
namespace Avalonia.Skia.Lottie;
internal record struct LottiePayload(LottieCommand LottieCommand, Animation? Animation = null, Stretch? Stretch = null, StretchDirection? StretchDirection = null, int? RepeatCount = null);

17
ilspy_lottie/Avalonia.Skia.Lottie/ServiceProviderExtensions.cs

@ -0,0 +1,17 @@
using System;
using Avalonia.Markup.Xaml;
namespace Avalonia.Skia.Lottie;
internal static class ServiceProviderExtensions
{
public static T GetService<T>(this IServiceProvider sp)
{
return (T)(sp?.GetService(typeof(T)));
}
public static Uri GetContextBaseUri(this IServiceProvider ctx)
{
return ctx.GetService<IUriContext>().BaseUri;
}
}

17
ilspy_lottie/Properties/AssemblyInfo.cs

@ -0,0 +1,17 @@
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using Avalonia.Metadata;
[assembly: XmlnsDefinition("https://github.com/avaloniaui", "Avalonia.Skia.Lottie")]
[assembly: AssemblyCompany("Wiesław Šoltés")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyCopyright("Copyright © Wiesław Šoltés 2023")]
[assembly: AssemblyDescription("An lottie animation player control for Avalonia.")]
[assembly: AssemblyFileVersion("11.0.0.0")]
[assembly: AssemblyInformationalVersion("11.0.0")]
[assembly: AssemblyProduct("Avalonia.Skia.Lottie")]
[assembly: AssemblyTitle("Avalonia.Skia.Lottie")]
[assembly: AssemblyVersion("11.0.0.0")]
[module: System.Runtime.CompilerServices.RefSafetyRules(11)]

33
modify.md

@ -2,6 +2,39 @@
## 2025年修改记录
### 修复 BreathePageView 页面切换闪退问题
- **日期**: 2025年1月
- **问题**: 第一次进入 BreathePageView 没问题,导航到其他页面后再切换回来时出现 `System.ExecutionEngineException` 闪退(HResult: -2146233082)
- **问题分析**:
- ❌ **生命周期管理缺失**: BreathePageView 没有正确处理页面激活/停用生命周期,导致 Lottie 控件资源管理混乱
- ❌ **XAML 绑定时机问题**: 页面重新激活时,XAML 绑定立即更新 Path 属性,但 Lottie 控件的资源可能还未完全清理
- ❌ **资源释放冲突**: 当页面重新加载时,控件试图访问已经被释放或正在释放的 Skia 底层资源,导致 ExecutionEngineException
- ❌ **控件实例复用**: 同一个 Lottie 控件实例在页面切换时被重复使用,导致资源状态混乱
- **修改文件**:
- `AuroraDesk.Presentation/Views/Pages/BreathePageView.axaml` (使用 ContentControl 作为容器)
- `AuroraDesk.Presentation/Views/Pages/BreathePageView.axaml.cs` (动态创建和销毁 Lottie 控件)
- **主要修复**:
- ✅ **条件渲染**: 使用 `ContentControl` 作为容器,不在 XAML 中直接放置 Lottie 控件,改为在代码后台动态创建
- ✅ **动态创建控件**: 在页面激活时(`WhenActivated`)创建全新的 Lottie 控件实例,确保每次都是干净的状态
- ✅ **完全销毁控件**: 在页面停用时,通过设置 `ContentControl.Content = null` 完全移除控件,触发控件的 `OnUnloaded` 和资源清理
- ✅ **生命周期管理**: 使用 `WhenActivated` 管理页面生命周期,确保控件在正确的时机创建和销毁
- ✅ **订阅管理**: 正确管理 `RepeatCount``AnimationPath` 的订阅,在停用时自动清理
- ✅ **线程安全**: 所有 UI 操作都通过 `Dispatcher.UIThread.Post` 在 UI 线程上执行
- ✅ **异常处理**: 添加 try-catch 捕获所有异常,避免崩溃
- **技术细节**:
- 在 XAML 中使用 `<ContentControl x:Name="LottieContainer" />` 作为容器
- 在 `CreateLottieControl` 方法中动态创建新的 `Lottie` 控件实例
- 设置控件的初始属性(RepeatCount、Stretch、对齐方式等)
- 订阅 `ViewModel.RepeatCount``ViewModel.AnimationPath` 变化
- 将控件添加到 `ContentControl.Content`,触发加载和初始化
- 在 `DestroyLottieControl` 中设置 `Content = null`,触发控件的卸载和资源清理
- 所有订阅都添加到 `CompositeDisposable`,确保在停用时自动清理
- **效果**:
- ✅ **页面切换正常**: 每次页面激活时创建全新的控件实例,避免了资源冲突
- ✅ **资源管理正确**: 通过完全移除控件,确保 Skia 资源被正确释放
- ✅ **异常安全**: 即使出现异常,也不会导致应用崩溃,只会记录日志
- ✅ **彻底解决**: 通过动态创建和销毁控件,从根本上避免了 ExecutionEngineException
### 新增文件浏览页面
- **日期**: 2025年11月14日
- **调整内容**:

Loading…
Cancel
Save