Refactor Badge motion

This commit is contained in:
polarboy 2024-10-07 16:17:00 +08:00
parent 6f37d63bdb
commit a97994a132
19 changed files with 989 additions and 1079 deletions

View File

@ -1,6 +1,7 @@
using Avalonia;
using Avalonia.Animation;
using Avalonia.Animation.Easings;
using Avalonia.Controls;
using Avalonia.Media.Transformation;
using Avalonia.Threading;
@ -16,7 +17,7 @@ internal class AbstractMotion : IMotion
public Easing Easing { get; }
public FillMode PropertyValueFillMode { get; }
public AbstractMotion(TimeSpan duration, Easing? easing = null, FillMode fillMode = FillMode.None)
public AbstractMotion(TimeSpan duration, Easing? easing = null, FillMode fillMode = FillMode.Forward)
{
Animations = new List<Animation>();
Duration = duration;
@ -38,7 +39,7 @@ internal class AbstractMotion : IMotion
{
aboutToStart();
}
actor.NotifyMotionPreStart();
NotifyPreStart();
foreach (var animation in Animations)
@ -50,7 +51,7 @@ internal class AbstractMotion : IMotion
{
completedAction();
}
actor.NotifyMotionCompleted();
NotifyCompleted();
});
}
@ -140,4 +141,21 @@ internal class AbstractMotion : IMotion
};
return animation;
}
}
internal class RenderTransformOriginRestore : IDisposable
{
RelativePoint _origin;
Control _target;
public RenderTransformOriginRestore(Control target)
{
_target = target;
_origin = target.RenderTransformOrigin;
}
public void Dispose()
{
_target.RenderTransformOrigin = _origin;
}
}

View File

@ -1,208 +0,0 @@
using Avalonia;
using Avalonia.Animation;
using Avalonia.Animation.Easings;
using Avalonia.Styling;
namespace AtomUI.MotionScene;
internal static partial class MotionFactory
{
public static MotionConfig BuildCollapseMotion(Direction direction, TimeSpan duration, Easing? easing = null,
FillMode fillMode = FillMode.None)
{
easing ??= new CubicEaseOut();
var animations = new List<Animation>();
RelativePoint transformOrigin = default;
var isHorizontal = direction == Direction.Left || direction == Direction.Right;
var animation = new Animation
{
Duration = duration,
Easing = easing,
FillMode = fillMode
};
var startFrame = new KeyFrame
{
Cue = new Cue(0.0)
};
{
var opacitySetter = new Setter
{
Property = Visual.OpacityProperty,
Value = 1.0
};
startFrame.Setters.Add(opacitySetter);
if (isHorizontal)
{
var scaleXSetter = new Setter
{
Property = MotionActorControl.MotionTransformProperty,
Value = BuildScaleXTransform(1.0)
};
startFrame.Setters.Add(scaleXSetter);
}
else
{
var scaleYSetter = new Setter
{
Property = MotionActorControl.MotionTransformProperty,
Value = BuildScaleYTransform(1.0)
};
startFrame.Setters.Add(scaleYSetter);
}
}
animation.Children.Add(startFrame);
var endFrame = new KeyFrame
{
Cue = new Cue(1.0)
};
{
var opacitySetter = new Setter
{
Property = Visual.OpacityProperty,
Value = 0.0
};
endFrame.Setters.Add(opacitySetter);
if (isHorizontal)
{
var scaleXSetter = new Setter
{
Property = MotionActorControl.MotionTransformProperty,
Value = BuildScaleXTransform(0.0)
};
endFrame.Setters.Add(scaleXSetter);
}
else
{
var scaleYSetter = new Setter
{
Property = MotionActorControl.MotionTransformProperty,
Value = BuildScaleYTransform(0.0)
};
endFrame.Setters.Add(scaleYSetter);
}
}
animation.Children.Add(endFrame);
if (direction == Direction.Left)
{
transformOrigin = new RelativePoint(1, 0.5, RelativeUnit.Relative);
}
else if (direction == Direction.Right)
{
transformOrigin = new RelativePoint(0, 0.5, RelativeUnit.Relative);
}
else if (direction == Direction.Top)
{
transformOrigin = new RelativePoint(0.5, 1.0, RelativeUnit.Relative);
}
else
{
transformOrigin = new RelativePoint(0.5, 0.0, RelativeUnit.Relative);
}
animations.Add(animation);
return new MotionConfig(transformOrigin, animations);
}
public static MotionConfig BuildExpandMotion(Direction direction, TimeSpan duration, Easing? easing = null,
FillMode fillMode = FillMode.None)
{
easing ??= new CubicEaseIn();
var animations = new List<Animation>();
RelativePoint transformOrigin = default;
var isHorizontal = direction == Direction.Left || direction == Direction.Right;
var animation = new Animation
{
Duration = duration,
Easing = easing,
FillMode = fillMode
};
var startFrame = new KeyFrame
{
Cue = new Cue(0.0)
};
{
var opacitySetter = new Setter
{
Property = Visual.OpacityProperty,
Value = 0.0
};
startFrame.Setters.Add(opacitySetter);
if (isHorizontal)
{
var scaleXSetter = new Setter
{
Property = MotionActorControl.MotionTransformProperty,
Value = BuildScaleXTransform(0.01)
};
startFrame.Setters.Add(scaleXSetter);
}
else
{
var scaleYSetter = new Setter
{
Property = MotionActorControl.MotionTransformProperty,
Value = BuildScaleYTransform(0.01)
};
startFrame.Setters.Add(scaleYSetter);
}
}
animation.Children.Add(startFrame);
var endFrame = new KeyFrame
{
Cue = new Cue(1.0)
};
{
var opacitySetter = new Setter
{
Property = Visual.OpacityProperty,
Value = 1.0
};
endFrame.Setters.Add(opacitySetter);
if (isHorizontal)
{
var scaleXSetter = new Setter
{
Property = MotionActorControl.MotionTransformProperty,
Value = BuildScaleXTransform(1.0)
};
endFrame.Setters.Add(scaleXSetter);
}
else
{
var scaleYSetter = new Setter
{
Property = MotionActorControl.MotionTransformProperty,
Value = BuildScaleYTransform(1.0)
};
endFrame.Setters.Add(scaleYSetter);
}
}
animation.Children.Add(endFrame);
if (direction == Direction.Left)
{
transformOrigin = new RelativePoint(1.0, 0.5, RelativeUnit.Relative);
}
else if (direction == Direction.Right)
{
transformOrigin = new RelativePoint(0.0, 0.5, RelativeUnit.Relative);
}
else if (direction == Direction.Top)
{
transformOrigin = new RelativePoint(0.5, 1.0, RelativeUnit.Relative);
}
else
{
transformOrigin = new RelativePoint(0.5, 1.0, RelativeUnit.Relative);
}
animations.Add(animation);
return new MotionConfig(transformOrigin, animations);
}
}

View File

@ -12,7 +12,7 @@ internal class CollapseMotion : AbstractMotion
public CollapseMotion(Direction direction,
TimeSpan duration,
Easing? easing = null,
FillMode fillMode = FillMode.None)
FillMode fillMode = FillMode.Forward)
: base(duration, easing ?? new CubicEaseOut(), fillMode)
{
Direction = direction;
@ -115,7 +115,7 @@ internal class ExpandMotion : AbstractMotion
public ExpandMotion(Direction direction,
TimeSpan duration,
Easing? easing = null,
FillMode fillMode = FillMode.None)
FillMode fillMode = FillMode.Forward)
: base(duration, easing ?? new CubicEaseIn(), fillMode)
{
Direction = direction;

View File

@ -0,0 +1,96 @@
using Avalonia;
using Avalonia.Animation;
using Avalonia.Animation.Easings;
using Avalonia.Styling;
namespace AtomUI.MotionScene;
internal class FadeInMotion : AbstractMotion
{
public FadeInMotion(TimeSpan duration,
Easing? easing = null,
FillMode fillMode = FillMode.Forward)
: base(duration, easing ?? new LinearEasing(), fillMode)
{
}
protected override void Configure()
{
var animation = CreateAnimation();
var startFrame = new KeyFrame
{
Cue = new Cue(0.0)
};
{
var opacitySetter = new Setter
{
Property = Visual.OpacityProperty,
Value = 0.0
};
startFrame.Setters.Add(opacitySetter);
}
animation.Children.Add(startFrame);
var endFrame = new KeyFrame
{
Cue = new Cue(1.0)
};
{
var opacitySetter = new Setter
{
Property = Visual.OpacityProperty,
Value = 1.0
};
endFrame.Setters.Add(opacitySetter);
}
animation.Children.Add(endFrame);
RenderTransformOrigin = new RelativePoint(0.0, 0.0, RelativeUnit.Relative);
Animations.Add(animation);
}
}
internal class FadeOutMotion : AbstractMotion
{
public FadeOutMotion(TimeSpan duration,
Easing? easing = null,
FillMode fillMode = FillMode.Forward)
: base(duration, easing ?? new LinearEasing(), fillMode)
{
}
protected override void Configure()
{
var animation = CreateAnimation();
var startFrame = new KeyFrame
{
Cue = new Cue(0.0)
};
{
var opacitySetter = new Setter
{
Property = Visual.OpacityProperty,
Value = 1.0
};
startFrame.Setters.Add(opacitySetter);
}
animation.Children.Add(startFrame);
var endFrame = new KeyFrame
{
Cue = new Cue(1.0)
};
{
var opacitySetter = new Setter
{
Property = Visual.OpacityProperty,
Value = 0.0
};
endFrame.Setters.Add(opacitySetter);
}
animation.Children.Add(endFrame);
RenderTransformOrigin = new RelativePoint(0.0, 0.0, RelativeUnit.Relative);
Animations.Add(animation);
}
}

View File

@ -30,6 +30,13 @@ internal class MotionActorControl : Decorator
public Control? MotionTransformRoot => Child;
#endregion
#region
public event EventHandler? PreStart;
public event EventHandler? Completed;
#endregion
/// <summary>
@ -394,4 +401,14 @@ internal class MotionActorControl : Decorator
{
return (a.Width + AcceptableDelta < b.Width) || (a.Height + AcceptableDelta < b.Height);
}
internal virtual void NotifyMotionPreStart()
{
PreStart?.Invoke(this, EventArgs.Empty);
}
internal virtual void NotifyMotionCompleted()
{
Completed?.Invoke(this, EventArgs.Empty);
}
}

