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 14 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
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
Expand Up @@ -86,7 +86,62 @@
</mct:AnimationBehavior.AnimationType>
</mct:AnimationBehavior>
</Frame.Behaviors>
<Label Text="Click this Frame" HorizontalOptions="Center"/>
<Label Text="Click to Fade this Frame" HorizontalOptions="Center"/>
</Frame>

<Frame BackgroundColor="LightGreen" Margin="16,0">
<Frame.Behaviors>
<mct:AnimationBehavior Command="{Binding AnimationCommand}">
<mct:AnimationBehavior.AnimationType>
<mct:FlipHorizontalAnimation />
</mct:AnimationBehavior.AnimationType>
</mct:AnimationBehavior>
</Frame.Behaviors>
<Label Text="Click to Flip this Frame horizontally" HorizontalOptions="Center"/>
</Frame>

<Frame BackgroundColor="LightGreen" Margin="16,0">
<Frame.Behaviors>
<mct:AnimationBehavior Command="{Binding AnimationCommand}">
<mct:AnimationBehavior.AnimationType>
<mct:FlipVerticalAnimation />
</mct:AnimationBehavior.AnimationType>
</mct:AnimationBehavior>
</Frame.Behaviors>
<Label Text="Click to Flip this Frame vertically" HorizontalOptions="Center"/>
</Frame>

<Frame BackgroundColor="LightGreen" Margin="16,0">
<Frame.Behaviors>
<mct:AnimationBehavior Command="{Binding AnimationCommand}">
<mct:AnimationBehavior.AnimationType>
<mct:RotateAnimation />
</mct:AnimationBehavior.AnimationType>
</mct:AnimationBehavior>
</Frame.Behaviors>
<Label Text="Click to Rotate this Frame" HorizontalOptions="Center"/>
</Frame>

<Frame BackgroundColor="LightGreen" Margin="16,0">
<Frame.Behaviors>
<mct:AnimationBehavior Command="{Binding AnimationCommand}">
<mct:AnimationBehavior.AnimationType>
<mct:ScaleAnimation />
</mct:AnimationBehavior.AnimationType>
</mct:AnimationBehavior>
</Frame.Behaviors>
<Label Text="Click to Scale this Frame" HorizontalOptions="Center"/>
</Frame>

<Frame BackgroundColor="LightGreen" Margin="16,0">
<Frame.Behaviors>
<mct:AnimationBehavior Command="{Binding AnimationCommand}">
<mct:AnimationBehavior.AnimationType>
<mct:ShakeAnimation />
</mct:AnimationBehavior.AnimationType>
</mct:AnimationBehavior>
</Frame.Behaviors>
<Label Text="Click to Shake this Frame" HorizontalOptions="Center"/>
</Frame>

<Button Text="Animate the frame above" Command="{Binding TriggerAnimationCommand}" />
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
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();

double allowance = (animation.Length * 0.1) + 50;

stopwatch.ElapsedMilliseconds.Should().BeCloseTo(animation.Length, (uint)allowance);

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(Timeout = (int)TestDuration.Short)]
public async Task AnimateShouldThrowWithNullView()
{
FadeAnimation animation = new();
FadeAnimation animation = CreateAnimation();

#pragma warning disable CS8625 // Cannot convert null literal to non-nullable reference type.
await Assert.ThrowsAsync<ArgumentNullException>(() => animation.Animate(null, CancellationToken.None));
Expand Down Expand Up @@ -54,7 +54,7 @@ public async Task CancellationTokenExpired()
[Fact(Timeout = (int)TestDuration.Medium)]
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 FlipVerticalAnimationTests : BaseAnimationTests<FlipVerticalAnimation>
{
}
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, CancellationToken token =

var originalOpacity = view.Opacity;

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

await view.FadeTo(Opacity, duration, Easing).WaitAsync(token);
await view.FadeTo(originalOpacity, duration, Easing).WaitAsync(token);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
using Microsoft.Maui.Controls;

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(600)
{

}

/// <inheritdoc />
protected override double DefaultRotation { get; set; } = 90;

/// <inheritdoc />
public override async Task Animate(VisualElement view)
{
ArgumentNullException.ThrowIfNull(view);

var duration = Length / 2;

await view.RotateYTo(Rotation, duration, Easing);
await view.RotateYTo(0, 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 vertically.
/// </summary>
public class FlipVerticalAnimation : RotateAnimation
{
/// <summary>
/// Initializes a new instance of <see cref="FlipVerticalAnimation"/>.
/// </summary>
public FlipVerticalAnimation() : base(600)
{

}

/// <inheritdoc />
protected override double DefaultRotation { get; set; } = 90;

/// <inheritdoc />
public override async Task Animate(VisualElement view)
{
ArgumentNullException.ThrowIfNull(view);

var duration = Length / 2;

await view.RotateXTo(Rotation, duration, Easing);
await view.RotateXTo(0, duration, Easing);
}
}
61 changes: 61 additions & 0 deletions src/CommunityToolkit.Maui/Animations/RotateAnimation.shared.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
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)
{
ArgumentNullException.ThrowIfNull(view);

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.
/// </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);
}
}
Loading
Loading