From d3329e19d89f004351eb4ea060c01e9a21d827a0 Mon Sep 17 00:00:00 2001 From: Jumar Macato Date: Wed, 5 Dec 2018 20:50:10 +0800 Subject: [PATCH 01/31] Simplify iteration number calculation. --- src/Avalonia.Animation/AnimationInstance`1.cs | 29 ++++++++++++------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/src/Avalonia.Animation/AnimationInstance`1.cs b/src/Avalonia.Animation/AnimationInstance`1.cs index 8184e68d42..52cb9e72c0 100644 --- a/src/Avalonia.Animation/AnimationInstance`1.cs +++ b/src/Avalonia.Animation/AnimationInstance`1.cs @@ -16,7 +16,7 @@ namespace Avalonia.Animation private T _lastInterpValue; private T _firstKFValue; private long _repeatCount; - private double _currentIteration; + private long _currentIteration; private bool _isLooping; private bool _gotFirstKFValue; private bool _iterationDelay; @@ -72,7 +72,7 @@ namespace Avalonia.Animation _onCompleteAction = OnComplete; _interpolator = Interpolator; _baseClock = baseClock; - } + } protected override void Unsubscribed() { @@ -147,45 +147,51 @@ namespace Avalonia.Animation { _currentIteration = 1; } + // time is currently the second to nth iteration. else if (time > iterationEndpoint) { - //Subtract first iteration to properly get the subsequent iteration time + // Subtract first iteration to properly get the subsequent iteration time. iterationTime -= iterationEndpoint; + // Ignore delays on subsequent iterations if it's not the initial + // iteration unless _iterationDelay is enabled. if (!_iterationDelay & delayEndpoint > TimeSpan.Zero) { delayEndpoint = TimeSpan.Zero; iterationEndpoint = _duration; } - //Calculate the current iteration number - _currentIteration = Math.Min(_repeatCount,(int)Math.Floor((double)((double)iterationTime.Ticks / iterationEndpoint.Ticks)) + 2); + // Calculate the current iteration number + _currentIteration = (iterationTime.Ticks / iterationEndpoint.Ticks) + 2; } else { return; } - // Determine if the current iteration should have its normalized time inverted. + // Determine if the current iteration should have its normalized time reversed. bool isCurIterReverse = _animationDirection == PlaybackDirection.Normal ? false : _animationDirection == PlaybackDirection.Alternate ? (_currentIteration % 2 == 0) ? false : true : _animationDirection == PlaybackDirection.AlternateReverse ? (_currentIteration % 2 == 0) ? true : false : _animationDirection == PlaybackDirection.Reverse ? true : false; - + if (!_isLooping) { - var totalTime = _iterationDelay ? _repeatCount * ( _duration.Ticks + _delay.Ticks) : _repeatCount * _duration.Ticks + _delay.Ticks; + var totalTime = _iterationDelay ? _repeatCount * (_duration.Ticks + _delay.Ticks) : _repeatCount * _duration.Ticks + _delay.Ticks; + + // Clamp value when animations ends. if (time.Ticks >= totalTime) { var easedTime = _easeFunc.Ease(isCurIterReverse ? 0.0 : 1.0); _lastInterpValue = _interpolator(easedTime, _neutralValue); - + DoComplete(); return; } } - iterationTime = TimeSpan.FromTicks((long)(iterationTime.Ticks % iterationEndpoint.Ticks)); - + + iterationTime = TimeSpan.FromTicks(iterationTime.Ticks % iterationEndpoint.Ticks); + if (delayEndpoint > TimeSpan.Zero & iterationTime < delayEndpoint) { DoDelay(); @@ -199,6 +205,7 @@ namespace Avalonia.Animation // Normalize time var interpVal = (double)iterationTime.Ticks / iterationEndpoint.Ticks; + // Check if normalized time needs to be reversed. if (isCurIterReverse) interpVal = 1 - interpVal; From 52a9e72405f9c30ec597af08b876b2e524ddc49a Mon Sep 17 00:00:00 2001 From: Jumar Macato Date: Thu, 6 Dec 2018 00:10:29 +0800 Subject: [PATCH 02/31] Simplify the animations iteration algorithm --- src/Avalonia.Animation/Animation.cs | 11 +- src/Avalonia.Animation/AnimationInstance`1.cs | 114 +++++++----------- 2 files changed, 44 insertions(+), 81 deletions(-) diff --git a/src/Avalonia.Animation/Animation.cs b/src/Avalonia.Animation/Animation.cs index d7efc69e10..8b5e760455 100644 --- a/src/Avalonia.Animation/Animation.cs +++ b/src/Avalonia.Animation/Animation.cs @@ -54,17 +54,12 @@ namespace Avalonia.Animation /// Describes a delay to be added before the animation starts, and optionally between /// repeats of the animation if is set. /// - public TimeSpan Delay { get; set; } + public TimeSpan Delay { get; set; } = TimeSpan.Zero; /// - /// Gets or sets a value indicating whether will be applied between - /// iterations of the animation. + /// Gets or sets the amount of delay time between iterations. /// - /// - /// If this property is not set, then will only be applied to the first - /// iteration of the animation. - /// - public bool DelayBetweenIterations { get; set; } + public TimeSpan DelayBetweenIterations { get; set; } = TimeSpan.Zero; private readonly static List<(Func Condition, Type Animator)> Animators = new List<(Func, Type)> { diff --git a/src/Avalonia.Animation/AnimationInstance`1.cs b/src/Avalonia.Animation/AnimationInstance`1.cs index 52cb9e72c0..69b99f7888 100644 --- a/src/Avalonia.Animation/AnimationInstance`1.cs +++ b/src/Avalonia.Animation/AnimationInstance`1.cs @@ -15,18 +15,18 @@ namespace Avalonia.Animation { private T _lastInterpValue; private T _firstKFValue; - private long _repeatCount; + private float _iterationCount; private long _currentIteration; private bool _isLooping; private bool _gotFirstKFValue; - private bool _iterationDelay; private FillMode _fillMode; private PlaybackDirection _animationDirection; private Animator _parent; private Animatable _targetControl; private T _neutralValue; private double _speedRatio; - private TimeSpan _delay; + private TimeSpan _initialDelay; + private TimeSpan _iterationDelay; private TimeSpan _duration; private Easings.Easing _easeFunc; private Action _onCompleteAction; @@ -50,20 +50,20 @@ namespace Avalonia.Animation _speedRatio = animation.SpeedRatio; - _delay = animation.Delay; + _initialDelay = animation.Delay; _duration = animation.Duration; _iterationDelay = animation.DelayBetweenIterations; switch (animation.RepeatCount.RepeatType) { case RepeatType.None: - _repeatCount = 1; + _iterationCount = 1; break; case RepeatType.Loop: - _isLooping = true; + _iterationCount = float.PositiveInfinity; break; case RepeatType.Repeat: - _repeatCount = (long)animation.RepeatCount.Value; + _iterationCount = animation.RepeatCount.Value; break; } @@ -76,7 +76,7 @@ namespace Avalonia.Animation protected override void Unsubscribed() { - //Animation may have been stopped before it has finished + // Animation may have been stopped before it has finished. ApplyFinalFill(); _timerSubscription?.Dispose(); @@ -138,83 +138,51 @@ namespace Avalonia.Animation private void InternalStep(TimeSpan time) { DoPlayStates(); - var delayEndpoint = _delay; - var iterationEndpoint = delayEndpoint + _duration; - var iterationTime = time; - //determine if time is currently in the first iteration. - if (time >= TimeSpan.Zero & time <= iterationEndpoint) - { - _currentIteration = 1; - } - // time is currently the second to nth iteration. - else if (time > iterationEndpoint) - { - // Subtract first iteration to properly get the subsequent iteration time. - iterationTime -= iterationEndpoint; - - // Ignore delays on subsequent iterations if it's not the initial - // iteration unless _iterationDelay is enabled. - if (!_iterationDelay & delayEndpoint > TimeSpan.Zero) - { - delayEndpoint = TimeSpan.Zero; - iterationEndpoint = _duration; - } + var indexTime = time.Ticks; + var iterDuration = _duration.Ticks * _speedRatio; + var iterDelay = _iterationDelay.Ticks * _speedRatio; + var initDelay = _initialDelay.Ticks * _speedRatio; - // Calculate the current iteration number - _currentIteration = (iterationTime.Ticks / iterationEndpoint.Ticks) + 2; - } - else + if (indexTime > 0 & indexTime <= initDelay) { - return; + DoDelay(); } - - // Determine if the current iteration should have its normalized time reversed. - bool isCurIterReverse = _animationDirection == PlaybackDirection.Normal ? false : - _animationDirection == PlaybackDirection.Alternate ? (_currentIteration % 2 == 0) ? false : true : - _animationDirection == PlaybackDirection.AlternateReverse ? (_currentIteration % 2 == 0) ? true : false : - _animationDirection == PlaybackDirection.Reverse ? true : false; - - if (!_isLooping) + else { - var totalTime = _iterationDelay ? _repeatCount * (_duration.Ticks + _delay.Ticks) : _repeatCount * _duration.Ticks + _delay.Ticks; - - // Clamp value when animations ends. - if (time.Ticks >= totalTime) - { - var easedTime = _easeFunc.Ease(isCurIterReverse ? 0.0 : 1.0); - _lastInterpValue = _interpolator(easedTime, _neutralValue); + var fullIterationTime = iterDuration + iterDelay; + var opsTime = indexTime - initDelay; + var playbackTime = opsTime % fullIterationTime; + _currentIteration = (long)Math.Floor(opsTime / fullIterationTime); + + if ((_currentIteration + 1) > _iterationCount) DoComplete(); - return; - } - } - iterationTime = TimeSpan.FromTicks(iterationTime.Ticks % iterationEndpoint.Ticks); - - if (delayEndpoint > TimeSpan.Zero & iterationTime < delayEndpoint) - { - DoDelay(); - } - else - { - // Offset the delay time - iterationTime -= delayEndpoint; - iterationEndpoint -= delayEndpoint; + if (playbackTime <= iterDuration) + { + var normalizedTime = playbackTime / iterDuration; - // Normalize time - var interpVal = (double)iterationTime.Ticks / iterationEndpoint.Ticks; + bool isCurIterReverse = _animationDirection == PlaybackDirection.Normal ? false : + _animationDirection == PlaybackDirection.Alternate ? (_currentIteration % 2 == 0) ? false : true : + _animationDirection == PlaybackDirection.AlternateReverse ? (_currentIteration % 2 == 0) ? true : false : + _animationDirection == PlaybackDirection.Reverse ? true : false; - // Check if normalized time needs to be reversed. - if (isCurIterReverse) - interpVal = 1 - interpVal; + // Check if normalized time needs to be reversed. + if (isCurIterReverse) + normalizedTime = 1 - normalizedTime; - // Ease and interpolate - var easedTime = _easeFunc.Ease(interpVal); - _lastInterpValue = _interpolator(easedTime, _neutralValue); + // Ease and interpolate + var easedTime = _easeFunc.Ease(normalizedTime); + _lastInterpValue = _interpolator(easedTime, _neutralValue); - PublishNext(_lastInterpValue); + PublishNext(_lastInterpValue); + } + else if (playbackTime > iterDuration & playbackTime <= fullIterationTime & iterDelay > 0) + { + DoDelay(); + } } } } -} +} \ No newline at end of file From b489e3a935447fd54ee903bf499a1ee2d85145bc Mon Sep 17 00:00:00 2001 From: Jumar Macato Date: Thu, 6 Dec 2018 01:19:21 +0800 Subject: [PATCH 03/31] Additional fixes for AnimationInstance. --- src/Avalonia.Animation/AnimationInstance`1.cs | 31 ++++++++++--------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/src/Avalonia.Animation/AnimationInstance`1.cs b/src/Avalonia.Animation/AnimationInstance`1.cs index 69b99f7888..b27be609a5 100644 --- a/src/Avalonia.Animation/AnimationInstance`1.cs +++ b/src/Avalonia.Animation/AnimationInstance`1.cs @@ -15,9 +15,8 @@ namespace Avalonia.Animation { private T _lastInterpValue; private T _firstKFValue; - private float _iterationCount; - private long _currentIteration; - private bool _isLooping; + private ulong? _iterationCount; + private ulong _currentIteration; private bool _gotFirstKFValue; private FillMode _fillMode; private PlaybackDirection _animationDirection; @@ -58,10 +57,7 @@ namespace Avalonia.Animation { case RepeatType.None: _iterationCount = 1; - break; - case RepeatType.Loop: - _iterationCount = float.PositiveInfinity; - break; + break; case RepeatType.Repeat: _iterationCount = animation.RepeatCount.Value; break; @@ -138,7 +134,8 @@ namespace Avalonia.Animation private void InternalStep(TimeSpan time) { DoPlayStates(); - + + // Scale timebases according to speedratio. var indexTime = time.Ticks; var iterDuration = _duration.Ticks * _speedRatio; var iterDelay = _iterationDelay.Ticks * _speedRatio; @@ -150,25 +147,27 @@ namespace Avalonia.Animation } else { - var fullIterationTime = iterDuration + iterDelay; + // Calculate timebases. + var iterationTime = iterDuration + iterDelay; var opsTime = indexTime - initDelay; - var playbackTime = opsTime % fullIterationTime; + var playbackTime = opsTime % iterationTime; - _currentIteration = (long)Math.Floor(opsTime / fullIterationTime); + _currentIteration = (ulong)(opsTime / iterationTime); + // Stop animation when the current iteration is beyond the iteration count. if ((_currentIteration + 1) > _iterationCount) DoComplete(); if (playbackTime <= iterDuration) { + // Normalize time for interpolation. var normalizedTime = playbackTime / iterDuration; + // Check if normalized time needs to be reversed. bool isCurIterReverse = _animationDirection == PlaybackDirection.Normal ? false : _animationDirection == PlaybackDirection.Alternate ? (_currentIteration % 2 == 0) ? false : true : _animationDirection == PlaybackDirection.AlternateReverse ? (_currentIteration % 2 == 0) ? true : false : _animationDirection == PlaybackDirection.Reverse ? true : false; - - // Check if normalized time needs to be reversed. if (isCurIterReverse) normalizedTime = 1 - normalizedTime; @@ -178,7 +177,11 @@ namespace Avalonia.Animation PublishNext(_lastInterpValue); } - else if (playbackTime > iterDuration & playbackTime <= fullIterationTime & iterDelay > 0) + else if (playbackTime > iterDuration & + playbackTime <= iterationTime & + iterDelay > 0 & + // The last iteration's trailing delay should be skipped. + (_currentIteration + 1) < _iterationCount) { DoDelay(); } From a941eaeb4e4b0c0b8d3c99d50991bad972e46ece Mon Sep 17 00:00:00 2001 From: Jumar Macato Date: Thu, 6 Dec 2018 01:26:31 +0800 Subject: [PATCH 04/31] Make SpeedRatio bindable. --- src/Avalonia.Animation/Animation.cs | 33 +++++-- src/Avalonia.Animation/AnimationInstance`1.cs | 18 ++-- src/Avalonia.Animation/KeyFrames.cs | 33 +++++++ src/Avalonia.Visuals/Animation/CrossFade.cs | 40 +++++---- src/Avalonia.Visuals/Animation/PageSlide.cs | 86 ++++++++++--------- 5 files changed, 137 insertions(+), 73 deletions(-) create mode 100644 src/Avalonia.Animation/KeyFrames.cs diff --git a/src/Avalonia.Animation/Animation.cs b/src/Avalonia.Animation/Animation.cs index 8b5e760455..c79ee7b8a8 100644 --- a/src/Avalonia.Animation/Animation.cs +++ b/src/Avalonia.Animation/Animation.cs @@ -9,14 +9,21 @@ using System.Reactive.Linq; using System.Threading.Tasks; using Avalonia.Animation.Easings; using Avalonia.Collections; +using Avalonia.Metadata; namespace Avalonia.Animation { /// /// Tracks the progress of an animation. /// - public class Animation : AvaloniaList, IAnimation + public class Animation : AvaloniaObject, IAnimation { + /// + /// Gets the children of the . + /// + [Content] + public KeyFrames Children { get; } = new KeyFrames(); + /// /// Gets or sets the active time of this animation. /// @@ -42,10 +49,6 @@ namespace Avalonia.Animation /// public Easing Easing { get; set; } = new LinearEasing(); - /// - /// Gets or sets the speed multiple for this animation. - /// - public double SpeedRatio { get; set; } = 1d; /// /// Gets or sets the delay time for this animation. @@ -61,6 +64,24 @@ namespace Avalonia.Animation /// public TimeSpan DelayBetweenIterations { get; set; } = TimeSpan.Zero; + public static readonly DirectProperty SpeedRatioProperty = + AvaloniaProperty.RegisterDirect( + nameof(_speedRatio), + o => o._speedRatio, + (o, v) => o._speedRatio = v, + 1d); + + private double _speedRatio = 1d; + + /// + /// Gets or sets the speed multiple for this animation. + /// + public double SpeedRatio + { + get { return _speedRatio; } + set { SetAndRaise(SpeedRatioProperty, ref _speedRatio, value); } + } + private readonly static List<(Func Condition, Type Animator)> Animators = new List<(Func, Type)> { ( prop => typeof(double).IsAssignableFrom(prop.PropertyType), typeof(DoubleAnimator) ) @@ -90,7 +111,7 @@ namespace Avalonia.Animation var animatorKeyFrames = new List(); var subscriptions = new List(); - foreach (var keyframe in this) + foreach (var keyframe in Children) { foreach (var setter in keyframe) { diff --git a/src/Avalonia.Animation/AnimationInstance`1.cs b/src/Avalonia.Animation/AnimationInstance`1.cs index b27be609a5..b793dde8f9 100644 --- a/src/Avalonia.Animation/AnimationInstance`1.cs +++ b/src/Avalonia.Animation/AnimationInstance`1.cs @@ -30,7 +30,7 @@ namespace Avalonia.Animation private Easings.Easing _easeFunc; private Action _onCompleteAction; private Func _interpolator; - private IDisposable _timerSubscription; + private IDisposable _timerSub, _speedRatioSub; private readonly IClock _baseClock; private IClock _clock; @@ -47,7 +47,8 @@ namespace Avalonia.Animation _targetControl = control; _neutralValue = (T)_targetControl.GetValue(_parent.Property); - _speedRatio = animation.SpeedRatio; + _speedRatioSub = animation.GetObservable(Animation.SpeedRatioProperty) + .Subscribe(p => _speedRatio = p); _initialDelay = animation.Delay; _duration = animation.Duration; @@ -57,7 +58,7 @@ namespace Avalonia.Animation { case RepeatType.None: _iterationCount = 1; - break; + break; case RepeatType.Repeat: _iterationCount = animation.RepeatCount.Value; break; @@ -75,14 +76,15 @@ namespace Avalonia.Animation // Animation may have been stopped before it has finished. ApplyFinalFill(); - _timerSubscription?.Dispose(); + _timerSub?.Dispose(); + _speedRatioSub?.Dispose(); _clock.PlayState = PlayState.Stop; } protected override void Subscribed() { _clock = new Clock(_baseClock); - _timerSubscription = _clock.Subscribe(Step); + _timerSub = _clock.Subscribe(Step); } public void Step(TimeSpan frameTick) @@ -134,7 +136,7 @@ namespace Avalonia.Animation private void InternalStep(TimeSpan time) { DoPlayStates(); - + // Scale timebases according to speedratio. var indexTime = time.Ticks; var iterDuration = _duration.Ticks * _speedRatio; @@ -153,7 +155,7 @@ namespace Avalonia.Animation var playbackTime = opsTime % iterationTime; _currentIteration = (ulong)(opsTime / iterationTime); - + // Stop animation when the current iteration is beyond the iteration count. if ((_currentIteration + 1) > _iterationCount) DoComplete(); @@ -178,7 +180,7 @@ namespace Avalonia.Animation PublishNext(_lastInterpValue); } else if (playbackTime > iterDuration & - playbackTime <= iterationTime & + playbackTime <= iterationTime & iterDelay > 0 & // The last iteration's trailing delay should be skipped. (_currentIteration + 1) < _iterationCount) diff --git a/src/Avalonia.Animation/KeyFrames.cs b/src/Avalonia.Animation/KeyFrames.cs new file mode 100644 index 0000000000..9e3b1d9f51 --- /dev/null +++ b/src/Avalonia.Animation/KeyFrames.cs @@ -0,0 +1,33 @@ +// Copyright (c) The Avalonia Project. All rights reserved. +// Licensed under the MIT license. See licence.md file in the project root for full license information. + +using System; +using System.Collections.Generic; +using Avalonia.Collections; + +namespace Avalonia.Animation +{ + /// + /// A collection of s. + /// + public class KeyFrames : AvaloniaList + { + /// + /// Initializes a new instance of the class. + /// + public KeyFrames() + { + ResetBehavior = ResetBehavior.Remove; + } + + /// + /// Initializes a new instance of the class. + /// + /// The initial items in the collection. + public KeyFrames(IEnumerable items) + : base(items) + { + ResetBehavior = ResetBehavior.Remove; + } + } +} \ No newline at end of file diff --git a/src/Avalonia.Visuals/Animation/CrossFade.cs b/src/Avalonia.Visuals/Animation/CrossFade.cs index d5ddf1c7f5..b07123e050 100644 --- a/src/Avalonia.Visuals/Animation/CrossFade.cs +++ b/src/Avalonia.Visuals/Animation/CrossFade.cs @@ -21,7 +21,7 @@ namespace Avalonia.Animation /// Initializes a new instance of the class. /// public CrossFade() - :this(TimeSpan.Zero) + : this(TimeSpan.Zero) { } @@ -33,30 +33,36 @@ namespace Avalonia.Animation { _fadeOutAnimation = new Animation { - new KeyFrame - ( - new Setter + Children = + { + new KeyFrame + ( + new Setter + { + Property = Visual.OpacityProperty, + Value = 0d + } + ) { - Property = Visual.OpacityProperty, - Value = 0d + Cue = new Cue(1d) } - ) - { - Cue = new Cue(1d) } }; _fadeInAnimation = new Animation { - new KeyFrame - ( - new Setter + Children = + { + new KeyFrame + ( + new Setter + { + Property = Visual.OpacityProperty, + Value = 0d + } + ) { - Property = Visual.OpacityProperty, - Value = 0d + Cue = new Cue(0d) } - ) - { - Cue = new Cue(0d) } }; _fadeOutAnimation.Duration = _fadeInAnimation.Duration = duration; diff --git a/src/Avalonia.Visuals/Animation/PageSlide.cs b/src/Avalonia.Visuals/Animation/PageSlide.cs index c5d43068c1..c1848d7daa 100644 --- a/src/Avalonia.Visuals/Animation/PageSlide.cs +++ b/src/Avalonia.Visuals/Animation/PageSlide.cs @@ -74,34 +74,34 @@ namespace Avalonia.Animation var distance = Orientation == SlideAxis.Horizontal ? parent.Bounds.Width : parent.Bounds.Height; var translateProperty = Orientation == SlideAxis.Horizontal ? TranslateTransform.XProperty : TranslateTransform.YProperty; - - // TODO: Implement relevant transition logic here (or discard this class) - // in favor of XAML based transition for pages if (from != null) { var animation = new Animation { - new KeyFrame - ( - new Setter - { - Property = translateProperty, - Value = 0d - } - ) + Children = { - Cue = new Cue(0d) - }, - new KeyFrame - ( - new Setter + new KeyFrame + ( + new Setter + { + Property = translateProperty, + Value = 0d + } + ) { - Property = translateProperty, - Value = forward ? -distance : distance + Cue = new Cue(0d) + }, + new KeyFrame + ( + new Setter + { + Property = translateProperty, + Value = forward ? -distance : distance + } + ) + { + Cue = new Cue(1d) } - ) - { - Cue = new Cue(1d) } }; animation.Duration = Duration; @@ -113,29 +113,31 @@ namespace Avalonia.Animation to.IsVisible = true; var animation = new Animation { - - new KeyFrame - ( - new Setter - { - Property = translateProperty, - Value = forward ? distance : -distance - } - ) + Children = { - Cue = new Cue(0d) - }, - new KeyFrame - ( - new Setter + new KeyFrame + ( + new Setter + { + Property = translateProperty, + Value = forward ? distance : -distance + } + ) { - Property = translateProperty, - Value = 0d - } - ) - { - Cue = new Cue(1d) - }, + Cue = new Cue(0d) + }, + new KeyFrame + ( + new Setter + { + Property = translateProperty, + Value = 0d + } + ) + { + Cue = new Cue(1d) + }, + } }; animation.Duration = Duration; tasks.Add(animation.RunAsync(to)); From 7e023ee5af1e531373f3615cae7a15582c120822 Mon Sep 17 00:00:00 2001 From: Jumar Macato Date: Thu, 6 Dec 2018 01:45:55 +0800 Subject: [PATCH 05/31] [Breaking Change] Replace RepeatCount with IterationCount. --- samples/RenderDemo/Pages/AnimationsPage.xaml | 4 +- samples/RenderDemo/Pages/ClippingPage.xaml | 2 +- src/Avalonia.Animation/Animation.cs | 4 +- src/Avalonia.Animation/AnimationInstance`1.cs | 13 +- src/Avalonia.Animation/IterationCount.cs | 176 ++++++++++++++++ ...rter.cs => IterationCountTypeConverter.cs} | 4 +- src/Avalonia.Animation/RepeatCount.cs | 199 ------------------ src/Avalonia.Themes.Default/ProgressBar.xaml | 4 +- 8 files changed, 188 insertions(+), 218 deletions(-) create mode 100644 src/Avalonia.Animation/IterationCount.cs rename src/Avalonia.Animation/{RepeatCountTypeConverter.cs => IterationCountTypeConverter.cs} (83%) delete mode 100644 src/Avalonia.Animation/RepeatCount.cs diff --git a/samples/RenderDemo/Pages/AnimationsPage.xaml b/samples/RenderDemo/Pages/AnimationsPage.xaml index 1646708797..9f8e7b5fa5 100644 --- a/samples/RenderDemo/Pages/AnimationsPage.xaml +++ b/samples/RenderDemo/Pages/AnimationsPage.xaml @@ -43,7 +43,7 @@ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"> + @@ -121,6 +149,7 @@ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"> + From 87e8bca8793c40b5b320f611213122ee9d343a4a Mon Sep 17 00:00:00 2001 From: Jumar Macato Date: Mon, 10 Dec 2018 01:03:12 +0800 Subject: [PATCH 25/31] Minor fixes. --- samples/RenderDemo/Pages/AnimationsPage.xaml | 2 +- .../Animation/Animators/SolidColorBrushAnimator.cs | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/samples/RenderDemo/Pages/AnimationsPage.xaml b/samples/RenderDemo/Pages/AnimationsPage.xaml index e6e0eff941..53f8e855e0 100644 --- a/samples/RenderDemo/Pages/AnimationsPage.xaml +++ b/samples/RenderDemo/Pages/AnimationsPage.xaml @@ -149,7 +149,7 @@ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"> - + diff --git a/src/Avalonia.Visuals/Animation/Animators/SolidColorBrushAnimator.cs b/src/Avalonia.Visuals/Animation/Animators/SolidColorBrushAnimator.cs index 43cc9d5476..518d3d2ca7 100644 --- a/src/Avalonia.Visuals/Animation/Animators/SolidColorBrushAnimator.cs +++ b/src/Avalonia.Visuals/Animation/Animators/SolidColorBrushAnimator.cs @@ -7,8 +7,7 @@ using Avalonia.Media.Immutable; namespace Avalonia.Animation.Animators { /// - /// Animator that interpolates through - /// gamma sRGB color space for better visual result. + /// Animator that handles . /// public class SolidColorBrushAnimator : Animator { From 13eb3655714558ed3d66f34b83e1d6ee136b6ccf Mon Sep 17 00:00:00 2001 From: Jumar Macato Date: Mon, 10 Dec 2018 11:54:38 +0800 Subject: [PATCH 26/31] Fix SCBA. --- .../Animators/SolidColorBrushAnimator.cs | 53 +++++++++++-------- 1 file changed, 31 insertions(+), 22 deletions(-) diff --git a/src/Avalonia.Visuals/Animation/Animators/SolidColorBrushAnimator.cs b/src/Avalonia.Visuals/Animation/Animators/SolidColorBrushAnimator.cs index 518d3d2ca7..c3de578959 100644 --- a/src/Avalonia.Visuals/Animation/Animators/SolidColorBrushAnimator.cs +++ b/src/Avalonia.Visuals/Animation/Animators/SolidColorBrushAnimator.cs @@ -13,53 +13,62 @@ namespace Avalonia.Animation.Animators { ColorAnimator colorAnimator; + void InitializeColorAnimator() + { + colorAnimator = new ColorAnimator(); + + foreach (AnimatorKeyFrame keyframe in this) + { + colorAnimator.Add(keyframe); + } + + colorAnimator.Property = SolidColorBrush.ColorProperty; + } + public override IDisposable Apply(Animation animation, Animatable control, IClock clock, IObservable match, Action onComplete) { var ctrl = (Visual)control; foreach (var keyframe in this) { - // Return if the keyframe value is not a SolidColorBrush if (keyframe.Value as ISolidColorBrush == null) - { return Disposable.Empty; - } - // Preprocess values to Color if the xaml parser converts them to ISCB + // Preprocess keyframe values to Color if the xaml parser converts them to ISCB. if (keyframe.Value.GetType() == typeof(ImmutableSolidColorBrush)) { keyframe.Value = ((ImmutableSolidColorBrush)keyframe.Value).Color; } } - // Make sure that the target property has SCB instead of the immutable one nor null. + // Add SCB if the target prop is empty. + if (control.GetValue(Property) == null) + control.SetValue(Property, new SolidColorBrush(Colors.Transparent)); var targetVal = control.GetValue(Property); - SolidColorBrush targetSCB = null; - - if (targetVal == null) - targetSCB = new SolidColorBrush(Colors.Transparent); - else if (typeof(ISolidColorBrush).IsAssignableFrom(targetVal.GetType())) - targetSCB = new SolidColorBrush(((ISolidColorBrush)targetVal).Color); - else - return Disposable.Empty; - - control.SetValue(Property, targetSCB); - - if (colorAnimator == null) + // Continue if target prop is not empty & is a SolidColorBrush derivative. + if (typeof(ISolidColorBrush).IsAssignableFrom(targetVal.GetType())) { - colorAnimator = new ColorAnimator(); + if (colorAnimator == null) + InitializeColorAnimator(); + + SolidColorBrush finalTarget; - foreach (AnimatorKeyFrame keyframe in this) + // If it's ISCB, change it back to SCB. + if (targetVal.GetType() == typeof(ImmutableSolidColorBrush)) { - colorAnimator.Add(keyframe); + var col = (ImmutableSolidColorBrush)targetVal; + targetVal = new SolidColorBrush(col.Color); + control.SetValue(Property, targetVal); } - colorAnimator.Property = SolidColorBrush.ColorProperty; + finalTarget = targetVal as SolidColorBrush; + + return colorAnimator.Apply(animation, finalTarget, clock ?? control.Clock, match, onComplete); } - return colorAnimator.Apply(animation, targetSCB, clock ?? control.Clock, match, onComplete); + return Disposable.Empty; } public override SolidColorBrush Interpolate(double p, SolidColorBrush o, SolidColorBrush n) => null; From 739309c28f116f3f9ce56a70a884dd330c879393 Mon Sep 17 00:00:00 2001 From: Jumar Macato Date: Mon, 10 Dec 2018 15:58:53 +0800 Subject: [PATCH 27/31] Replace binary-search on keyframe selection with naive approach for now. --- .../Animators/Animator`1.cs | 36 ++++--------------- 1 file changed, 7 insertions(+), 29 deletions(-) diff --git a/src/Avalonia.Animation/Animators/Animator`1.cs b/src/Avalonia.Animation/Animators/Animator`1.cs index e79aa128d6..e42489d6a6 100644 --- a/src/Avalonia.Animation/Animators/Animator`1.cs +++ b/src/Avalonia.Animation/Animators/Animator`1.cs @@ -78,7 +78,7 @@ namespace Avalonia.Animation.Animators double t0 = firstKeyframe.Cue.CueValue; double t1 = lastKeyframe.Cue.CueValue; - double progress = (animationTime - t0) / (t1 - t0); + double progress = (animationTime - t0) / (t1 - t0); T oldValue, newValue; @@ -97,30 +97,11 @@ namespace Avalonia.Animation.Animators private int FindClosestBeforeKeyFrame(double time) { - int FindClosestBeforeKeyFrame(int startIndex, int length) - { - if (length == 0 || length == 1) - { - return startIndex; - } - - int middle = startIndex + (length / 2); + for (int i = 0; i < _convertedKeyframes.Count; i++) + if (_convertedKeyframes[i].Cue.CueValue > time) + return i - 1; - if (_convertedKeyframes[middle].Cue.CueValue < time) - { - return FindClosestBeforeKeyFrame(middle, length - middle); - } - else if (_convertedKeyframes[middle].Cue.CueValue > time) - { - return FindClosestBeforeKeyFrame(startIndex, middle - startIndex); - } - else - { - return middle; - } - } - - return FindClosestBeforeKeyFrame(0, _convertedKeyframes.Count); + throw new Exception("Index time is out of keyframe time range."); } /// @@ -139,13 +120,10 @@ namespace Avalonia.Animation.Animators } /// - /// Interpolates a value given the desired time. + /// Interpolates in-between two key values given the desired progress time. /// public abstract T Interpolate(double progress, T oldValue, T newValue); - /// - /// Verifies, converts and sorts keyframe values according to this class's target type. - /// private void VerifyConvertKeyFrames() { foreach (AnimatorKeyFrame keyframe in this) @@ -193,4 +171,4 @@ namespace Avalonia.Animation.Animators } } } -} \ No newline at end of file +} From 2fe9378ebc79b0f7f32a45bca84c15b514d54b3f Mon Sep 17 00:00:00 2001 From: Jumar Macato Date: Mon, 10 Dec 2018 16:01:21 +0800 Subject: [PATCH 28/31] Adjust rainbow example --- samples/RenderDemo/Pages/AnimationsPage.xaml | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/samples/RenderDemo/Pages/AnimationsPage.xaml b/samples/RenderDemo/Pages/AnimationsPage.xaml index 53f8e855e0..b5a8ff6dea 100644 --- a/samples/RenderDemo/Pages/AnimationsPage.xaml +++ b/samples/RenderDemo/Pages/AnimationsPage.xaml @@ -106,19 +106,20 @@ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">