Skip to content
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

Draft
wants to merge 15 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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);
Copy link
Collaborator

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?

Copy link
Contributor Author

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

Copy link
Collaborator

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?

Copy link
Contributor Author

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 the BaseAnimation is that value we pass into the constructor. The 50 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.


stopwatch.Reset();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,12 @@

namespace CommunityToolkit.Maui.UnitTests.Animations;

public class FadeAnimationTests : BaseTest
public class FadeAnimationTests : BaseAnimationTests<FadeAnimation>
{
[Fact]
public async Task AnimateShouldThrowWithNullView()
{
FadeAnimation animation = new();
FadeAnimation animation = CreateAnimation();

var performAnimation = () => animation.Animate(null!);

Expand All @@ -20,7 +20,7 @@ public async Task AnimateShouldThrowWithNullView()
[Fact]
public async Task AnimateShouldReturnToOriginalOpacity()
{
FadeAnimation animation = new();
FadeAnimation animation = CreateAnimation();

var label = new Label
{
Expand Down
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
@@ -1,4 +1,5 @@
namespace CommunityToolkit.Maui.Animations;

/// <summary>
/// Abstract class for animation types to inherit.
/// </summary>
Expand Down
8 changes: 5 additions & 3 deletions src/CommunityToolkit.Maui/Animations/FadeAnimation.shared.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ public class FadeAnimation : BaseAnimation
/// <summary>
/// Initializes a new instance of <see cref="FadeAnimation"/>.
/// </summary>
public FadeAnimation() : base(300)
public FadeAnimation() : base(600)
{

}
Expand All @@ -41,7 +41,9 @@ public override async Task Animate(VisualElement view)

var originalOpacity = view.Opacity;

await view.FadeTo(Opacity, Length, Easing);
await view.FadeTo(originalOpacity, Length, Easing);
var duration = Length / 2;

await view.FadeTo(Opacity, duration, Easing);
await view.FadeTo(originalOpacity, duration, Easing);
}
}
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);
}
}
}

62 changes: 62 additions & 0 deletions src/CommunityToolkit.Maui/Animations/RotateAnimation.shared.cs
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;
}
}
}

48 changes: 48 additions & 0 deletions src/CommunityToolkit.Maui/Animations/ScaleAnimation.shared.cs
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);
}
}
73 changes: 73 additions & 0 deletions src/CommunityToolkit.Maui/Animations/ShakeAnimation.shared.cs
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;
Copy link
Contributor Author

Choose a reason for hiding this comment

The 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 awaits however the test results in a failure because the animation completes within roughly 200ms. Given the above I calculate with a Length == 300, duration should equal 50 and therefore result in a passing test. However it doesn't appear to work


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;
}
}

3 changes: 3 additions & 0 deletions src/CommunityToolkit.Maui/CommunityToolkit.Maui.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,9 @@
<Configurations>Debug;Release</Configurations>
</PropertyGroup>

<PropertyGroup Condition="'$(Configuration)|$(TargetFramework)|$(Platform)'=='Debug|net7.0-ios|AnyCPU'">
<CreatePackage>false</CreatePackage>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\CommunityToolkit.Maui.Core\CommunityToolkit.Maui.Core.csproj" />
</ItemGroup>
Expand Down