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

Update docs animations #9746

Merged
merged 8 commits into from
Sep 30, 2022
Merged
Show file tree
Hide file tree
Changes from 3 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
506 changes: 319 additions & 187 deletions src/Core/src/Animations/Animation.cs

Large diffs are not rendered by default.

158 changes: 83 additions & 75 deletions src/Core/src/Animations/AnimationManager.cs
Original file line number Diff line number Diff line change
@@ -1,106 +1,114 @@
using System;
using System.Collections.Generic;

namespace Microsoft.Maui.Animations
namespace Microsoft.Maui.Animations;

/// <inheritdoc/>
jfversluis marked this conversation as resolved.
Show resolved Hide resolved
public class AnimationManager : IAnimationManager, IDisposable
{
public class AnimationManager : IAnimationManager, IDisposable
readonly List<Animation> _animations = new();
long _lastUpdate;
bool _disposedValue;

/// <inheritdoc/>
public AnimationManager(ITicker ticker)
{
readonly List<Animation> _animations = new();
long _lastUpdate;
bool _disposedValue;
_lastUpdate = GetCurrentTick();

public AnimationManager(ITicker ticker)
{
_lastUpdate = GetCurrentTick();
Ticker = ticker;
Ticker.Fire = OnFire;
}

Ticker = ticker;
Ticker.Fire = OnFire;
}
/// <inheritdoc/>
public ITicker Ticker { get; }

public ITicker Ticker { get; }
/// <inheritdoc/>
public double SpeedModifier { get; set; } = 1;

public double SpeedModifier { get; set; } = 1;
/// <inheritdoc/>
public bool AutoStartTicker { get; set; } = true;

public bool AutoStartTicker { get; set; } = true;
/// <inheritdoc/>
public void Add(Animation animation)
{
// If animations are disabled, don't do anything
if (!Ticker.SystemEnabled)
return;

if (!_animations.Contains(animation))
_animations.Add(animation);
if (!Ticker.IsRunning && AutoStartTicker)
Start();
}

public void Add(Animation animation)
{
// If animations are disabled, don't do anything
if (!Ticker.SystemEnabled)
return;
/// <inheritdoc/>
public void Remove(Animation animation)
{
_animations.TryRemove(animation);

if (!_animations.Contains(animation))
_animations.Add(animation);
if (!Ticker.IsRunning && AutoStartTicker)
Start();
}
if (_animations.Count == 0)
End();
}

public void Remove(Animation animation)
{
_animations.TryRemove(animation);
void Start()
{
_lastUpdate = GetCurrentTick();
Ticker.Start();
}

if (_animations.Count == 0)
End();
}
void End() =>
Ticker?.Stop();

void Start()
{
_lastUpdate = GetCurrentTick();
Ticker.Start();
}
long GetCurrentTick() =>
Environment.TickCount & int.MaxValue;

void End() =>
Ticker?.Stop();
void OnFire()
{
var now = GetCurrentTick();
var milliseconds = TimeSpan.FromMilliseconds(now - _lastUpdate).TotalMilliseconds;
_lastUpdate = now;

long GetCurrentTick() =>
Environment.TickCount & int.MaxValue;
var animations = new List<Animation>(_animations);
animations.ForEach(OnAnimationTick);

void OnFire()
{
var now = GetCurrentTick();
var milliseconds = TimeSpan.FromMilliseconds(now - _lastUpdate).TotalMilliseconds;
_lastUpdate = now;
if (_animations.Count == 0)
End();

var animations = new List<Animation>(_animations);
animations.ForEach(OnAnimationTick);
void OnAnimationTick(Animation animation)
{
if (animation.HasFinished)
{
_animations.TryRemove(animation);
animation.RemoveFromParent();
return;
}

if (_animations.Count == 0)
End();
animation.Tick(milliseconds * SpeedModifier);

void OnAnimationTick(Animation animation)
if (animation.HasFinished)
{
if (animation.HasFinished)
{
_animations.TryRemove(animation);
animation.RemoveFromParent();
return;
}

animation.Tick(milliseconds * SpeedModifier);

if (animation.HasFinished)
{
_animations.TryRemove(animation);
animation.RemoveFromParent();
}
_animations.TryRemove(animation);
animation.RemoveFromParent();
}
}
}

protected virtual void Dispose(bool disposing)
/// <inheritdoc/>
protected virtual void Dispose(bool disposing)
{
if (!_disposedValue)
{
if (!_disposedValue)
{
if (disposing && Ticker is IDisposable disposable)
disposable.Dispose();
if (disposing && Ticker is IDisposable disposable)
disposable.Dispose();

_disposedValue = true;
}
_disposedValue = true;
}
}

public void Dispose()
{
Dispose(disposing: true);
GC.SuppressFinalize(this);
}
/// <inheritdoc/>
public void Dispose()
{
Dispose(disposing: true);
GC.SuppressFinalize(this);
}
}
193 changes: 115 additions & 78 deletions src/Core/src/Animations/Easing.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,90 +2,127 @@
using System.ComponentModel;
using Microsoft.Maui.Converters;

namespace Microsoft.Maui
namespace Microsoft.Maui;

/// <include file="../../docs/Microsoft.Maui/Easing.xml" path="Type[@FullName='Microsoft.Maui.Easing']/Docs" />
[TypeConverter(typeof(EasingTypeConverter))]
public class Easing
{
/// <include file="../../docs/Microsoft.Maui/Easing.xml" path="Type[@FullName='Microsoft.Maui.Easing']/Docs" />
[TypeConverter(typeof(EasingTypeConverter))]
public class Easing
/// <include file="../../docs/Microsoft.Maui/Easing.xml" path="//Member[@MemberName='Default']/Docs" />
public static Easing Default => CubicInOut;

/// <summary>
/// Linear transformation.
/// </summary>
public static readonly Easing Linear = new(x => x);

/// <summary>
/// Smoothly decelerates.
/// </summary>
public static readonly Easing SinOut = new(x => Math.Sin(x * Math.PI * 0.5f));

/// <summary>
/// Smoothly accelerates.
/// </summary>
public static readonly Easing SinIn = new(x => 1.0f - Math.Cos(x * Math.PI * 0.5f));

/// <summary>
/// Accelerates in and decelerates out.
/// </summary>
public static readonly Easing SinInOut = new(x => -Math.Cos(Math.PI * x) / 2.0f + 0.5f);

/// <summary>
/// Starts slowly and accelerates.
/// </summary>
public static readonly Easing CubicIn = new(x => x * x * x);

/// <summary>
/// Starts quickly and the decelerates.
/// </summary>
public static readonly Easing CubicOut = new(x => Math.Pow(x - 1.0f, 3.0f) + 1.0f);

/// <summary>
/// Accelerates and decelerates. Often a natural-looking choice.
/// </summary>
public static readonly Easing CubicInOut = new(x => x < 0.5f ? Math.Pow(x * 2.0f, 3.0f) / 2.0f : (Math.Pow((x - 1) * 2.0f, 3.0f) + 2.0f) / 2.0f);

/// <summary>
/// Leaps to final values, bounces 3 times, and settles.
/// </summary>
public static readonly Easing BounceOut;

/// <summary>
/// Jumps towards, and then bounces as it settles at the final value.
/// </summary>
public static readonly Easing BounceIn;

/// <summary>
/// Moves away and then leaps toward the final value.
/// </summary>
public static readonly Easing SpringIn = new(x => x * x * ((1.70158f + 1) * x - 1.70158f));

/// <summary>
/// Overshoots and then returns.
/// </summary>
public static readonly Easing SpringOut = new(x => (x - 1) * (x - 1) * ((1.70158f + 1) * (x - 1) + 1.70158f) + 1);

readonly Func<double, double> _easingFunc;

static Easing()
{
/// <include file="../../docs/Microsoft.Maui/Easing.xml" path="//Member[@MemberName='Default']/Docs" />
public static Easing Default => CubicInOut;
/// <include file="../../docs/Microsoft.Maui/Easing.xml" path="//Member[@MemberName='Linear']/Docs" />
public static readonly Easing Linear = new Easing(x => x);

/// <include file="../../docs/Microsoft.Maui/Easing.xml" path="//Member[@MemberName='SinOut']/Docs" />
public static readonly Easing SinOut = new Easing(x => Math.Sin(x * Math.PI * 0.5f));
/// <include file="../../docs/Microsoft.Maui/Easing.xml" path="//Member[@MemberName='SinIn']/Docs" />
public static readonly Easing SinIn = new Easing(x => 1.0f - Math.Cos(x * Math.PI * 0.5f));
/// <include file="../../docs/Microsoft.Maui/Easing.xml" path="//Member[@MemberName='SinInOut']/Docs" />
public static readonly Easing SinInOut = new Easing(x => -Math.Cos(Math.PI * x) / 2.0f + 0.5f);

/// <include file="../../docs/Microsoft.Maui/Easing.xml" path="//Member[@MemberName='CubicIn']/Docs" />
public static readonly Easing CubicIn = new Easing(x => x * x * x);
/// <include file="../../docs/Microsoft.Maui/Easing.xml" path="//Member[@MemberName='CubicOut']/Docs" />
public static readonly Easing CubicOut = new Easing(x => Math.Pow(x - 1.0f, 3.0f) + 1.0f);

/// <include file="../../docs/Microsoft.Maui/Easing.xml" path="//Member[@MemberName='CubicInOut']/Docs" />
public static readonly Easing CubicInOut = new Easing(x => x < 0.5f ? Math.Pow(x * 2.0f, 3.0f) / 2.0f : (Math.Pow((x - 1) * 2.0f, 3.0f) + 2.0f) / 2.0f);

/// <include file="../../docs/Microsoft.Maui/Easing.xml" path="//Member[@MemberName='BounceOut']/Docs" />
public static readonly Easing BounceOut;
/// <include file="../../docs/Microsoft.Maui/Easing.xml" path="//Member[@MemberName='BounceIn']/Docs" />
public static readonly Easing BounceIn;

/// <include file="../../docs/Microsoft.Maui/Easing.xml" path="//Member[@MemberName='SpringIn']/Docs" />
public static readonly Easing SpringIn = new Easing(x => x * x * ((1.70158f + 1) * x - 1.70158f));
/// <include file="../../docs/Microsoft.Maui/Easing.xml" path="//Member[@MemberName='SpringOut']/Docs" />
public static readonly Easing SpringOut = new Easing(x => (x - 1) * (x - 1) * ((1.70158f + 1) * (x - 1) + 1.70158f) + 1);

readonly Func<double, double> _easingFunc;

static Easing()
BounceOut = new Easing(p =>
{
BounceOut = new Easing(p =>
if (p < 1 / 2.75f)
{
if (p < 1 / 2.75f)
{
return 7.5625f * p * p;
}
if (p < 2 / 2.75f)
{
p -= 1.5f / 2.75f;

return 7.5625f * p * p + .75f;
}
if (p < 2.5f / 2.75f)
{
p -= 2.25f / 2.75f;

return 7.5625f * p * p + .9375f;
}
p -= 2.625f / 2.75f;

return 7.5625f * p * p + .984375f;
});

BounceIn = new Easing(p => 1.0f - BounceOut.Ease(1 - p));
}

/// <include file="../../docs/Microsoft.Maui/Easing.xml" path="//Member[@MemberName='.ctor']/Docs" />
public Easing(Func<double, double> easingFunc)
{
if (easingFunc == null)
throw new ArgumentNullException("easingFunc");
return 7.5625f * p * p;
}
if (p < 2 / 2.75f)
{
p -= 1.5f / 2.75f;

return 7.5625f * p * p + .75f;
}
if (p < 2.5f / 2.75f)
{
p -= 2.25f / 2.75f;

_easingFunc = easingFunc;
}
return 7.5625f * p * p + .9375f;
}
p -= 2.625f / 2.75f;

/// <include file="../../docs/Microsoft.Maui/Easing.xml" path="//Member[@MemberName='Ease']/Docs" />
public double Ease(double v)
{
return _easingFunc(v);
}
return 7.5625f * p * p + .984375f;
});

public static implicit operator Easing(Func<double, double> func)
{
return new Easing(func);
}
BounceIn = new Easing(p => 1.0f - BounceOut.Ease(1 - p));
}

/// <summary>
/// Creates a new <see cref="Easing" /> object with the <paramref name="easingFunc" /> function.
/// </summary>
/// <param name="easingFunc">A function that maps animation times.</param>
/// <exception cref="ArgumentNullException">Thrown when <paramref name="easingFunc"/> is <see langword="null"/>.</exception>
public Easing(Func<double, double> easingFunc)
{
_easingFunc = easingFunc ?? throw new ArgumentNullException(nameof(easingFunc));
}

/// <summary>
/// Applies the easing function to the specified value <paramref name="v" />.
/// </summary>
/// <param name="v">A value in the range [0,1] to which the easing function should be applied.</param>
/// <returns>The value of the easing function when applied to the value <paramref name="v" />.</returns>
public double Ease(double v)
{
return _easingFunc(v);
}

/// <summary>
/// Converts a function into an <see cref="T:Microsoft.Maui.Easing" />.
/// </summary>
/// <param name="func">An easing function.</param>
/// <remarks>An easing function should return a value of (or near) 0 at 0 and 1 (or near) for 1.</remarks>
public static implicit operator Easing(Func<double, double> func)
{
return new Easing(func);
}
}
Loading