-
Notifications
You must be signed in to change notification settings - Fork 422
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Initial work on adding remaining animations #1232
base: main
Are you sure you want to change the base?
Changes from 1 commit
163ed96
ed99dee
2560b69
40723e4
3e18d3a
815b4fc
3306205
ec6a24a
1e12715
fa62c5f
2ffb89a
1e1bd6b
4ccb3b8
6590859
4a837eb
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
using System.Diagnostics; | ||
using CommunityToolkit.Maui.Animations; | ||
using CommunityToolkit.Maui.UnitTests.Mocks; | ||
using FluentAssertions; | ||
using Xunit; | ||
|
||
namespace CommunityToolkit.Maui.UnitTests.Animations; | ||
|
||
public abstract class BaseAnimationTests<TAnimation> : BaseTest where TAnimation : BaseAnimation, new() | ||
{ | ||
protected virtual TAnimation CreateAnimation() => new(); | ||
|
||
[Fact] | ||
public async Task LengthShouldDictateFullAnimationLength() | ||
{ | ||
var animation = CreateAnimation(); | ||
|
||
Label label = new(); | ||
|
||
label.EnableAnimations(); | ||
|
||
var stopwatch = new Stopwatch(); | ||
stopwatch.Start(); | ||
await animation.Animate(label); | ||
stopwatch.Stop(); | ||
|
||
stopwatch.ElapsedMilliseconds.Should().BeCloseTo(animation.Length, 50); | ||
|
||
stopwatch.Reset(); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
using CommunityToolkit.Maui.Animations; | ||
|
||
namespace CommunityToolkit.Maui.UnitTests.Animations; | ||
|
||
public class FlipHorizontalAnimationTests : BaseAnimationTests<FlipHorizontalAnimation> | ||
{ | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
using CommunityToolkit.Maui.Animations; | ||
|
||
namespace CommunityToolkit.Maui.UnitTests.Animations; | ||
|
||
public class RotateAnimationTests : BaseAnimationTests<RotateAnimation> | ||
{ | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
using CommunityToolkit.Maui.Animations; | ||
|
||
namespace CommunityToolkit.Maui.UnitTests.Animations; | ||
|
||
public class ScaleAnimationTests : BaseAnimationTests<ScaleAnimation> | ||
{ | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
using CommunityToolkit.Maui.Animations; | ||
|
||
namespace CommunityToolkit.Maui.UnitTests.Animations; | ||
|
||
public class ShakeAnimationTests : BaseAnimationTests<ShakeAnimation> | ||
{ | ||
|
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
namespace CommunityToolkit.Maui.Animations; | ||
|
||
/// <summary> | ||
/// Animation that will flip the supplied view horizontally. | ||
/// </summary> | ||
public class FlipHorizontalAnimation : RotateAnimation | ||
{ | ||
/// <summary> | ||
/// Initializes a new instance of <see cref="FlipHorizontalAnimation"/>. | ||
/// </summary> | ||
public FlipHorizontalAnimation() : base(300) | ||
{ | ||
|
||
} | ||
|
||
/// <inheritdoc /> | ||
protected override double DefaultRotation { get; set; } = 90; | ||
TheCodeTraveler marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
/// <inheritdoc /> | ||
public override async Task Animate(VisualElement view) | ||
{ | ||
if (view != null) | ||
{ | ||
await view.RotateYTo(Rotation, Length, Easing); | ||
await view.RotateYTo(0, Length, Easing); | ||
} | ||
} | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
namespace CommunityToolkit.Maui.Animations; | ||
|
||
/// <summary> | ||
/// Animation that will rotate the supplied view by the specified <see cref="Rotation"/>. | ||
/// </summary> | ||
public class RotateAnimation : BaseAnimation | ||
{ | ||
/// <summary> | ||
/// Backing BindableProperty for the <see cref="Rotation"/> property. | ||
/// </summary> | ||
public static readonly BindableProperty RotationProperty = | ||
BindableProperty.Create( | ||
nameof(Rotation), | ||
typeof(double), | ||
typeof(RotateAnimation), | ||
180.0, | ||
BindingMode.TwoWay, | ||
defaultValueCreator: GetDefaultRotationProperty); | ||
|
||
/// <summary> | ||
/// Gets or sets the rotation used by the animation. | ||
/// </summary> | ||
public double Rotation | ||
VladislavAntonyuk marked this conversation as resolved.
Show resolved
Hide resolved
|
||
{ | ||
get => (double)GetValue(RotationProperty); | ||
set => SetValue(RotationProperty, value); | ||
} | ||
|
||
static object GetDefaultRotationProperty(BindableObject bindable) | ||
=> ((RotateAnimation)bindable).DefaultRotation; | ||
|
||
/// <summary> | ||
/// Initializes a new instance of <see cref="RotateAnimation"/>. | ||
/// </summary> | ||
public RotateAnimation() : base(200) | ||
{ | ||
} | ||
|
||
/// <summary> | ||
/// Initializes a new instance of <see cref="RotateAnimation"/>. | ||
/// </summary> | ||
/// <param name="defaultLength">The default length of the animation.</param> | ||
protected RotateAnimation(uint defaultLength) : base(defaultLength) | ||
{ | ||
} | ||
|
||
/// <summary> | ||
/// Gets or sets the default rotation used by the animation. | ||
/// </summary> | ||
protected virtual double DefaultRotation { get; set; } = 180.0; | ||
|
||
/// <inheritdoc /> | ||
public override async Task Animate(VisualElement view) | ||
{ | ||
if (view != null) | ||
{ | ||
await view.RotateTo(Rotation, Length, Easing); | ||
view.Rotation = 0; | ||
} | ||
} | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
namespace CommunityToolkit.Maui.Animations; | ||
|
||
/// <summary> | ||
/// Animation that will scale the supplied view to the specified <see cref="Scale"/> and then back down to its original scale. | ||
/// </summary> | ||
public class ScaleAnimation : BaseAnimation | ||
{ | ||
/// <summary> | ||
/// Backing BindableProperty for the <see cref="Scale"/> property. | ||
/// </summary> | ||
public static readonly BindableProperty ScaleProperty = | ||
BindableProperty.Create( | ||
nameof(Scale), | ||
typeof(double), | ||
typeof(ScaleAnimation), | ||
1.2, | ||
BindingMode.TwoWay); | ||
|
||
/// <summary> | ||
/// Gets or sets the opacity to fade to before returning to the elements current Scale. | ||
TheCodeTraveler marked this conversation as resolved.
Show resolved
Hide resolved
|
||
/// </summary> | ||
public double Scale | ||
{ | ||
get => (double)GetValue(ScaleProperty); | ||
set => SetValue(ScaleProperty, value); | ||
} | ||
|
||
/// <summary> | ||
/// Initializes a new instance of <see cref="ScaleAnimation"/>. | ||
/// </summary> | ||
public ScaleAnimation() : base(340) | ||
bijington marked this conversation as resolved.
Show resolved
Hide resolved
|
||
{ | ||
|
||
} | ||
|
||
/// <inheritdoc /> | ||
public override async Task Animate(VisualElement view) | ||
{ | ||
ArgumentNullException.ThrowIfNull(view); | ||
bijington marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
var originalScale = view.Scale; | ||
|
||
var duration = Length / 2; | ||
|
||
await view.ScaleTo(Scale, duration, Easing); | ||
await view.ScaleTo(originalScale, duration, Easing); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,73 @@ | ||
namespace CommunityToolkit.Maui.Animations; | ||
|
||
/// <summary> | ||
/// Animation that will shake the supplied view on the x-axis, starting with the <see cref="StartFactor"/> | ||
/// and then reducing each time by the <see cref="ReducingAmount"/>. | ||
/// </summary> | ||
public class ShakeAnimation : BaseAnimation | ||
{ | ||
/// <summary> | ||
/// Backing BindableProperty for the <see cref="StartFactor"/> property. | ||
/// </summary> | ||
public static readonly BindableProperty StartFactorProperty = | ||
BindableProperty.Create( | ||
nameof(StartFactor), | ||
typeof(double), | ||
typeof(ShakeAnimation), | ||
15.0, | ||
BindingMode.TwoWay); | ||
|
||
/// <summary> | ||
/// Gets or sets the start factor, this is the biggest movement during the shake. | ||
/// </summary> | ||
public double StartFactor | ||
{ | ||
get => (double)GetValue(StartFactorProperty); | ||
set => SetValue(StartFactorProperty, value); | ||
} | ||
|
||
/// <summary> | ||
/// Backing BindableProperty for the <see cref="ReducingAmount"/> property. | ||
/// </summary> | ||
public static readonly BindableProperty ReducingAmountProperty = | ||
BindableProperty.Create( | ||
nameof(ReducingAmount), | ||
typeof(double), | ||
typeof(ShakeAnimation), | ||
5.0, | ||
BindingMode.TwoWay); | ||
|
||
/// <summary> | ||
/// Gets or sets the amount to reduce the <see cref="StartFactor"/> by on each return to 0 on the x-axis. | ||
/// </summary> | ||
public double ReducingAmount | ||
{ | ||
get => (double)GetValue(ReducingAmountProperty); | ||
set => SetValue(ReducingAmountProperty, value); | ||
} | ||
|
||
/// <summary> | ||
/// Initializes a new instance of <see cref="ShakeAnimation"/>. | ||
/// </summary> | ||
public ShakeAnimation() : base(300) | ||
{ | ||
|
||
} | ||
|
||
/// <inheritdoc /> | ||
public override async Task Animate(VisualElement view) | ||
{ | ||
ArgumentNullException.ThrowIfNull(view); | ||
|
||
var duration = (uint)(Length / Math.Ceiling(StartFactor / ReducingAmount)) / 2; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I must be missing something obvious but I can't work out why this fails when being tested. The current values mean there are 3 shakes crossing the x == 0 point twice. This should result in 6 |
||
|
||
for (var i = StartFactor; i > 0; i -= ReducingAmount) | ||
{ | ||
await view.TranslateTo(-i, 0, duration, Easing); | ||
await view.TranslateTo(i, 0, duration, Easing); | ||
} | ||
|
||
view.TranslationX = 0; | ||
} | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can we get this value from some constant in Animation from library code?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yep happy to stick that somewhere more central
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since we have different default duration for each animation, should this value be the same as the animation duration we pass to BaseAnimation?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This might highlight that the code doesn't read well. the
Length
property of theBaseAnimation
is that value we pass into the constructor. The50
is my attempt at verifying that the completed duration was within 50 milliseconds of the expected duration. Perhaps this could be a calculated percentage instead. I still need to find a reliable way of verifying this because running the tests locally don't yield positive test results for all yet.