From a941eaeb4e4b0c0b8d3c99d50991bad972e46ece Mon Sep 17 00:00:00 2001 From: Jumar Macato Date: Thu, 6 Dec 2018 01:26:31 +0800 Subject: [PATCH] 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));