View File

@ -1,57 +0,0 @@
using Avalonia;
using Avalonia.Animation;
using Avalonia.Media.Transformation;
namespace AtomUI.MotionScene;
internal record MotionConfig
{
public RelativePoint RenderTransformOrigin { get; }
public IList<Animation> Animations { get; }
public MotionConfig(RelativePoint renderTransformOrigin, IList<Animation> animations)
{
RenderTransformOrigin = renderTransformOrigin;
Animations = animations;
}
}
internal static partial class MotionFactory
{
public static TransformOperations BuildScaleTransform(double scaleX, double scaleY)
{
var builder = new TransformOperations.Builder(1);
builder.AppendScale(scaleX, scaleY);
return builder.Build();
}
public static TransformOperations BuildScaleTransform(double scale)
{
return BuildScaleTransform(scale, scale);
}
public static TransformOperations BuildScaleXTransform(double scale)
{
return BuildScaleTransform(scale, 1.0);
}
public static TransformOperations BuildScaleYTransform(double scale)
{
return BuildScaleTransform(1.0, scale);
}
public static TransformOperations BuildTranslateTransform(double offsetX, double offsetY)
{
var builder = new TransformOperations.Builder(1);
builder.AppendTranslate(offsetX, offsetY);
return builder.Build();
}
public static TransformOperations BuildTranslateScaleAndTransform(double scaleX, double scaleY, double offsetX, double offsetY)
{
var builder = new TransformOperations.Builder(2);
builder.AppendScale(scaleX, scaleY);
builder.AppendTranslate(offsetX, offsetY);
return builder.Build();
}
}

View File

@ -1,5 +1,4 @@
using Avalonia;
using Avalonia.Controls;
using System.Reactive.Disposables;
using Avalonia.Threading;
namespace AtomUI.MotionScene;
@ -7,43 +6,74 @@ namespace AtomUI.MotionScene;
internal static class MotionInvoker
{
public static void Invoke(MotionActorControl actor,
MotionConfig motionConfig,
AbstractMotion motion,
Action? aboutToStart = null,
Action? completedAction = null,
CancellationToken cancellationToken = default)
{
Dispatcher.UIThread.InvokeAsync(async () =>
Dispatcher.UIThread.Invoke(async () =>
{
using var originRestore = new RenderTransformOriginRestore(actor);
actor.RenderTransformOrigin = motionConfig.RenderTransformOrigin;
if (aboutToStart != null)
{
aboutToStart();
}
foreach (var animation in motionConfig.Animations)
{
await animation.RunAsync(actor, cancellationToken);
}
if (completedAction != null)
{
completedAction();
}
await motion.RunAsync(actor, aboutToStart, completedAction, cancellationToken);
});
}
public static async Task InvokeAsync(MotionActorControl actor,
AbstractMotion motion,
Action? aboutToStart = null,
Action? completedAction = null,
CancellationToken cancellationToken = default)
{
await motion.RunAsync(actor, aboutToStart, completedAction, cancellationToken);
}
public static void InvokeInPopupLayer(SceneMotionActorControl actor,
MotionConfig motionConfig,
AbstractMotion motion,
Action? aboutToStart = null,
Action? completedAction = null,
CancellationToken cancellationToken = default)
{
SceneLayer? sceneLayer = PrepareSceneLayer(actor);
Dispatcher.UIThread.Invoke(async () =>
{
await InvokeInPopupLayerAsync(actor, motion, aboutToStart, completedAction, cancellationToken);
});
}
private static SceneLayer PrepareSceneLayer(SceneMotionActorControl actor)
public static async Task InvokeInPopupLayerAsync(SceneMotionActorControl actor,
AbstractMotion motion,
Action? aboutToStart = null,
Action? completedAction = null,
CancellationToken cancellationToken = default)
{
SceneLayer sceneLayer = PrepareSceneLayer(motion, actor);
var compositeDisposable = new CompositeDisposable();
compositeDisposable.Add(Disposable.Create(sceneLayer, (state) =>
{
Dispatcher.UIThread.Invoke(async () =>
{
await Task.Delay(300);
sceneLayer.Hide();
sceneLayer.Dispose();
});
}));
var ghost = actor.GetAnimatableGhost();
sceneLayer.SetMotionTarget(ghost);
actor.NotifyMotionTargetAddedToScene(ghost);
sceneLayer.Show();
sceneLayer.Topmost = true;
actor.NotifySceneShowed();
await motion.RunAsync(actor, aboutToStart, () =>
{
if (completedAction is not null)
{
completedAction();
}
compositeDisposable.Dispose();
}, cancellationToken);
}
private static SceneLayer PrepareSceneLayer(AbstractMotion motion, SceneMotionActorControl actor)
{
if (actor.SceneParent is null)
{
@ -54,24 +84,7 @@ internal static class MotionInvoker
// TODO 这里除了 Popup 这种顶层元素以外,还会不会有其他的顶层元素种类
// 暂时先处理 Popup 这种情况
var sceneLayer = new SceneLayer(actor.SceneParent, actor.SceneParent.PlatformImpl!.CreatePopup()!);
actor.NotifySceneLayerCreated(sceneLayer);
actor.NotifySceneLayerCreated(motion, sceneLayer);
return sceneLayer;
}
}
internal class RenderTransformOriginRestore : IDisposable
{
RelativePoint _origin;
Control _target;
public RenderTransformOriginRestore(Control target)
{
_target = target;
_origin = target.RenderTransformOrigin;
}
public void Dispose()
{
_target.RenderTransformOrigin = _origin;
}
}

View File

@ -13,7 +13,7 @@ internal class MoveDownInMotion : AbstractMotion
public MoveDownInMotion(double offset,
TimeSpan duration,
Easing? easing = null,
FillMode fillMode = FillMode.None)
FillMode fillMode = FillMode.Forward)
: base(duration, easing ?? new QuinticEaseOut(), fillMode)
{
Offset = offset;
@ -97,7 +97,7 @@ internal class MoveDownOutMotion : AbstractMotion
public MoveDownOutMotion(double offset,
TimeSpan duration,
Easing? easing = null,
FillMode fillMode = FillMode.None)
FillMode fillMode = FillMode.Forward)
: base(duration, easing ?? new QuinticEaseIn(), fillMode)
{
Offset = offset;
@ -182,7 +182,7 @@ internal class MoveUpInMotion : AbstractMotion
public MoveUpInMotion(double offset,
TimeSpan duration,
Easing? easing = null,
FillMode fillMode = FillMode.None)
FillMode fillMode = FillMode.Forward)
: base(duration, easing ?? new QuinticEaseOut(), fillMode)
{
Offset = offset;
@ -266,7 +266,7 @@ internal class MoveUpOutMotion : AbstractMotion
public MoveUpOutMotion(double offset,
TimeSpan duration,
Easing? easing = null,
FillMode fillMode = FillMode.None)
FillMode fillMode = FillMode.Forward)
: base(duration, easing ?? new QuinticEaseIn(), fillMode)
{
Offset = offset;
@ -360,7 +360,7 @@ internal class MoveLeftInMotion : AbstractMotion
public MoveLeftInMotion(double offset,
TimeSpan duration,
Easing? easing = null,
FillMode fillMode = FillMode.None)
FillMode fillMode = FillMode.Forward)
: base(duration, easing ?? new QuinticEaseOut(), fillMode)
{
Offset = offset;
@ -447,7 +447,7 @@ internal class MoveLeftOutMotion : AbstractMotion
public MoveLeftOutMotion(double offset,
TimeSpan duration,
Easing? easing = null,
FillMode fillMode = FillMode.None)
FillMode fillMode = FillMode.Forward)
: base(duration, easing ?? new QuinticEaseIn(), fillMode)
{
Offset = offset;
@ -530,7 +530,7 @@ internal class MoveRightInMotion : AbstractMotion
public MoveRightInMotion(double offset,
TimeSpan duration,
Easing? easing = null,
FillMode fillMode = FillMode.None)
FillMode fillMode = FillMode.Forward)
: base(duration, easing ?? new QuinticEaseOut(), fillMode)
{
Offset = offset;
@ -615,7 +615,7 @@ internal class MoveRightOutMotion : AbstractMotion
public MoveRightOutMotion(double offset,
TimeSpan duration,
Easing? easing = null,
FillMode fillMode = FillMode.None)
FillMode fillMode = FillMode.Forward)
: base(duration, easing ?? new QuinticEaseIn(), fillMode)
{
Offset = offset;

View File

@ -6,6 +6,8 @@ namespace AtomUI.MotionScene;
internal class SceneMotionActorControl : MotionActorControl
{
public event EventHandler? SceneShowed;
#region
/// <summary>
@ -25,12 +27,17 @@ internal class SceneMotionActorControl : MotionActorControl
{
return _ghost ?? this;
}
protected virtual Point CalculateTopLevelGhostPosition()
{
return default;
}
/// <summary>
/// 在这个接口中Actor 根据自己的需求对 sceneLayer 进行设置,主要就是位置和大小
/// </summary>
/// <param name="sceneLayer"></param>
public virtual void NotifySceneLayerCreated(SceneLayer sceneLayer)
public virtual void NotifySceneLayerCreated(AbstractMotion motion, SceneLayer sceneLayer)
{
var ghost = GetAnimatableGhost();
@ -46,9 +53,9 @@ internal class SceneMotionActorControl : MotionActorControl
motionTargetSize = ghost.DesiredSize;
}
// var sceneSize = _motion.CalculateSceneSize(motionTargetSize);
// var scenePosition = _motion.CalculateScenePosition(motionTargetSize, CalculateGhostPosition());
// sceneLayer.MoveAndResize(scenePosition, sceneSize);
var sceneSize = motion.CalculateSceneSize(motionTargetSize);
var scenePosition = motion.CalculateScenePosition(motionTargetSize, CalculateTopLevelGhostPosition());
sceneLayer.MoveAndResize(scenePosition, sceneSize);
}
/// <summary>
@ -61,11 +68,8 @@ internal class SceneMotionActorControl : MotionActorControl
Canvas.SetTop(motionTarget, 0);
}
internal virtual void NotifyMotionStarted()
{
}
internal virtual void NotifyMotionCompleted()
public virtual void NotifySceneShowed()
{
SceneShowed?.Invoke(this, EventArgs.Empty);
}
}

