/// <summary> /// Performs the animation specified in the options /// </summary> /// <param name="options">animation options</param> /// <returns>an async task</returns> public static async Task AnimateAsync(AnimatorOptions options) { if (options.DelayProvider == null && Time.CurrentTime != null) { options.DelayProvider = Time.CurrentTime; } else if (options.DelayProvider == null) { options.DelayProvider = new WallClockDelayProvider(); } var originalFrom = options.From; var originalTo = options.To; try { var i = 0; while (i == 0 || (options.Loop != null && options.Loop.IsExpired == false)) { i++; await AnimateAsyncInternal(options); if (options.AutoReverse) { if (options.AutoReverseDelay > 0) { await options.DelayProvider.DelayAsync(TimeSpan.FromMilliseconds(options.AutoReverseDelay)); } var temp = options.From; options.From = options.To; options.To = temp; options.OnReversedChanged?.Invoke(true); await AnimateAsyncInternal(options); if (options.AutoReverseDelay > 0) { await options.DelayProvider.DelayAsync(TimeSpan.FromMilliseconds(options.AutoReverseDelay)); } options.From = originalFrom; options.To = originalTo; options.OnReversedChanged?.Invoke(false); } } } finally { options.From = originalFrom; options.To = originalTo; } }
private static async Task AnimateAsyncInternal(AnimatorOptions options) { var animationTime = TimeSpan.FromMilliseconds(options.Duration); if (animationTime == TimeSpan.Zero) { #if DEBUG options.Debug?.Invoke("NoOp animation"); #endif options.Set(options.To); } var numberOfFrames = (float)(ConsoleMath.Round(animationTime.TotalSeconds * TargetFramesPerSecond)); numberOfFrames = Math.Max(numberOfFrames, 2); #if DEBUG options.Debug?.Invoke($"Frames: {numberOfFrames}"); #endif var timeBetweenFrames = TimeSpan.FromMilliseconds(ConsoleMath.Round(animationTime.TotalMilliseconds / numberOfFrames)); #if DEBUG options.Debug?.Invoke($"Time between frames: {timeBetweenFrames.TotalMilliseconds} ms"); #endif var initialValue = options.From; options.Set(initialValue); #if DEBUG options.Debug?.Invoke($"InitialValue: {initialValue}"); #endif var delta = options.To - initialValue; #if DEBUG options.Debug?.Invoke($"Delta: {delta}"); #endif var workSw = Stopwatch.StartNew(); for (float i = 1; i <= numberOfFrames; i++) { var percentageDone = i / numberOfFrames; if (options.EasingFunction != null) { percentageDone = options.EasingFunction(percentageDone); } var scheduledTimeAfterThisFrame = TimeSpan.FromMilliseconds(timeBetweenFrames.TotalMilliseconds * i); var newValue = initialValue + (delta * percentageDone); options.OnSet?.Invoke(percentageDone); options.Set(newValue); #if DEBUG options.Debug?.Invoke($"Set value to {newValue} at percentage {percentageDone}"); #endif var delayTime = options.DelayProvider is WallClockDelayProvider?TimeSpan.FromMilliseconds(Math.Max(0, scheduledTimeAfterThisFrame.TotalMilliseconds - workSw.Elapsed.TotalMilliseconds)) : timeBetweenFrames; #if DEBUG options.Debug?.Invoke($"Delayed for {delayTime.TotalMilliseconds} ms at percentage {percentageDone}"); #endif if (options.IsCancelled != null && options.IsCancelled()) { return; } if (delayTime == TimeSpan.Zero) { await options.YieldAsync(); } else { await options.DelayAsync(delayTime); } } }