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 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
210 changes: 172 additions & 38 deletions src/Core/src/Animations/Animation.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,27 @@

namespace Microsoft.Maui.Animations
{
/// <summary>
/// Represents an animation.
/// </summary>
public class Animation : IDisposable, IEnumerable
{
/// <summary>
/// Instantiate a new <see cref="Animation"/> object.
/// </summary>
public Animation()
{

}

/// <summary>
/// Instantiate a new <see cref="Animation"/> object with the given parameters.
/// </summary>
/// <param name="callback">The <see cref="Action{T}"/> that is invoked after each tick of this animation.</param>
/// <param name="start">Specifies a delay (in seconds) taken into account before the animation starts.</param>
/// <param name="duration">Specifies the duration that this animation should take in seconds.</param>
/// <param name="easing">The easing function to apply to this animation.</param>
/// <param name="finished">A callback <see cref="Action{T}"/> that is invoked after the animation has finished.</param>
public Animation(Action<double> callback, double start = 0.0f, double duration = 1.0f, Easing? easing = null, Action? finished = null)
{
StartDelay = start;
Expand All @@ -21,51 +36,122 @@ public Animation(Action<double> callback, double start = 0.0f, double duration =
Step = callback;

}

/// <summary>
/// Instantiate a new <see cref="Animation"/> object that consists of the given list of child animations.
/// </summary>
/// <param name="animations">A <see cref="List{T}"/> that contains <see cref="Animation"/> objects that will be children of the newly instantiated animation.</param>
public Animation(List<Animation> animations)
{
childrenAnimations = animations;
}

internal WeakReference<IAnimator>? Parent { get; set; }

/// <summary>
/// A callback that is invoked when this animation finishes.
/// </summary>
public Action? Finished { get; set; }

/// <summary>
/// A callback that is invoked after each tick of this animation.
/// </summary>
public Action<double>? Step { get; set; }
bool paused;
public bool IsPaused => paused;
protected List<Animation> childrenAnimations = new List<Animation>();

bool _paused;

/// <summary>
/// Specifies whether this animation is currently paused.
/// </summary>
public bool IsPaused => _paused;

/// <summary>
/// Collection of child animations associated to this animation.
/// </summary>
protected List<Animation> childrenAnimations = new();

/// <summary>
/// The name of this animation.
/// </summary>
public string? Name { get; set; }

/// <summary>
/// The delay (in seconds) taken into account before the animation starts.
/// </summary>
public double StartDelay { get; set; }

/// <summary>
/// The duration of this animation in seconds.
/// </summary>
public double Duration { get; set; }

/// <summary>
/// The current timestamp (in seconds) of the animation.
/// </summary>
public double CurrentTime { get; protected set; }

/// <summary>
/// Progress of this animation in percentage.
/// </summary>
public double Progress { get; protected set; }

/// <summary>
/// The <see cref="Easing"/> function that is applied to this animation.
/// </summary>
public Easing Easing { get; set; } = Easing.Default;

/// <summary>
/// Specifies whether this animation has finished.
/// </summary>
public bool HasFinished { get; protected set; }

/// <summary>
/// Specifies whether this animation should repeat.
/// </summary>
public bool Repeats { get; set; }

double _skippedSeconds;
int _usingResource = 0;

/// <summary>
/// Provides an <see cref="IEnumerator"/> of the child animations.
/// </summary>
/// <returns><see cref="IEnumerator"/> of <see cref="Animation"/></returns>
public IEnumerator GetEnumerator() => childrenAnimations.GetEnumerator();

/// <summary>
/// Adds a new child animation to this animation with the specified parameters.
/// </summary>
/// <param name="beginAt">Specifies a delay (in seconds) taken into account before the added child animation starts.</param>
/// <param name="duration">Specifies the duration (in seconds) that the added child animation should take.</param>
/// <param name="animation">The <see cref="Animation"/> object to add to this animation as a child.</param>
/// <exception cref="ArgumentOutOfRangeException">Thrown when <paramref name="beginAt"/> or <paramref name="duration"/> is less than 0 or more than 1.</exception>
/// <exception cref="ArgumentException">Thrown when <paramref name="duration"/> is less than or equal to <paramref name="beginAt"/>.</exception>
public void Add(double beginAt, double duration, Animation animation)
{
if (beginAt < 0 || beginAt > 1)
throw new ArgumentOutOfRangeException("beginAt");
throw new ArgumentOutOfRangeException(nameof(beginAt));

if (duration < 0 || duration > 1)
throw new ArgumentOutOfRangeException("finishAt");
throw new ArgumentOutOfRangeException(nameof(duration));

if (duration <= beginAt)
throw new ArgumentException("finishAt must be greater than beginAt");
throw new ArgumentException($"{nameof(duration)} must be greater than {nameof(beginAt)}");

animation.StartDelay = beginAt;
animation.Duration = duration;
childrenAnimations.Add(animation);
}

/// <summary>
/// Method to trigger an update for this animation.
/// </summary>
/// <param name="milliseconds">Number of milliseconds that have passed since the last tick.</param>
public void Tick(double milliseconds)
{
if (IsPaused)
return;

if (0 == Interlocked.Exchange(ref _usingResource, 1))
{
try
Expand All @@ -85,15 +171,29 @@ public void Tick(double milliseconds)
_skippedSeconds += milliseconds;
}
}

/// <summary>
/// A reference to the <see cref="IAnimationManager"/> that manages this animation.
/// </summary>
public IAnimationManager? AnimationManager => animationManger;

/// <summary>
/// A reference to the <see cref="IAnimationManager"/> that manages this animation.
/// </summary>
protected IAnimationManager? animationManger;

/// <summary>
/// Executes the logic to update all animations within this animation.
/// </summary>
/// <param name="millisecondsSinceLastUpdate">Number of milliseconds that have passed since the last tick.</param>
protected virtual void OnTick(double millisecondsSinceLastUpdate)
{
if (HasFinished)
return;

var secondsSinceLastUpdate = millisecondsSinceLastUpdate / 1000.0;
CurrentTime += secondsSinceLastUpdate;

if (childrenAnimations.Any())
{
var hasFinished = true;
Expand All @@ -105,27 +205,33 @@ protected virtual void OnTick(double millisecondsSinceLastUpdate)
hasFinished = false;

}
HasFinished = hasFinished;


HasFinished = hasFinished;
}
else
{

var start = CurrentTime - StartDelay;

if (CurrentTime < StartDelay)
return;

var percent = Math.Min(start / Duration, 1);
Update(percent);
}

if (HasFinished)
{
Finished?.Invoke();

if (Repeats)
Reset();
}
}

/// <summary>
/// Updates this animation by updating <see cref="Progress"/> and invoking <see cref="Step"/>.
/// </summary>
/// <param name="percent">Progress of this animation in percentage.</param>
public virtual void Update(double percent)
{
try
Expand All @@ -141,105 +247,133 @@ public virtual void Update(double percent)
HasFinished = true;
}
}

/// <summary>
/// Sets the <see cref="IAnimationManager"/> for this animation.
/// </summary>
/// <param name="animationManger">Reference to the <see cref="IAnimationManager"/> that will manage this animation.</param>
public void Commit(IAnimationManager animationManger)
{
this.animationManger = animationManger;
animationManger.Add(this);

}

/// <summary>
/// Creates an animation that includes both the original animation and a reversed version of the same animation.
/// </summary>
/// <returns>An <see cref="Animation"/> object with the original animation and the reversed animation.</returns>
/// <remarks>You can get just the reversed animation by using <see cref="CreateReverse"/>.</remarks>
public Animation CreateAutoReversing()
{
var reveresedChildren = childrenAnimations.ToList();
reveresedChildren.Reverse();
var reveresed = CreateReverse();
var reversedChildren = childrenAnimations.ToList();
reversedChildren.Reverse();
var reversed = CreateReverse();

var parentAnimation = new Animation
{
Duration = reveresed.StartDelay + reveresed.Duration,
Duration = reversed.StartDelay + reversed.Duration,
Repeats = Repeats,
childrenAnimations =
{
this,
reveresed,
}
{
this,
reversed,
}
};

Repeats = false;
return parentAnimation;
}

/// <summary>
/// Creates a reversed version of the current animation, including reversing the child animations.
/// </summary>
/// <returns>An <see cref="Animation"/> object that is the reversed version of this animation.</returns>
/// <remarks>You can the forward and reverse animation by using <see cref="CreateAutoReversing"/>.</remarks>
protected virtual Animation CreateReverse()
{
var reveresedChildren = childrenAnimations.ToList();
reveresedChildren.Reverse();
var reversedChildren = childrenAnimations.ToList();
reversedChildren.Reverse();

return new Animation
{
Easing = Easing,
Duration = Duration,
StartDelay = StartDelay + Duration,
childrenAnimations = reveresedChildren,
childrenAnimations = reversedChildren,
};
}

/// <summary>
/// Resets the animation (and all child animations) to its initial state.
/// </summary>
public virtual void Reset()
{
CurrentTime = 0;
HasFinished = false;

foreach (var x in childrenAnimations)
x.Reset();
}

/// <summary>
/// Pauses the animation.
/// </summary>
public void Pause()
{
paused = true;
_paused = true;
animationManger?.Remove(this);
}

/// <summary>
/// Resumes the animation.
/// </summary>
public void Resume()
{
paused = false;
_paused = false;
animationManger?.Add(this);
}

/// <summary>
/// Removes this animation from it's <see cref="Parent"/>.
/// If there is no <see cref="Parent"/>, nothing will happen.
/// </summary>
public void RemoveFromParent()
{
IAnimator? view = null;
if (this.Parent?.TryGetTarget(out view) ?? false)
if (Parent?.TryGetTarget(out view) ?? false)
view?.RemoveAnimation(this);
}

/// <inheritdoc/>
public bool IsDisposed => _disposedValue;

private bool _disposedValue = false; // To detect redundant calls



#region IDisposable Support
public bool IsDisposed => disposedValue;


private bool disposedValue = false; // To detect redundant calls

/// <inheritdoc/>
protected virtual void Dispose(bool disposing)
{
if (!disposedValue)
if (!_disposedValue)
{
if (disposing)
{
foreach (var child in childrenAnimations)
child.Dispose();

childrenAnimations.Clear();
}
disposedValue = true;

_disposedValue = true;
animationManger?.Remove(this);
Finished = null;
Step = null;
}
}


// This code added to correctly implement the disposable pattern.
/// <inheritdoc/>
public void Dispose()
{
// Do not change this code. Put cleanup code in Dispose(bool disposing) above.
Dispose(true);
}

#endregion
}
}
Loading