View File

@ -5,21 +5,18 @@ using Avalonia.Styling;
namespace AtomUI.MotionScene;
internal static partial class MotionFactory
internal class SlideUpInMotion : AbstractMotion
{
public static MotionConfig BuildSlideUpInMotion(TimeSpan duration, Easing? easing = null,
FillMode fillMode = FillMode.None)
public SlideUpInMotion(TimeSpan duration,
Easing? easing = null,
FillMode fillMode = FillMode.Forward)
: base(duration, easing ?? new CubicEaseOut(), fillMode)
{
easing ??= new CubicEaseOut();
var animations = new List<Animation>();
RelativePoint transformOrigin = default;
var animation = new Animation
{
Duration = duration,
Easing = easing,
FillMode = fillMode
};
}
protected override void Configure()
{
var animation = CreateAnimation();
var startFrame = new KeyFrame
{
Cue = new Cue(0.0)
@ -59,25 +56,24 @@ internal static partial class MotionFactory
endFrame.Setters.Add(scaleYSetter);
}
animation.Children.Add(endFrame);
transformOrigin = new RelativePoint(0.0, 0.0, RelativeUnit.Relative);
RenderTransformOrigin = new RelativePoint(0.0, 0.0, RelativeUnit.Relative);
animations.Add(animation);
return new MotionConfig(transformOrigin, animations);
Animations.Add(animation);
}
}
internal class SlideUpOutMotion : AbstractMotion
{
public SlideUpOutMotion(TimeSpan duration,
Easing? easing = null,
FillMode fillMode = FillMode.Forward)
: base(duration, easing ?? new CubicEaseIn(), fillMode)
{
}
public static MotionConfig BuildSlideUpOutMotion(TimeSpan duration, Easing? easing = null,
FillMode fillMode = FillMode.None)
protected override void Configure()
{
easing ??= new CubicEaseIn();
var animations = new List<Animation>();
RelativePoint transformOrigin = default;
var animation = new Animation
{
Duration = duration,
Easing = easing,
FillMode = fillMode
};
var animation = CreateAnimation();
var startFrame = new KeyFrame
{
Cue = new Cue(0.0)
@ -118,25 +114,24 @@ internal static partial class MotionFactory
endFrame.Setters.Add(scaleYSetter);
}
animation.Children.Add(endFrame);
transformOrigin = new RelativePoint(0.0, 0.0, RelativeUnit.Relative);
RenderTransformOrigin = new RelativePoint(0.0, 0.0, RelativeUnit.Relative);
animations.Add(animation);
return new MotionConfig(transformOrigin, animations);
Animations.Add(animation);
}
}
internal class SlideDownInMotion : AbstractMotion
{
public SlideDownInMotion(TimeSpan duration,
Easing? easing = null,
FillMode fillMode = FillMode.Forward)
: base(duration, easing ?? new CubicEaseOut(), fillMode)
{
}
public static MotionConfig BuildSlideDownInMotion(TimeSpan duration, Easing? easing = null,
FillMode fillMode = FillMode.None)
protected override void Configure()
{
easing ??= new CubicEaseOut();
var animations = new List<Animation>();
RelativePoint transformOrigin = default;
var animation = new Animation
{
Duration = duration,
Easing = easing,
FillMode = fillMode
};
var animation = CreateAnimation();
var startFrame = new KeyFrame
{
Cue = new Cue(0.0)
@ -177,25 +172,24 @@ internal static partial class MotionFactory
endFrame.Setters.Add(scaleYSetter);
}
animation.Children.Add(endFrame);
transformOrigin = new RelativePoint(1.0, 1.0, RelativeUnit.Relative);
RenderTransformOrigin = new RelativePoint(1.0, 1.0, RelativeUnit.Relative);
animations.Add(animation);
return new MotionConfig(transformOrigin, animations);
Animations.Add(animation);
}
}
internal class SlideDownOutMotion : AbstractMotion
{
public SlideDownOutMotion(TimeSpan duration,
Easing? easing = null,
FillMode fillMode = FillMode.Forward)
: base(duration, easing ?? new CubicEaseIn(), fillMode)
{
}
public static MotionConfig BuildSlideDownOutMotion(TimeSpan duration, Easing? easing = null,
FillMode fillMode = FillMode.None)
protected override void Configure()
{
easing ??= new CubicEaseIn();
var animations = new List<Animation>();
RelativePoint transformOrigin = default;
var animation = new Animation
{
Duration = duration,
Easing = easing,
FillMode = fillMode
};
var animation = CreateAnimation();
var startFrame = new KeyFrame
{
Cue = new Cue(0.0)
@ -236,25 +230,24 @@ internal static partial class MotionFactory
endFrame.Setters.Add(scaleYSetter);
}
animation.Children.Add(endFrame);
transformOrigin = new RelativePoint(1.0, 1.0, RelativeUnit.Relative);
RenderTransformOrigin = new RelativePoint(1.0, 1.0, RelativeUnit.Relative);
animations.Add(animation);
return new MotionConfig(transformOrigin, animations);
Animations.Add(animation);
}
}
internal class SlideLeftInMotion : AbstractMotion
{
public SlideLeftInMotion(TimeSpan duration,
Easing? easing = null,
FillMode fillMode = FillMode.Forward)
: base(duration, easing ?? new CubicEaseOut(), fillMode)
{
}
public static MotionConfig BuildSlideLeftInMotion(TimeSpan duration, Easing? easing = null,
FillMode fillMode = FillMode.None)
protected override void Configure()
{
easing ??= new CubicEaseOut();
var animations = new List<Animation>();
RelativePoint transformOrigin = default;
var animation = new Animation
{
Duration = duration,
Easing = easing,
FillMode = fillMode
};
var animation = CreateAnimation();
var startFrame = new KeyFrame
{
Cue = new Cue(0.0)
@ -295,25 +288,24 @@ internal static partial class MotionFactory
endFrame.Setters.Add(scaleXSetter);
}
animation.Children.Add(endFrame);
transformOrigin = new RelativePoint(0.0, 0.0, RelativeUnit.Relative);
RenderTransformOrigin = new RelativePoint(0.0, 0.0, RelativeUnit.Relative);
animations.Add(animation);
return new MotionConfig(transformOrigin, animations);
Animations.Add(animation);
}
}
internal class SlideLeftOutMotion : AbstractMotion
{
public SlideLeftOutMotion(TimeSpan duration,
Easing? easing = null,
FillMode fillMode = FillMode.Forward)
: base(duration, easing ?? new CubicEaseIn(), fillMode)
{
}
public static MotionConfig BuildSlideLeftOutMotion(TimeSpan duration, Easing? easing = null,
FillMode fillMode = FillMode.None)
protected override void Configure()
{
easing ??= new CubicEaseIn();
var animations = new List<Animation>();
RelativePoint transformOrigin = default;
var animation = new Animation
{
Duration = duration,
Easing = easing,
FillMode = fillMode
};
var animation = CreateAnimation();
var startFrame = new KeyFrame
{
Cue = new Cue(0.0)
@ -354,25 +346,82 @@ internal static partial class MotionFactory
endFrame.Setters.Add(scaleXSetter);
}
animation.Children.Add(endFrame);
transformOrigin = new RelativePoint(0.0, 0.0, RelativeUnit.Relative);
RenderTransformOrigin = new RelativePoint(0.0, 0.0, RelativeUnit.Relative);
animations.Add(animation);
return new MotionConfig(transformOrigin, animations);
Animations.Add(animation);
}
}
internal class SlideRightInMotion : AbstractMotion
{
public SlideRightInMotion(TimeSpan duration,
Easing? easing = null,
FillMode fillMode = FillMode.Forward)
: base(duration, easing ?? new CubicEaseOut(), fillMode)
{
}
public static MotionConfig BuildSlideRightInMotion(TimeSpan duration, Easing? easing = null,
FillMode fillMode = FillMode.None)
protected override void Configure()
{
easing ??= new CubicEaseOut();
var animations = new List<Animation>();
RelativePoint transformOrigin = default;
var animation = new Animation
var animation = CreateAnimation();
var startFrame = new KeyFrame
{
Duration = duration,
Easing = easing,
FillMode = fillMode
Cue = new Cue(0.0)
};
{
var opacitySetter = new Setter
{
Property = Visual.OpacityProperty,
Value = 1.0
};
startFrame.Setters.Add(opacitySetter);
var scaleXSetter = new Setter
{
Property = MotionActorControl.MotionTransformProperty,
Value = BuildScaleXTransform(1.0)
};
startFrame.Setters.Add(scaleXSetter);
}
animation.Children.Add(startFrame);
var endFrame = new KeyFrame
{
Cue = new Cue(1.0)
};
{
var opacitySetter = new Setter
{
Property = Visual.OpacityProperty,
Value = 0.0
};
endFrame.Setters.Add(opacitySetter);
var scaleXSetter = new Setter
{
Property = MotionActorControl.MotionTransformProperty,
Value = BuildScaleXTransform(0.0)
};
endFrame.Setters.Add(scaleXSetter);
}
animation.Children.Add(endFrame);
RenderTransformOrigin = new RelativePoint(1.0, 0.0, RelativeUnit.Relative);
Animations.Add(animation);
}
}
internal class SlideRightOutMotion : AbstractMotion
{
public SlideRightOutMotion(TimeSpan duration,
Easing? easing = null,
FillMode fillMode = FillMode.Forward)
: base(duration, easing ?? new CubicEaseIn(), fillMode)
{
}
protected override void Configure()
{
var animation = CreateAnimation();
var startFrame = new KeyFrame
{
Cue = new Cue(0.0)
@ -413,68 +462,8 @@ internal static partial class MotionFactory
endFrame.Setters.Add(scaleXSetter);
}
animation.Children.Add(endFrame);
transformOrigin = new RelativePoint(1.0, 0.0, RelativeUnit.Relative);
RenderTransformOrigin = new RelativePoint(1.0, 0.0, RelativeUnit.Relative);
animations.Add(animation);
return new MotionConfig(transformOrigin, animations);
Animations.Add(animation);
}
public static MotionConfig BuildSlideRightOutMotion(TimeSpan duration, Easing? easing = null,
FillMode fillMode = FillMode.None)
{
easing ??= new CubicEaseIn();
var animations = new List<Animation>();
RelativePoint transformOrigin = default;
var animation = new Animation
{
Duration = duration,
Easing = easing,
FillMode = fillMode
};
var startFrame = new KeyFrame
{
Cue = new Cue(0.0)
};
{
var opacitySetter = new Setter
{
Property = Visual.OpacityProperty,
Value = 1.0
};
startFrame.Setters.Add(opacitySetter);
var scaleXSetter = new Setter
{
Property = MotionActorControl.MotionTransformProperty,
Value = BuildScaleXTransform(1.0)
};
startFrame.Setters.Add(scaleXSetter);
}
animation.Children.Add(startFrame);
var endFrame = new KeyFrame
{
Cue = new Cue(1.0)
};
{
var opacitySetter = new Setter
{
Property = Visual.OpacityProperty,
Value = 0.0
};
endFrame.Setters.Add(opacitySetter);
var scaleXSetter = new Setter
{
Property = MotionActorControl.MotionTransformProperty,
Value = BuildScaleXTransform(0.0)
};
endFrame.Setters.Add(scaleXSetter);
}
animation.Children.Add(endFrame);
transformOrigin = new RelativePoint(1.0, 0.0, RelativeUnit.Relative);
animations.Add(animation);
return new MotionConfig(transformOrigin, animations);
}
}
}

