You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
108 lines
3.5 KiB
108 lines
3.5 KiB
1 year ago
|
using System;
|
||
|
using Avalonia;
|
||
|
using Avalonia.Controls;
|
||
|
using Avalonia.Rendering.Composition;
|
||
|
|
||
|
namespace StabilityMatrix.Avalonia.Animations;
|
||
|
|
||
|
public class ItemsRepeaterArrangeAnimation : AvaloniaObject
|
||
|
{
|
||
|
public static readonly AttachedProperty<bool> EnableItemsArrangeAnimationProperty =
|
||
|
AvaloniaProperty.RegisterAttached<ItemsRepeater, bool>(
|
||
|
"EnableItemsArrangeAnimation",
|
||
|
typeof(ItemsRepeaterArrangeAnimation)
|
||
|
);
|
||
|
|
||
|
static ItemsRepeaterArrangeAnimation()
|
||
|
{
|
||
|
EnableItemsArrangeAnimationProperty.Changed.AddClassHandler<ItemsRepeater>(
|
||
|
OnEnableItemsArrangeAnimationChanged
|
||
|
);
|
||
|
}
|
||
|
|
||
|
private static void OnEnableItemsArrangeAnimationChanged(
|
||
|
ItemsRepeater itemsRepeater,
|
||
|
AvaloniaPropertyChangedEventArgs eventArgs
|
||
|
)
|
||
|
{
|
||
|
if (eventArgs.NewValue is true)
|
||
|
{
|
||
|
itemsRepeater.ElementPrepared += OnElementPrepared;
|
||
|
itemsRepeater.ElementIndexChanged += OnElementIndexChanged;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// itemsRepeater.Opened -= OnOpened;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private static void CreateAnimation(Visual item)
|
||
|
{
|
||
|
var compositionVisual =
|
||
|
ElementComposition.GetElementVisual(item) ?? throw new NullReferenceException();
|
||
|
|
||
|
if (compositionVisual.ImplicitAnimations is { } animations && animations.HasKey("Offset"))
|
||
|
{
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
var compositor = compositionVisual.Compositor;
|
||
|
|
||
|
var offsetAnimation = compositor.CreateVector3KeyFrameAnimation();
|
||
|
offsetAnimation.Target = "Offset";
|
||
|
// Using the "this.FinalValue" to indicate the last value of the Offset property
|
||
|
offsetAnimation.InsertExpressionKeyFrame(1.0f, "this.FinalValue");
|
||
|
offsetAnimation.Duration = TimeSpan.FromMilliseconds(150);
|
||
|
|
||
|
// Create a new implicit animation collection and bind the offset animation
|
||
|
var implicitAnimationCollection = compositor.CreateImplicitAnimationCollection();
|
||
|
implicitAnimationCollection["Offset"] = offsetAnimation;
|
||
|
compositionVisual.ImplicitAnimations = implicitAnimationCollection;
|
||
|
}
|
||
|
|
||
|
private static void OnElementPrepared(object? sender, ItemsRepeaterElementPreparedEventArgs e)
|
||
|
{
|
||
|
if (
|
||
|
sender is not ItemsRepeater itemsRepeater
|
||
|
|| !GetEnableItemsArrangeAnimation(itemsRepeater)
|
||
|
)
|
||
|
return;
|
||
|
|
||
|
CreateAnimation(itemsRepeater);
|
||
|
}
|
||
|
|
||
|
private static void OnElementIndexChanged(
|
||
|
object? sender,
|
||
|
ItemsRepeaterElementIndexChangedEventArgs e
|
||
|
)
|
||
|
{
|
||
|
if (
|
||
|
sender is not ItemsRepeater itemsRepeater
|
||
|
|| !GetEnableItemsArrangeAnimation(itemsRepeater)
|
||
|
)
|
||
|
return;
|
||
|
|
||
|
CreateAnimation(itemsRepeater);
|
||
|
}
|
||
|
|
||
|
/*private static void OnOpened(object sender, EventArgs e)
|
||
|
{
|
||
|
if (sender is not WindowBase windowBase || !GetEnableScaleShowAnimation(windowBase))
|
||
|
return;
|
||
|
|
||
|
// Here we explicitly animate the "Scale" property
|
||
|
// The implementation is the same as `Offset` at the beginning, but just with the Scale property
|
||
|
windowBase.StartWindowScaleAnimation();
|
||
|
}*/
|
||
|
|
||
|
public static bool GetEnableItemsArrangeAnimation(ItemsRepeater element)
|
||
|
{
|
||
|
return element.GetValue(EnableItemsArrangeAnimationProperty);
|
||
|
}
|
||
|
|
||
|
public static void SetEnableItemsArrangeAnimation(ItemsRepeater element, bool value)
|
||
|
{
|
||
|
element.SetValue(EnableItemsArrangeAnimationProperty, value);
|
||
|
}
|
||
|
}
|