Browse Source

Use the correct value for animations stopped during a visual tree detach (#20995)

* Add failing test for FillMode on visual tree detach

* Update animation fill value when detached from visual tree
pull/20996/head
Julien Lebosquain 6 months ago
committed by GitHub
parent
commit
2dfd8515a7
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 15
      src/Avalonia.Base/Animation/AnimationInstance`1.cs
  2. 38
      tests/Avalonia.Base.UnitTests/Animation/AnimationIterationTests.cs

15
src/Avalonia.Base/Animation/AnimationInstance`1.cs

@ -148,7 +148,11 @@ namespace Avalonia.Animation
}
// Stop and dispose the animation when detached from the visual tree.
_detachedHandler = (_, _) => DoComplete();
_detachedHandler = (_, _) =>
{
SetFinalValue();
DoComplete();
};
visual.DetachedFromVisualTree += _detachedHandler;
}
@ -172,6 +176,12 @@ namespace Avalonia.Animation
}
}
private void SetFinalValue()
{
var easedTime = _easeFunc!.Ease(_playbackReversed ? 0.0 : 1.0);
_lastInterpValue = _interpolator(easedTime, _neutralValue);
}
private void ApplyFinalFill()
{
if (_animator.Property is null)
@ -237,8 +247,7 @@ namespace Avalonia.Animation
// when the duration is set to zero while animating and snap to the last iterated value.
if (_currentIteration + 1 > _iterationCount || _duration == TimeSpan.Zero)
{
var easedTime = _easeFunc!.Ease(_playbackReversed ? 0.0 : 1.0);
_lastInterpValue = _interpolator(easedTime, _neutralValue);
SetFinalValue();
DoComplete();
return;
}

38
tests/Avalonia.Base.UnitTests/Animation/AnimationIterationTests.cs

@ -1264,6 +1264,44 @@ namespace Avalonia.Base.UnitTests.Animation
Assert.Equal(200d, border.Width);
}
[Fact]
public void FillMode_Applies_Final_Value_When_Visual_Detached_During_Animation()
{
var keyframe1 = new KeyFrame
{
Setters = { new Setter(Layoutable.WidthProperty, 100d) },
Cue = new Cue(0d)
};
var keyframe2 = new KeyFrame
{
Setters = { new Setter(Layoutable.WidthProperty, 300d) },
Cue = new Cue(1d)
};
var animation = new Animation
{
Duration = TimeSpan.FromSeconds(5),
IterationCount = new IterationCount(1),
FillMode = FillMode.Forward,
Children = { keyframe1, keyframe2 }
};
var border = new Border { Height = 100d, Width = 50d };
var root = new TestRoot(border);
var clock = new TestClock();
var animationRun = animation.RunAsync(border, clock, TestContext.Current.CancellationToken);
clock.Step(TimeSpan.Zero);
Assert.Equal(100d, border.Width);
// Detach from visual tree immediately
root.Child = null;
// The final value should be applied
Assert.True(animationRun.IsCompleted);
Assert.Equal(300d, border.Width);
}
private sealed class FakeAnimator : InterpolatingAnimator<double>
{
public double LastProgress { get; set; } = double.NaN;

Loading…
Cancel
Save