View File

@ -6,21 +6,18 @@ using Avalonia.Styling;
namespace AtomUI.Controls.Badge;
internal static class BadgeMotionFactory
internal class BadgeZoomBadgeInMotion : AbstractMotion
{
public static MotionConfig BuildBadgeZoomBadgeInMotion(TimeSpan duration, Easing? easing = null,
FillMode fillMode = FillMode.None)
public BadgeZoomBadgeInMotion(TimeSpan duration,
Easing? easing = null,
FillMode fillMode = FillMode.Forward)
: base(duration, easing ?? new ExponentialEaseOut(), fillMode)
{
easing ??= new ExponentialEaseOut();
var animations = new List<Animation>();
RelativePoint transformOrigin = default;
var animation = new Animation
{
Duration = duration,
Easing = easing,
FillMode = fillMode
};
}
protected override void Configure()
{
var animation = CreateAnimation();
var startFrame = new KeyFrame
{
Cue = new Cue(0.0)
@ -36,7 +33,7 @@ internal static class BadgeMotionFactory
var transformSetter = new Setter
{
Property = MotionActorControl.MotionTransformProperty,
Value = MotionFactory.BuildScaleTransform(0.0)
Value = BuildScaleTransform(0.01)
};
startFrame.Setters.Add(transformSetter);
}
@ -57,30 +54,29 @@ internal static class BadgeMotionFactory
var transformSetter = new Setter
{
Property = MotionActorControl.MotionTransformProperty,
Value = MotionFactory.BuildScaleTransform(1.0)
Value = BuildScaleTransform(1.0)
};
endFrame.Setters.Add(transformSetter);
}
animation.Children.Add(endFrame);
transformOrigin = new RelativePoint(0.5, 0.5, RelativeUnit.Relative);
RenderTransformOrigin = new RelativePoint(0.5, 0.5, RelativeUnit.Relative);
animations.Add(animation);
return new MotionConfig(transformOrigin, animations);
Animations.Add(animation);
}
}
internal class BadgeZoomBadgeOutMotion : AbstractMotion
{
public BadgeZoomBadgeOutMotion(TimeSpan duration,
Easing? easing = null,
FillMode fillMode = FillMode.Forward)
: base(duration, easing ?? new ExponentialEaseIn(), fillMode)
{
}
public static MotionConfig BuildBadgeZoomBadgeOutMotion(TimeSpan duration, Easing? easing = null,
FillMode fillMode = FillMode.None)
protected override void Configure()
{
easing ??= new ExponentialEaseIn();
var animations = new List<Animation>();
RelativePoint transformOrigin = default;
var animation = new Animation
{
Duration = duration,
Easing = easing,
FillMode = fillMode
};
var animation = CreateAnimation();
var startFrame = new KeyFrame
{
Cue = new Cue(0.0)
@ -96,7 +92,7 @@ internal static class BadgeMotionFactory
var transformSetter = new Setter
{
Property = MotionActorControl.MotionTransformProperty,
Value = MotionFactory.BuildScaleTransform(1.0)
Value = BuildScaleTransform(1.0)
};
startFrame.Setters.Add(transformSetter);
}
@ -117,30 +113,29 @@ internal static class BadgeMotionFactory
var transformSetter = new Setter
{
Property = MotionActorControl.MotionTransformProperty,
Value = MotionFactory.BuildScaleTransform(0.0)
Value = BuildScaleTransform(0.01)
};
endFrame.Setters.Add(transformSetter);
}
animation.Children.Add(endFrame);
transformOrigin = new RelativePoint(0.5, 0.5, RelativeUnit.Relative);
RenderTransformOrigin = new RelativePoint(0.5, 0.5, RelativeUnit.Relative);
animations.Add(animation);
return new MotionConfig(transformOrigin, animations);
Animations.Add(animation);
}
}
internal class CountBadgeNoWrapperZoomBadgeInMotion : AbstractMotion
{
public CountBadgeNoWrapperZoomBadgeInMotion(TimeSpan duration,
Easing? easing = null,
FillMode fillMode = FillMode.Forward)
: base(duration, easing ?? new CircularEaseOut(), fillMode)
{
}
public static MotionConfig BuildCountBadgeNoWrapperZoomBadgeInMotion(TimeSpan duration, Easing? easing = null,
FillMode fillMode = FillMode.None)
protected override void Configure()
{
easing ??= new CircularEaseOut();
var animations = new List<Animation>();
RelativePoint transformOrigin = default;
var animation = new Animation
{
Duration = duration,
Easing = easing,
FillMode = fillMode
};
var animation = CreateAnimation();
var startFrame = new KeyFrame
{
Cue = new Cue(0.0)
@ -156,7 +151,7 @@ internal static class BadgeMotionFactory
var transformSetter = new Setter
{
Property = MotionActorControl.MotionTransformProperty,
Value = MotionFactory.BuildScaleTransform(0.0, 0.0)
Value = BuildScaleTransform(0.01, 0.01)
};
startFrame.Setters.Add(transformSetter);
}
@ -177,30 +172,29 @@ internal static class BadgeMotionFactory
var transformSetter = new Setter
{
Property = MotionActorControl.MotionTransformProperty,
Value = MotionFactory.BuildScaleTransform(1.0, 1.0)
Value = BuildScaleTransform(1.0, 1.0)
};
endFrame.Setters.Add(transformSetter);
}
animation.Children.Add(endFrame);
transformOrigin = new RelativePoint(0.0, 0.0, RelativeUnit.Relative);
RenderTransformOrigin = new RelativePoint(0.5, 0.5, RelativeUnit.Relative);
animations.Add(animation);
return new MotionConfig(transformOrigin, animations);
Animations.Add(animation);
}
}
internal class CountBadgeNoWrapperZoomBadgeOutMotion : AbstractMotion
{
public CountBadgeNoWrapperZoomBadgeOutMotion(TimeSpan duration,
Easing? easing = null,
FillMode fillMode = FillMode.Forward)
: base(duration, easing ?? new CircularEaseIn(), fillMode)
{
}
public static MotionConfig BuildCountBadgeNoWrapperZoomBadgeOutMotion(TimeSpan duration, Easing? easing = null,
FillMode fillMode = FillMode.None)
protected override void Configure()
{
easing ??= new CircularEaseIn();
var animations = new List<Animation>();
RelativePoint transformOrigin = default;
var animation = new Animation
{
Duration = duration,
Easing = easing,
FillMode = fillMode
};
var animation = CreateAnimation();
var startFrame = new KeyFrame
{
Cue = new Cue(0.0)
@ -216,7 +210,7 @@ internal static class BadgeMotionFactory
var transformSetter = new Setter
{
Property = MotionActorControl.MotionTransformProperty,
Value = MotionFactory.BuildScaleTransform(1.0, 1.0)
Value = BuildScaleTransform(1.0, 1.0)
};
startFrame.Setters.Add(transformSetter);
}
@ -237,14 +231,14 @@ internal static class BadgeMotionFactory
var transformSetter = new Setter
{
Property = MotionActorControl.MotionTransformProperty,
Value = MotionFactory.BuildScaleTransform(0.0, 0.0)
Value = BuildScaleTransform(0.01, 0.01)
};
endFrame.Setters.Add(transformSetter);
}
animation.Children.Add(endFrame);
transformOrigin = new RelativePoint(0.0, 0.0, RelativeUnit.Relative);
RenderTransformOrigin = new RelativePoint(0.5, 0.5, RelativeUnit.Relative);
animations.Add(animation);
return new MotionConfig(transformOrigin, animations);
Animations.Add(animation);
}
}
}

View File

@ -3,7 +3,6 @@ using AtomUI.MotionScene;
using AtomUI.Theme.Styling;
using AtomUI.Utils;
using Avalonia;
using Avalonia.Animation;
using Avalonia.Controls;
using Avalonia.Controls.Primitives;
using Avalonia.Media;
@ -231,9 +230,8 @@ internal class CountBadgeAdorner : TemplatedControl
if (_indicatorMotionActor is not null)
{
_indicatorMotionActor.IsVisible = false;
var zoomBadgeInMotionConfig = BadgeMotionFactory.BuildBadgeZoomBadgeInMotion(MotionDuration, null,
FillMode.Forward);
MotionInvoker.Invoke(_indicatorMotionActor, zoomBadgeInMotionConfig, () =>
var motion = new BadgeZoomBadgeInMotion(MotionDuration);
MotionInvoker.Invoke(_indicatorMotionActor, motion, () =>
{
_indicatorMotionActor.IsVisible = true;
}, null, _motionCancellationTokenSource!.Token);
@ -244,12 +242,11 @@ internal class CountBadgeAdorner : TemplatedControl
{
if (_indicatorMotionActor is not null)
{
var zoomBadgeOutMotionConfig = BadgeMotionFactory.BuildBadgeZoomBadgeOutMotion(MotionDuration, null,
FillMode.Forward);
var motion = new BadgeZoomBadgeOutMotion(MotionDuration);
_motionCancellationTokenSource?.Cancel();
_motionCancellationTokenSource = new CancellationTokenSource();
MotionInvoker.Invoke(_indicatorMotionActor, zoomBadgeOutMotionConfig, null, () =>
MotionInvoker.Invoke(_indicatorMotionActor, motion, null, () =>
{
completedAction();
}, _motionCancellationTokenSource.Token);

View File

@ -15,13 +15,13 @@ internal class DotBadgeAdorner : TemplatedControl
public static readonly StyledProperty<IBrush?> BadgeDotColorProperty =
AvaloniaProperty.Register<DotBadgeAdorner, IBrush?>(
nameof(BadgeDotColor));
public static readonly DirectProperty<DotBadgeAdorner, DotBadgeStatus?> StatusProperty =
AvaloniaProperty.RegisterDirect<DotBadgeAdorner, DotBadgeStatus?>(
nameof(Status),
o => o.Status,
(o, v) => o.Status = v);
internal IBrush? BadgeDotColor
{
get => GetValue(BadgeDotColorProperty);
@ -73,7 +73,7 @@ internal class DotBadgeAdorner : TemplatedControl
get => GetValue(OffsetProperty);
set => SetValue(OffsetProperty, value);
}
#region
internal static readonly StyledProperty<TimeSpan> MotionDurationProperty =
@ -85,9 +85,9 @@ internal class DotBadgeAdorner : TemplatedControl
get => GetValue(MotionDurationProperty);
set => SetValue(MotionDurationProperty, value);
}
#endregion
private MotionActorControl? _indicatorMotionActor;
private CancellationTokenSource? _motionCancellationTokenSource;
@ -109,28 +109,23 @@ internal class DotBadgeAdorner : TemplatedControl
if (_indicatorMotionActor is not null)
{
_indicatorMotionActor.IsVisible = false;
var zoomBadgeInMotionConfig = BadgeMotionFactory.BuildBadgeZoomBadgeInMotion(MotionDuration, null,
FillMode.Forward);
MotionInvoker.Invoke(_indicatorMotionActor, zoomBadgeInMotionConfig, () =>
{
_indicatorMotionActor.IsVisible = true;
}, null, _motionCancellationTokenSource!.Token);
_motionCancellationTokenSource?.Cancel();
_motionCancellationTokenSource = new CancellationTokenSource();
var motion = new BadgeZoomBadgeInMotion(MotionDuration, null, FillMode.Forward);
MotionInvoker.Invoke(_indicatorMotionActor, motion, () => _indicatorMotionActor.IsVisible = true,
null, _motionCancellationTokenSource.Token);
}
}
private void ApplyHideMotion(Action completedAction)
{
if (_indicatorMotionActor is not null)
{
var zoomBadgeOutMotionConfig = BadgeMotionFactory.BuildBadgeZoomBadgeOutMotion(MotionDuration, null,
FillMode.Forward);
_motionCancellationTokenSource?.Cancel();
_motionCancellationTokenSource = new CancellationTokenSource();
MotionInvoker.Invoke(_indicatorMotionActor, zoomBadgeOutMotionConfig, null, () =>
{
completedAction();
}, _motionCancellationTokenSource.Token);
_motionCancellationTokenSource = new CancellationTokenSource();
var motion = new BadgeZoomBadgeOutMotion(MotionDuration, null, FillMode.Forward);
MotionInvoker.Invoke(_indicatorMotionActor, motion, null, () => completedAction(),
_motionCancellationTokenSource.Token);
}
}
@ -145,7 +140,7 @@ internal class DotBadgeAdorner : TemplatedControl
}
}
}
private void SetupBadgeColor()
{
if (Status is not null)
@ -190,6 +185,7 @@ internal class DotBadgeAdorner : TemplatedControl
offsetY -= dotSize.Height / 3;
_indicatorMotionActor.Arrange(new Rect(new Point(offsetX, offsetY), dotSize));
}
return size;
}
@ -199,16 +195,16 @@ internal class DotBadgeAdorner : TemplatedControl
{
return;
}
adornerLayer.Children.Remove(this);
AdornerLayer.SetAdornedElement(this, adorned);
AdornerLayer.SetIsClipEnabled(this, false);
adornerLayer.Children.Add(this);
_motionCancellationTokenSource?.Cancel();
_motionCancellationTokenSource = new CancellationTokenSource();
_motionCancellationTokenSource = new CancellationTokenSource();
ApplyShowMotion();
}

View File

@ -3,7 +3,6 @@ using AtomUI.MotionScene;
using AtomUI.Theme.Styling;
using AtomUI.Utils;
using Avalonia;
using Avalonia.Animation;
using Avalonia.Animation.Easings;
using Avalonia.Automation.Peers;
using Avalonia.Controls;
@ -308,9 +307,8 @@ public class CollapseItem : HeaderedContentControl, ISelectable
}
InAnimating = true;
var slideDownInMotionConfig = MotionFactory.BuildSlideUpInMotion(MotionDuration, new CubicEaseOut(),
FillMode.Forward);
MotionInvoker.Invoke(_motionActor, slideDownInMotionConfig, () =>
var motion = new SlideUpInMotion(MotionDuration, new CubicEaseOut());
MotionInvoker.Invoke(_motionActor, motion, () =>
{
_motionActor.SetCurrentValue(IsVisibleProperty, true);
}, () =>
@ -333,9 +331,8 @@ public class CollapseItem : HeaderedContentControl, ISelectable
}
InAnimating = true;
var slideDownOutMotionConfig = MotionFactory.BuildSlideUpOutMotion(MotionDuration, new CubicEaseIn(),
FillMode.Forward);
MotionInvoker.Invoke(_motionActor, slideDownOutMotionConfig, null, () =>
var motion = new SlideUpOutMotion(MotionDuration, new CubicEaseIn());
MotionInvoker.Invoke(_motionActor, motion, null, () =>
{
_motionActor.SetCurrentValue(IsVisibleProperty, false);
InAnimating = false;

View File

@ -4,7 +4,6 @@ using AtomUI.Theme.Data;
using AtomUI.Theme.Styling;
using AtomUI.Utils;
using Avalonia;
using Avalonia.Animation;
using Avalonia.Animation.Easings;
using Avalonia.Controls;
using Avalonia.Controls.Primitives;
@ -194,6 +193,7 @@ public class Expander : AvaloniaExpander
{
return;
}
IsExpanded = !IsExpanded;
};
}
@ -294,6 +294,7 @@ public class Expander : AvaloniaExpander
{
return;
}
IsExpanded = !IsExpanded;
}
}
@ -311,52 +312,46 @@ public class Expander : AvaloniaExpander
CollapseItemContent();
}
}
private void ExpandItemContent()
{
if (_motionActor is null || _animating)
{
return;
}
if (!_enableAnimation)
{
_motionActor.IsVisible = true;
return;
}
_animating = true;
var expandMotionConfig = MotionFactory.BuildExpandMotion(DirectionFromExpandDirection(ExpandDirection),
var motion = new ExpandMotion(DirectionFromExpandDirection(ExpandDirection),
MotionDuration,
new CubicEaseOut(),
FillMode.Forward);
MotionInvoker.Invoke(_motionActor, expandMotionConfig, () =>
{
_motionActor.SetCurrentValue(IsVisibleProperty, true);
}, () =>
{
_animating = false;
});
new CubicEaseOut());
MotionInvoker.Invoke(_motionActor, motion, () => { _motionActor.SetCurrentValue(IsVisibleProperty, true); },
() => { _animating = false; });
}
private void CollapseItemContent()
{
if (_motionActor is null || _animating)
{
return;
}
if (!_enableAnimation)
{
_motionActor.IsVisible = false;
return;
}
_animating = true;
var slideDownOutMotionConfig = MotionFactory.BuildCollapseMotion(DirectionFromExpandDirection(ExpandDirection),
var motion = new CollapseMotion(DirectionFromExpandDirection(ExpandDirection),
MotionDuration,
new CubicEaseIn(),
FillMode.Forward);
MotionInvoker.Invoke(_motionActor, slideDownOutMotionConfig, null, () =>
new CubicEaseIn());
MotionInvoker.Invoke(_motionActor, motion, null, () =>
{
_motionActor.SetCurrentValue(IsVisibleProperty, false);
_animating = false;
@ -371,6 +366,8 @@ public class Expander : AvaloniaExpander
ExpandDirection.Up => Direction.Top,
ExpandDirection.Right => Direction.Right,
ExpandDirection.Down => Direction.Bottom,
_ => throw new ArgumentOutOfRangeException(nameof(expandDirection), expandDirection,
"Invalid value for ExpandDirection")
};
}

View File

@ -3,7 +3,6 @@ using AtomUI.MotionScene;
using AtomUI.Theme.Styling;
using AtomUI.Utils;
using Avalonia;
using Avalonia.Animation;
using Avalonia.Animation.Easings;
using Avalonia.Controls;
using Avalonia.Controls.Metadata;
@ -198,9 +197,8 @@ public class MessageCard : TemplatedControl
if (_motionActor is not null)
{
_motionActor.IsVisible = false;
var moveUpInMotionConfig = MotionFactory.BuildMoveUpInMotion(AnimationMaxOffsetY, _openCloseMotionDuration, new CubicEaseOut(),
FillMode.Forward);
MotionInvoker.Invoke(_motionActor, moveUpInMotionConfig, () =>
var motion = new MoveUpInMotion(AnimationMaxOffsetY, _openCloseMotionDuration, new CubicEaseOut());
MotionInvoker.Invoke(_motionActor, motion, () =>
{
_motionActor.IsVisible = true;
});
@ -211,9 +209,8 @@ public class MessageCard : TemplatedControl
{
if (_motionActor is not null)
{
var moveUpOutMotionConfig = MotionFactory.BuildMoveUpOutMotion(AnimationMaxOffsetY, _openCloseMotionDuration, new CubicEaseIn(),
FillMode.Forward);
MotionInvoker.Invoke(_motionActor, moveUpOutMotionConfig, null, () =>
var motion = new MoveUpOutMotion(AnimationMaxOffsetY, _openCloseMotionDuration, new CubicEaseIn());
MotionInvoker.Invoke(_motionActor, motion, null, () =>
{
IsClosed = true;
});

View File

@ -87,7 +87,7 @@ public class NavMenuItem : HeaderedSelectingItemsControl,
/// </summary>
public static readonly StyledProperty<bool> IsCheckedProperty =
AvaloniaProperty.Register<NavMenuItem, bool>(nameof(IsChecked));
/// <summary>
/// Defines the <see cref="Level"/> property.
/// </summary>
@ -182,6 +182,7 @@ public class NavMenuItem : HeaderedSelectingItemsControl,
}
private bool _hasSubMenu;
/// <summary>
/// Gets or sets a value that indicates whether the <see cref="NavMenuItem"/> has a submenu.
/// </summary>
@ -190,8 +191,9 @@ public class NavMenuItem : HeaderedSelectingItemsControl,
get => _hasSubMenu;
set => SetAndRaise(HasSubMenuProperty, ref _hasSubMenu, value);
}
private int _level;
/// <summary>
/// Gets the level/indentation of the item.
/// </summary>
@ -208,7 +210,7 @@ public class NavMenuItem : HeaderedSelectingItemsControl,
/// <inheritdoc/>
bool INavMenuItem.IsPointerOverSubMenu => _popup?.IsPointerOverPopup ?? false;
/// <summary>
/// 获取或者设置菜单项的 Key
/// </summary>
@ -243,91 +245,96 @@ public class NavMenuItem : HeaderedSelectingItemsControl,
internal static readonly StyledProperty<double> ActiveBarWidthProperty =
NavMenu.ActiveBarWidthProperty.AddOwner<NavMenuItem>();
internal static readonly StyledProperty<double> ActiveBarHeightProperty =
NavMenu.ActiveBarHeightProperty.AddOwner<NavMenuItem>();
internal static readonly DirectProperty<NavMenuItem, double> EffectiveActiveBarWidthProperty =
AvaloniaProperty.RegisterDirect<NavMenuItem, double>(nameof(EffectiveActiveBarWidth),
o => o.EffectiveActiveBarWidth,
o => o.EffectiveActiveBarWidth,
(o, v) => o.EffectiveActiveBarWidth = v);
internal static readonly DirectProperty<NavMenuItem, double> EffectivePopupMinWidthProperty =
AvaloniaProperty.RegisterDirect<NavMenuItem, double>(nameof(EffectivePopupMinWidth),
o => o.EffectivePopupMinWidth,
o => o.EffectivePopupMinWidth,
(o, v) => o.EffectivePopupMinWidth = v);
internal static readonly DirectProperty<NavMenuItem, double> PopupMinWidthProperty =
AvaloniaProperty.RegisterDirect<NavMenuItem, double>(nameof(PopupMinWidth),
o => o.PopupMinWidth,
o => o.PopupMinWidth,
(o, v) => o.PopupMinWidth = v);
internal static readonly DirectProperty<NavMenuItem, NavMenuMode> ModeProperty =
AvaloniaProperty.RegisterDirect<NavMenuItem, NavMenuMode>(nameof(Mode),
o => o.Mode,
o => o.Mode,
(o, v) => o.Mode = v);
internal static readonly DirectProperty<NavMenuItem, TimeSpan> OpenCloseMotionDurationProperty =
AvaloniaProperty.RegisterDirect<NavMenuItem, TimeSpan>(nameof(OpenCloseMotionDuration),
o => o.OpenCloseMotionDuration,
o => o.OpenCloseMotionDuration,
(o, v) => o.OpenCloseMotionDuration = v);
internal static readonly DirectProperty<NavMenuItem, bool> HasSubMenuProperty =
AvaloniaProperty.RegisterDirect<NavMenuItem, bool>(nameof(HasSubMenu),
o => o.HasSubMenu,
o => o.HasSubMenu,
(o, v) => o.HasSubMenu = v);
internal static readonly DirectProperty<NavMenuItem, double> InlineItemIndentUnitProperty =
AvaloniaProperty.RegisterDirect<NavMenuItem, double>(nameof(InlineItemIndentUnit),
o => o.InlineItemIndentUnit,
o => o.InlineItemIndentUnit,
(o, v) => o.InlineItemIndentUnit = v);
internal static readonly DirectProperty<NavMenuItem, bool> IsDarkStyleProperty =
AvaloniaProperty.RegisterDirect<NavMenuItem, bool>(nameof(IsDarkStyle),
o => o.IsDarkStyle,
o => o.IsDarkStyle,
(o, v) => o.IsDarkStyle = v);
internal double ActiveBarWidth
{
get => GetValue(ActiveBarWidthProperty);
set => SetValue(ActiveBarWidthProperty, value);
}
internal double ActiveBarHeight
{
get => GetValue(ActiveBarHeightProperty);
set => SetValue(ActiveBarHeightProperty, value);
}
private double _effectiveActiveBarWidth;
internal double EffectiveActiveBarWidth
{
get => _effectiveActiveBarWidth;
set => SetAndRaise(EffectiveActiveBarWidthProperty, ref _effectiveActiveBarWidth, value);
}
private double _effectivePopupMinWidth;
internal double EffectivePopupMinWidth
{
get => _effectivePopupMinWidth;
set => SetAndRaise(EffectivePopupMinWidthProperty, ref _effectivePopupMinWidth, value);
}
private double _popupMinWidth;
internal double PopupMinWidth
{
get => _popupMinWidth;
set => SetAndRaise(PopupMinWidthProperty, ref _popupMinWidth, value);
}
private TimeSpan _openCloseMotionDuration;
internal TimeSpan OpenCloseMotionDuration
{
get => _openCloseMotionDuration;
set => SetAndRaise(OpenCloseMotionDurationProperty, ref _openCloseMotionDuration, value);
}
private NavMenuMode _mode;
internal NavMenuMode Mode
{
get => _mode;
@ -341,7 +348,7 @@ public class NavMenuItem : HeaderedSelectingItemsControl,
get => _inlineItemIndentUnit;
set => SetAndRaise(InlineItemIndentUnitProperty, ref _inlineItemIndentUnit, value);
}
private bool _isDarkStyle;
internal bool IsDarkStyle
@ -349,7 +356,7 @@ public class NavMenuItem : HeaderedSelectingItemsControl,
get => _isDarkStyle;
set => SetAndRaise(IsDarkStyleProperty, ref _isDarkStyle, value);
}
#endregion
#region
@ -437,7 +444,7 @@ public class NavMenuItem : HeaderedSelectingItemsControl,
private EventHandler CanExecuteChangedHandler => _canExecuteChangeHandler ??= new(CanExecuteChanged);
#endregion
internal static PlatformKeyGestureConverter KeyGestureConverter = new();
/// <summary>
@ -472,7 +479,7 @@ public class NavMenuItem : HeaderedSelectingItemsControl,
AffectsRender<MenuItem>(BackgroundProperty);
UpdatePseudoClasses();
}
/// <summary>
/// Opens the submenu.
/// </summary>
@ -488,6 +495,7 @@ public class NavMenuItem : HeaderedSelectingItemsControl,
return;
}
}
SetCurrentValue(IsSubMenuOpenProperty, true);
}
@ -506,6 +514,7 @@ public class NavMenuItem : HeaderedSelectingItemsControl,
return;
}
}
SetCurrentValue(IsSubMenuOpenProperty, false);
}
@ -549,7 +558,7 @@ public class NavMenuItem : HeaderedSelectingItemsControl,
}
base.OnAttachedToLogicalTree(e);
Level = CalculateDistanceFromLogicalParent<NavMenu>(this) - 1;
(var command, var parameter) = (Command, CommandParameter);
@ -568,7 +577,8 @@ public class NavMenuItem : HeaderedSelectingItemsControl,
}
_isEmbeddedInMenu = parent?.FindLogicalAncestorOfType<INavMenu>(true) != null;
TokenResourceBinder.CreateTokenBinding(this, InlineItemIndentUnitProperty, NavMenuTokenResourceKey.InlineItemIndentUnit);
TokenResourceBinder.CreateTokenBinding(this, InlineItemIndentUnitProperty,
NavMenuTokenResourceKey.InlineItemIndentUnit);
}
/// <inheritdoc />
@ -637,7 +647,7 @@ public class NavMenuItem : HeaderedSelectingItemsControl,
protected virtual void OnSubmenuOpened(RoutedEventArgs e)
{
var menuItem = e.Source as NavMenuItem;
if (menuItem != null && menuItem.Parent == this)
{
// TODO 我们在这里对模式做一个区分, Inline 暂时不互斥关闭,后面有时间看是否加一个互斥的标记
@ -649,7 +659,7 @@ public class NavMenuItem : HeaderedSelectingItemsControl,
{
child.IsSubMenuOpen = false;
}
}
}
}
}
}
@ -683,13 +693,15 @@ public class NavMenuItem : HeaderedSelectingItemsControl,
SetupItemIcon();
if (Mode == NavMenuMode.Inline)
{
_childItemsLayoutTransform = e.NameScope.Find<MotionActorControl>(InlineNavMenuItemTheme.ChildItemsLayoutTransformPart);
_childItemsLayoutTransform =
e.NameScope.Find<MotionActorControl>(InlineNavMenuItemTheme.ChildItemsLayoutTransformPart);
if (_childItemsLayoutTransform is not null)
{
_childItemsLayoutTransform.SetCurrentValue(MotionActorControl.IsVisibleProperty, IsSubMenuOpen);
}
TokenResourceBinder.CreateGlobalTokenBinding(this, OpenCloseMotionDurationProperty, GlobalTokenResourceKey.MotionDurationSlow);
TokenResourceBinder.CreateGlobalTokenBinding(this, OpenCloseMotionDurationProperty,
GlobalTokenResourceKey.MotionDurationSlow);
}
if (Transitions is null)
@ -710,7 +722,7 @@ public class NavMenuItem : HeaderedSelectingItemsControl,
Icon.IconMode = IconMode.Active;
}
private void HandleHeaderFrameExit(object? sender, PointerEventArgs args)
{
if (!IsDarkStyle || Icon is null || HasSubMenu)
@ -726,7 +738,6 @@ public class NavMenuItem : HeaderedSelectingItemsControl,
{
Icon.IconMode = IconMode.Normal;
}
}
protected override void UpdateDataValidation(
@ -847,6 +858,7 @@ public class NavMenuItem : HeaderedSelectingItemsControl,
{
HeaderChanged(change);
}
if (change.Property == ParentProperty)
{
UpdatePseudoClasses();
@ -878,7 +890,8 @@ public class NavMenuItem : HeaderedSelectingItemsControl,
else if (change.Property == ModeProperty)
{
SetupItemContainerTheme(true);
} else if (change.Property == ItemCountProperty)
}
else if (change.Property == ItemCountProperty)
{
HasSubMenu = ItemCount > 0;
}
@ -901,22 +914,32 @@ public class NavMenuItem : HeaderedSelectingItemsControl,
if (Icon is not null && Icon is PathIcon menuItemIcon)
{
BindUtils.RelayBind(this, IsEnabledProperty, menuItemIcon, PathIcon.IsEnabledProperty);
TokenResourceBinder.CreateTokenBinding(menuItemIcon, PathIcon.WidthProperty, NavMenuTokenResourceKey.ItemIconSize);
TokenResourceBinder.CreateTokenBinding(menuItemIcon, PathIcon.HeightProperty, NavMenuTokenResourceKey.ItemIconSize);
TokenResourceBinder.CreateTokenBinding(menuItemIcon, PathIcon.WidthProperty,
NavMenuTokenResourceKey.ItemIconSize);
TokenResourceBinder.CreateTokenBinding(menuItemIcon, PathIcon.HeightProperty,
NavMenuTokenResourceKey.ItemIconSize);
if (IsDarkStyle)
{
TokenResourceBinder.CreateTokenBinding(menuItemIcon, PathIcon.NormalFilledBrushProperty, NavMenuTokenResourceKey.DarkItemColor);
TokenResourceBinder.CreateTokenBinding(menuItemIcon, PathIcon.SelectedFilledBrushProperty, NavMenuTokenResourceKey.DarkItemSelectedColor);
TokenResourceBinder.CreateTokenBinding(menuItemIcon, PathIcon.ActiveFilledBrushProperty, NavMenuTokenResourceKey.DarkItemHoverColor);
TokenResourceBinder.CreateTokenBinding(menuItemIcon, PathIcon.DisabledFilledBrushProperty, NavMenuTokenResourceKey.DarkItemDisabledColor);
TokenResourceBinder.CreateTokenBinding(menuItemIcon, PathIcon.NormalFilledBrushProperty,
NavMenuTokenResourceKey.DarkItemColor);
TokenResourceBinder.CreateTokenBinding(menuItemIcon, PathIcon.SelectedFilledBrushProperty,
NavMenuTokenResourceKey.DarkItemSelectedColor);
TokenResourceBinder.CreateTokenBinding(menuItemIcon, PathIcon.ActiveFilledBrushProperty,
NavMenuTokenResourceKey.DarkItemHoverColor);
TokenResourceBinder.CreateTokenBinding(menuItemIcon, PathIcon.DisabledFilledBrushProperty,
NavMenuTokenResourceKey.DarkItemDisabledColor);
}
else
{
TokenResourceBinder.CreateTokenBinding(menuItemIcon, PathIcon.NormalFilledBrushProperty, NavMenuTokenResourceKey.ItemColor);
TokenResourceBinder.CreateTokenBinding(menuItemIcon, PathIcon.SelectedFilledBrushProperty, NavMenuTokenResourceKey.ItemSelectedColor);
TokenResourceBinder.CreateTokenBinding(menuItemIcon, PathIcon.ActiveFilledBrushProperty, NavMenuTokenResourceKey.ItemHoverColor);
TokenResourceBinder.CreateTokenBinding(menuItemIcon, PathIcon.DisabledFilledBrushProperty, NavMenuTokenResourceKey.ItemDisabledColor);
TokenResourceBinder.CreateTokenBinding(menuItemIcon, PathIcon.NormalFilledBrushProperty,
NavMenuTokenResourceKey.ItemColor);
TokenResourceBinder.CreateTokenBinding(menuItemIcon, PathIcon.SelectedFilledBrushProperty,
NavMenuTokenResourceKey.ItemSelectedColor);
TokenResourceBinder.CreateTokenBinding(menuItemIcon, PathIcon.ActiveFilledBrushProperty,
NavMenuTokenResourceKey.ItemHoverColor);
TokenResourceBinder.CreateTokenBinding(menuItemIcon, PathIcon.DisabledFilledBrushProperty,
NavMenuTokenResourceKey.ItemDisabledColor);
}
}
}
@ -928,6 +951,7 @@ public class NavMenuItem : HeaderedSelectingItemsControl,
{
width = _horizontalFrame.Bounds.Width;
}
EffectiveActiveBarWidth = ActiveBarWidth * width;
}
@ -967,7 +991,7 @@ public class NavMenuItem : HeaderedSelectingItemsControl,
Focusable = true;
}
}
private void UpdatePseudoClasses()
{
PseudoClasses.Set(TopLevelPC, IsTopLevel);
@ -1041,7 +1065,8 @@ public class NavMenuItem : HeaderedSelectingItemsControl,
RaiseEvent(new RoutedEventArgs(SubmenuOpenedEvent));
PseudoClasses.Add(StdPseudoClass.Open);
OpenInlineItem();
} else
}
else
{
PseudoClasses.Remove(StdPseudoClass.Open);
CloseInlineItem();
@ -1055,6 +1080,7 @@ public class NavMenuItem : HeaderedSelectingItemsControl,
{
item.TryUpdateCanExecute();
}
RaiseEvent(new RoutedEventArgs(SubmenuOpenedEvent));
PseudoClasses.Add(StdPseudoClass.Open);
}
@ -1065,7 +1091,7 @@ public class NavMenuItem : HeaderedSelectingItemsControl,
}
}
}
internal void OpenInlineItem()
{
if (_childItemsLayoutTransform is not null)
@ -1074,20 +1100,15 @@ public class NavMenuItem : HeaderedSelectingItemsControl,
{
return;
}
_animating = true;
var slideDownInMotionConfig = MotionFactory.BuildSlideUpInMotion(_openCloseMotionDuration, new CubicEaseOut(),
FillMode.Forward);
MotionInvoker.Invoke(_childItemsLayoutTransform, slideDownInMotionConfig, () =>
{
_childItemsLayoutTransform.SetCurrentValue(IsVisibleProperty, true);
}, () =>
{
_animating = false;
});
var motion = new SlideUpInMotion(_openCloseMotionDuration, new CubicEaseOut());
MotionInvoker.Invoke(_childItemsLayoutTransform, motion,
() => { _childItemsLayoutTransform.SetCurrentValue(IsVisibleProperty, true); },
() => { _animating = false; });
}
}
internal void CloseInlineItem()
{
if (_childItemsLayoutTransform is not null)
@ -1096,11 +1117,10 @@ public class NavMenuItem : HeaderedSelectingItemsControl,
{
return;
}
_animating = true;
var slideDownOutMotionConfig = MotionFactory.BuildSlideUpOutMotion(_openCloseMotionDuration, new CubicEaseIn(),
FillMode.Forward);
MotionInvoker.Invoke(_childItemsLayoutTransform, slideDownOutMotionConfig, null, () =>
var motion = new SlideUpOutMotion(_openCloseMotionDuration, new CubicEaseIn());
MotionInvoker.Invoke(_childItemsLayoutTransform, motion, null, () =>
{
_childItemsLayoutTransform.SetCurrentValue(IsVisibleProperty, false);
_animating = false;
@ -1173,7 +1193,7 @@ public class NavMenuItem : HeaderedSelectingItemsControl,
return true;
}
private void SetupItemContainerTheme(bool force = false)
{
if (ItemContainerTheme is null || force)
@ -1181,11 +1201,13 @@ public class NavMenuItem : HeaderedSelectingItemsControl,
_itemContainerThemeDisposable?.Dispose();
if (Mode == NavMenuMode.Inline)
{
_itemContainerThemeDisposable = TokenResourceBinder.CreateGlobalResourceBinding(this, ItemContainerThemeProperty, InlineNavMenuItemTheme.ID);
_itemContainerThemeDisposable =
TokenResourceBinder.CreateGlobalResourceBinding(this, ItemContainerThemeProperty,
InlineNavMenuItemTheme.ID);
}
}
}
protected override void PrepareContainerForItemOverride(Control element, object? item, int index)
{
base.PrepareContainerForItemOverride(element, item, index);
@ -1221,7 +1243,7 @@ public class NavMenuItem : HeaderedSelectingItemsControl,
ItemsPanel = new FuncTemplate<Panel?>(() => new StackPanel());
RefreshContainers();
}
private static int CalculateDistanceFromLogicalParent<T>(ILogical? logical, int @default = -1) where T : class
{
var result = 0;
@ -1247,5 +1269,4 @@ public class NavMenuItem : HeaderedSelectingItemsControl,
var targetRect = new Rect(offset, targetFrame.Bounds.Size);
return targetRect.Contains(point);
}
}

View File

@ -240,30 +240,30 @@ public class NotificationCard : ContentControl
return;
}
MotionConfig? motionConfig;
AbstractMotion? motion = default;
if (Position == NotificationPosition.TopLeft || Position == NotificationPosition.BottomLeft)
{
motionConfig = MotionFactory.BuildMoveLeftInMotion(AnimationMaxOffsetX, _openCloseMotionDuration, new CubicEaseOut(),
motion = new MoveLeftInMotion(AnimationMaxOffsetX, _openCloseMotionDuration, new CubicEaseOut(),
FillMode.Forward);
}
else if (Position == NotificationPosition.TopRight || Position == NotificationPosition.BottomRight)
{
motionConfig = MotionFactory.BuildMoveRightInMotion(AnimationMaxOffsetX, _openCloseMotionDuration, new CubicEaseOut(),
motion = new MoveRightInMotion(AnimationMaxOffsetX, _openCloseMotionDuration, new CubicEaseOut(),
FillMode.Forward);
}
else if (Position == NotificationPosition.TopCenter)
{
motionConfig = MotionFactory.BuildMoveUpInMotion(AnimationMaxOffsetY, _openCloseMotionDuration, new CubicEaseOut(),
motion = new MoveUpInMotion(AnimationMaxOffsetY, _openCloseMotionDuration, new CubicEaseOut(),
FillMode.Forward);
}
else
{
motionConfig = MotionFactory.BuildMoveDownInMotion(AnimationMaxOffsetY, _openCloseMotionDuration, new CubicEaseOut(),
motion = new MoveDownInMotion(AnimationMaxOffsetY, _openCloseMotionDuration, new CubicEaseOut(),
FillMode.Forward);
}
_motionActor.IsVisible = false;
MotionInvoker.Invoke(_motionActor, motionConfig, () =>
MotionInvoker.Invoke(_motionActor, motion, () =>
{
_motionActor.IsVisible = true;
});
@ -275,29 +275,29 @@ public class NotificationCard : ContentControl
{
return;
}
MotionConfig? motionConfig;
AbstractMotion? motion = default;
if (Position == NotificationPosition.TopLeft || Position == NotificationPosition.BottomLeft)
{
motionConfig = MotionFactory.BuildMoveLeftOutMotion(AnimationMaxOffsetX, _openCloseMotionDuration, new CubicEaseIn(),
motion = new MoveLeftOutMotion(AnimationMaxOffsetX, _openCloseMotionDuration, new CubicEaseIn(),
FillMode.Forward);
}
else if (Position == NotificationPosition.TopRight || Position == NotificationPosition.BottomRight)
{
motionConfig = MotionFactory.BuildMoveRightOutMotion(AnimationMaxOffsetX, _openCloseMotionDuration, new CubicEaseIn(),
motion = new MoveRightOutMotion(AnimationMaxOffsetX, _openCloseMotionDuration, new CubicEaseIn(),
FillMode.Forward);
}
else if (Position == NotificationPosition.TopCenter)
{
motionConfig = MotionFactory.BuildMoveUpOutMotion(AnimationMaxOffsetY, _openCloseMotionDuration, new CubicEaseIn(),
motion = new MoveUpOutMotion(AnimationMaxOffsetY, _openCloseMotionDuration, new CubicEaseIn(),
FillMode.Forward);
}
else
{
motionConfig = MotionFactory.BuildMoveDownOutMotion(AnimationMaxOffsetY, _openCloseMotionDuration, new CubicEaseIn(),
motion = new MoveDownOutMotion(AnimationMaxOffsetY, _openCloseMotionDuration, new CubicEaseIn(),
FillMode.Forward);
}
MotionInvoker.Invoke(_motionActor, motionConfig, null, () =>
MotionInvoker.Invoke(_motionActor, motion, null, () =>
{
IsClosed = true;
});