From 2556b50992dcfe429d0e47ae1ed10313e5757bf2 Mon Sep 17 00:00:00 2001 From: Jumar Macato Date: Wed, 21 Mar 2018 12:17:31 +0800 Subject: [PATCH 001/149] Rename Carousel & TabControls Transition property to PageTransitions to avoid confusion to the upcoming Transitions from Avalonia.Animations. --- samples/ControlCatalog/MainView.xaml | 4 ++-- .../ControlCatalog/Pages/CarouselPage.xaml | 4 ++-- .../ControlCatalog/Pages/CarouselPage.xaml.cs | 6 +++--- samples/ControlCatalog/SideBar.xaml | 2 +- samples/RenderTest/MainWindow.xaml | 4 ++-- samples/RenderTest/SideBar.xaml | 2 +- src/Avalonia.Controls/Carousel.cs | 12 ++++++------ .../Presenters/CarouselPresenter.cs | 19 ++++++++++--------- src/Avalonia.Controls/TabControl.cs | 12 ++++++------ src/Avalonia.Themes.Default/Carousel.xaml | 2 +- src/Avalonia.Themes.Default/TabControl.xaml | 2 +- .../CarouselTests.cs | 2 +- .../TabControlTests.cs | 2 +- 13 files changed, 37 insertions(+), 36 deletions(-) diff --git a/samples/ControlCatalog/MainView.xaml b/samples/ControlCatalog/MainView.xaml index 142d0d42b1..db0f3cce7c 100644 --- a/samples/ControlCatalog/MainView.xaml +++ b/samples/ControlCatalog/MainView.xaml @@ -2,9 +2,9 @@ xmlns:pages="clr-namespace:ControlCatalog.Pages;assembly=ControlCatalog" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"> - + - + diff --git a/samples/ControlCatalog/Pages/CarouselPage.xaml b/samples/ControlCatalog/Pages/CarouselPage.xaml index 4a5b2cea26..3468b71fd8 100644 --- a/samples/ControlCatalog/Pages/CarouselPage.xaml +++ b/samples/ControlCatalog/Pages/CarouselPage.xaml @@ -8,9 +8,9 @@ - + - + diff --git a/samples/ControlCatalog/Pages/CarouselPage.xaml.cs b/samples/ControlCatalog/Pages/CarouselPage.xaml.cs index 272290e679..5e24bfb58f 100644 --- a/samples/ControlCatalog/Pages/CarouselPage.xaml.cs +++ b/samples/ControlCatalog/Pages/CarouselPage.xaml.cs @@ -37,13 +37,13 @@ namespace ControlCatalog.Pages switch (_transition.SelectedIndex) { case 0: - _carousel.Transition = null; + _carousel.PageTransition = null; break; case 1: - _carousel.Transition = new PageSlide(TimeSpan.FromSeconds(0.25), _orientation.SelectedIndex == 0 ? PageSlide.SlideAxis.Horizontal : PageSlide.SlideAxis.Vertical); + _carousel.PageTransition = new PageSlide(TimeSpan.FromSeconds(0.25), _orientation.SelectedIndex == 0 ? PageSlide.SlideAxis.Horizontal : PageSlide.SlideAxis.Vertical); break; case 2: - _carousel.Transition = new CrossFade(TimeSpan.FromSeconds(0.25)); + _carousel.PageTransition = new CrossFade(TimeSpan.FromSeconds(0.25)); break; } } diff --git a/samples/ControlCatalog/SideBar.xaml b/samples/ControlCatalog/SideBar.xaml index 7fcd828ba3..3013c5a80e 100644 --- a/samples/ControlCatalog/SideBar.xaml +++ b/samples/ControlCatalog/SideBar.xaml @@ -20,7 +20,7 @@ MemberSelector="{x:Static TabControl.ContentSelector}" Items="{TemplateBinding Items}" SelectedIndex="{TemplateBinding Path=SelectedIndex}" - Transition="{TemplateBinding Transition}" + PageTransition="{TemplateBinding PageTransition}" Grid.Row="1"/> diff --git a/samples/RenderTest/MainWindow.xaml b/samples/RenderTest/MainWindow.xaml index 9e9a600161..f63dec230e 100644 --- a/samples/RenderTest/MainWindow.xaml +++ b/samples/RenderTest/MainWindow.xaml @@ -22,9 +22,9 @@ - + - + diff --git a/samples/RenderTest/SideBar.xaml b/samples/RenderTest/SideBar.xaml index 7fcd828ba3..3013c5a80e 100644 --- a/samples/RenderTest/SideBar.xaml +++ b/samples/RenderTest/SideBar.xaml @@ -20,7 +20,7 @@ MemberSelector="{x:Static TabControl.ContentSelector}" Items="{TemplateBinding Items}" SelectedIndex="{TemplateBinding Path=SelectedIndex}" - Transition="{TemplateBinding Transition}" + PageTransition="{TemplateBinding PageTransition}" Grid.Row="1"/> diff --git a/src/Avalonia.Controls/Carousel.cs b/src/Avalonia.Controls/Carousel.cs index e7c934091a..069bf40820 100644 --- a/src/Avalonia.Controls/Carousel.cs +++ b/src/Avalonia.Controls/Carousel.cs @@ -21,10 +21,10 @@ namespace Avalonia.Controls AvaloniaProperty.Register(nameof(IsVirtualized), true); /// - /// Defines the property. + /// Defines the property. /// - public static readonly StyledProperty TransitionProperty = - AvaloniaProperty.Register(nameof(Transition)); + public static readonly StyledProperty PageTransitionProperty = + AvaloniaProperty.Register(nameof(PageTransition)); /// /// The default value of for @@ -57,10 +57,10 @@ namespace Avalonia.Controls /// /// Gets or sets the transition to use when moving between pages. /// - public IPageTransition Transition + public IPageTransition PageTransition { - get { return GetValue(TransitionProperty); } - set { SetValue(TransitionProperty, value); } + get { return GetValue(PageTransitionProperty); } + set { SetValue(PageTransitionProperty, value); } } /// diff --git a/src/Avalonia.Controls/Presenters/CarouselPresenter.cs b/src/Avalonia.Controls/Presenters/CarouselPresenter.cs index b50b110761..5156a11b30 100644 --- a/src/Avalonia.Controls/Presenters/CarouselPresenter.cs +++ b/src/Avalonia.Controls/Presenters/CarouselPresenter.cs @@ -33,12 +33,13 @@ namespace Avalonia.Controls.Presenters (o, v) => o.SelectedIndex = v); /// - /// Defines the property. + /// Defines the property. /// - public static readonly StyledProperty TransitionProperty = - Carousel.TransitionProperty.AddOwner(); + public static readonly StyledProperty PageTransitionProperty = + Carousel.PageTransitionProperty.AddOwner(); private int _selectedIndex = -1; + private Task _current; private Task _currentTransition; private int _queuedTransitionIndex = -1; @@ -88,10 +89,10 @@ namespace Avalonia.Controls.Presenters /// /// Gets or sets a transition to use when switching pages. /// - public IPageTransition Transition + public IPageTransition PageTransition { - get { return GetValue(TransitionProperty); } - set { SetValue(TransitionProperty, value); } + get { return GetValue(PageTransitionProperty); } + set { SetValue(PageTransitionProperty, value); } } /// @@ -121,7 +122,7 @@ namespace Avalonia.Controls.Presenters } /// - /// Moves to the selected page, animating if a is set. + /// Moves to the selected page, animating if a is set. /// /// The index of the old page. /// The index of the new page. @@ -144,9 +145,9 @@ namespace Avalonia.Controls.Presenters to = GetOrCreateContainer(toIndex); } - if (Transition != null && (from != null || to != null)) + if (PageTransition != null && (from != null || to != null)) { - await Transition.Start((Visual)from, (Visual)to, fromIndex < toIndex); + await PageTransition.Start((Visual)from, (Visual)to, fromIndex < toIndex); } else if (to != null) { diff --git a/src/Avalonia.Controls/TabControl.cs b/src/Avalonia.Controls/TabControl.cs index 231274b873..70cf8b4e05 100644 --- a/src/Avalonia.Controls/TabControl.cs +++ b/src/Avalonia.Controls/TabControl.cs @@ -14,10 +14,10 @@ namespace Avalonia.Controls public class TabControl : SelectingItemsControl { /// - /// Defines the property. + /// Defines the property. /// - public static readonly StyledProperty TransitionProperty = - Avalonia.Controls.Carousel.TransitionProperty.AddOwner(); + public static readonly StyledProperty PageTransitionProperty = + Avalonia.Controls.Carousel.PageTransitionProperty.AddOwner(); /// /// Defines an that selects the content of a . @@ -68,10 +68,10 @@ namespace Avalonia.Controls /// /// Gets or sets the transition to use when switching tabs. /// - public IPageTransition Transition + public IPageTransition PageTransition { - get { return GetValue(TransitionProperty); } - set { SetValue(TransitionProperty, value); } + get { return GetValue(PageTransitionProperty); } + set { SetValue(PageTransitionProperty, value); } } /// diff --git a/src/Avalonia.Themes.Default/Carousel.xaml b/src/Avalonia.Themes.Default/Carousel.xaml index c7bf296ede..efe12c4333 100644 --- a/src/Avalonia.Themes.Default/Carousel.xaml +++ b/src/Avalonia.Themes.Default/Carousel.xaml @@ -10,7 +10,7 @@ Margin="{TemplateBinding Padding}" MemberSelector="{TemplateBinding MemberSelector}" SelectedIndex="{TemplateBinding SelectedIndex}" - Transition="{TemplateBinding Transition}"/> + PageTransition="{TemplateBinding PageTransition}"/> diff --git a/src/Avalonia.Themes.Default/TabControl.xaml b/src/Avalonia.Themes.Default/TabControl.xaml index f8fb4a1f49..0196c1f3ab 100644 --- a/src/Avalonia.Themes.Default/TabControl.xaml +++ b/src/Avalonia.Themes.Default/TabControl.xaml @@ -14,7 +14,7 @@ MemberSelector="{x:Static TabControl.ContentSelector}" Items="{TemplateBinding Items}" SelectedIndex="{TemplateBinding Path=SelectedIndex}" - Transition="{TemplateBinding Transition}" + PageTransition="{TemplateBinding PageTransition}" Grid.Row="1"/> diff --git a/tests/Avalonia.Controls.UnitTests/CarouselTests.cs b/tests/Avalonia.Controls.UnitTests/CarouselTests.cs index 4ce83056c6..4d38ec9856 100644 --- a/tests/Avalonia.Controls.UnitTests/CarouselTests.cs +++ b/tests/Avalonia.Controls.UnitTests/CarouselTests.cs @@ -102,7 +102,7 @@ namespace Avalonia.Controls.UnitTests [~CarouselPresenter.ItemsProperty] = control[~Carousel.ItemsProperty], [~CarouselPresenter.ItemsPanelProperty] = control[~Carousel.ItemsPanelProperty], [~CarouselPresenter.SelectedIndexProperty] = control[~Carousel.SelectedIndexProperty], - [~CarouselPresenter.TransitionProperty] = control[~Carousel.TransitionProperty], + [~CarouselPresenter.PageTransitionProperty] = control[~Carousel.PageTransitionProperty], }; } } diff --git a/tests/Avalonia.Controls.UnitTests/TabControlTests.cs b/tests/Avalonia.Controls.UnitTests/TabControlTests.cs index 67b224d6a0..322c14c6bd 100644 --- a/tests/Avalonia.Controls.UnitTests/TabControlTests.cs +++ b/tests/Avalonia.Controls.UnitTests/TabControlTests.cs @@ -313,7 +313,7 @@ namespace Avalonia.Controls.UnitTests [!CarouselPresenter.ItemsPanelProperty] = control[!ItemsControl.ItemsPanelProperty], [!CarouselPresenter.MemberSelectorProperty] = control[!ItemsControl.MemberSelectorProperty], [!CarouselPresenter.SelectedIndexProperty] = control[!SelectingItemsControl.SelectedIndexProperty], - [~CarouselPresenter.TransitionProperty] = control[~Carousel.TransitionProperty], + [~CarouselPresenter.PageTransitionProperty] = control[~Carousel.PageTransitionProperty], }; } From ce4ef46df02b52554fc966c2c33420eeeafbea93 Mon Sep 17 00:00:00 2001 From: Jumar Macato Date: Wed, 21 Mar 2018 20:27:25 +0800 Subject: [PATCH 002/149] *Added initial implementation of new Transitions *Added default easing classes *Added easing type converter --- src/Avalonia.Animation/Easing/BackEaseIn.cs | 21 +++++ .../Easing/BackEaseInOut.cs | 32 +++++++ src/Avalonia.Animation/Easing/BackEaseOut.cs | 21 +++++ src/Avalonia.Animation/Easing/BounceEaseIn.cs | 20 ++++ .../Easing/BounceEaseInOut.cs | 27 ++++++ .../Easing/BounceEaseOut.cs | 19 ++++ .../Easing/CircularEaseIn.cs | 23 +++++ .../Easing/CircularEaseInOut.cs | 29 ++++++ .../Easing/CircularEaseOut.cs | 22 +++++ src/Avalonia.Animation/Easing/CubicEaseIn.cs | 18 ++++ .../Easing/CubicEaseInOut.cs | 29 ++++++ src/Avalonia.Animation/Easing/CubicEaseOut.cs | 19 ++++ src/Avalonia.Animation/Easing/Easing.cs | 58 ++++++++++++ .../Easing/ElasticEaseIn.cs | 22 +++++ .../Easing/ElasticEaseInOut.cs | 30 ++++++ .../Easing/ElasticEaseOut.cs | 23 +++++ .../Easing/ExponentialEaseIn.cs | 21 +++++ .../Easing/ExponentialEaseInOut.cs | 30 ++++++ .../Easing/ExponentialEaseOut.cs | 21 +++++ src/Avalonia.Animation/Easing/LinearEasing.cs | 18 ++++ .../Easing/QuadraticEaseIn.cs | 18 ++++ .../Easing/QuadraticEaseInOut.cs | 28 ++++++ .../Easing/QuadraticEaseOut.cs | 19 ++++ .../Easing/QuarticEaseIn.cs | 20 ++++ .../Easing/QuarticEaseInOut.cs | 29 ++++++ .../Easing/QuarticEaseOut.cs | 20 ++++ .../Easing/QuinticEaseIn.cs | 18 ++++ .../Easing/QuinticEaseInOut.cs | 28 ++++++ .../Easing/QuinticEaseOut.cs | 19 ++++ src/Avalonia.Animation/Easing/SineEaseIn.cs | 20 ++++ .../Easing/SineEaseInOut.cs | 20 ++++ src/Avalonia.Animation/Easing/SineEaseOut.cs | 21 +++++ .../Helpers/BounceEaseHelper.cs | 39 ++++++++ .../Helpers/DoubleHelper.cs | 18 ++++ .../Helpers/EasingConstants.cs | 18 ++++ src/Avalonia.Animation/Timing.cs | 93 +++++++++++++++++++ .../Transitions/DoubleTransition.cs | 32 +++++++ .../Transitions/Transition.cs | 59 ++++++++++++ .../Transitions/Transitions.cs | 21 +++++ .../Converters/EasingTypeConverter.cs | 24 +++++ 40 files changed, 1067 insertions(+) create mode 100644 src/Avalonia.Animation/Easing/BackEaseIn.cs create mode 100644 src/Avalonia.Animation/Easing/BackEaseInOut.cs create mode 100644 src/Avalonia.Animation/Easing/BackEaseOut.cs create mode 100644 src/Avalonia.Animation/Easing/BounceEaseIn.cs create mode 100644 src/Avalonia.Animation/Easing/BounceEaseInOut.cs create mode 100644 src/Avalonia.Animation/Easing/BounceEaseOut.cs create mode 100644 src/Avalonia.Animation/Easing/CircularEaseIn.cs create mode 100644 src/Avalonia.Animation/Easing/CircularEaseInOut.cs create mode 100644 src/Avalonia.Animation/Easing/CircularEaseOut.cs create mode 100644 src/Avalonia.Animation/Easing/CubicEaseIn.cs create mode 100644 src/Avalonia.Animation/Easing/CubicEaseInOut.cs create mode 100644 src/Avalonia.Animation/Easing/CubicEaseOut.cs create mode 100644 src/Avalonia.Animation/Easing/Easing.cs create mode 100644 src/Avalonia.Animation/Easing/ElasticEaseIn.cs create mode 100644 src/Avalonia.Animation/Easing/ElasticEaseInOut.cs create mode 100644 src/Avalonia.Animation/Easing/ElasticEaseOut.cs create mode 100644 src/Avalonia.Animation/Easing/ExponentialEaseIn.cs create mode 100644 src/Avalonia.Animation/Easing/ExponentialEaseInOut.cs create mode 100644 src/Avalonia.Animation/Easing/ExponentialEaseOut.cs create mode 100644 src/Avalonia.Animation/Easing/LinearEasing.cs create mode 100644 src/Avalonia.Animation/Easing/QuadraticEaseIn.cs create mode 100644 src/Avalonia.Animation/Easing/QuadraticEaseInOut.cs create mode 100644 src/Avalonia.Animation/Easing/QuadraticEaseOut.cs create mode 100644 src/Avalonia.Animation/Easing/QuarticEaseIn.cs create mode 100644 src/Avalonia.Animation/Easing/QuarticEaseInOut.cs create mode 100644 src/Avalonia.Animation/Easing/QuarticEaseOut.cs create mode 100644 src/Avalonia.Animation/Easing/QuinticEaseIn.cs create mode 100644 src/Avalonia.Animation/Easing/QuinticEaseInOut.cs create mode 100644 src/Avalonia.Animation/Easing/QuinticEaseOut.cs create mode 100644 src/Avalonia.Animation/Easing/SineEaseIn.cs create mode 100644 src/Avalonia.Animation/Easing/SineEaseInOut.cs create mode 100644 src/Avalonia.Animation/Easing/SineEaseOut.cs create mode 100644 src/Avalonia.Animation/Helpers/BounceEaseHelper.cs create mode 100644 src/Avalonia.Animation/Helpers/DoubleHelper.cs create mode 100644 src/Avalonia.Animation/Helpers/EasingConstants.cs create mode 100644 src/Avalonia.Animation/Timing.cs create mode 100644 src/Avalonia.Animation/Transitions/DoubleTransition.cs create mode 100644 src/Avalonia.Animation/Transitions/Transition.cs create mode 100644 src/Avalonia.Animation/Transitions/Transitions.cs create mode 100644 src/Markup/Avalonia.Markup.Xaml/Converters/EasingTypeConverter.cs diff --git a/src/Avalonia.Animation/Easing/BackEaseIn.cs b/src/Avalonia.Animation/Easing/BackEaseIn.cs new file mode 100644 index 0000000000..c846d6116d --- /dev/null +++ b/src/Avalonia.Animation/Easing/BackEaseIn.cs @@ -0,0 +1,21 @@ +// 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; + +namespace Avalonia.Animation +{ + /// + /// Eases in a value + /// using a overshooting cubic function. + /// + public class BackEaseIn : Easing + { + /// + public override double Ease(double progress) + { + double p = progress; + return p * p * p - p * Math.Sin(p * Math.PI); + } + } +} diff --git a/src/Avalonia.Animation/Easing/BackEaseInOut.cs b/src/Avalonia.Animation/Easing/BackEaseInOut.cs new file mode 100644 index 0000000000..4b190b4da9 --- /dev/null +++ b/src/Avalonia.Animation/Easing/BackEaseInOut.cs @@ -0,0 +1,32 @@ +// 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; + +namespace Avalonia.Animation.Easings +{ + /// + /// Eases a value + /// using a piecewise overshooting cubic function. + /// + public class BackEaseInOut : Easing + { + /// + public override double Ease(double progress) + { + double p = progress; + + if (p < 0.5d) + { + double f = 2d * p; + return 0.5d * (f * f * f - f * Math.Sin(f * Math.PI)); + } + else + { + double f = (1d - (2d * p - 1d)); + return 0.5d * (1d - (f * f * f - f * Math.Sin(f * Math.PI))) + 0.5d; + } + } + + } +} diff --git a/src/Avalonia.Animation/Easing/BackEaseOut.cs b/src/Avalonia.Animation/Easing/BackEaseOut.cs new file mode 100644 index 0000000000..f671d61040 --- /dev/null +++ b/src/Avalonia.Animation/Easing/BackEaseOut.cs @@ -0,0 +1,21 @@ +// 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; + +namespace Avalonia.Animation +{ + /// + /// Eases out a value + /// using a overshooting cubic function. + /// + public class BackEaseOut : Easing + { + /// + public override double Ease(double progress) + { + double p = 1d - progress; + return 1 - (p * p * p - p * Math.Sin(p * Math.PI)); + } + } +} diff --git a/src/Avalonia.Animation/Easing/BounceEaseIn.cs b/src/Avalonia.Animation/Easing/BounceEaseIn.cs new file mode 100644 index 0000000000..5cf34e3f9a --- /dev/null +++ b/src/Avalonia.Animation/Easing/BounceEaseIn.cs @@ -0,0 +1,20 @@ +// 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. + + +namespace Avalonia.Animation +{ + /// + /// Eases in a value + /// using a simulated bounce function. + /// + public class BounceEaseIn : Easing + { + /// + public override double Ease(double progress) + { + return 1 - BounceEaseHelper.Bounce(1 - progress); + } + + } +} diff --git a/src/Avalonia.Animation/Easing/BounceEaseInOut.cs b/src/Avalonia.Animation/Easing/BounceEaseInOut.cs new file mode 100644 index 0000000000..6db0424658 --- /dev/null +++ b/src/Avalonia.Animation/Easing/BounceEaseInOut.cs @@ -0,0 +1,27 @@ +// 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. + +namespace Avalonia.Animation +{ + /// + /// Eases a value + /// using a piecewise simulated bounce function. + /// + public class BounceEaseInOut : Easing + { + /// + public override double Ease(double progress) + { + double p = progress; + if (p < 0.5d) + { + return 0.5f * (1 - BounceEaseHelper.Bounce(1 - (p * 2))); + } + else + { + return 0.5f * BounceEaseHelper.Bounce(p * 2 - 1) + 0.5f; + } + } + + } +} diff --git a/src/Avalonia.Animation/Easing/BounceEaseOut.cs b/src/Avalonia.Animation/Easing/BounceEaseOut.cs new file mode 100644 index 0000000000..9cb4e9633d --- /dev/null +++ b/src/Avalonia.Animation/Easing/BounceEaseOut.cs @@ -0,0 +1,19 @@ +// 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. + + +namespace Avalonia.Animation +{ + /// + /// Eases out a value + /// using a simulated bounce function. + /// + public class BounceEaseOut : Easing + { + /// + public override double Ease(double progress) + { + return BounceEaseHelper.Bounce(progress); + } + } +} diff --git a/src/Avalonia.Animation/Easing/CircularEaseIn.cs b/src/Avalonia.Animation/Easing/CircularEaseIn.cs new file mode 100644 index 0000000000..21b77db582 --- /dev/null +++ b/src/Avalonia.Animation/Easing/CircularEaseIn.cs @@ -0,0 +1,23 @@ +// 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; + +namespace Avalonia.Animation +{ + /// + /// Eases in a value + /// using the shifted fourth quadrant of + /// the unit circle. + /// + public class CircularEaseIn : Easing + { + /// + public override double Ease(double progress) + { + double p = progress; + return 1d - Math.Sqrt(1d - (p * p)); + } + + } +} diff --git a/src/Avalonia.Animation/Easing/CircularEaseInOut.cs b/src/Avalonia.Animation/Easing/CircularEaseInOut.cs new file mode 100644 index 0000000000..a5198a019f --- /dev/null +++ b/src/Avalonia.Animation/Easing/CircularEaseInOut.cs @@ -0,0 +1,29 @@ +// 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; + +namespace Avalonia.Animation +{ + /// + /// Eases a value + /// using a piecewise unit circle function. + /// + public class CircularEaseInOut : Easing + { + /// + public override double Ease(double progress) + { + double p = progress; + if (p < 0.5d) + { + return 0.5d * (1d - Math.Sqrt(1d - 4d * (p * p))); + } + else + { + return 0.5d * (Math.Sqrt(-((2d * p) - 3d) * ((2d * p) - 1d)) + 1d); + } + } + + } +} diff --git a/src/Avalonia.Animation/Easing/CircularEaseOut.cs b/src/Avalonia.Animation/Easing/CircularEaseOut.cs new file mode 100644 index 0000000000..33391ed816 --- /dev/null +++ b/src/Avalonia.Animation/Easing/CircularEaseOut.cs @@ -0,0 +1,22 @@ +// 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; + +namespace Avalonia.Animation +{ + /// + /// Eases out a value + /// using the shifted second quadrant of + /// the unit circle. + /// + public class CircularEaseOut : Easing + { + /// + public override double Ease(double progress) + { + double p = progress; + return Math.Sqrt((2d - p) * p); + } + } +} diff --git a/src/Avalonia.Animation/Easing/CubicEaseIn.cs b/src/Avalonia.Animation/Easing/CubicEaseIn.cs new file mode 100644 index 0000000000..653164d7ab --- /dev/null +++ b/src/Avalonia.Animation/Easing/CubicEaseIn.cs @@ -0,0 +1,18 @@ +// 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. + +namespace Avalonia.Animation +{ + /// + /// Eases in a value + /// using a cubic equation. + /// + public class CubicEaseIn : Easing + { + /// + public override double Ease(double progress) + { + return progress * progress * progress; + } + } +} diff --git a/src/Avalonia.Animation/Easing/CubicEaseInOut.cs b/src/Avalonia.Animation/Easing/CubicEaseInOut.cs new file mode 100644 index 0000000000..ffdce75d45 --- /dev/null +++ b/src/Avalonia.Animation/Easing/CubicEaseInOut.cs @@ -0,0 +1,29 @@ +// 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. + +namespace Avalonia.Animation +{ + /// + /// Eases a value + /// using a piece-wise cubic equation. + /// + public class CubicEaseInOut : Easing + { + /// + public override double Ease(double progress) + { + double p = progress; + + if (progress < 0.5d) + { + return 4d * p * p * p; + } + else + { + double f = ((2d * p) - 2d); + return 0.5d * f * f * f + 1d; + } + } + } + +} diff --git a/src/Avalonia.Animation/Easing/CubicEaseOut.cs b/src/Avalonia.Animation/Easing/CubicEaseOut.cs new file mode 100644 index 0000000000..6bbe756477 --- /dev/null +++ b/src/Avalonia.Animation/Easing/CubicEaseOut.cs @@ -0,0 +1,19 @@ +// 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. + +namespace Avalonia.Animation +{ + /// + /// Eases out a value + /// using a cubic equation. + /// + public class CubicEaseOut : Easing + { + /// + public override double Ease(double progress) + { + double f = (progress - 1d); + return f * f * f + 1d; + } + } +} diff --git a/src/Avalonia.Animation/Easing/Easing.cs b/src/Avalonia.Animation/Easing/Easing.cs new file mode 100644 index 0000000000..75b98e8a3a --- /dev/null +++ b/src/Avalonia.Animation/Easing/Easing.cs @@ -0,0 +1,58 @@ +using Avalonia.Collections; +using System; +using System.Collections.Generic; +using System.Text; +using System.Reflection; +using System.Linq; +using System.ComponentModel; + +namespace Avalonia.Animation +{ + /// + /// Base class for all Easing classes. + /// + public abstract class Easing : IEasing + { + /// + public abstract double Ease(double progress); + + static Dictionary _easingTypes; + + static readonly Type s_thisType = typeof(Easing); + + /// + /// Parses a Easing type string. + /// + /// The Easing type string. + /// Returns the instance of the parsed type. + public static Easing Parse(string e) + { + // TODO: There should be a better way to + // find all the subclasses than this method... + if (_easingTypes == null) + { + _easingTypes = new Dictionary(); + + var derivedTypes = AppDomain.CurrentDomain.GetAssemblies() + .SelectMany(p => p.GetTypes()) + .Where(p => p.Namespace == s_thisType.Namespace) + .Where(p => p.IsSubclassOf(s_thisType)) + .Select(p=>p).ToList(); + + foreach (var easingType in derivedTypes) + _easingTypes.Add(easingType.Name, easingType); + } + + if (_easingTypes.ContainsKey(e)) + { + var type = _easingTypes[e]; + return (Easing)Activator.CreateInstance(type); + } + else + { + throw new FormatException($"Easing \"{e}\" was not found in {s_thisType.Namespace} namespace."); + } + } + + } +} diff --git a/src/Avalonia.Animation/Easing/ElasticEaseIn.cs b/src/Avalonia.Animation/Easing/ElasticEaseIn.cs new file mode 100644 index 0000000000..b0a95d8dc0 --- /dev/null +++ b/src/Avalonia.Animation/Easing/ElasticEaseIn.cs @@ -0,0 +1,22 @@ +// 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; + +namespace Avalonia.Animation +{ + /// + /// Eases in a value + /// using a damped sine function. + /// + public class ElasticEaseIn : Easing + { + /// + public override double Ease(double progress) + { + double p = progress; + return Math.Sin(13d * EasingConstants.HALFPI * p) * Math.Pow(2d, 10d * (p - 1)); + } + } + +} diff --git a/src/Avalonia.Animation/Easing/ElasticEaseInOut.cs b/src/Avalonia.Animation/Easing/ElasticEaseInOut.cs new file mode 100644 index 0000000000..85dcfbd618 --- /dev/null +++ b/src/Avalonia.Animation/Easing/ElasticEaseInOut.cs @@ -0,0 +1,30 @@ +// 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; + +namespace Avalonia.Animation +{ + /// + /// Eases a value + /// using a piecewise damped sine function. + /// + public class ElasticEaseInOut : Easing + { + /// + public override double Ease(double progress) + { + double p = progress; + + if (p < 0.5d) + { + return 0.5d * Math.Sin(13d * EasingConstants.HALFPI * (2d * p)) * Math.Pow(2d, 10d * ((2d * p) - 1d)); + } + else + { + return 0.5d * (Math.Sin(-13d * EasingConstants.HALFPI * ((2d * p - 1d) + 1d)) * Math.Pow(2d, -10d * (2d * p - 1d)) + 2d); + } + } + + } +} diff --git a/src/Avalonia.Animation/Easing/ElasticEaseOut.cs b/src/Avalonia.Animation/Easing/ElasticEaseOut.cs new file mode 100644 index 0000000000..830198468d --- /dev/null +++ b/src/Avalonia.Animation/Easing/ElasticEaseOut.cs @@ -0,0 +1,23 @@ +// 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; + +namespace Avalonia.Animation +{ + /// + /// Eases out a value + /// using a damped sine function. + /// + public class ElasticEaseOut : Easing + { + /// + public override double Ease(double progress) + { + double p = progress; + return Math.Sin(-13d * EasingConstants.HALFPI * (p + 1)) * Math.Pow(2d, -10d * p) + 1d; + + } + + } +} diff --git a/src/Avalonia.Animation/Easing/ExponentialEaseIn.cs b/src/Avalonia.Animation/Easing/ExponentialEaseIn.cs new file mode 100644 index 0000000000..5b9154d29c --- /dev/null +++ b/src/Avalonia.Animation/Easing/ExponentialEaseIn.cs @@ -0,0 +1,21 @@ +// 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; + +namespace Avalonia.Animation +{ + /// + /// Eases in a value + /// using a exponential function. + /// + public class ExponentialEaseIn : Easing + { + /// + public override double Ease(double progress) + { + double p = progress; + return (p == 0.0d) ? p : Math.Pow(2d, 10d * (p - 1d)); + } + } +} diff --git a/src/Avalonia.Animation/Easing/ExponentialEaseInOut.cs b/src/Avalonia.Animation/Easing/ExponentialEaseInOut.cs new file mode 100644 index 0000000000..0eaef18464 --- /dev/null +++ b/src/Avalonia.Animation/Easing/ExponentialEaseInOut.cs @@ -0,0 +1,30 @@ +// 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; + +namespace Avalonia.Animation +{ + /// + /// Eases a value + /// using a piecewise exponential function. + /// + public class ExponentialEaseInOut : Easing + { + /// + public override double Ease(double progress) + { + double p = progress; + + if (p < 0.5d) + { + return 0.5d * Math.Pow(2d, (20d * p) - 10d); + } + else + { + return -0.5d * Math.Pow(2d, (-20d * p) + 10d) + 1d; + } + } + + } +} diff --git a/src/Avalonia.Animation/Easing/ExponentialEaseOut.cs b/src/Avalonia.Animation/Easing/ExponentialEaseOut.cs new file mode 100644 index 0000000000..c071d18d2e --- /dev/null +++ b/src/Avalonia.Animation/Easing/ExponentialEaseOut.cs @@ -0,0 +1,21 @@ +// 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; + +namespace Avalonia.Animation +{ + /// + /// Eases out a value + /// using a exponential function. + /// + public class ExponentialEaseOut : Easing + { + /// + public override double Ease(double progress) + { + double p = progress; + return (p == 1.0d) ? p : 1d - Math.Pow(2d, -10d * p); + } + } +} diff --git a/src/Avalonia.Animation/Easing/LinearEasing.cs b/src/Avalonia.Animation/Easing/LinearEasing.cs new file mode 100644 index 0000000000..fcbf6053f4 --- /dev/null +++ b/src/Avalonia.Animation/Easing/LinearEasing.cs @@ -0,0 +1,18 @@ +// 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. + +namespace Avalonia.Animation +{ + /// + /// Linearly eases a value. + /// + public class LinearEasing : Easing + { + /// + public override double Ease(double progress) + { + return progress; + } + } + +} diff --git a/src/Avalonia.Animation/Easing/QuadraticEaseIn.cs b/src/Avalonia.Animation/Easing/QuadraticEaseIn.cs new file mode 100644 index 0000000000..641fba4892 --- /dev/null +++ b/src/Avalonia.Animation/Easing/QuadraticEaseIn.cs @@ -0,0 +1,18 @@ +// 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. + +namespace Avalonia.Animation +{ + /// + /// Eases in a value + /// using a quadratic function. + /// + public class QuadraticEaseIn : Easing + { + /// + public override double Ease(double progress) + { + return progress * progress; + } + } +} diff --git a/src/Avalonia.Animation/Easing/QuadraticEaseInOut.cs b/src/Avalonia.Animation/Easing/QuadraticEaseInOut.cs new file mode 100644 index 0000000000..384c3caf32 --- /dev/null +++ b/src/Avalonia.Animation/Easing/QuadraticEaseInOut.cs @@ -0,0 +1,28 @@ +// 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. + +namespace Avalonia.Animation +{ + /// + /// Eases a value + /// using a piece-wise quadratic function. + /// + public class QuadraticEaseInOut : Easing + { + /// + public override double Ease(double progress) + { + double p = progress; + + if (progress < 0.5d) + { + return 2d * p * p; + } + else + { + return (-2d * p * p) + (4d * p) - 1d; + } + } + + } +} diff --git a/src/Avalonia.Animation/Easing/QuadraticEaseOut.cs b/src/Avalonia.Animation/Easing/QuadraticEaseOut.cs new file mode 100644 index 0000000000..14fd877ad4 --- /dev/null +++ b/src/Avalonia.Animation/Easing/QuadraticEaseOut.cs @@ -0,0 +1,19 @@ +// 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. + +namespace Avalonia.Animation +{ + /// + /// Eases out a value + /// using a quadratic function. + /// + public class QuadraticEaseOut : Easing + { + /// + public override double Ease(double progress) + { + return -(progress * (progress - 2d)); + } + + } +} diff --git a/src/Avalonia.Animation/Easing/QuarticEaseIn.cs b/src/Avalonia.Animation/Easing/QuarticEaseIn.cs new file mode 100644 index 0000000000..a5efa241be --- /dev/null +++ b/src/Avalonia.Animation/Easing/QuarticEaseIn.cs @@ -0,0 +1,20 @@ +// 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. + +namespace Avalonia.Animation +{ + /// + /// Eases in a value + /// using a quartic equation. + /// + public class QuarticEaseIn : Easing + { + /// + public override double Ease(double progress) + { + return progress * progress * progress * progress; + + } + + } +} diff --git a/src/Avalonia.Animation/Easing/QuarticEaseInOut.cs b/src/Avalonia.Animation/Easing/QuarticEaseInOut.cs new file mode 100644 index 0000000000..d5cd2622cb --- /dev/null +++ b/src/Avalonia.Animation/Easing/QuarticEaseInOut.cs @@ -0,0 +1,29 @@ +// 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. + +namespace Avalonia.Animation +{ + /// + /// Eases a value + /// using a piece-wise quartic equation. + /// + public class QuarticEaseInOut : Easing + { + /// + public override double Ease(double progress) + { + double p = progress; + + if (p < 0.5d) + { + return 8d * p * p * p * p; + } + else + { + double f = (p - 1d); + return -8d * f * f * f * f + 1d; + } + } + + } +} diff --git a/src/Avalonia.Animation/Easing/QuarticEaseOut.cs b/src/Avalonia.Animation/Easing/QuarticEaseOut.cs new file mode 100644 index 0000000000..5ecb3a7a0b --- /dev/null +++ b/src/Avalonia.Animation/Easing/QuarticEaseOut.cs @@ -0,0 +1,20 @@ +// 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. + +namespace Avalonia.Animation +{ + /// + /// Eases out a value + /// using a quartic equation. + /// + public class QuarticEaseOut : Easing + { + /// + public override double Ease(double progress) + { + double f = (progress - 1d); + return f * f * f * (1d - progress) + 1d; + } + + } +} diff --git a/src/Avalonia.Animation/Easing/QuinticEaseIn.cs b/src/Avalonia.Animation/Easing/QuinticEaseIn.cs new file mode 100644 index 0000000000..8dc6ad6bc8 --- /dev/null +++ b/src/Avalonia.Animation/Easing/QuinticEaseIn.cs @@ -0,0 +1,18 @@ +// 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. + +namespace Avalonia.Animation +{ + /// + /// Eases in a value + /// using a quartic equation. + /// + public class QuinticEaseIn : Easing + { + /// + public override double Ease(double progress) + { + return progress * progress * progress * progress * progress; + } + } +} diff --git a/src/Avalonia.Animation/Easing/QuinticEaseInOut.cs b/src/Avalonia.Animation/Easing/QuinticEaseInOut.cs new file mode 100644 index 0000000000..87f3c38dad --- /dev/null +++ b/src/Avalonia.Animation/Easing/QuinticEaseInOut.cs @@ -0,0 +1,28 @@ +// 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. + +namespace Avalonia.Animation +{ + /// + /// Eases a value + /// using a piece-wise quartic equation. + /// + public class QuinticEaseInOut : Easing + { + /// + public override double Ease(double progress) + { + double p = progress; + + if (progress < 0.5d) + { + return 16d * p * p * p * p * p; + } + else + { + double f = ((2d * p) - 2d); + return 0.5d * f * f * f * f * f + 1d; + } + } + } +} diff --git a/src/Avalonia.Animation/Easing/QuinticEaseOut.cs b/src/Avalonia.Animation/Easing/QuinticEaseOut.cs new file mode 100644 index 0000000000..f4054c8da2 --- /dev/null +++ b/src/Avalonia.Animation/Easing/QuinticEaseOut.cs @@ -0,0 +1,19 @@ +// 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. + +namespace Avalonia.Animation +{ + /// + /// Eases out a value + /// using a quartic equation. + /// + public class QuinticEaseOut : Easing + { + /// + public override double Ease(double progress) + { + double f = (progress - 1d); + return f * f * f * f * f + 1d; + } + } +} diff --git a/src/Avalonia.Animation/Easing/SineEaseIn.cs b/src/Avalonia.Animation/Easing/SineEaseIn.cs new file mode 100644 index 0000000000..49c633ec5c --- /dev/null +++ b/src/Avalonia.Animation/Easing/SineEaseIn.cs @@ -0,0 +1,20 @@ +// 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; + +namespace Avalonia.Animation +{ + /// + /// Eases in a value + /// using the quarter-wave of sine function. + /// + public class SineEaseIn : Easing + { + /// + public override double Ease(double progress) + { + return Math.Sin((progress - 1) * EasingConstants.HALFPI) + 1; + } + } +} diff --git a/src/Avalonia.Animation/Easing/SineEaseInOut.cs b/src/Avalonia.Animation/Easing/SineEaseInOut.cs new file mode 100644 index 0000000000..94b528c728 --- /dev/null +++ b/src/Avalonia.Animation/Easing/SineEaseInOut.cs @@ -0,0 +1,20 @@ +// 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; + +namespace Avalonia.Animation +{ + /// + /// Eases a value + /// using a half sine wave function. + /// + public class SineEaseInOut : Easing + { + /// + public override double Ease(double progress) + { + return 0.5d * (1d - Math.Cos(progress * Math.PI)); + } + } +} diff --git a/src/Avalonia.Animation/Easing/SineEaseOut.cs b/src/Avalonia.Animation/Easing/SineEaseOut.cs new file mode 100644 index 0000000000..7d0658c389 --- /dev/null +++ b/src/Avalonia.Animation/Easing/SineEaseOut.cs @@ -0,0 +1,21 @@ +// 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; + +namespace Avalonia.Animation +{ + /// + /// Eases out a value + /// using the quarter-wave of sine function + /// with shifted phase. + /// + public class SineEaseOut : Easing + { + /// + public override double Ease(double progress) + { + return Math.Sin(progress * EasingConstants.HALFPI); + } + } +} diff --git a/src/Avalonia.Animation/Helpers/BounceEaseHelper.cs b/src/Avalonia.Animation/Helpers/BounceEaseHelper.cs new file mode 100644 index 0000000000..b0da648fd3 --- /dev/null +++ b/src/Avalonia.Animation/Helpers/BounceEaseHelper.cs @@ -0,0 +1,39 @@ +// 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. + +namespace Avalonia.Animation +{ + /// + /// Helper static class for BounceEase classes. + /// + internal static class BounceEaseHelper + { + /// + /// Returns the consequent value of + /// a simulated bounce function. + /// + /// The amount of progress from 0 to 1. + /// The result of the easing function + internal static double Bounce(double progress) + { + double p = progress; + if (p < 4d / 11.0d) + { + return (121d * p * p) / 16.0d; + } + else if (p < 8d / 11.0d) + { + return (363d / 40.0d * p * p) - (99d / 10.0d * p) + 17d / 5.0d; + } + else if (p < 9d / 10.0d) + { + return (4356d / 361.0d * p * p) - (35442d / 1805.0d * p) + 16061d / 1805.0d; + } + else + { + return (54d / 5.0d * p * p) - (513d / 25.0d * p) + 268d / 25.0d; + } + } + + } +} diff --git a/src/Avalonia.Animation/Helpers/DoubleHelper.cs b/src/Avalonia.Animation/Helpers/DoubleHelper.cs new file mode 100644 index 0000000000..9d4fd4955f --- /dev/null +++ b/src/Avalonia.Animation/Helpers/DoubleHelper.cs @@ -0,0 +1,18 @@ +// 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 System.Text; + +namespace Avalonia.Animation.Helpers +{ + internal static class DoubleHelper + { + internal static bool AboutEqual(double x, double y) + { + double epsilon = Math.Max(Math.Abs(x), Math.Abs(y)) * 1E-15; + return Math.Abs(x - y) <= epsilon; + } + } +} diff --git a/src/Avalonia.Animation/Helpers/EasingConstants.cs b/src/Avalonia.Animation/Helpers/EasingConstants.cs new file mode 100644 index 0000000000..b6a52ab4a4 --- /dev/null +++ b/src/Avalonia.Animation/Helpers/EasingConstants.cs @@ -0,0 +1,18 @@ +// 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; + +namespace Avalonia.Animation +{ + /// + /// Helper static class for easing mathematical constants. + /// + internal static class EasingConstants + { + /// + /// Half of + /// + internal static double HALFPI = Math.PI / 2d; + } +} diff --git a/src/Avalonia.Animation/Timing.cs b/src/Avalonia.Animation/Timing.cs new file mode 100644 index 0000000000..a22dc1a6a7 --- /dev/null +++ b/src/Avalonia.Animation/Timing.cs @@ -0,0 +1,93 @@ +// 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.Diagnostics; +using System.Linq; +using System.Reactive.Linq; +using Avalonia.Data; +using Avalonia.Threading; + +namespace Avalonia.Animation +{ + /// + /// Provides global timing functions for animations. + /// + public static class Timing + { + /// + /// The number of frames per second. + /// + public const int FramesPerSecond = 60; + + /// + /// The time span of each frame. + /// + private static readonly TimeSpan Tick = TimeSpan.FromSeconds(1.0 / FramesPerSecond); + + /// + /// Initializes static members of the class. + /// + static Timing() + { + Stopwatch = new Stopwatch(); + Stopwatch.Start(); + Timer = Observable.Interval(Tick, AvaloniaScheduler.Instance) + .Select(_ => Stopwatch.Elapsed) + .Publish() + .RefCount(); + } + + /// + /// The stopwatch used to track time. + /// + /// + /// The stopwatch used to track time. + /// + public static Stopwatch Stopwatch + { + get; + } + + /// + /// Gets the animation timer. + /// + /// + /// The animation timer ticks times per second. The + /// parameter passed to a subsciber is the time span since the animation system was + /// initialized. + /// + /// + /// The animation timer. + /// + public static IObservable Timer + { + get; + } + + /// + /// Gets a timer that fires every frame for the specified duration. + /// + /// The duration of the animation. + /// + /// An observable that notifies the subscriber of the progress along the animation. + /// + /// + /// The parameter passed to the subscriber is the progress along the animation, with + /// 0 being the start and 1 being the end. The observable is guaranteed to fire 0 + /// immediately on subscribe and 1 at the end of the duration. + /// + public static IObservable GetTimer(TimeSpan duration) + { + var startTime = Stopwatch.Elapsed.Ticks; + var endTime = startTime + duration.Ticks; + return Timer + .TakeWhile(x => x.Ticks < endTime) + .Select(x => (x.Ticks - startTime) / (double)duration.Ticks) + .StartWith(0.0) + .Concat(Observable.Return(1.0)); + } + + + } +} diff --git a/src/Avalonia.Animation/Transitions/DoubleTransition.cs b/src/Avalonia.Animation/Transitions/DoubleTransition.cs new file mode 100644 index 0000000000..fbe164aa90 --- /dev/null +++ b/src/Avalonia.Animation/Transitions/DoubleTransition.cs @@ -0,0 +1,32 @@ +// 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 Avalonia.Metadata; +using System; + +namespace Avalonia.Animation +{ + /// + /// Transitions object that handles properties with types. + /// + public class DoubleTransition : Transition + { + /// + public DoubleTransition() : base() + { + + } + + /// + public override AvaloniaProperty Property { get; set; } + + /// + public override void Apply(Animatable control) + { + //throw new NotImplementedException(); + + + + } + } +} diff --git a/src/Avalonia.Animation/Transitions/Transition.cs b/src/Avalonia.Animation/Transitions/Transition.cs new file mode 100644 index 0000000000..8a90940784 --- /dev/null +++ b/src/Avalonia.Animation/Transitions/Transition.cs @@ -0,0 +1,59 @@ +// 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 Avalonia.Metadata; +using System; + +namespace Avalonia.Animation +{ + internal interface ITransition + { + /// + /// Applies the transition to the specified . + /// + /// + void Apply(Animatable control); + + /// + /// Gets the property to be animated. + /// + AvaloniaProperty Property { get; set; } + } + + /// + /// Defines how a property should be animated using a transition. + /// + public abstract class Transition : ITransition + { + /// + /// Gets the duration of the animation. + /// + public TimeSpan Duration { get; set; } + + /// + /// Instantiates the base abstract class . + /// + public Transition() + { + if(!(typeof(T) == Property.PropertyType)) + { + throw new InvalidCastException + ($"Invalid property type {typeof(T).Name} for this {this.GetType().Name}"); + } + } + + /// + /// Gets the easing class to be used. + /// + public IEasing Easing { get; set; } + + /// + public abstract AvaloniaProperty Property { get; set; } + + /// + public abstract void Apply(Animatable control); + + } + + +} diff --git a/src/Avalonia.Animation/Transitions/Transitions.cs b/src/Avalonia.Animation/Transitions/Transitions.cs new file mode 100644 index 0000000000..50adf246ae --- /dev/null +++ b/src/Avalonia.Animation/Transitions/Transitions.cs @@ -0,0 +1,21 @@ +// 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 Avalonia.Collections; + +namespace Avalonia.Animation +{ + /// + /// A collection of definitions. + /// + public class Transitions : AvaloniaList + { + /// + /// Initializes a new instance of the class. + /// + public Transitions() + { + ResetBehavior = ResetBehavior.Remove; + } + } +} diff --git a/src/Markup/Avalonia.Markup.Xaml/Converters/EasingTypeConverter.cs b/src/Markup/Avalonia.Markup.Xaml/Converters/EasingTypeConverter.cs new file mode 100644 index 0000000000..851d579581 --- /dev/null +++ b/src/Markup/Avalonia.Markup.Xaml/Converters/EasingTypeConverter.cs @@ -0,0 +1,24 @@ +// 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 Avalonia.Animation; +using System; +using System.ComponentModel; +using System.Globalization; + +namespace Avalonia.Markup.Xaml.Converters +{ + + public class EasingTypeConverter : TypeConverter + { + public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType) + { + return sourceType == typeof(string); + } + + public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value) + { + return Easing.Parse((string)value); + } + } +} \ No newline at end of file From 7becd1ea92a17b9fe800273a8f4c4c14bf4becb8 Mon Sep 17 00:00:00 2001 From: Jumar Macato Date: Wed, 21 Mar 2018 20:27:25 +0800 Subject: [PATCH 003/149] *Added initial implementation of new Transitions *Added default easing classes *Added easing type converter --- .../RenderTest/Pages/AnimationsPage.xaml.cs | 20 ++-- samples/RenderTest/Pages/ClippingPage.xaml.cs | 16 +-- src/Avalonia.Animation/Animatable.cs | 88 ++++++++++++--- src/Avalonia.Animation/AnimationExtensions.cs | 60 +++++------ src/Avalonia.Animation/Animation`1.cs | 100 +++++++++--------- src/Avalonia.Animation/Easing/BackEaseIn.cs | 21 ++++ .../Easing/BackEaseInOut.cs | 32 ++++++ src/Avalonia.Animation/Easing/BackEaseOut.cs | 21 ++++ src/Avalonia.Animation/Easing/BounceEaseIn.cs | 20 ++++ .../Easing/BounceEaseInOut.cs | 27 +++++ .../Easing/BounceEaseOut.cs | 19 ++++ .../Easing/CircularEaseIn.cs | 23 ++++ .../Easing/CircularEaseInOut.cs | 29 +++++ .../Easing/CircularEaseOut.cs | 22 ++++ src/Avalonia.Animation/Easing/CubicEaseIn.cs | 18 ++++ .../Easing/CubicEaseInOut.cs | 29 +++++ src/Avalonia.Animation/Easing/CubicEaseOut.cs | 19 ++++ src/Avalonia.Animation/Easing/Easing.cs | 58 ++++++++++ .../Easing/ElasticEaseIn.cs | 22 ++++ .../Easing/ElasticEaseInOut.cs | 30 ++++++ .../Easing/ElasticEaseOut.cs | 23 ++++ .../Easing/ExponentialEaseIn.cs | 21 ++++ .../Easing/ExponentialEaseInOut.cs | 30 ++++++ .../Easing/ExponentialEaseOut.cs | 21 ++++ src/Avalonia.Animation/Easing/LinearEasing.cs | 18 ++++ .../Easing/QuadraticEaseIn.cs | 18 ++++ .../Easing/QuadraticEaseInOut.cs | 28 +++++ .../Easing/QuadraticEaseOut.cs | 19 ++++ .../Easing/QuarticEaseIn.cs | 20 ++++ .../Easing/QuarticEaseInOut.cs | 29 +++++ .../Easing/QuarticEaseOut.cs | 20 ++++ .../Easing/QuinticEaseIn.cs | 18 ++++ .../Easing/QuinticEaseInOut.cs | 28 +++++ .../Easing/QuinticEaseOut.cs | 19 ++++ src/Avalonia.Animation/Easing/SineEaseIn.cs | 20 ++++ .../Easing/SineEaseInOut.cs | 20 ++++ src/Avalonia.Animation/Easing/SineEaseOut.cs | 21 ++++ .../Helpers/BounceEaseHelper.cs | 39 +++++++ .../Helpers/DoubleHelper.cs | 18 ++++ .../Helpers/EasingConstants.cs | 18 ++++ src/Avalonia.Animation/IEasing.cs | 11 +- src/Avalonia.Animation/IEasing`1.cs | 24 ----- src/Avalonia.Animation/LinearDoubleEasing.cs | 41 ------- src/Avalonia.Animation/LinearEasing.cs | 35 ------ .../Properties/AssemblyInfo.cs | 2 + src/Avalonia.Animation/PropertyTransition.cs | 50 --------- .../{Animate.cs => Timing.cs} | 57 ++-------- .../Transitions/DoubleTransition.cs | 32 ++++++ .../Transitions/Transition.cs | 59 +++++++++++ .../Transitions.cs} | 8 +- src/Avalonia.Controls/ProgressBar.cs | 12 +-- src/Avalonia.Visuals/Animation/CrossFade.cs | 30 +++--- src/Avalonia.Visuals/Animation/PageSlide.cs | 30 +++--- .../Avalonia.Markup.Xaml.csproj | 1 + .../Converters/EasingTypeConverter.cs | 24 +++++ .../AvaloniaDefaultTypeConverters.cs | 2 + 56 files changed, 1179 insertions(+), 361 deletions(-) create mode 100644 src/Avalonia.Animation/Easing/BackEaseIn.cs create mode 100644 src/Avalonia.Animation/Easing/BackEaseInOut.cs create mode 100644 src/Avalonia.Animation/Easing/BackEaseOut.cs create mode 100644 src/Avalonia.Animation/Easing/BounceEaseIn.cs create mode 100644 src/Avalonia.Animation/Easing/BounceEaseInOut.cs create mode 100644 src/Avalonia.Animation/Easing/BounceEaseOut.cs create mode 100644 src/Avalonia.Animation/Easing/CircularEaseIn.cs create mode 100644 src/Avalonia.Animation/Easing/CircularEaseInOut.cs create mode 100644 src/Avalonia.Animation/Easing/CircularEaseOut.cs create mode 100644 src/Avalonia.Animation/Easing/CubicEaseIn.cs create mode 100644 src/Avalonia.Animation/Easing/CubicEaseInOut.cs create mode 100644 src/Avalonia.Animation/Easing/CubicEaseOut.cs create mode 100644 src/Avalonia.Animation/Easing/Easing.cs create mode 100644 src/Avalonia.Animation/Easing/ElasticEaseIn.cs create mode 100644 src/Avalonia.Animation/Easing/ElasticEaseInOut.cs create mode 100644 src/Avalonia.Animation/Easing/ElasticEaseOut.cs create mode 100644 src/Avalonia.Animation/Easing/ExponentialEaseIn.cs create mode 100644 src/Avalonia.Animation/Easing/ExponentialEaseInOut.cs create mode 100644 src/Avalonia.Animation/Easing/ExponentialEaseOut.cs create mode 100644 src/Avalonia.Animation/Easing/LinearEasing.cs create mode 100644 src/Avalonia.Animation/Easing/QuadraticEaseIn.cs create mode 100644 src/Avalonia.Animation/Easing/QuadraticEaseInOut.cs create mode 100644 src/Avalonia.Animation/Easing/QuadraticEaseOut.cs create mode 100644 src/Avalonia.Animation/Easing/QuarticEaseIn.cs create mode 100644 src/Avalonia.Animation/Easing/QuarticEaseInOut.cs create mode 100644 src/Avalonia.Animation/Easing/QuarticEaseOut.cs create mode 100644 src/Avalonia.Animation/Easing/QuinticEaseIn.cs create mode 100644 src/Avalonia.Animation/Easing/QuinticEaseInOut.cs create mode 100644 src/Avalonia.Animation/Easing/QuinticEaseOut.cs create mode 100644 src/Avalonia.Animation/Easing/SineEaseIn.cs create mode 100644 src/Avalonia.Animation/Easing/SineEaseInOut.cs create mode 100644 src/Avalonia.Animation/Easing/SineEaseOut.cs create mode 100644 src/Avalonia.Animation/Helpers/BounceEaseHelper.cs create mode 100644 src/Avalonia.Animation/Helpers/DoubleHelper.cs create mode 100644 src/Avalonia.Animation/Helpers/EasingConstants.cs delete mode 100644 src/Avalonia.Animation/IEasing`1.cs delete mode 100644 src/Avalonia.Animation/LinearDoubleEasing.cs delete mode 100644 src/Avalonia.Animation/LinearEasing.cs delete mode 100644 src/Avalonia.Animation/PropertyTransition.cs rename src/Avalonia.Animation/{Animate.cs => Timing.cs} (53%) create mode 100644 src/Avalonia.Animation/Transitions/DoubleTransition.cs create mode 100644 src/Avalonia.Animation/Transitions/Transition.cs rename src/Avalonia.Animation/{PropertyTransitions.cs => Transitions/Transitions.cs} (59%) create mode 100644 src/Markup/Avalonia.Markup.Xaml/Converters/EasingTypeConverter.cs diff --git a/samples/RenderTest/Pages/AnimationsPage.xaml.cs b/samples/RenderTest/Pages/AnimationsPage.xaml.cs index 991896c3b5..d2e1750ba7 100644 --- a/samples/RenderTest/Pages/AnimationsPage.xaml.cs +++ b/samples/RenderTest/Pages/AnimationsPage.xaml.cs @@ -56,17 +56,17 @@ namespace RenderTest.Pages RenderTransform = new ScaleTransform(), }; - var start = Animate.Stopwatch.Elapsed; - var index = i % (Count / 2); - var degrees = Animate.Timer - .Select(x => (x - start).TotalSeconds) - .Where(x => (x % Count) >= index && (x % Count) < index + 1) - .Select(x => (x % 1) / 1); + //var start = Animate.Stopwatch.Elapsed; + //var index = i % (Count / 2); + //var degrees = Animate.Timer + // .Select(x => (x - start).TotalSeconds) + // .Where(x => (x % Count) >= index && (x % Count) < index + 1) + // .Select(x => (x % 1) / 1); - element.RenderTransform.Bind( - ScaleTransform.ScaleXProperty, - degrees, - BindingPriority.Animation); + //element.RenderTransform.Bind( + // ScaleTransform.ScaleXProperty, + // degrees, + // BindingPriority.Animation); ellipse.PointerEnter += Ellipse_PointerEnter; ellipse.PointerLeave += Ellipse_PointerLeave; diff --git a/samples/RenderTest/Pages/ClippingPage.xaml.cs b/samples/RenderTest/Pages/ClippingPage.xaml.cs index b416f12943..5bcb21d531 100644 --- a/samples/RenderTest/Pages/ClippingPage.xaml.cs +++ b/samples/RenderTest/Pages/ClippingPage.xaml.cs @@ -27,14 +27,14 @@ namespace RenderTest.Pages private void CreateAnimations() { - var clipped = this.FindControl("clipChild"); - var degrees = Animate.Timer.Select(x => x.TotalMilliseconds / 5); - clipped.RenderTransform = new RotateTransform(); - clipped.RenderTransform.Bind(RotateTransform.AngleProperty, degrees, BindingPriority.Animation); - clipped.Bind( - Border.BackgroundProperty, - clipped.GetObservable(Control.IsPointerOverProperty) - .Select(x => x ? Brushes.Crimson : AvaloniaProperty.UnsetValue)); + //var clipped = this.FindControl("clipChild"); + //var degrees = Animate.Timer.Select(x => x.TotalMilliseconds / 5); + //clipped.RenderTransform = new RotateTransform(); + //clipped.RenderTransform.Bind(RotateTransform.AngleProperty, degrees, BindingPriority.Animation); + //clipped.Bind( + // Border.BackgroundProperty, + // clipped.GetObservable(Control.IsPointerOverProperty) + // .Select(x => x ? Brushes.Crimson : AvaloniaProperty.UnsetValue)); } private void WireUpCheckbox() diff --git a/src/Avalonia.Animation/Animatable.cs b/src/Avalonia.Animation/Animatable.cs index 0231ffa0be..8b73cd2aa5 100644 --- a/src/Avalonia.Animation/Animatable.cs +++ b/src/Avalonia.Animation/Animatable.cs @@ -3,6 +3,8 @@ using System.Linq; using Avalonia.Data; +using System; +using System.Reactive.Linq; namespace Avalonia.Animation { @@ -11,46 +13,102 @@ namespace Avalonia.Animation /// public class Animatable : AvaloniaObject { - /// - /// The property transitions for the control. - /// - private PropertyTransitions _propertyTransitions; + + private Transitions _transitions; /// /// Gets or sets the property transitions for the control. /// - /// - /// The property transitions for the control. - /// - public PropertyTransitions PropertyTransitions + public Transitions Transitions { get { - return _propertyTransitions ?? (_propertyTransitions = new PropertyTransitions()); + return _transitions ?? (_transitions = new Transitions()); } set { - _propertyTransitions = value; + SetAndRaise(TransitionsProperty, ref _transitions, value); } } /// - /// Reacts to a change in a value in order to animate the - /// change if a is set for the property.. + /// Gets or sets the property transitions for the control. + /// + public static readonly DirectProperty TransitionsProperty = + AvaloniaProperty.RegisterDirect(nameof(Transitions), o => o.Transitions); + + /// + /// Reacts to a change in a value in + /// order to animate the change if a + /// is set for the property. /// /// The event args. protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs e) { - if (e.Priority != BindingPriority.Animation && _propertyTransitions != null) + if (e.Priority != BindingPriority.Animation && Transitions != null) { - var match = _propertyTransitions.FirstOrDefault(x => x.Property == e.Property); + var match = Transitions.FirstOrDefault(x => x.Property == e.Property); + if (match != null) { - Animate.Property(this, e.Property, e.OldValue, e.NewValue, match.Easing, match.Duration); + + // //BindAnimateProperty(this, e.Property, e.OldValue, e.NewValue, match.Easing, match.Duration); } } } + + /// + /// Animates a . + /// + /// The target object. + /// The target property. + /// The value of the property at the start of the animation. + /// The value of the property at the end of the animation. + /// The easing function to use. + /// The duration of the animation. + /// An that can be used to track or stop the animation. + //public static Animation BindAnimateProperty( + // IAvaloniaObject target, + // AvaloniaProperty property, + // object start, + // object finish, + // IEasing easing, + // TimeSpan duration) + //{ + // var k = start.GetType(); + // if (k == typeof(double)) + // { + // var o = Timing.GetTimer(duration).Select(progress => easing.Ease(progress, start, finish)); + // return new Animation(o, target.Bind(property, o, BindingPriority.Animation)); + // } + // else + // return null; + //} + + ///// + ///// Animates a . + ///// + ///// The property type. + ///// The target object. + ///// The target property. + ///// The value of the property at the start of the animation. + ///// The value of the property at the end of the animation. + ///// The easing function to use. + ///// The duration of the animation. + ///// An that can be used to track or stop the animation. + //public static Animation Property( + // IAvaloniaObject target, + // AvaloniaProperty property, + // T start, + // T finish, + // IEasing easing, + // TimeSpan duration) + //{ + // var o = Timing.GetTimer(duration).Select(progress => easing.Ease(progress, start, finish)); + // return new Animation(o, target.Bind(property, o, BindingPriority.Animation)); + //} + } } diff --git a/src/Avalonia.Animation/AnimationExtensions.cs b/src/Avalonia.Animation/AnimationExtensions.cs index eecca00605..4efea1f4ba 100644 --- a/src/Avalonia.Animation/AnimationExtensions.cs +++ b/src/Avalonia.Animation/AnimationExtensions.cs @@ -10,36 +10,36 @@ namespace Avalonia.Animation /// public static class AnimationExtensions { - /// - /// Returns a new for the specified - /// using linear easing. - /// - /// The type of the . - /// The property to animate. - /// The animation duration in milliseconds. - /// - /// A that can be added to the - /// collection. - /// - public static PropertyTransition Transition(this AvaloniaProperty property, int milliseconds) - { - return Transition(property, TimeSpan.FromMilliseconds(milliseconds)); - } + ///// + ///// Returns a new for the specified + ///// using linear easing. + ///// + ///// The type of the . + ///// The property to animate. + ///// The animation duration in milliseconds. + ///// + ///// A that can be added to the + ///// collection. + ///// + //public static Transition Transition(this AvaloniaProperty property, int milliseconds) + //{ + // return Transition(property, TimeSpan.FromMilliseconds(milliseconds)); + //} - /// - /// Returns a new for the specified - /// using linear easing. - /// - /// The type of the . - /// The property to animate. - /// The animation duration. - /// - /// A that can be added to the - /// collection. - /// - public static PropertyTransition Transition(this AvaloniaProperty property, TimeSpan duration) - { - return new PropertyTransition(property, duration, LinearEasing.For()); - } + ///// + ///// Returns a new for the specified + ///// using linear easing. + ///// + ///// The type of the . + ///// The property to animate. + ///// The animation duration. + ///// + ///// A that can be added to the + ///// collection. + ///// + //public static Transition Transition(this AvaloniaProperty property, TimeSpan duration) + //{ + // return new Transition(property, duration, LinearEasing.For()); + //} } } diff --git a/src/Avalonia.Animation/Animation`1.cs b/src/Avalonia.Animation/Animation`1.cs index 37ac241e5c..e99dea96c6 100644 --- a/src/Avalonia.Animation/Animation`1.cs +++ b/src/Avalonia.Animation/Animation`1.cs @@ -1,56 +1,56 @@ -// 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. +//// 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; -namespace Avalonia.Animation -{ - /// - /// Tracks the progress of an animation. - /// - /// The type of the value being animated./ - public class Animation : IObservable, IDisposable - { - /// - /// The animation being tracked. - /// - private readonly IObservable _inner; +//namespace Avalonia.Animation +//{ +// /// +// /// Tracks the progress of an animation. +// /// +// /// The type of the value being animated./ +// public class Animation : IObservable, IDisposable +// { +// /// +// /// The animation being tracked. +// /// +// private readonly IObservable _inner; - /// - /// The disposable used to cancel the animation. - /// - private readonly IDisposable _subscription; +// /// +// /// The disposable used to cancel the animation. +// /// +// private readonly IDisposable _subscription; - /// - /// Initializes a new instance of the class. - /// - /// The animation observable being tracked. - /// A disposable used to cancel the animation. - public Animation(IObservable inner, IDisposable subscription) - { - _inner = inner; - _subscription = subscription; - } +// /// +// /// Initializes a new instance of the class. +// /// +// /// The animation observable being tracked. +// /// A disposable used to cancel the animation. +// public Animation(IObservable inner, IDisposable subscription) +// { +// _inner = inner; +// _subscription = subscription; +// } - /// - /// Cancels the animation. - /// - public void Dispose() - { - _subscription.Dispose(); - } +// /// +// /// Cancels the animation. +// /// +// public void Dispose() +// { +// _subscription.Dispose(); +// } - /// - /// Notifies the provider that an observer is to receive notifications. - /// - /// The observer. - /// - /// A reference to an interface that allows observers to stop receiving notifications - /// before the provider has finished sending them. - /// - public IDisposable Subscribe(IObserver observer) - { - return _inner.Subscribe(observer); - } - } -} +// /// +// /// Notifies the provider that an observer is to receive notifications. +// /// +// /// The observer. +// /// +// /// A reference to an interface that allows observers to stop receiving notifications +// /// before the provider has finished sending them. +// /// +// public IDisposable Subscribe(IObserver observer) +// { +// return _inner.Subscribe(observer); +// } +// } +//} diff --git a/src/Avalonia.Animation/Easing/BackEaseIn.cs b/src/Avalonia.Animation/Easing/BackEaseIn.cs new file mode 100644 index 0000000000..c846d6116d --- /dev/null +++ b/src/Avalonia.Animation/Easing/BackEaseIn.cs @@ -0,0 +1,21 @@ +// 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; + +namespace Avalonia.Animation +{ + /// + /// Eases in a value + /// using a overshooting cubic function. + /// + public class BackEaseIn : Easing + { + /// + public override double Ease(double progress) + { + double p = progress; + return p * p * p - p * Math.Sin(p * Math.PI); + } + } +} diff --git a/src/Avalonia.Animation/Easing/BackEaseInOut.cs b/src/Avalonia.Animation/Easing/BackEaseInOut.cs new file mode 100644 index 0000000000..4b190b4da9 --- /dev/null +++ b/src/Avalonia.Animation/Easing/BackEaseInOut.cs @@ -0,0 +1,32 @@ +// 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; + +namespace Avalonia.Animation.Easings +{ + /// + /// Eases a value + /// using a piecewise overshooting cubic function. + /// + public class BackEaseInOut : Easing + { + /// + public override double Ease(double progress) + { + double p = progress; + + if (p < 0.5d) + { + double f = 2d * p; + return 0.5d * (f * f * f - f * Math.Sin(f * Math.PI)); + } + else + { + double f = (1d - (2d * p - 1d)); + return 0.5d * (1d - (f * f * f - f * Math.Sin(f * Math.PI))) + 0.5d; + } + } + + } +} diff --git a/src/Avalonia.Animation/Easing/BackEaseOut.cs b/src/Avalonia.Animation/Easing/BackEaseOut.cs new file mode 100644 index 0000000000..f671d61040 --- /dev/null +++ b/src/Avalonia.Animation/Easing/BackEaseOut.cs @@ -0,0 +1,21 @@ +// 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; + +namespace Avalonia.Animation +{ + /// + /// Eases out a value + /// using a overshooting cubic function. + /// + public class BackEaseOut : Easing + { + /// + public override double Ease(double progress) + { + double p = 1d - progress; + return 1 - (p * p * p - p * Math.Sin(p * Math.PI)); + } + } +} diff --git a/src/Avalonia.Animation/Easing/BounceEaseIn.cs b/src/Avalonia.Animation/Easing/BounceEaseIn.cs new file mode 100644 index 0000000000..5cf34e3f9a --- /dev/null +++ b/src/Avalonia.Animation/Easing/BounceEaseIn.cs @@ -0,0 +1,20 @@ +// 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. + + +namespace Avalonia.Animation +{ + /// + /// Eases in a value + /// using a simulated bounce function. + /// + public class BounceEaseIn : Easing + { + /// + public override double Ease(double progress) + { + return 1 - BounceEaseHelper.Bounce(1 - progress); + } + + } +} diff --git a/src/Avalonia.Animation/Easing/BounceEaseInOut.cs b/src/Avalonia.Animation/Easing/BounceEaseInOut.cs new file mode 100644 index 0000000000..6db0424658 --- /dev/null +++ b/src/Avalonia.Animation/Easing/BounceEaseInOut.cs @@ -0,0 +1,27 @@ +// 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. + +namespace Avalonia.Animation +{ + /// + /// Eases a value + /// using a piecewise simulated bounce function. + /// + public class BounceEaseInOut : Easing + { + /// + public override double Ease(double progress) + { + double p = progress; + if (p < 0.5d) + { + return 0.5f * (1 - BounceEaseHelper.Bounce(1 - (p * 2))); + } + else + { + return 0.5f * BounceEaseHelper.Bounce(p * 2 - 1) + 0.5f; + } + } + + } +} diff --git a/src/Avalonia.Animation/Easing/BounceEaseOut.cs b/src/Avalonia.Animation/Easing/BounceEaseOut.cs new file mode 100644 index 0000000000..9cb4e9633d --- /dev/null +++ b/src/Avalonia.Animation/Easing/BounceEaseOut.cs @@ -0,0 +1,19 @@ +// 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. + + +namespace Avalonia.Animation +{ + /// + /// Eases out a value + /// using a simulated bounce function. + /// + public class BounceEaseOut : Easing + { + /// + public override double Ease(double progress) + { + return BounceEaseHelper.Bounce(progress); + } + } +} diff --git a/src/Avalonia.Animation/Easing/CircularEaseIn.cs b/src/Avalonia.Animation/Easing/CircularEaseIn.cs new file mode 100644 index 0000000000..21b77db582 --- /dev/null +++ b/src/Avalonia.Animation/Easing/CircularEaseIn.cs @@ -0,0 +1,23 @@ +// 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; + +namespace Avalonia.Animation +{ + /// + /// Eases in a value + /// using the shifted fourth quadrant of + /// the unit circle. + /// + public class CircularEaseIn : Easing + { + /// + public override double Ease(double progress) + { + double p = progress; + return 1d - Math.Sqrt(1d - (p * p)); + } + + } +} diff --git a/src/Avalonia.Animation/Easing/CircularEaseInOut.cs b/src/Avalonia.Animation/Easing/CircularEaseInOut.cs new file mode 100644 index 0000000000..a5198a019f --- /dev/null +++ b/src/Avalonia.Animation/Easing/CircularEaseInOut.cs @@ -0,0 +1,29 @@ +// 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; + +namespace Avalonia.Animation +{ + /// + /// Eases a value + /// using a piecewise unit circle function. + /// + public class CircularEaseInOut : Easing + { + /// + public override double Ease(double progress) + { + double p = progress; + if (p < 0.5d) + { + return 0.5d * (1d - Math.Sqrt(1d - 4d * (p * p))); + } + else + { + return 0.5d * (Math.Sqrt(-((2d * p) - 3d) * ((2d * p) - 1d)) + 1d); + } + } + + } +} diff --git a/src/Avalonia.Animation/Easing/CircularEaseOut.cs b/src/Avalonia.Animation/Easing/CircularEaseOut.cs new file mode 100644 index 0000000000..33391ed816 --- /dev/null +++ b/src/Avalonia.Animation/Easing/CircularEaseOut.cs @@ -0,0 +1,22 @@ +// 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; + +namespace Avalonia.Animation +{ + /// + /// Eases out a value + /// using the shifted second quadrant of + /// the unit circle. + /// + public class CircularEaseOut : Easing + { + /// + public override double Ease(double progress) + { + double p = progress; + return Math.Sqrt((2d - p) * p); + } + } +} diff --git a/src/Avalonia.Animation/Easing/CubicEaseIn.cs b/src/Avalonia.Animation/Easing/CubicEaseIn.cs new file mode 100644 index 0000000000..653164d7ab --- /dev/null +++ b/src/Avalonia.Animation/Easing/CubicEaseIn.cs @@ -0,0 +1,18 @@ +// 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. + +namespace Avalonia.Animation +{ + /// + /// Eases in a value + /// using a cubic equation. + /// + public class CubicEaseIn : Easing + { + /// + public override double Ease(double progress) + { + return progress * progress * progress; + } + } +} diff --git a/src/Avalonia.Animation/Easing/CubicEaseInOut.cs b/src/Avalonia.Animation/Easing/CubicEaseInOut.cs new file mode 100644 index 0000000000..ffdce75d45 --- /dev/null +++ b/src/Avalonia.Animation/Easing/CubicEaseInOut.cs @@ -0,0 +1,29 @@ +// 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. + +namespace Avalonia.Animation +{ + /// + /// Eases a value + /// using a piece-wise cubic equation. + /// + public class CubicEaseInOut : Easing + { + /// + public override double Ease(double progress) + { + double p = progress; + + if (progress < 0.5d) + { + return 4d * p * p * p; + } + else + { + double f = ((2d * p) - 2d); + return 0.5d * f * f * f + 1d; + } + } + } + +} diff --git a/src/Avalonia.Animation/Easing/CubicEaseOut.cs b/src/Avalonia.Animation/Easing/CubicEaseOut.cs new file mode 100644 index 0000000000..6bbe756477 --- /dev/null +++ b/src/Avalonia.Animation/Easing/CubicEaseOut.cs @@ -0,0 +1,19 @@ +// 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. + +namespace Avalonia.Animation +{ + /// + /// Eases out a value + /// using a cubic equation. + /// + public class CubicEaseOut : Easing + { + /// + public override double Ease(double progress) + { + double f = (progress - 1d); + return f * f * f + 1d; + } + } +} diff --git a/src/Avalonia.Animation/Easing/Easing.cs b/src/Avalonia.Animation/Easing/Easing.cs new file mode 100644 index 0000000000..75b98e8a3a --- /dev/null +++ b/src/Avalonia.Animation/Easing/Easing.cs @@ -0,0 +1,58 @@ +using Avalonia.Collections; +using System; +using System.Collections.Generic; +using System.Text; +using System.Reflection; +using System.Linq; +using System.ComponentModel; + +namespace Avalonia.Animation +{ + /// + /// Base class for all Easing classes. + /// + public abstract class Easing : IEasing + { + /// + public abstract double Ease(double progress); + + static Dictionary _easingTypes; + + static readonly Type s_thisType = typeof(Easing); + + /// + /// Parses a Easing type string. + /// + /// The Easing type string. + /// Returns the instance of the parsed type. + public static Easing Parse(string e) + { + // TODO: There should be a better way to + // find all the subclasses than this method... + if (_easingTypes == null) + { + _easingTypes = new Dictionary(); + + var derivedTypes = AppDomain.CurrentDomain.GetAssemblies() + .SelectMany(p => p.GetTypes()) + .Where(p => p.Namespace == s_thisType.Namespace) + .Where(p => p.IsSubclassOf(s_thisType)) + .Select(p=>p).ToList(); + + foreach (var easingType in derivedTypes) + _easingTypes.Add(easingType.Name, easingType); + } + + if (_easingTypes.ContainsKey(e)) + { + var type = _easingTypes[e]; + return (Easing)Activator.CreateInstance(type); + } + else + { + throw new FormatException($"Easing \"{e}\" was not found in {s_thisType.Namespace} namespace."); + } + } + + } +} diff --git a/src/Avalonia.Animation/Easing/ElasticEaseIn.cs b/src/Avalonia.Animation/Easing/ElasticEaseIn.cs new file mode 100644 index 0000000000..b0a95d8dc0 --- /dev/null +++ b/src/Avalonia.Animation/Easing/ElasticEaseIn.cs @@ -0,0 +1,22 @@ +// 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; + +namespace Avalonia.Animation +{ + /// + /// Eases in a value + /// using a damped sine function. + /// + public class ElasticEaseIn : Easing + { + /// + public override double Ease(double progress) + { + double p = progress; + return Math.Sin(13d * EasingConstants.HALFPI * p) * Math.Pow(2d, 10d * (p - 1)); + } + } + +} diff --git a/src/Avalonia.Animation/Easing/ElasticEaseInOut.cs b/src/Avalonia.Animation/Easing/ElasticEaseInOut.cs new file mode 100644 index 0000000000..85dcfbd618 --- /dev/null +++ b/src/Avalonia.Animation/Easing/ElasticEaseInOut.cs @@ -0,0 +1,30 @@ +// 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; + +namespace Avalonia.Animation +{ + /// + /// Eases a value + /// using a piecewise damped sine function. + /// + public class ElasticEaseInOut : Easing + { + /// + public override double Ease(double progress) + { + double p = progress; + + if (p < 0.5d) + { + return 0.5d * Math.Sin(13d * EasingConstants.HALFPI * (2d * p)) * Math.Pow(2d, 10d * ((2d * p) - 1d)); + } + else + { + return 0.5d * (Math.Sin(-13d * EasingConstants.HALFPI * ((2d * p - 1d) + 1d)) * Math.Pow(2d, -10d * (2d * p - 1d)) + 2d); + } + } + + } +} diff --git a/src/Avalonia.Animation/Easing/ElasticEaseOut.cs b/src/Avalonia.Animation/Easing/ElasticEaseOut.cs new file mode 100644 index 0000000000..830198468d --- /dev/null +++ b/src/Avalonia.Animation/Easing/ElasticEaseOut.cs @@ -0,0 +1,23 @@ +// 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; + +namespace Avalonia.Animation +{ + /// + /// Eases out a value + /// using a damped sine function. + /// + public class ElasticEaseOut : Easing + { + /// + public override double Ease(double progress) + { + double p = progress; + return Math.Sin(-13d * EasingConstants.HALFPI * (p + 1)) * Math.Pow(2d, -10d * p) + 1d; + + } + + } +} diff --git a/src/Avalonia.Animation/Easing/ExponentialEaseIn.cs b/src/Avalonia.Animation/Easing/ExponentialEaseIn.cs new file mode 100644 index 0000000000..5b9154d29c --- /dev/null +++ b/src/Avalonia.Animation/Easing/ExponentialEaseIn.cs @@ -0,0 +1,21 @@ +// 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; + +namespace Avalonia.Animation +{ + /// + /// Eases in a value + /// using a exponential function. + /// + public class ExponentialEaseIn : Easing + { + /// + public override double Ease(double progress) + { + double p = progress; + return (p == 0.0d) ? p : Math.Pow(2d, 10d * (p - 1d)); + } + } +} diff --git a/src/Avalonia.Animation/Easing/ExponentialEaseInOut.cs b/src/Avalonia.Animation/Easing/ExponentialEaseInOut.cs new file mode 100644 index 0000000000..0eaef18464 --- /dev/null +++ b/src/Avalonia.Animation/Easing/ExponentialEaseInOut.cs @@ -0,0 +1,30 @@ +// 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; + +namespace Avalonia.Animation +{ + /// + /// Eases a value + /// using a piecewise exponential function. + /// + public class ExponentialEaseInOut : Easing + { + /// + public override double Ease(double progress) + { + double p = progress; + + if (p < 0.5d) + { + return 0.5d * Math.Pow(2d, (20d * p) - 10d); + } + else + { + return -0.5d * Math.Pow(2d, (-20d * p) + 10d) + 1d; + } + } + + } +} diff --git a/src/Avalonia.Animation/Easing/ExponentialEaseOut.cs b/src/Avalonia.Animation/Easing/ExponentialEaseOut.cs new file mode 100644 index 0000000000..c071d18d2e --- /dev/null +++ b/src/Avalonia.Animation/Easing/ExponentialEaseOut.cs @@ -0,0 +1,21 @@ +// 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; + +namespace Avalonia.Animation +{ + /// + /// Eases out a value + /// using a exponential function. + /// + public class ExponentialEaseOut : Easing + { + /// + public override double Ease(double progress) + { + double p = progress; + return (p == 1.0d) ? p : 1d - Math.Pow(2d, -10d * p); + } + } +} diff --git a/src/Avalonia.Animation/Easing/LinearEasing.cs b/src/Avalonia.Animation/Easing/LinearEasing.cs new file mode 100644 index 0000000000..fcbf6053f4 --- /dev/null +++ b/src/Avalonia.Animation/Easing/LinearEasing.cs @@ -0,0 +1,18 @@ +// 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. + +namespace Avalonia.Animation +{ + /// + /// Linearly eases a value. + /// + public class LinearEasing : Easing + { + /// + public override double Ease(double progress) + { + return progress; + } + } + +} diff --git a/src/Avalonia.Animation/Easing/QuadraticEaseIn.cs b/src/Avalonia.Animation/Easing/QuadraticEaseIn.cs new file mode 100644 index 0000000000..641fba4892 --- /dev/null +++ b/src/Avalonia.Animation/Easing/QuadraticEaseIn.cs @@ -0,0 +1,18 @@ +// 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. + +namespace Avalonia.Animation +{ + /// + /// Eases in a value + /// using a quadratic function. + /// + public class QuadraticEaseIn : Easing + { + /// + public override double Ease(double progress) + { + return progress * progress; + } + } +} diff --git a/src/Avalonia.Animation/Easing/QuadraticEaseInOut.cs b/src/Avalonia.Animation/Easing/QuadraticEaseInOut.cs new file mode 100644 index 0000000000..384c3caf32 --- /dev/null +++ b/src/Avalonia.Animation/Easing/QuadraticEaseInOut.cs @@ -0,0 +1,28 @@ +// 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. + +namespace Avalonia.Animation +{ + /// + /// Eases a value + /// using a piece-wise quadratic function. + /// + public class QuadraticEaseInOut : Easing + { + /// + public override double Ease(double progress) + { + double p = progress; + + if (progress < 0.5d) + { + return 2d * p * p; + } + else + { + return (-2d * p * p) + (4d * p) - 1d; + } + } + + } +} diff --git a/src/Avalonia.Animation/Easing/QuadraticEaseOut.cs b/src/Avalonia.Animation/Easing/QuadraticEaseOut.cs new file mode 100644 index 0000000000..14fd877ad4 --- /dev/null +++ b/src/Avalonia.Animation/Easing/QuadraticEaseOut.cs @@ -0,0 +1,19 @@ +// 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. + +namespace Avalonia.Animation +{ + /// + /// Eases out a value + /// using a quadratic function. + /// + public class QuadraticEaseOut : Easing + { + /// + public override double Ease(double progress) + { + return -(progress * (progress - 2d)); + } + + } +} diff --git a/src/Avalonia.Animation/Easing/QuarticEaseIn.cs b/src/Avalonia.Animation/Easing/QuarticEaseIn.cs new file mode 100644 index 0000000000..a5efa241be --- /dev/null +++ b/src/Avalonia.Animation/Easing/QuarticEaseIn.cs @@ -0,0 +1,20 @@ +// 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. + +namespace Avalonia.Animation +{ + /// + /// Eases in a value + /// using a quartic equation. + /// + public class QuarticEaseIn : Easing + { + /// + public override double Ease(double progress) + { + return progress * progress * progress * progress; + + } + + } +} diff --git a/src/Avalonia.Animation/Easing/QuarticEaseInOut.cs b/src/Avalonia.Animation/Easing/QuarticEaseInOut.cs new file mode 100644 index 0000000000..d5cd2622cb --- /dev/null +++ b/src/Avalonia.Animation/Easing/QuarticEaseInOut.cs @@ -0,0 +1,29 @@ +// 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. + +namespace Avalonia.Animation +{ + /// + /// Eases a value + /// using a piece-wise quartic equation. + /// + public class QuarticEaseInOut : Easing + { + /// + public override double Ease(double progress) + { + double p = progress; + + if (p < 0.5d) + { + return 8d * p * p * p * p; + } + else + { + double f = (p - 1d); + return -8d * f * f * f * f + 1d; + } + } + + } +} diff --git a/src/Avalonia.Animation/Easing/QuarticEaseOut.cs b/src/Avalonia.Animation/Easing/QuarticEaseOut.cs new file mode 100644 index 0000000000..5ecb3a7a0b --- /dev/null +++ b/src/Avalonia.Animation/Easing/QuarticEaseOut.cs @@ -0,0 +1,20 @@ +// 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. + +namespace Avalonia.Animation +{ + /// + /// Eases out a value + /// using a quartic equation. + /// + public class QuarticEaseOut : Easing + { + /// + public override double Ease(double progress) + { + double f = (progress - 1d); + return f * f * f * (1d - progress) + 1d; + } + + } +} diff --git a/src/Avalonia.Animation/Easing/QuinticEaseIn.cs b/src/Avalonia.Animation/Easing/QuinticEaseIn.cs new file mode 100644 index 0000000000..8dc6ad6bc8 --- /dev/null +++ b/src/Avalonia.Animation/Easing/QuinticEaseIn.cs @@ -0,0 +1,18 @@ +// 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. + +namespace Avalonia.Animation +{ + /// + /// Eases in a value + /// using a quartic equation. + /// + public class QuinticEaseIn : Easing + { + /// + public override double Ease(double progress) + { + return progress * progress * progress * progress * progress; + } + } +} diff --git a/src/Avalonia.Animation/Easing/QuinticEaseInOut.cs b/src/Avalonia.Animation/Easing/QuinticEaseInOut.cs new file mode 100644 index 0000000000..87f3c38dad --- /dev/null +++ b/src/Avalonia.Animation/Easing/QuinticEaseInOut.cs @@ -0,0 +1,28 @@ +// 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. + +namespace Avalonia.Animation +{ + /// + /// Eases a value + /// using a piece-wise quartic equation. + /// + public class QuinticEaseInOut : Easing + { + /// + public override double Ease(double progress) + { + double p = progress; + + if (progress < 0.5d) + { + return 16d * p * p * p * p * p; + } + else + { + double f = ((2d * p) - 2d); + return 0.5d * f * f * f * f * f + 1d; + } + } + } +} diff --git a/src/Avalonia.Animation/Easing/QuinticEaseOut.cs b/src/Avalonia.Animation/Easing/QuinticEaseOut.cs new file mode 100644 index 0000000000..f4054c8da2 --- /dev/null +++ b/src/Avalonia.Animation/Easing/QuinticEaseOut.cs @@ -0,0 +1,19 @@ +// 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. + +namespace Avalonia.Animation +{ + /// + /// Eases out a value + /// using a quartic equation. + /// + public class QuinticEaseOut : Easing + { + /// + public override double Ease(double progress) + { + double f = (progress - 1d); + return f * f * f * f * f + 1d; + } + } +} diff --git a/src/Avalonia.Animation/Easing/SineEaseIn.cs b/src/Avalonia.Animation/Easing/SineEaseIn.cs new file mode 100644 index 0000000000..49c633ec5c --- /dev/null +++ b/src/Avalonia.Animation/Easing/SineEaseIn.cs @@ -0,0 +1,20 @@ +// 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; + +namespace Avalonia.Animation +{ + /// + /// Eases in a value + /// using the quarter-wave of sine function. + /// + public class SineEaseIn : Easing + { + /// + public override double Ease(double progress) + { + return Math.Sin((progress - 1) * EasingConstants.HALFPI) + 1; + } + } +} diff --git a/src/Avalonia.Animation/Easing/SineEaseInOut.cs b/src/Avalonia.Animation/Easing/SineEaseInOut.cs new file mode 100644 index 0000000000..94b528c728 --- /dev/null +++ b/src/Avalonia.Animation/Easing/SineEaseInOut.cs @@ -0,0 +1,20 @@ +// 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; + +namespace Avalonia.Animation +{ + /// + /// Eases a value + /// using a half sine wave function. + /// + public class SineEaseInOut : Easing + { + /// + public override double Ease(double progress) + { + return 0.5d * (1d - Math.Cos(progress * Math.PI)); + } + } +} diff --git a/src/Avalonia.Animation/Easing/SineEaseOut.cs b/src/Avalonia.Animation/Easing/SineEaseOut.cs new file mode 100644 index 0000000000..7d0658c389 --- /dev/null +++ b/src/Avalonia.Animation/Easing/SineEaseOut.cs @@ -0,0 +1,21 @@ +// 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; + +namespace Avalonia.Animation +{ + /// + /// Eases out a value + /// using the quarter-wave of sine function + /// with shifted phase. + /// + public class SineEaseOut : Easing + { + /// + public override double Ease(double progress) + { + return Math.Sin(progress * EasingConstants.HALFPI); + } + } +} diff --git a/src/Avalonia.Animation/Helpers/BounceEaseHelper.cs b/src/Avalonia.Animation/Helpers/BounceEaseHelper.cs new file mode 100644 index 0000000000..b0da648fd3 --- /dev/null +++ b/src/Avalonia.Animation/Helpers/BounceEaseHelper.cs @@ -0,0 +1,39 @@ +// 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. + +namespace Avalonia.Animation +{ + /// + /// Helper static class for BounceEase classes. + /// + internal static class BounceEaseHelper + { + /// + /// Returns the consequent value of + /// a simulated bounce function. + /// + /// The amount of progress from 0 to 1. + /// The result of the easing function + internal static double Bounce(double progress) + { + double p = progress; + if (p < 4d / 11.0d) + { + return (121d * p * p) / 16.0d; + } + else if (p < 8d / 11.0d) + { + return (363d / 40.0d * p * p) - (99d / 10.0d * p) + 17d / 5.0d; + } + else if (p < 9d / 10.0d) + { + return (4356d / 361.0d * p * p) - (35442d / 1805.0d * p) + 16061d / 1805.0d; + } + else + { + return (54d / 5.0d * p * p) - (513d / 25.0d * p) + 268d / 25.0d; + } + } + + } +} diff --git a/src/Avalonia.Animation/Helpers/DoubleHelper.cs b/src/Avalonia.Animation/Helpers/DoubleHelper.cs new file mode 100644 index 0000000000..9d4fd4955f --- /dev/null +++ b/src/Avalonia.Animation/Helpers/DoubleHelper.cs @@ -0,0 +1,18 @@ +// 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 System.Text; + +namespace Avalonia.Animation.Helpers +{ + internal static class DoubleHelper + { + internal static bool AboutEqual(double x, double y) + { + double epsilon = Math.Max(Math.Abs(x), Math.Abs(y)) * 1E-15; + return Math.Abs(x - y) <= epsilon; + } + } +} diff --git a/src/Avalonia.Animation/Helpers/EasingConstants.cs b/src/Avalonia.Animation/Helpers/EasingConstants.cs new file mode 100644 index 0000000000..b6a52ab4a4 --- /dev/null +++ b/src/Avalonia.Animation/Helpers/EasingConstants.cs @@ -0,0 +1,18 @@ +// 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; + +namespace Avalonia.Animation +{ + /// + /// Helper static class for easing mathematical constants. + /// + internal static class EasingConstants + { + /// + /// Half of + /// + internal static double HALFPI = Math.PI / 2d; + } +} diff --git a/src/Avalonia.Animation/IEasing.cs b/src/Avalonia.Animation/IEasing.cs index 79a8197a4b..fc1f2efc18 100644 --- a/src/Avalonia.Animation/IEasing.cs +++ b/src/Avalonia.Animation/IEasing.cs @@ -4,20 +4,13 @@ namespace Avalonia.Animation { /// - /// Defines the interface for easing functions. + /// Defines the interface for easing classes. /// public interface IEasing { /// /// Returns the value of the transition for the specified progress. /// - /// The progress of the transition, from 0 to 1. - /// The start value of the transition. - /// The end value of the transition. - /// - /// A value between and as determined - /// by . - /// - object Ease(double progress, object start, object finish); + double Ease(double progress); } } diff --git a/src/Avalonia.Animation/IEasing`1.cs b/src/Avalonia.Animation/IEasing`1.cs deleted file mode 100644 index f59636edc9..0000000000 --- a/src/Avalonia.Animation/IEasing`1.cs +++ /dev/null @@ -1,24 +0,0 @@ -// 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. - -namespace Avalonia.Animation -{ - /// - /// Defines the interface for easing functions. - /// - /// The type of the property being transitioned. - public interface IEasing : IEasing - { - /// - /// Returns the value of the transition for the specified progress. - /// - /// The progress of the transition, from 0 to 1. - /// The start value of the transition. - /// The end value of the transition. - /// - /// A value between and as determined - /// by . - /// - T Ease(double progress, T start, T finish); - } -} diff --git a/src/Avalonia.Animation/LinearDoubleEasing.cs b/src/Avalonia.Animation/LinearDoubleEasing.cs deleted file mode 100644 index bae01b7ec7..0000000000 --- a/src/Avalonia.Animation/LinearDoubleEasing.cs +++ /dev/null @@ -1,41 +0,0 @@ -// 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. - -namespace Avalonia.Animation -{ - /// - /// Linearly eases a double value. - /// - public class LinearDoubleEasing : IEasing - { - /// - /// Returns the value of the transition for the specified progress. - /// - /// The progress of the transition, from 0 to 1. - /// The start value of the transition. - /// The end value of the transition. - /// - /// A value between and as determined - /// by . - /// - public double Ease(double progress, double start, double finish) - { - return ((finish - start) * progress) + start; - } - - /// - /// Returns the value of the transition for the specified progress. - /// - /// The progress of the transition, from 0 to 1. - /// The start value of the transition. - /// The end value of the transition. - /// - /// A value between and as determined - /// by . - /// - object IEasing.Ease(double progress, object start, object finish) - { - return Ease(progress, (double)start, (double)finish); - } - } -} diff --git a/src/Avalonia.Animation/LinearEasing.cs b/src/Avalonia.Animation/LinearEasing.cs deleted file mode 100644 index 04583af106..0000000000 --- a/src/Avalonia.Animation/LinearEasing.cs +++ /dev/null @@ -1,35 +0,0 @@ -// 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; - -namespace Avalonia.Animation -{ - /// - /// Returns a linear for the specified type. - /// - /// - /// Unfortunately this class is needed as there's no way to create a true generic easing - /// function at compile time, as mathematical operators don't have an interface. - /// - public static class LinearEasing - { - /// - /// A linear easing function for the specified type. - /// - /// The type. - /// An easing function. - public static IEasing For() - { - if (typeof(T) == typeof(double)) - { - return (IEasing)new LinearDoubleEasing(); - } - else - { - throw new NotSupportedException( - $"Don't know how to create a LinearEasing for type '{typeof(T).FullName}'."); - } - } - } -} diff --git a/src/Avalonia.Animation/Properties/AssemblyInfo.cs b/src/Avalonia.Animation/Properties/AssemblyInfo.cs index a41edcc7b1..7e4f2b340f 100644 --- a/src/Avalonia.Animation/Properties/AssemblyInfo.cs +++ b/src/Avalonia.Animation/Properties/AssemblyInfo.cs @@ -1,6 +1,8 @@ // 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 Avalonia.Metadata; using System.Reflection; [assembly: AssemblyTitle("Avalonia.Animation")] +[assembly: XmlnsDefinition("https://github.com/avaloniaui", "Avalonia.Animation")] \ No newline at end of file diff --git a/src/Avalonia.Animation/PropertyTransition.cs b/src/Avalonia.Animation/PropertyTransition.cs deleted file mode 100644 index 7953dd20a9..0000000000 --- a/src/Avalonia.Animation/PropertyTransition.cs +++ /dev/null @@ -1,50 +0,0 @@ -// 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; - -namespace Avalonia.Animation -{ - /// - /// Defines how a property should be animated using a transition. - /// - public class PropertyTransition - { - /// - /// Initializes a new instance of the class. - /// - /// The property to be animated/ - /// The duration of the animation. - /// The easing function to use. - public PropertyTransition(AvaloniaProperty property, TimeSpan duration, IEasing easing) - { - Property = property; - Duration = duration; - Easing = easing; - } - - /// - /// Gets the property to be animated. - /// - /// - /// The property to be animated. - /// - public AvaloniaProperty Property { get; } - - /// - /// Gets the duration of the animation. - /// - /// - /// The duration of the animation. - /// - public TimeSpan Duration { get; } - - /// - /// Gets the easing function used. - /// - /// - /// The easing function. - /// - public IEasing Easing { get; } - } -} diff --git a/src/Avalonia.Animation/Animate.cs b/src/Avalonia.Animation/Timing.cs similarity index 53% rename from src/Avalonia.Animation/Animate.cs rename to src/Avalonia.Animation/Timing.cs index 3e23f635ce..a22dc1a6a7 100644 --- a/src/Avalonia.Animation/Animate.cs +++ b/src/Avalonia.Animation/Timing.cs @@ -11,9 +11,9 @@ using Avalonia.Threading; namespace Avalonia.Animation { /// - /// Utilities for creating animations. + /// Provides global timing functions for animations. /// - public static class Animate + public static class Timing { /// /// The number of frames per second. @@ -26,9 +26,9 @@ namespace Avalonia.Animation private static readonly TimeSpan Tick = TimeSpan.FromSeconds(1.0 / FramesPerSecond); /// - /// Initializes static members of the class. + /// Initializes static members of the class. /// - static Animate() + static Timing() { Stopwatch = new Stopwatch(); Stopwatch.Start(); @@ -46,7 +46,8 @@ namespace Avalonia.Animation /// public static Stopwatch Stopwatch { - get; } + get; + } /// /// Gets the animation timer. @@ -61,7 +62,8 @@ namespace Avalonia.Animation /// public static IObservable Timer { - get; } + get; + } /// /// Gets a timer that fires every frame for the specified duration. @@ -86,49 +88,6 @@ namespace Avalonia.Animation .Concat(Observable.Return(1.0)); } - /// - /// Animates a . - /// - /// The target object. - /// The target property. - /// The value of the property at the start of the animation. - /// The value of the property at the end of the animation. - /// The easing function to use. - /// The duration of the animation. - /// An that can be used to track or stop the animation. - public static Animation Property( - IAvaloniaObject target, - AvaloniaProperty property, - object start, - object finish, - IEasing easing, - TimeSpan duration) - { - var o = GetTimer(duration).Select(progress => easing.Ease(progress, start, finish)); - return new Animation(o, target.Bind(property, o, BindingPriority.Animation)); - } - /// - /// Animates a . - /// - /// The property type. - /// The target object. - /// The target property. - /// The value of the property at the start of the animation. - /// The value of the property at the end of the animation. - /// The easing function to use. - /// The duration of the animation. - /// An that can be used to track or stop the animation. - public static Animation Property( - IAvaloniaObject target, - AvaloniaProperty property, - T start, - T finish, - IEasing easing, - TimeSpan duration) - { - var o = GetTimer(duration).Select(progress => easing.Ease(progress, start, finish)); - return new Animation(o, target.Bind(property, o, BindingPriority.Animation)); - } } } diff --git a/src/Avalonia.Animation/Transitions/DoubleTransition.cs b/src/Avalonia.Animation/Transitions/DoubleTransition.cs new file mode 100644 index 0000000000..fbe164aa90 --- /dev/null +++ b/src/Avalonia.Animation/Transitions/DoubleTransition.cs @@ -0,0 +1,32 @@ +// 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 Avalonia.Metadata; +using System; + +namespace Avalonia.Animation +{ + /// + /// Transitions object that handles properties with types. + /// + public class DoubleTransition : Transition + { + /// + public DoubleTransition() : base() + { + + } + + /// + public override AvaloniaProperty Property { get; set; } + + /// + public override void Apply(Animatable control) + { + //throw new NotImplementedException(); + + + + } + } +} diff --git a/src/Avalonia.Animation/Transitions/Transition.cs b/src/Avalonia.Animation/Transitions/Transition.cs new file mode 100644 index 0000000000..1a91f4caf0 --- /dev/null +++ b/src/Avalonia.Animation/Transitions/Transition.cs @@ -0,0 +1,59 @@ +// 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 Avalonia.Metadata; +using System; + +namespace Avalonia.Animation +{ + public interface ITransition + { + /// + /// Applies the transition to the specified . + /// + /// + void Apply(Animatable control); + + /// + /// Gets the property to be animated. + /// + AvaloniaProperty Property { get; set; } + } + + /// + /// Defines how a property should be animated using a transition. + /// + public abstract class Transition : ITransition + { + /// + /// Gets the duration of the animation. + /// + public TimeSpan Duration { get; set; } + + /// + /// Instantiates the base abstract class . + /// + public Transition() + { + if(!(typeof(T) == Property.PropertyType)) + { + throw new InvalidCastException + ($"Invalid property type {typeof(T).Name} for this {this.GetType().Name}"); + } + } + + /// + /// Gets the easing class to be used. + /// + public IEasing Easing { get; set; } + + /// + public abstract AvaloniaProperty Property { get; set; } + + /// + public abstract void Apply(Animatable control); + + } + + +} diff --git a/src/Avalonia.Animation/PropertyTransitions.cs b/src/Avalonia.Animation/Transitions/Transitions.cs similarity index 59% rename from src/Avalonia.Animation/PropertyTransitions.cs rename to src/Avalonia.Animation/Transitions/Transitions.cs index ea0c8f0467..50adf246ae 100644 --- a/src/Avalonia.Animation/PropertyTransitions.cs +++ b/src/Avalonia.Animation/Transitions/Transitions.cs @@ -6,14 +6,14 @@ using Avalonia.Collections; namespace Avalonia.Animation { /// - /// A collection of definitions. + /// A collection of definitions. /// - public class PropertyTransitions : AvaloniaList + public class Transitions : AvaloniaList { /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// - public PropertyTransitions() + public Transitions() { ResetBehavior = ResetBehavior.Remove; } diff --git a/src/Avalonia.Controls/ProgressBar.cs b/src/Avalonia.Controls/ProgressBar.cs index 34954dd0d5..c195df6d26 100644 --- a/src/Avalonia.Controls/ProgressBar.cs +++ b/src/Avalonia.Controls/ProgressBar.cs @@ -114,12 +114,12 @@ namespace Avalonia.Controls private IndeterminateAnimation(ProgressBar progressBar) { _progressBar = new WeakReference(progressBar); - _startTime = Animate.Stopwatch.Elapsed; - _indeterminateBindSubscription = Animate.Timer.TakeWhile(x => (x - _startTime).TotalSeconds <= 4.0) - .Select(GetAnimationRect) - .Finally(() => _startTime = Animate.Stopwatch.Elapsed) - .Repeat() - .Subscribe(AnimationTick); + //_startTime = Animate.Stopwatch.Elapsed; + //_indeterminateBindSubscription = Animate.Timer.TakeWhile(x => (x - _startTime).TotalSeconds <= 4.0) + // .Select(GetAnimationRect) + // .Finally(() => _startTime = Animate.Stopwatch.Elapsed) + // .Repeat() + // .Subscribe(AnimationTick); } public static IndeterminateAnimation StartAnimation(ProgressBar progressBar) diff --git a/src/Avalonia.Visuals/Animation/CrossFade.cs b/src/Avalonia.Visuals/Animation/CrossFade.cs index 8acf6582bb..c35011e5a0 100644 --- a/src/Avalonia.Visuals/Animation/CrossFade.cs +++ b/src/Avalonia.Visuals/Animation/CrossFade.cs @@ -58,13 +58,13 @@ namespace Avalonia.Animation if (from != null) { - tasks.Add(Animate.Property( - (IAvaloniaObject)from, - Visual.OpacityProperty, - from.Opacity, - 0, - LinearEasing.For(), - Duration).ToTask()); + //tasks.Add(Animate.Property( + // (IAvaloniaObject)from, + // Visual.OpacityProperty, + // from.Opacity, + // 0, + // LinearEasing.For(), + // Duration).ToTask()); } if (to != null) @@ -72,16 +72,16 @@ namespace Avalonia.Animation to.Opacity = 0; to.IsVisible = true; - tasks.Add(Animate.Property( - (IAvaloniaObject)to, - Visual.OpacityProperty, - 0, - 1, - LinearEasing.For(), - Duration).ToTask()); + //tasks.Add(Animate.Property( + // (IAvaloniaObject)to, + // Visual.OpacityProperty, + // 0, + // 1, + // LinearEasing.For(), + // Duration).ToTask()); } - await Task.WhenAll(tasks.ToArray()); + //await Task.WhenAll(tasks.ToArray()); if (from != null) { diff --git a/src/Avalonia.Visuals/Animation/PageSlide.cs b/src/Avalonia.Visuals/Animation/PageSlide.cs index ebc358379d..3218ed1e67 100644 --- a/src/Avalonia.Visuals/Animation/PageSlide.cs +++ b/src/Avalonia.Visuals/Animation/PageSlide.cs @@ -78,13 +78,13 @@ namespace Avalonia.Animation { var transform = new TranslateTransform(); from.RenderTransform = transform; - tasks.Add(Animate.Property( - transform, - translateProperty, - 0.0, - forward ? -distance : distance, - LinearEasing.For(), - Duration).ToTask()); + //tasks.Add(Animate.Property( + // transform, + // translateProperty, + // 0.0, + // forward ? -distance : distance, + // LinearEasing.For(), + // Duration).ToTask()); } if (to != null) @@ -92,16 +92,16 @@ namespace Avalonia.Animation var transform = new TranslateTransform(); to.RenderTransform = transform; to.IsVisible = true; - tasks.Add(Animate.Property( - transform, - translateProperty, - forward ? distance : -distance, - 0.0, - LinearEasing.For(), - Duration).ToTask()); + //tasks.Add(Animate.Property( + // transform, + // translateProperty, + // forward ? distance : -distance, + // 0.0, + // LinearEasing.For(), + // Duration).ToTask()); } - await Task.WhenAll(tasks.ToArray()); + //await Task.WhenAll(tasks.ToArray()); if (from != null) { diff --git a/src/Markup/Avalonia.Markup.Xaml/Avalonia.Markup.Xaml.csproj b/src/Markup/Avalonia.Markup.Xaml/Avalonia.Markup.Xaml.csproj index 0ce2a1a992..305da95119 100644 --- a/src/Markup/Avalonia.Markup.Xaml/Avalonia.Markup.Xaml.csproj +++ b/src/Markup/Avalonia.Markup.Xaml/Avalonia.Markup.Xaml.csproj @@ -31,6 +31,7 @@ + diff --git a/src/Markup/Avalonia.Markup.Xaml/Converters/EasingTypeConverter.cs b/src/Markup/Avalonia.Markup.Xaml/Converters/EasingTypeConverter.cs new file mode 100644 index 0000000000..851d579581 --- /dev/null +++ b/src/Markup/Avalonia.Markup.Xaml/Converters/EasingTypeConverter.cs @@ -0,0 +1,24 @@ +// 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 Avalonia.Animation; +using System; +using System.ComponentModel; +using System.Globalization; + +namespace Avalonia.Markup.Xaml.Converters +{ + + public class EasingTypeConverter : TypeConverter + { + public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType) + { + return sourceType == typeof(string); + } + + public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value) + { + return Easing.Parse((string)value); + } + } +} \ No newline at end of file diff --git a/src/Markup/Avalonia.Markup.Xaml/PortableXaml/AvaloniaDefaultTypeConverters.cs b/src/Markup/Avalonia.Markup.Xaml/PortableXaml/AvaloniaDefaultTypeConverters.cs index 1cf5b6a58e..bde9a8d766 100644 --- a/src/Markup/Avalonia.Markup.Xaml/PortableXaml/AvaloniaDefaultTypeConverters.cs +++ b/src/Markup/Avalonia.Markup.Xaml/PortableXaml/AvaloniaDefaultTypeConverters.cs @@ -12,6 +12,7 @@ using Avalonia.Controls; using Avalonia.Input; using Avalonia.Collections; using Avalonia.Controls.Templates; +using Avalonia.Animation; namespace Avalonia.Markup.Xaml.PortableXaml { @@ -47,6 +48,7 @@ namespace Avalonia.Markup.Xaml.PortableXaml //{ typeof(Uri), typeof(Converters.UriTypeConverter) }, { typeof(Cursor), typeof(CursorTypeConverter) }, { typeof(WindowIcon), typeof(IconTypeConverter) }, + { typeof(Easing), typeof(EasingTypeConverter) }, //{ typeof(FontWeight), typeof(FontWeightConverter) }, }; From 23aeff1ceecef0628ae2610b06fbe3c2eea24335 Mon Sep 17 00:00:00 2001 From: Jumar Macato Date: Wed, 21 Mar 2018 22:01:18 +0800 Subject: [PATCH 004/149] *Commented out stray xml comment. --- src/Avalonia.Animation/Animatable.cs | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/Avalonia.Animation/Animatable.cs b/src/Avalonia.Animation/Animatable.cs index 8b73cd2aa5..1a187a3521 100644 --- a/src/Avalonia.Animation/Animatable.cs +++ b/src/Avalonia.Animation/Animatable.cs @@ -59,16 +59,16 @@ namespace Avalonia.Animation } } - /// - /// Animates a . - /// - /// The target object. - /// The target property. - /// The value of the property at the start of the animation. - /// The value of the property at the end of the animation. - /// The easing function to use. - /// The duration of the animation. - /// An that can be used to track or stop the animation. + ///// + ///// Animates a . + ///// + ///// The target object. + ///// The target property. + ///// The value of the property at the start of the animation. + ///// The value of the property at the end of the animation. + ///// The easing function to use. + ///// The duration of the animation. + ///// An that can be used to track or stop the animation. //public static Animation BindAnimateProperty( // IAvaloniaObject target, // AvaloniaProperty property, From f0868a595e21040d7b3ad4e772d7653c69d0ba15 Mon Sep 17 00:00:00 2001 From: Jumar Macato Date: Wed, 21 Mar 2018 22:01:18 +0800 Subject: [PATCH 005/149] *Commented out stray xml comment. --- src/Avalonia.Animation/Animatable.cs | 20 +++++++++---------- .../Transitions/Transitions.cs | 2 +- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/Avalonia.Animation/Animatable.cs b/src/Avalonia.Animation/Animatable.cs index 8b73cd2aa5..1a187a3521 100644 --- a/src/Avalonia.Animation/Animatable.cs +++ b/src/Avalonia.Animation/Animatable.cs @@ -59,16 +59,16 @@ namespace Avalonia.Animation } } - /// - /// Animates a . - /// - /// The target object. - /// The target property. - /// The value of the property at the start of the animation. - /// The value of the property at the end of the animation. - /// The easing function to use. - /// The duration of the animation. - /// An that can be used to track or stop the animation. + ///// + ///// Animates a . + ///// + ///// The target object. + ///// The target property. + ///// The value of the property at the start of the animation. + ///// The value of the property at the end of the animation. + ///// The easing function to use. + ///// The duration of the animation. + ///// An that can be used to track or stop the animation. //public static Animation BindAnimateProperty( // IAvaloniaObject target, // AvaloniaProperty property, diff --git a/src/Avalonia.Animation/Transitions/Transitions.cs b/src/Avalonia.Animation/Transitions/Transitions.cs index 50adf246ae..8497d762d2 100644 --- a/src/Avalonia.Animation/Transitions/Transitions.cs +++ b/src/Avalonia.Animation/Transitions/Transitions.cs @@ -6,7 +6,7 @@ using Avalonia.Collections; namespace Avalonia.Animation { /// - /// A collection of definitions. + /// A collection of definitions. /// public class Transitions : AvaloniaList { From 378bdc6c8cdb4d6db5b0db13e6899b2822a46dbb Mon Sep 17 00:00:00 2001 From: Jumar Macato Date: Thu, 22 Mar 2018 15:57:54 +0800 Subject: [PATCH 006/149] Fixed transition backend, transitions are now working. --- samples/RenderTest/SideBar.xaml | 9 ++ src/Avalonia.Animation/Animatable.cs | 82 ++----------------- .../Transitions/DoubleTransition.cs | 22 ++--- .../Transitions/Transition.cs | 41 ++++++---- src/Avalonia.Visuals/Animation/PageSlide.cs | 8 +- .../AvaloniaDefaultTypeConverters.cs | 1 + 6 files changed, 58 insertions(+), 105 deletions(-) diff --git a/samples/RenderTest/SideBar.xaml b/samples/RenderTest/SideBar.xaml index 3013c5a80e..f11f37bfa5 100644 --- a/samples/RenderTest/SideBar.xaml +++ b/samples/RenderTest/SideBar.xaml @@ -32,8 +32,17 @@ + + + + + + + diff --git a/src/Avalonia.Animation/Animatable.cs b/src/Avalonia.Animation/Animatable.cs index 1a187a3521..0cc7cc416c 100644 --- a/src/Avalonia.Animation/Animatable.cs +++ b/src/Avalonia.Animation/Animatable.cs @@ -13,35 +13,24 @@ namespace Avalonia.Animation /// public class Animatable : AvaloniaObject { - - private Transitions _transitions; + /// + /// + /// + public static readonly StyledProperty TransitionsProperty = + AvaloniaProperty.Register(nameof(Transitions)); /// /// Gets or sets the property transitions for the control. /// public Transitions Transitions { - get - { - return _transitions ?? (_transitions = new Transitions()); - } - - set - { - SetAndRaise(TransitionsProperty, ref _transitions, value); - } + get { return GetValue(TransitionsProperty); } + set { SetValue(TransitionsProperty, value); } } - /// - /// Gets or sets the property transitions for the control. - /// - public static readonly DirectProperty TransitionsProperty = - AvaloniaProperty.RegisterDirect(nameof(Transitions), o => o.Transitions); - /// /// Reacts to a change in a value in - /// order to animate the change if a - /// is set for the property. + /// order to animate the change if a is set for the property. /// /// The event args. protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs e) @@ -50,65 +39,12 @@ namespace Avalonia.Animation { var match = Transitions.FirstOrDefault(x => x.Property == e.Property); - if (match != null) { - - // //BindAnimateProperty(this, e.Property, e.OldValue, e.NewValue, match.Easing, match.Duration); + match.Apply(this, e.OldValue, e.NewValue); } } } - ///// - ///// Animates a . - ///// - ///// The target object. - ///// The target property. - ///// The value of the property at the start of the animation. - ///// The value of the property at the end of the animation. - ///// The easing function to use. - ///// The duration of the animation. - ///// An that can be used to track or stop the animation. - //public static Animation BindAnimateProperty( - // IAvaloniaObject target, - // AvaloniaProperty property, - // object start, - // object finish, - // IEasing easing, - // TimeSpan duration) - //{ - // var k = start.GetType(); - // if (k == typeof(double)) - // { - // var o = Timing.GetTimer(duration).Select(progress => easing.Ease(progress, start, finish)); - // return new Animation(o, target.Bind(property, o, BindingPriority.Animation)); - // } - // else - // return null; - //} - - ///// - ///// Animates a . - ///// - ///// The property type. - ///// The target object. - ///// The target property. - ///// The value of the property at the start of the animation. - ///// The value of the property at the end of the animation. - ///// The easing function to use. - ///// The duration of the animation. - ///// An that can be used to track or stop the animation. - //public static Animation Property( - // IAvaloniaObject target, - // AvaloniaProperty property, - // T start, - // T finish, - // IEasing easing, - // TimeSpan duration) - //{ - // var o = Timing.GetTimer(duration).Select(progress => easing.Ease(progress, start, finish)); - // return new Animation(o, target.Bind(property, o, BindingPriority.Animation)); - //} - } } diff --git a/src/Avalonia.Animation/Transitions/DoubleTransition.cs b/src/Avalonia.Animation/Transitions/DoubleTransition.cs index fbe164aa90..a4cb255545 100644 --- a/src/Avalonia.Animation/Transitions/DoubleTransition.cs +++ b/src/Avalonia.Animation/Transitions/DoubleTransition.cs @@ -3,6 +3,7 @@ using Avalonia.Metadata; using System; +using System.Reactive.Linq; namespace Avalonia.Animation { @@ -12,21 +13,14 @@ namespace Avalonia.Animation public class DoubleTransition : Transition { /// - public DoubleTransition() : base() + public override void DoInterpolation(Animatable control, IObservable progress, double oldValue, double newValue) { - - } - - /// - public override AvaloniaProperty Property { get; set; } - - /// - public override void Apply(Animatable control) - { - //throw new NotImplementedException(); - - - + var delta = newValue - oldValue; + var transition = progress.Select(p => + { + return Easing.Ease(p) * delta + oldValue; + }); + control.Bind(Property, transition.Select(p=>(object)p), Data.BindingPriority.Animation); } } } diff --git a/src/Avalonia.Animation/Transitions/Transition.cs b/src/Avalonia.Animation/Transitions/Transition.cs index 1a91f4caf0..4232a1214f 100644 --- a/src/Avalonia.Animation/Transitions/Transition.cs +++ b/src/Avalonia.Animation/Transitions/Transition.cs @@ -11,13 +11,14 @@ namespace Avalonia.Animation /// /// Applies the transition to the specified . /// - /// - void Apply(Animatable control); + void Apply(Animatable control, object oldValue, object newValue); /// /// Gets the property to be animated. /// AvaloniaProperty Property { get; set; } + + } /// @@ -29,29 +30,41 @@ namespace Avalonia.Animation /// Gets the duration of the animation. /// public TimeSpan Duration { get; set; } - + /// - /// Instantiates the base abstract class . + /// Gets the easing class to be used. /// - public Transition() + public IEasing Easing { get; set; } + + private AvaloniaProperty _prop; + + /// + public AvaloniaProperty Property { - if(!(typeof(T) == Property.PropertyType)) + get { - throw new InvalidCastException - ($"Invalid property type {typeof(T).Name} for this {this.GetType().Name}"); + return _prop; + } + set + { + if (!(typeof(T) == value.PropertyType)) + throw new InvalidCastException + ($"Invalid property type {typeof(T).Name} for this {GetType().Name}"); + + _prop = value; } } /// - /// Gets the easing class to be used. + /// Apply interpolation to the property. /// - public IEasing Easing { get; set; } + public abstract void DoInterpolation(Animatable control, IObservable progress, T oldValue, T newValue); /// - public abstract AvaloniaProperty Property { get; set; } - - /// - public abstract void Apply(Animatable control); + public void Apply(Animatable control, object oldValue, object newValue) + { + DoInterpolation(control, Timing.GetTimer(Duration), (T)oldValue, (T)newValue); + } } diff --git a/src/Avalonia.Visuals/Animation/PageSlide.cs b/src/Avalonia.Visuals/Animation/PageSlide.cs index 3218ed1e67..483bb2b44d 100644 --- a/src/Avalonia.Visuals/Animation/PageSlide.cs +++ b/src/Avalonia.Visuals/Animation/PageSlide.cs @@ -76,8 +76,8 @@ namespace Avalonia.Animation if (from != null) { - var transform = new TranslateTransform(); - from.RenderTransform = transform; + //var transform = new TranslateTransform(); + //from.RenderTransform = transform; //tasks.Add(Animate.Property( // transform, // translateProperty, @@ -89,8 +89,8 @@ namespace Avalonia.Animation if (to != null) { - var transform = new TranslateTransform(); - to.RenderTransform = transform; + //var transform = new TranslateTransform(); + //to.RenderTransform = transform; to.IsVisible = true; //tasks.Add(Animate.Property( // transform, diff --git a/src/Markup/Avalonia.Markup.Xaml/PortableXaml/AvaloniaDefaultTypeConverters.cs b/src/Markup/Avalonia.Markup.Xaml/PortableXaml/AvaloniaDefaultTypeConverters.cs index bde9a8d766..5f0f8d8d46 100644 --- a/src/Markup/Avalonia.Markup.Xaml/PortableXaml/AvaloniaDefaultTypeConverters.cs +++ b/src/Markup/Avalonia.Markup.Xaml/PortableXaml/AvaloniaDefaultTypeConverters.cs @@ -49,6 +49,7 @@ namespace Avalonia.Markup.Xaml.PortableXaml { typeof(Cursor), typeof(CursorTypeConverter) }, { typeof(WindowIcon), typeof(IconTypeConverter) }, { typeof(Easing), typeof(EasingTypeConverter) }, + { typeof(IEasing), typeof(EasingTypeConverter) }, //{ typeof(FontWeight), typeof(FontWeightConverter) }, }; From fe66f3837e6ce8d6e9e65ee496fc5252a282cba8 Mon Sep 17 00:00:00 2001 From: Jumar Macato Date: Thu, 22 Mar 2018 17:47:47 +0800 Subject: [PATCH 007/149] *Added numerical transition classes. *Disabled old animations code in CrossFade, PageSlide, CarouselPresenter and ProgressBar. --- .../Transitions/DoubleTransition.cs | 11 ++---- .../Transitions/FloatTransition.cs | 23 +++++++++++ .../Transitions/ITransition.cs | 26 +++++++++++++ .../Transitions/IntegerTransition.cs | 23 +++++++++++ .../Transitions/Transition.cs | 25 +++--------- .../Presenters/CarouselPresenter.cs | 2 +- src/Avalonia.Controls/ProgressBar.cs | 38 +++++++++---------- src/Avalonia.Visuals/Animation/CrossFade.cs | 3 ++ src/Avalonia.Visuals/Animation/PageSlide.cs | 3 ++ 9 files changed, 108 insertions(+), 46 deletions(-) create mode 100644 src/Avalonia.Animation/Transitions/FloatTransition.cs create mode 100644 src/Avalonia.Animation/Transitions/ITransition.cs create mode 100644 src/Avalonia.Animation/Transitions/IntegerTransition.cs diff --git a/src/Avalonia.Animation/Transitions/DoubleTransition.cs b/src/Avalonia.Animation/Transitions/DoubleTransition.cs index a4cb255545..e7ae53ef81 100644 --- a/src/Avalonia.Animation/Transitions/DoubleTransition.cs +++ b/src/Avalonia.Animation/Transitions/DoubleTransition.cs @@ -8,19 +8,16 @@ using System.Reactive.Linq; namespace Avalonia.Animation { /// - /// Transitions object that handles properties with types. + /// Transition class that handles with types. /// public class DoubleTransition : Transition { /// - public override void DoInterpolation(Animatable control, IObservable progress, double oldValue, double newValue) + public override IObservable DoInterpolation(IObservable progress, double oldValue, double newValue) { var delta = newValue - oldValue; - var transition = progress.Select(p => - { - return Easing.Ease(p) * delta + oldValue; - }); - control.Bind(Property, transition.Select(p=>(object)p), Data.BindingPriority.Animation); + return progress + .Select(p => Easing.Ease(p) * delta + oldValue); } } } diff --git a/src/Avalonia.Animation/Transitions/FloatTransition.cs b/src/Avalonia.Animation/Transitions/FloatTransition.cs new file mode 100644 index 0000000000..c1d2fb0b0b --- /dev/null +++ b/src/Avalonia.Animation/Transitions/FloatTransition.cs @@ -0,0 +1,23 @@ +// 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 Avalonia.Metadata; +using System; +using System.Reactive.Linq; + +namespace Avalonia.Animation +{ + /// + /// Transition class that handles with types. + /// + public class FloatTransition : Transition + { + /// + public override IObservable DoInterpolation(IObservable progress, float oldValue, float newValue) + { + var delta = newValue - oldValue; + return progress + .Select(p => (float)Easing.Ease(p) * delta + oldValue); + } + } +} diff --git a/src/Avalonia.Animation/Transitions/ITransition.cs b/src/Avalonia.Animation/Transitions/ITransition.cs new file mode 100644 index 0000000000..124a770c8d --- /dev/null +++ b/src/Avalonia.Animation/Transitions/ITransition.cs @@ -0,0 +1,26 @@ +// 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 Avalonia.Metadata; +using System; +using System.Reactive.Linq; + +namespace Avalonia.Animation +{ + /// + /// Interface for objects. + /// + public interface ITransition + { + /// + /// Applies the transition to the specified . + /// + IDisposable Apply(Animatable control, object oldValue, object newValue); + + /// + /// Gets the property to be animated. + /// + AvaloniaProperty Property { get; set; } + + } +} diff --git a/src/Avalonia.Animation/Transitions/IntegerTransition.cs b/src/Avalonia.Animation/Transitions/IntegerTransition.cs new file mode 100644 index 0000000000..cb56956a80 --- /dev/null +++ b/src/Avalonia.Animation/Transitions/IntegerTransition.cs @@ -0,0 +1,23 @@ +// 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 Avalonia.Metadata; +using System; +using System.Reactive.Linq; + +namespace Avalonia.Animation +{ + /// + /// Transition class that handles with types. + /// + public class IntegerTransition : Transition + { + /// + public override IObservable DoInterpolation(IObservable progress, int oldValue, int newValue) + { + var delta = newValue - oldValue; + return progress + .Select(p => (int)(Easing.Ease(p) * delta + oldValue)); + } + } +} diff --git a/src/Avalonia.Animation/Transitions/Transition.cs b/src/Avalonia.Animation/Transitions/Transition.cs index 4232a1214f..728908c234 100644 --- a/src/Avalonia.Animation/Transitions/Transition.cs +++ b/src/Avalonia.Animation/Transitions/Transition.cs @@ -3,24 +3,10 @@ using Avalonia.Metadata; using System; +using System.Reactive.Linq; namespace Avalonia.Animation { - public interface ITransition - { - /// - /// Applies the transition to the specified . - /// - void Apply(Animatable control, object oldValue, object newValue); - - /// - /// Gets the property to be animated. - /// - AvaloniaProperty Property { get; set; } - - - } - /// /// Defines how a property should be animated using a transition. /// @@ -49,7 +35,7 @@ namespace Avalonia.Animation { if (!(typeof(T) == value.PropertyType)) throw new InvalidCastException - ($"Invalid property type {typeof(T).Name} for this {GetType().Name}"); + ($"Invalid property type \"{typeof(T).Name}\" for this {GetType().Name} transition."); _prop = value; } @@ -58,12 +44,13 @@ namespace Avalonia.Animation /// /// Apply interpolation to the property. /// - public abstract void DoInterpolation(Animatable control, IObservable progress, T oldValue, T newValue); + public abstract IObservable DoInterpolation(IObservable progress, T oldValue, T newValue); /// - public void Apply(Animatable control, object oldValue, object newValue) + public IDisposable Apply(Animatable control, object oldValue, object newValue) { - DoInterpolation(control, Timing.GetTimer(Duration), (T)oldValue, (T)newValue); + var transition = DoInterpolation(Timing.GetTimer(Duration), (T)oldValue, (T)newValue).Select(p => (object)p); + return control.Bind(Property, transition, Data.BindingPriority.Animation); } } diff --git a/src/Avalonia.Controls/Presenters/CarouselPresenter.cs b/src/Avalonia.Controls/Presenters/CarouselPresenter.cs index 5156a11b30..c6ef5a14e9 100644 --- a/src/Avalonia.Controls/Presenters/CarouselPresenter.cs +++ b/src/Avalonia.Controls/Presenters/CarouselPresenter.cs @@ -39,7 +39,7 @@ namespace Avalonia.Controls.Presenters Carousel.PageTransitionProperty.AddOwner(); private int _selectedIndex = -1; - private Task _current; + // private Task _current; private Task _currentTransition; private int _queuedTransitionIndex = -1; diff --git a/src/Avalonia.Controls/ProgressBar.cs b/src/Avalonia.Controls/ProgressBar.cs index c195df6d26..e41d86636a 100644 --- a/src/Avalonia.Controls/ProgressBar.cs +++ b/src/Avalonia.Controls/ProgressBar.cs @@ -105,8 +105,8 @@ namespace Avalonia.Controls private class IndeterminateAnimation : IDisposable { private WeakReference _progressBar; - private IDisposable _indeterminateBindSubscription; - private TimeSpan _startTime; + //private IDisposable _indeterminateBindSubscription; + //private TimeSpan _startTime; private bool _disposed; public bool Disposed => _disposed; @@ -129,31 +129,31 @@ namespace Avalonia.Controls private Rect GetAnimationRect(TimeSpan time) { - if (_progressBar.TryGetTarget(out var progressBar)) - { - if (progressBar.Orientation == Orientation.Horizontal) - return new Rect(-progressBar._indicator.Width - 5 + (time - _startTime).TotalSeconds / 4.0 * (progressBar.Bounds.Width + progressBar._indicator.Width + 10), 0, progressBar._indicator.Bounds.Width, progressBar._indicator.Bounds.Height); - else - return new Rect(0, progressBar.Bounds.Height + 5 - (time - _startTime).TotalSeconds / 4.0 * (progressBar.Bounds.Height + progressBar._indicator.Height + 10), progressBar._indicator.Bounds.Width, progressBar._indicator.Bounds.Height); - } - else - { - _indeterminateBindSubscription.Dispose(); - return Rect.Empty; - } + //if (_progressBar.TryGetTarget(out var progressBar)) + //{ + // if (progressBar.Orientation == Orientation.Horizontal) + // return new Rect(-progressBar._indicator.Width - 5 + (time - _startTime).TotalSeconds / 4.0 * (progressBar.Bounds.Width + progressBar._indicator.Width + 10), 0, progressBar._indicator.Bounds.Width, progressBar._indicator.Bounds.Height); + // else + // return new Rect(0, progressBar.Bounds.Height + 5 - (time - _startTime).TotalSeconds / 4.0 * (progressBar.Bounds.Height + progressBar._indicator.Height + 10), progressBar._indicator.Bounds.Width, progressBar._indicator.Bounds.Height); + //} + //else + //{ + // _indeterminateBindSubscription.Dispose(); + return Rect.Empty; + //} } private void AnimationTick(Rect rect) { - if (_progressBar.TryGetTarget(out var progressBar)) - progressBar._indicator.Arrange(rect); - else - _indeterminateBindSubscription.Dispose(); + //if (_progressBar.TryGetTarget(out var progressBar)) + // progressBar._indicator.Arrange(rect); + //else + // _indeterminateBindSubscription.Dispose(); } public void Dispose() { - _indeterminateBindSubscription?.Dispose(); + //_indeterminateBindSubscription?.Dispose(); _disposed = true; } } diff --git a/src/Avalonia.Visuals/Animation/CrossFade.cs b/src/Avalonia.Visuals/Animation/CrossFade.cs index c35011e5a0..bf532c62c3 100644 --- a/src/Avalonia.Visuals/Animation/CrossFade.cs +++ b/src/Avalonia.Visuals/Animation/CrossFade.cs @@ -83,6 +83,9 @@ namespace Avalonia.Animation //await Task.WhenAll(tasks.ToArray()); + // FIXME: This is temporary until animations are fixed. + await Task.Delay(1); + if (from != null) { from.IsVisible = false; diff --git a/src/Avalonia.Visuals/Animation/PageSlide.cs b/src/Avalonia.Visuals/Animation/PageSlide.cs index 483bb2b44d..2eb0426bc5 100644 --- a/src/Avalonia.Visuals/Animation/PageSlide.cs +++ b/src/Avalonia.Visuals/Animation/PageSlide.cs @@ -103,6 +103,9 @@ namespace Avalonia.Animation //await Task.WhenAll(tasks.ToArray()); + // FIXME: This is temporary until animations are fixed. + await Task.Delay(1); + if (from != null) { from.IsVisible = false; From 28e3d73098b48e9e2fc79aa4f909156251c45400 Mon Sep 17 00:00:00 2001 From: Jumar Macato Date: Sat, 24 Mar 2018 16:01:51 +0800 Subject: [PATCH 008/149] Revert the library split, just transferring the base Animatable class to Avalonia.Visual was good enough. Apply important changes from my internal test branch --- samples/RenderTest/Program.cs | 16 +++--- samples/RenderTest/SideBar.xaml | 10 +++- src/Avalonia.Animation/AnimationExtensions.cs | 45 --------------- src/Avalonia.Animation/Animation`1.cs | 56 ------------------- .../Avalonia.Animation.csproj | 2 + src/Avalonia.Animation/{Easing => }/Easing.cs | 0 .../Helpers/DoubleHelper.cs | 2 +- .../{Transitions => }/Transition.cs | 25 ++++++--- .../Transitions/DoubleTransition.cs | 2 +- .../Transitions/FloatTransition.cs | 2 +- .../Transitions/IntegerTransition.cs | 2 +- .../Transitions/PointTransition.cs | 31 ++++++++++ .../Transitions/ThicknessTransition.cs | 35 ++++++++++++ src/Avalonia.Styling/Avalonia.Styling.csproj | 1 - .../Animation}/Animatable.cs | 7 ++- .../Animation}/Animation.cs | 0 .../Animation}/IEasing.cs | 0 .../Animation}/Timing.cs | 0 src/Avalonia.Visuals/Avalonia.Visuals.csproj | 1 - .../ITransition.cs | 2 +- .../Transitions.cs | 2 +- 21 files changed, 111 insertions(+), 130 deletions(-) delete mode 100644 src/Avalonia.Animation/AnimationExtensions.cs delete mode 100644 src/Avalonia.Animation/Animation`1.cs rename src/Avalonia.Animation/{Easing => }/Easing.cs (100%) rename src/Avalonia.Animation/{Transitions => }/Transition.cs (72%) create mode 100644 src/Avalonia.Animation/Transitions/PointTransition.cs create mode 100644 src/Avalonia.Animation/Transitions/ThicknessTransition.cs rename src/{Avalonia.Animation => Avalonia.Visuals/Animation}/Animatable.cs (83%) rename src/{Avalonia.Animation => Avalonia.Visuals/Animation}/Animation.cs (100%) rename src/{Avalonia.Animation => Avalonia.Visuals/Animation}/IEasing.cs (100%) rename src/{Avalonia.Animation => Avalonia.Visuals/Animation}/Timing.cs (100%) rename src/{Avalonia.Animation/Transitions => Avalonia.Visuals}/ITransition.cs (92%) rename src/{Avalonia.Animation/Transitions => Avalonia.Visuals}/Transitions.cs (89%) diff --git a/samples/RenderTest/Program.cs b/samples/RenderTest/Program.cs index a1a2b8c410..4177325872 100644 --- a/samples/RenderTest/Program.cs +++ b/samples/RenderTest/Program.cs @@ -10,15 +10,15 @@ namespace RenderTest { internal class Program { - static void Main(string[] args) - { - // TODO: Make this work with GTK/Skia/Cairo depending on command-line args - // again. - AppBuilder.Configure() + // TODO: Make this work with GTK/Skia/Cairo depending on command-line args + // again. + static void Main(string[] args) => BuildAvaloniaApp().Start(); + + // App configuration, used by the entry point and previewer + static AppBuilder BuildAvaloniaApp() + => AppBuilder.Configure() .UsePlatformDetect() .UseReactiveUI() - .LogToDebug() - .Start(); - } + .LogToDebug(); } } diff --git a/samples/RenderTest/SideBar.xaml b/samples/RenderTest/SideBar.xaml index f11f37bfa5..7a0b515748 100644 --- a/samples/RenderTest/SideBar.xaml +++ b/samples/RenderTest/SideBar.xaml @@ -1,4 +1,6 @@ - + - + diff --git a/src/Avalonia.Animation/AnimationExtensions.cs b/src/Avalonia.Animation/AnimationExtensions.cs deleted file mode 100644 index 4efea1f4ba..0000000000 --- a/src/Avalonia.Animation/AnimationExtensions.cs +++ /dev/null @@ -1,45 +0,0 @@ -// 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; - -namespace Avalonia.Animation -{ - /// - /// Defines animation extension methods. - /// - public static class AnimationExtensions - { - ///// - ///// Returns a new for the specified - ///// using linear easing. - ///// - ///// The type of the . - ///// The property to animate. - ///// The animation duration in milliseconds. - ///// - ///// A that can be added to the - ///// collection. - ///// - //public static Transition Transition(this AvaloniaProperty property, int milliseconds) - //{ - // return Transition(property, TimeSpan.FromMilliseconds(milliseconds)); - //} - - ///// - ///// Returns a new for the specified - ///// using linear easing. - ///// - ///// The type of the . - ///// The property to animate. - ///// The animation duration. - ///// - ///// A that can be added to the - ///// collection. - ///// - //public static Transition Transition(this AvaloniaProperty property, TimeSpan duration) - //{ - // return new Transition(property, duration, LinearEasing.For()); - //} - } -} diff --git a/src/Avalonia.Animation/Animation`1.cs b/src/Avalonia.Animation/Animation`1.cs deleted file mode 100644 index e99dea96c6..0000000000 --- a/src/Avalonia.Animation/Animation`1.cs +++ /dev/null @@ -1,56 +0,0 @@ -//// 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; - -//namespace Avalonia.Animation -//{ -// /// -// /// Tracks the progress of an animation. -// /// -// /// The type of the value being animated./ -// public class Animation : IObservable, IDisposable -// { -// /// -// /// The animation being tracked. -// /// -// private readonly IObservable _inner; - -// /// -// /// The disposable used to cancel the animation. -// /// -// private readonly IDisposable _subscription; - -// /// -// /// Initializes a new instance of the class. -// /// -// /// The animation observable being tracked. -// /// A disposable used to cancel the animation. -// public Animation(IObservable inner, IDisposable subscription) -// { -// _inner = inner; -// _subscription = subscription; -// } - -// /// -// /// Cancels the animation. -// /// -// public void Dispose() -// { -// _subscription.Dispose(); -// } - -// /// -// /// Notifies the provider that an observer is to receive notifications. -// /// -// /// The observer. -// /// -// /// A reference to an interface that allows observers to stop receiving notifications -// /// before the provider has finished sending them. -// /// -// public IDisposable Subscribe(IObserver observer) -// { -// return _inner.Subscribe(observer); -// } -// } -//} diff --git a/src/Avalonia.Animation/Avalonia.Animation.csproj b/src/Avalonia.Animation/Avalonia.Animation.csproj index 2101c5669d..cbf5914ea9 100644 --- a/src/Avalonia.Animation/Avalonia.Animation.csproj +++ b/src/Avalonia.Animation/Avalonia.Animation.csproj @@ -32,6 +32,8 @@ + + \ No newline at end of file diff --git a/src/Avalonia.Animation/Easing/Easing.cs b/src/Avalonia.Animation/Easing.cs similarity index 100% rename from src/Avalonia.Animation/Easing/Easing.cs rename to src/Avalonia.Animation/Easing.cs diff --git a/src/Avalonia.Animation/Helpers/DoubleHelper.cs b/src/Avalonia.Animation/Helpers/DoubleHelper.cs index 9d4fd4955f..bc95a2bb14 100644 --- a/src/Avalonia.Animation/Helpers/DoubleHelper.cs +++ b/src/Avalonia.Animation/Helpers/DoubleHelper.cs @@ -5,7 +5,7 @@ using System; using System.Collections.Generic; using System.Text; -namespace Avalonia.Animation.Helpers +namespace Avalonia.Animation { internal static class DoubleHelper { diff --git a/src/Avalonia.Animation/Transitions/Transition.cs b/src/Avalonia.Animation/Transition.cs similarity index 72% rename from src/Avalonia.Animation/Transitions/Transition.cs rename to src/Avalonia.Animation/Transition.cs index 728908c234..808e7f69de 100644 --- a/src/Avalonia.Animation/Transitions/Transition.cs +++ b/src/Avalonia.Animation/Transition.cs @@ -12,17 +12,28 @@ namespace Avalonia.Animation /// public abstract class Transition : ITransition { + private AvaloniaProperty _prop; + private IEasing _easing; + /// /// Gets the duration of the animation. /// public TimeSpan Duration { get; set; } - + /// /// Gets the easing class to be used. /// - public IEasing Easing { get; set; } - - private AvaloniaProperty _prop; + public IEasing Easing + { + get + { + return _easing ?? (_easing = new LinearEasing()); + } + set + { + _easing = value; + } + } /// public AvaloniaProperty Property @@ -33,7 +44,7 @@ namespace Avalonia.Animation } set { - if (!(typeof(T) == value.PropertyType)) + if (!(typeof(T) == value.PropertyType)) throw new InvalidCastException ($"Invalid property type \"{typeof(T).Name}\" for this {GetType().Name} transition."); @@ -44,12 +55,12 @@ namespace Avalonia.Animation /// /// Apply interpolation to the property. /// - public abstract IObservable DoInterpolation(IObservable progress, T oldValue, T newValue); + public abstract IObservable DoTransition(IObservable progress, T oldValue, T newValue); /// public IDisposable Apply(Animatable control, object oldValue, object newValue) { - var transition = DoInterpolation(Timing.GetTimer(Duration), (T)oldValue, (T)newValue).Select(p => (object)p); + var transition = DoTransition(Timing.GetTimer(Duration), (T)oldValue, (T)newValue).Select(p => (object)p); return control.Bind(Property, transition, Data.BindingPriority.Animation); } diff --git a/src/Avalonia.Animation/Transitions/DoubleTransition.cs b/src/Avalonia.Animation/Transitions/DoubleTransition.cs index e7ae53ef81..26f7f975e8 100644 --- a/src/Avalonia.Animation/Transitions/DoubleTransition.cs +++ b/src/Avalonia.Animation/Transitions/DoubleTransition.cs @@ -13,7 +13,7 @@ namespace Avalonia.Animation public class DoubleTransition : Transition { /// - public override IObservable DoInterpolation(IObservable progress, double oldValue, double newValue) + public override IObservable DoTransition(IObservable progress, double oldValue, double newValue) { var delta = newValue - oldValue; return progress diff --git a/src/Avalonia.Animation/Transitions/FloatTransition.cs b/src/Avalonia.Animation/Transitions/FloatTransition.cs index c1d2fb0b0b..b94295d311 100644 --- a/src/Avalonia.Animation/Transitions/FloatTransition.cs +++ b/src/Avalonia.Animation/Transitions/FloatTransition.cs @@ -13,7 +13,7 @@ namespace Avalonia.Animation public class FloatTransition : Transition { /// - public override IObservable DoInterpolation(IObservable progress, float oldValue, float newValue) + public override IObservable DoTransition(IObservable progress, float oldValue, float newValue) { var delta = newValue - oldValue; return progress diff --git a/src/Avalonia.Animation/Transitions/IntegerTransition.cs b/src/Avalonia.Animation/Transitions/IntegerTransition.cs index cb56956a80..200b93a3df 100644 --- a/src/Avalonia.Animation/Transitions/IntegerTransition.cs +++ b/src/Avalonia.Animation/Transitions/IntegerTransition.cs @@ -13,7 +13,7 @@ namespace Avalonia.Animation public class IntegerTransition : Transition { /// - public override IObservable DoInterpolation(IObservable progress, int oldValue, int newValue) + public override IObservable DoTransition(IObservable progress, int oldValue, int newValue) { var delta = newValue - oldValue; return progress diff --git a/src/Avalonia.Animation/Transitions/PointTransition.cs b/src/Avalonia.Animation/Transitions/PointTransition.cs new file mode 100644 index 0000000000..f29ae1b9eb --- /dev/null +++ b/src/Avalonia.Animation/Transitions/PointTransition.cs @@ -0,0 +1,31 @@ +// 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 Avalonia.Metadata; +using System; +using System.Reactive.Linq; + +namespace Avalonia.Animation +{ + /// + /// Transition class that handles with types. + /// + public class PointTransition : Transition + { + /// + public override IObservable DoTransition(IObservable progress, Point oldValue, Point newValue) + { + var deltaX = newValue.X - oldValue.Y; + var deltaY = newValue.X - oldValue.Y; + + return progress + .Select(p => + { + var f = Easing.Ease(p); + var nX = f * deltaX + oldValue.X; + var nY = f * deltaY + oldValue.Y; + return new Point(nX, nY); + }); + } + } +} diff --git a/src/Avalonia.Animation/Transitions/ThicknessTransition.cs b/src/Avalonia.Animation/Transitions/ThicknessTransition.cs new file mode 100644 index 0000000000..4f31d3eb48 --- /dev/null +++ b/src/Avalonia.Animation/Transitions/ThicknessTransition.cs @@ -0,0 +1,35 @@ +// 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 Avalonia.Metadata; +using System; +using System.Reactive.Linq; + +namespace Avalonia.Animation +{ + /// + /// Transition class that handles with types. + /// + public class ThicknessTransition : Transition + { + /// + public override IObservable DoTransition(IObservable progress, Thickness oldValue, Thickness newValue) + { + var deltaL = newValue.Left - oldValue.Left; + var deltaT = newValue.Top - oldValue.Top; + var deltaR = newValue.Right - oldValue.Right; + var deltaB = newValue.Bottom - oldValue.Bottom; + + return progress + .Select(p => + { + var f = Easing.Ease(p); + var nL = f * deltaL + oldValue.Left; + var nT = f * deltaT + oldValue.Right; + var nR = f * deltaR + oldValue.Top; + var nB = f * deltaB + oldValue.Bottom; + return new Thickness(nL, nT, nR, nB); + }); + } + } +} diff --git a/src/Avalonia.Styling/Avalonia.Styling.csproj b/src/Avalonia.Styling/Avalonia.Styling.csproj index b8083b52db..81b2bfd88b 100644 --- a/src/Avalonia.Styling/Avalonia.Styling.csproj +++ b/src/Avalonia.Styling/Avalonia.Styling.csproj @@ -32,7 +32,6 @@ - diff --git a/src/Avalonia.Animation/Animatable.cs b/src/Avalonia.Visuals/Animation/Animatable.cs similarity index 83% rename from src/Avalonia.Animation/Animatable.cs rename to src/Avalonia.Visuals/Animation/Animatable.cs index 0cc7cc416c..5b1f68a7a9 100644 --- a/src/Avalonia.Animation/Animatable.cs +++ b/src/Avalonia.Visuals/Animation/Animatable.cs @@ -5,6 +5,7 @@ using System.Linq; using Avalonia.Data; using System; using System.Reactive.Linq; +using Avalonia.Collections; namespace Avalonia.Animation { @@ -16,13 +17,13 @@ namespace Avalonia.Animation /// /// /// - public static readonly StyledProperty TransitionsProperty = - AvaloniaProperty.Register(nameof(Transitions)); + public static readonly StyledProperty> TransitionsProperty = + AvaloniaProperty.Register>(nameof(Transitions)); /// /// Gets or sets the property transitions for the control. /// - public Transitions Transitions + public AvaloniaList Transitions { get { return GetValue(TransitionsProperty); } set { SetValue(TransitionsProperty, value); } diff --git a/src/Avalonia.Animation/Animation.cs b/src/Avalonia.Visuals/Animation/Animation.cs similarity index 100% rename from src/Avalonia.Animation/Animation.cs rename to src/Avalonia.Visuals/Animation/Animation.cs diff --git a/src/Avalonia.Animation/IEasing.cs b/src/Avalonia.Visuals/Animation/IEasing.cs similarity index 100% rename from src/Avalonia.Animation/IEasing.cs rename to src/Avalonia.Visuals/Animation/IEasing.cs diff --git a/src/Avalonia.Animation/Timing.cs b/src/Avalonia.Visuals/Animation/Timing.cs similarity index 100% rename from src/Avalonia.Animation/Timing.cs rename to src/Avalonia.Visuals/Animation/Timing.cs diff --git a/src/Avalonia.Visuals/Avalonia.Visuals.csproj b/src/Avalonia.Visuals/Avalonia.Visuals.csproj index fe18b0e446..d57e649abe 100644 --- a/src/Avalonia.Visuals/Avalonia.Visuals.csproj +++ b/src/Avalonia.Visuals/Avalonia.Visuals.csproj @@ -27,7 +27,6 @@ true - diff --git a/src/Avalonia.Animation/Transitions/ITransition.cs b/src/Avalonia.Visuals/ITransition.cs similarity index 92% rename from src/Avalonia.Animation/Transitions/ITransition.cs rename to src/Avalonia.Visuals/ITransition.cs index 124a770c8d..f5dd91739a 100644 --- a/src/Avalonia.Animation/Transitions/ITransition.cs +++ b/src/Avalonia.Visuals/ITransition.cs @@ -8,7 +8,7 @@ using System.Reactive.Linq; namespace Avalonia.Animation { /// - /// Interface for objects. + /// Interface for Property Transition objects. /// public interface ITransition { diff --git a/src/Avalonia.Animation/Transitions/Transitions.cs b/src/Avalonia.Visuals/Transitions.cs similarity index 89% rename from src/Avalonia.Animation/Transitions/Transitions.cs rename to src/Avalonia.Visuals/Transitions.cs index 8497d762d2..7e742fc0b4 100644 --- a/src/Avalonia.Animation/Transitions/Transitions.cs +++ b/src/Avalonia.Visuals/Transitions.cs @@ -6,7 +6,7 @@ using Avalonia.Collections; namespace Avalonia.Animation { /// - /// A collection of definitions. + /// A collection of definitions. /// public class Transitions : AvaloniaList { From 3236223e0946337476d3141b79f2ad0699114cbc Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Sat, 24 Mar 2018 13:15:08 +0100 Subject: [PATCH 009/149] Made visuals depend on animations. Instead of `Avalonia.Animations` depending on `Avalonia.Visuals`, make it the other way round. --- .../Animation => Avalonia.Animation}/Animatable.cs | 0 .../Animation => Avalonia.Animation}/Animation.cs | 0 src/Avalonia.Animation/Avalonia.Animation.csproj | 2 -- .../Animation => Avalonia.Animation}/IEasing.cs | 0 src/{Avalonia.Visuals => Avalonia.Animation}/ITransition.cs | 0 .../Animation => Avalonia.Animation}/Timing.cs | 0 src/{Avalonia.Visuals => Avalonia.Animation}/Transitions.cs | 0 .../Animation}/PointTransition.cs | 0 .../Animation}/ThicknessTransition.cs | 0 src/Avalonia.Visuals/Avalonia.Visuals.csproj | 6 +++--- 10 files changed, 3 insertions(+), 5 deletions(-) rename src/{Avalonia.Visuals/Animation => Avalonia.Animation}/Animatable.cs (100%) rename src/{Avalonia.Visuals/Animation => Avalonia.Animation}/Animation.cs (100%) rename src/{Avalonia.Visuals/Animation => Avalonia.Animation}/IEasing.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Animation}/ITransition.cs (100%) rename src/{Avalonia.Visuals/Animation => Avalonia.Animation}/Timing.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Animation}/Transitions.cs (100%) rename src/{Avalonia.Animation/Transitions => Avalonia.Visuals/Animation}/PointTransition.cs (100%) rename src/{Avalonia.Animation/Transitions => Avalonia.Visuals/Animation}/ThicknessTransition.cs (100%) diff --git a/src/Avalonia.Visuals/Animation/Animatable.cs b/src/Avalonia.Animation/Animatable.cs similarity index 100% rename from src/Avalonia.Visuals/Animation/Animatable.cs rename to src/Avalonia.Animation/Animatable.cs diff --git a/src/Avalonia.Visuals/Animation/Animation.cs b/src/Avalonia.Animation/Animation.cs similarity index 100% rename from src/Avalonia.Visuals/Animation/Animation.cs rename to src/Avalonia.Animation/Animation.cs diff --git a/src/Avalonia.Animation/Avalonia.Animation.csproj b/src/Avalonia.Animation/Avalonia.Animation.csproj index cbf5914ea9..2101c5669d 100644 --- a/src/Avalonia.Animation/Avalonia.Animation.csproj +++ b/src/Avalonia.Animation/Avalonia.Animation.csproj @@ -32,8 +32,6 @@ - - \ No newline at end of file diff --git a/src/Avalonia.Visuals/Animation/IEasing.cs b/src/Avalonia.Animation/IEasing.cs similarity index 100% rename from src/Avalonia.Visuals/Animation/IEasing.cs rename to src/Avalonia.Animation/IEasing.cs diff --git a/src/Avalonia.Visuals/ITransition.cs b/src/Avalonia.Animation/ITransition.cs similarity index 100% rename from src/Avalonia.Visuals/ITransition.cs rename to src/Avalonia.Animation/ITransition.cs diff --git a/src/Avalonia.Visuals/Animation/Timing.cs b/src/Avalonia.Animation/Timing.cs similarity index 100% rename from src/Avalonia.Visuals/Animation/Timing.cs rename to src/Avalonia.Animation/Timing.cs diff --git a/src/Avalonia.Visuals/Transitions.cs b/src/Avalonia.Animation/Transitions.cs similarity index 100% rename from src/Avalonia.Visuals/Transitions.cs rename to src/Avalonia.Animation/Transitions.cs diff --git a/src/Avalonia.Animation/Transitions/PointTransition.cs b/src/Avalonia.Visuals/Animation/PointTransition.cs similarity index 100% rename from src/Avalonia.Animation/Transitions/PointTransition.cs rename to src/Avalonia.Visuals/Animation/PointTransition.cs diff --git a/src/Avalonia.Animation/Transitions/ThicknessTransition.cs b/src/Avalonia.Visuals/Animation/ThicknessTransition.cs similarity index 100% rename from src/Avalonia.Animation/Transitions/ThicknessTransition.cs rename to src/Avalonia.Visuals/Animation/ThicknessTransition.cs diff --git a/src/Avalonia.Visuals/Avalonia.Visuals.csproj b/src/Avalonia.Visuals/Avalonia.Visuals.csproj index d57e649abe..58fd9a5320 100644 --- a/src/Avalonia.Visuals/Avalonia.Visuals.csproj +++ b/src/Avalonia.Visuals/Avalonia.Visuals.csproj @@ -26,13 +26,13 @@ CS1591 true - - - Properties\SharedAssemblyInfo.cs + + + \ No newline at end of file From dd657e203d575e3f05ac5ce873799b4f6a0847ee Mon Sep 17 00:00:00 2001 From: Jumar Macato Date: Mon, 26 Mar 2018 21:49:01 +0800 Subject: [PATCH 010/149] *Implement Basic Keyframe Animations support. *Implement DoubleKeyFrames for Properties such as Opacity, etc. *Implement TransformKeyFrames with initial implementation of specialized logic for selecting RenderTransform of the target control properly. *Ported RenderTest to .NET Core for testing and to remove the crufty old .csproj format. *Replaced AnimationsPage with some samples of hover-activated animated red rectangles. --- samples/RenderTest/MainWindow.xaml | 3 +- samples/RenderTest/Pages/AnimationsPage.xaml | 70 +++++++ .../RenderTest/Pages/AnimationsPage.xaml.cs | 2 +- samples/RenderTest/RenderTest.csproj | 175 +++--------------- samples/RenderTest/SideBar.xaml | 2 +- .../ViewModels/MainWindowViewModel.cs | 2 +- src/Avalonia.Animation/Animatable.cs | 7 +- src/Avalonia.Animation/Animation.cs | 65 ++++--- src/Avalonia.Animation/Easing.cs | 2 +- src/Avalonia.Animation/Easing/BackEaseIn.cs | 2 +- src/Avalonia.Animation/Easing/BackEaseOut.cs | 2 +- src/Avalonia.Animation/Easing/BounceEaseIn.cs | 6 +- .../Easing/BounceEaseInOut.cs | 8 +- .../Easing/BounceEaseOut.cs | 6 +- .../Easing/CircularEaseIn.cs | 2 +- .../Easing/CircularEaseInOut.cs | 2 +- .../Easing/CircularEaseOut.cs | 2 +- src/Avalonia.Animation/Easing/CubicEaseIn.cs | 2 +- .../Easing/CubicEaseInOut.cs | 2 +- src/Avalonia.Animation/Easing/CubicEaseOut.cs | 2 +- .../Easing/ElasticEaseIn.cs | 5 +- .../Easing/ElasticEaseInOut.cs | 7 +- .../Easing/ElasticEaseOut.cs | 5 +- .../Easing/ExponentialEaseIn.cs | 2 +- .../Easing/ExponentialEaseInOut.cs | 2 +- .../Easing/ExponentialEaseOut.cs | 2 +- src/Avalonia.Animation/Easing/LinearEasing.cs | 2 +- .../Easing/QuadraticEaseIn.cs | 2 +- .../Easing/QuadraticEaseInOut.cs | 2 +- .../Easing/QuadraticEaseOut.cs | 2 +- .../Easing/QuarticEaseIn.cs | 2 +- .../Easing/QuarticEaseInOut.cs | 2 +- .../Easing/QuarticEaseOut.cs | 2 +- .../Easing/QuinticEaseIn.cs | 2 +- .../Easing/QuinticEaseInOut.cs | 2 +- .../Easing/QuinticEaseOut.cs | 2 +- src/Avalonia.Animation/Easing/SineEaseIn.cs | 5 +- .../Easing/SineEaseInOut.cs | 2 +- src/Avalonia.Animation/Easing/SineEaseOut.cs | 5 +- src/Avalonia.Animation/IAnimation.cs | 17 ++ src/Avalonia.Animation/IEasing.cs | 2 +- src/Avalonia.Animation/Keyframes/Cue.cs | 88 +++++++++ .../Keyframes/DoubleKeyFrames.cs | 87 +++++++++ .../Keyframes/IKeyFrames.cs | 17 ++ src/Avalonia.Animation/Keyframes/KeyFrame.cs | 80 ++++++++ src/Avalonia.Animation/Keyframes/KeyFrames.cs | 122 ++++++++++++ .../Properties/AssemblyInfo.cs | 7 +- src/Avalonia.Animation/Timing.cs | 21 +++ .../Transitions/DoubleTransition.cs | 2 +- .../Transitions/FloatTransition.cs | 2 +- .../{ => Transitions}/ITransition.cs | 4 +- .../Transitions/IntegerTransition.cs | 2 +- .../{ => Transitions}/Transition.cs | 7 +- .../{ => Transitions}/Transitions.cs | 2 +- .../BounceEaseUtils.cs} | 4 +- .../DoubleHelper.cs => Utils/DoubleUtils.cs} | 4 +- .../EasingUtils.cs} | 4 +- src/Avalonia.Styling/Avalonia.Styling.csproj | 1 + src/Avalonia.Styling/Styling/Style.cs | 21 ++- .../Animation/Keyframes/TransformKeyFrames.cs | 77 ++++++++ .../{ => Transitions}/PointTransition.cs | 4 +- .../{ => Transitions}/ThicknessTransition.cs | 4 +- .../Properties/AssemblyInfo.cs | 2 + .../Converters/EasingTypeConverter.cs | 2 +- .../AvaloniaDefaultTypeConverters.cs | 1 + 65 files changed, 756 insertions(+), 247 deletions(-) create mode 100644 src/Avalonia.Animation/IAnimation.cs create mode 100644 src/Avalonia.Animation/Keyframes/Cue.cs create mode 100644 src/Avalonia.Animation/Keyframes/DoubleKeyFrames.cs create mode 100644 src/Avalonia.Animation/Keyframes/IKeyFrames.cs create mode 100644 src/Avalonia.Animation/Keyframes/KeyFrame.cs create mode 100644 src/Avalonia.Animation/Keyframes/KeyFrames.cs rename src/Avalonia.Animation/{ => Transitions}/ITransition.cs (89%) rename src/Avalonia.Animation/{ => Transitions}/Transition.cs (93%) rename src/Avalonia.Animation/{ => Transitions}/Transitions.cs (93%) rename src/Avalonia.Animation/{Helpers/BounceEaseHelper.cs => Utils/BounceEaseUtils.cs} (94%) rename src/Avalonia.Animation/{Helpers/DoubleHelper.cs => Utils/DoubleUtils.cs} (86%) rename src/Avalonia.Animation/{Helpers/EasingConstants.cs => Utils/EasingUtils.cs} (85%) create mode 100644 src/Avalonia.Visuals/Animation/Keyframes/TransformKeyFrames.cs rename src/Avalonia.Visuals/Animation/{ => Transitions}/PointTransition.cs (92%) rename src/Avalonia.Visuals/Animation/{ => Transitions}/ThicknessTransition.cs (93%) diff --git a/samples/RenderTest/MainWindow.xaml b/samples/RenderTest/MainWindow.xaml index f63dec230e..51da5f342a 100644 --- a/samples/RenderTest/MainWindow.xaml +++ b/samples/RenderTest/MainWindow.xaml @@ -1,6 +1,7 @@ + xmlns:pages="clr-namespace:RenderTest.Pages;assembly=RenderTest" MinWidth="400" MinHeight="200" + > diff --git a/samples/RenderTest/Pages/AnimationsPage.xaml b/samples/RenderTest/Pages/AnimationsPage.xaml index 6eb7067245..5cd4e6e431 100644 --- a/samples/RenderTest/Pages/AnimationsPage.xaml +++ b/samples/RenderTest/Pages/AnimationsPage.xaml @@ -1,2 +1,72 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/samples/RenderTest/Pages/AnimationsPage.xaml.cs b/samples/RenderTest/Pages/AnimationsPage.xaml.cs index d2e1750ba7..925ba9260c 100644 --- a/samples/RenderTest/Pages/AnimationsPage.xaml.cs +++ b/samples/RenderTest/Pages/AnimationsPage.xaml.cs @@ -15,7 +15,7 @@ namespace RenderTest.Pages public AnimationsPage() { this.InitializeComponent(); - this.CreateAnimations(); + // this.CreateAnimations(); } private void InitializeComponent() diff --git a/samples/RenderTest/RenderTest.csproj b/samples/RenderTest/RenderTest.csproj index b33d5d3c70..855445812a 100644 --- a/samples/RenderTest/RenderTest.csproj +++ b/samples/RenderTest/RenderTest.csproj @@ -1,18 +1,10 @@ - - - + - Debug - AnyCPU - {F1FDC5B0-4654-416F-AE69-E3E9BBD87801} - WinExe - Properties + Exe + netcoreapp2.0 + false RenderTest RenderTest - v4.7 - 512 - true - AnyCPU @@ -33,152 +25,33 @@ prompt 4 - - - - - - - - - - - - - - - - App.xaml - - - DrawingPage.xaml - - - ClippingPage.xaml - - - AnimationsPage.xaml - - - - - MainWindow.xaml + + %(Filename) - - - - - - - - Designer - - - - - {d211e587-d8bc-45b9-95a4-f297c8fa5200} - Avalonia.Animation - - - {b09b78d8-9b26-48b0-9149-d64a2f120f3f} - Avalonia.Base - - - {d2221c82-4a25-4583-9b43-d791e3f6820c} - Avalonia.Controls - - - {799a7bb5-3c2c-48b6-85a7-406a12c420da} - Avalonia.DesignerSupport - - - {7062ae20-5dcc-4442-9645-8195bdece63e} - Avalonia.Diagnostics - - - {4a1abb09-9047-4bd5-a4ad-a055e52c5ee0} - Avalonia.DotNetFrameworkRuntime - - - {62024b2d-53eb-4638-b26b-85eeaa54866e} - Avalonia.Input - - - {6b0ed19d-a08b-461c-a9d9-a9ee40b0c06b} - Avalonia.Interactivity - - - {42472427-4774-4c81-8aff-9f27b8e31721} - Avalonia.Layout - - - {b61b66a3-b82d-4875-8001-89d3394fe0c9} - Avalonia.Logging.Serilog - - - {6417b24e-49c2-4985-8db2-3ab9d898ec91} - Avalonia.ReactiveUI - - - {eb582467-6abb-43a1-b052-e981ba910e3a} - Avalonia.Visuals - - - {f1baa01a-f176-4c6a-b39d-5b40bb1b148f} - Avalonia.Styling - - - {3e10a5fa-e8da-48b1-ad44-6a5b6cb7750f} - Avalonia.Themes.Default - - - {3e53a01a-b331-47f3-b828-4a5717e77a24} - Avalonia.Markup.Xaml - - - {6417e941-21bc-467b-a771-0de389353ce6} - Avalonia.Markup - - - {7d2d3083-71dd-4cc9-8907-39a0d86fb322} - Avalonia.Skia - - - {3e908f67-5543-4879-a1dc-08eace79b3cd} - Avalonia.Direct2D1 - - - {811a76cf-1cf6-440f-963b-bbe31bd72a82} - Avalonia.Win32 - - - - - Designer - - - - - Designer - - - - - Designer - - - - + Designer - - Designer - + + + + + + + + + + + + + + + + + - diff --git a/samples/RenderTest/SideBar.xaml b/samples/RenderTest/SideBar.xaml index 7a0b515748..29e5d854f9 100644 --- a/samples/RenderTest/SideBar.xaml +++ b/samples/RenderTest/SideBar.xaml @@ -37,7 +37,7 @@ - + diff --git a/samples/RenderTest/ViewModels/MainWindowViewModel.cs b/samples/RenderTest/ViewModels/MainWindowViewModel.cs index b2fa2e8b7a..02a2abeb89 100644 --- a/samples/RenderTest/ViewModels/MainWindowViewModel.cs +++ b/samples/RenderTest/ViewModels/MainWindowViewModel.cs @@ -5,7 +5,7 @@ namespace RenderTest.ViewModels { public class MainWindowViewModel : ReactiveObject { - private bool drawDirtyRects = true; + private bool drawDirtyRects = false; private bool drawFps = true; public MainWindowViewModel() diff --git a/src/Avalonia.Animation/Animatable.cs b/src/Avalonia.Animation/Animatable.cs index 5b1f68a7a9..c4ee1d72e5 100644 --- a/src/Avalonia.Animation/Animatable.cs +++ b/src/Avalonia.Animation/Animatable.cs @@ -6,6 +6,7 @@ using Avalonia.Data; using System; using System.Reactive.Linq; using Avalonia.Collections; +using Avalonia.Animation.Transitions; namespace Avalonia.Animation { @@ -17,13 +18,13 @@ namespace Avalonia.Animation /// /// /// - public static readonly StyledProperty> TransitionsProperty = - AvaloniaProperty.Register>(nameof(Transitions)); + public static readonly StyledProperty TransitionsProperty = + AvaloniaProperty.Register(nameof(Transitions)); /// /// Gets or sets the property transitions for the control. /// - public AvaloniaList Transitions + public Transitions.Transitions Transitions { get { return GetValue(TransitionsProperty); } set { SetValue(TransitionsProperty, value); } diff --git a/src/Avalonia.Animation/Animation.cs b/src/Avalonia.Animation/Animation.cs index 41e4e2fee1..bd83020185 100644 --- a/src/Avalonia.Animation/Animation.cs +++ b/src/Avalonia.Animation/Animation.cs @@ -1,55 +1,72 @@ // 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 Avalonia.Animation.Easings; +using Avalonia.Animation.Keyframes; +using Avalonia.Collections; +using Avalonia.Metadata; using System; +using System.Collections.Generic; namespace Avalonia.Animation { /// /// Tracks the progress of an animation. /// - public class Animation : IObservable, IDisposable + public class Animation : IDisposable, IAnimation { + private List_subscription = new List(); + /// - /// The animation being tracked. + /// Run time of this animation. /// - private readonly IObservable _inner; + public TimeSpan Duration { get; set; } /// - /// The disposable used to cancel the animation. + /// Delay time for animation. /// - private readonly IDisposable _subscription; + public TimeSpan Delay { get; set; } + + /// + /// Easing function to be used. + /// + public Easing Easing { get; set; } = new LinearEasing(); /// - /// Initializes a new instance of the class. + /// A list of objects. /// - /// The animation observable being tracked. - /// A disposable used to cancel the animation. - public Animation(IObservable inner, IDisposable subscription) - { - _inner = inner; - _subscription = subscription; - } + [Content] + public AvaloniaList Children { get; set; } = new AvaloniaList(); /// /// Cancels the animation. /// public void Dispose() { - _subscription.Dispose(); + foreach(var sub in _subscription) sub.Dispose(); } - /// - /// Notifies the provider that an observer is to receive notifications. - /// - /// The observer. - /// - /// A reference to an interface that allows observers to stop receiving notifications - /// before the provider has finished sending them. - /// - public IDisposable Subscribe(IObserver observer) + /// + public IDisposable Apply(Animatable control, IObservable matchObs) { - return _inner.Subscribe(observer); + foreach (IKeyFrames keyframes in Children) + { + _subscription.Add(keyframes.Apply(this, control, matchObs)); + } + return this; } + + ///// + ///// Notifies the provider that an observer is to receive notifications. + ///// + ///// The observer. + ///// + ///// A reference to an interface that allows observers to stop receiving notifications + ///// before the provider has finished sending them. + ///// + //public IDisposable Subscribe(IObserver observer) + //{ + // return _inner.Subscribe(observer); + //} } } diff --git a/src/Avalonia.Animation/Easing.cs b/src/Avalonia.Animation/Easing.cs index 75b98e8a3a..97c10f37ea 100644 --- a/src/Avalonia.Animation/Easing.cs +++ b/src/Avalonia.Animation/Easing.cs @@ -6,7 +6,7 @@ using System.Reflection; using System.Linq; using System.ComponentModel; -namespace Avalonia.Animation +namespace Avalonia.Animation.Easings { /// /// Base class for all Easing classes. diff --git a/src/Avalonia.Animation/Easing/BackEaseIn.cs b/src/Avalonia.Animation/Easing/BackEaseIn.cs index c846d6116d..4ea17a353a 100644 --- a/src/Avalonia.Animation/Easing/BackEaseIn.cs +++ b/src/Avalonia.Animation/Easing/BackEaseIn.cs @@ -3,7 +3,7 @@ using System; -namespace Avalonia.Animation +namespace Avalonia.Animation.Easings { /// /// Eases in a value diff --git a/src/Avalonia.Animation/Easing/BackEaseOut.cs b/src/Avalonia.Animation/Easing/BackEaseOut.cs index f671d61040..ed5628dd0b 100644 --- a/src/Avalonia.Animation/Easing/BackEaseOut.cs +++ b/src/Avalonia.Animation/Easing/BackEaseOut.cs @@ -3,7 +3,7 @@ using System; -namespace Avalonia.Animation +namespace Avalonia.Animation.Easings { /// /// Eases out a value diff --git a/src/Avalonia.Animation/Easing/BounceEaseIn.cs b/src/Avalonia.Animation/Easing/BounceEaseIn.cs index 5cf34e3f9a..6ed6f3fe82 100644 --- a/src/Avalonia.Animation/Easing/BounceEaseIn.cs +++ b/src/Avalonia.Animation/Easing/BounceEaseIn.cs @@ -2,7 +2,9 @@ // Licensed under the MIT license. See licence.md file in the project root for full license information. -namespace Avalonia.Animation +using Avalonia.Animation.Utils; + +namespace Avalonia.Animation.Easings { /// /// Eases in a value @@ -13,7 +15,7 @@ namespace Avalonia.Animation /// public override double Ease(double progress) { - return 1 - BounceEaseHelper.Bounce(1 - progress); + return 1 - BounceEaseUtils.Bounce(1 - progress); } } diff --git a/src/Avalonia.Animation/Easing/BounceEaseInOut.cs b/src/Avalonia.Animation/Easing/BounceEaseInOut.cs index 6db0424658..ed32b3df9d 100644 --- a/src/Avalonia.Animation/Easing/BounceEaseInOut.cs +++ b/src/Avalonia.Animation/Easing/BounceEaseInOut.cs @@ -1,7 +1,9 @@ // 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. -namespace Avalonia.Animation +using Avalonia.Animation.Utils; + +namespace Avalonia.Animation.Easings { /// /// Eases a value @@ -15,11 +17,11 @@ namespace Avalonia.Animation double p = progress; if (p < 0.5d) { - return 0.5f * (1 - BounceEaseHelper.Bounce(1 - (p * 2))); + return 0.5f * (1 - BounceEaseUtils.Bounce(1 - (p * 2))); } else { - return 0.5f * BounceEaseHelper.Bounce(p * 2 - 1) + 0.5f; + return 0.5f * BounceEaseUtils.Bounce(p * 2 - 1) + 0.5f; } } diff --git a/src/Avalonia.Animation/Easing/BounceEaseOut.cs b/src/Avalonia.Animation/Easing/BounceEaseOut.cs index 9cb4e9633d..555c8d9c79 100644 --- a/src/Avalonia.Animation/Easing/BounceEaseOut.cs +++ b/src/Avalonia.Animation/Easing/BounceEaseOut.cs @@ -1,8 +1,8 @@ // 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 Avalonia.Animation.Utils; - -namespace Avalonia.Animation +namespace Avalonia.Animation.Easings { /// /// Eases out a value @@ -13,7 +13,7 @@ namespace Avalonia.Animation /// public override double Ease(double progress) { - return BounceEaseHelper.Bounce(progress); + return BounceEaseUtils.Bounce(progress); } } } diff --git a/src/Avalonia.Animation/Easing/CircularEaseIn.cs b/src/Avalonia.Animation/Easing/CircularEaseIn.cs index 21b77db582..78b61226f7 100644 --- a/src/Avalonia.Animation/Easing/CircularEaseIn.cs +++ b/src/Avalonia.Animation/Easing/CircularEaseIn.cs @@ -3,7 +3,7 @@ using System; -namespace Avalonia.Animation +namespace Avalonia.Animation.Easings { /// /// Eases in a value diff --git a/src/Avalonia.Animation/Easing/CircularEaseInOut.cs b/src/Avalonia.Animation/Easing/CircularEaseInOut.cs index a5198a019f..61c1df9242 100644 --- a/src/Avalonia.Animation/Easing/CircularEaseInOut.cs +++ b/src/Avalonia.Animation/Easing/CircularEaseInOut.cs @@ -3,7 +3,7 @@ using System; -namespace Avalonia.Animation +namespace Avalonia.Animation.Easings { /// /// Eases a value diff --git a/src/Avalonia.Animation/Easing/CircularEaseOut.cs b/src/Avalonia.Animation/Easing/CircularEaseOut.cs index 33391ed816..2aa6161783 100644 --- a/src/Avalonia.Animation/Easing/CircularEaseOut.cs +++ b/src/Avalonia.Animation/Easing/CircularEaseOut.cs @@ -3,7 +3,7 @@ using System; -namespace Avalonia.Animation +namespace Avalonia.Animation.Easings { /// /// Eases out a value diff --git a/src/Avalonia.Animation/Easing/CubicEaseIn.cs b/src/Avalonia.Animation/Easing/CubicEaseIn.cs index 653164d7ab..1997493fc0 100644 --- a/src/Avalonia.Animation/Easing/CubicEaseIn.cs +++ b/src/Avalonia.Animation/Easing/CubicEaseIn.cs @@ -1,7 +1,7 @@ // 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. -namespace Avalonia.Animation +namespace Avalonia.Animation.Easings { /// /// Eases in a value diff --git a/src/Avalonia.Animation/Easing/CubicEaseInOut.cs b/src/Avalonia.Animation/Easing/CubicEaseInOut.cs index ffdce75d45..a3d788701c 100644 --- a/src/Avalonia.Animation/Easing/CubicEaseInOut.cs +++ b/src/Avalonia.Animation/Easing/CubicEaseInOut.cs @@ -1,7 +1,7 @@ // 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. -namespace Avalonia.Animation +namespace Avalonia.Animation.Easings { /// /// Eases a value diff --git a/src/Avalonia.Animation/Easing/CubicEaseOut.cs b/src/Avalonia.Animation/Easing/CubicEaseOut.cs index 6bbe756477..cfa42afeb9 100644 --- a/src/Avalonia.Animation/Easing/CubicEaseOut.cs +++ b/src/Avalonia.Animation/Easing/CubicEaseOut.cs @@ -1,7 +1,7 @@ // 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. -namespace Avalonia.Animation +namespace Avalonia.Animation.Easings { /// /// Eases out a value diff --git a/src/Avalonia.Animation/Easing/ElasticEaseIn.cs b/src/Avalonia.Animation/Easing/ElasticEaseIn.cs index b0a95d8dc0..3a44eca8da 100644 --- a/src/Avalonia.Animation/Easing/ElasticEaseIn.cs +++ b/src/Avalonia.Animation/Easing/ElasticEaseIn.cs @@ -1,9 +1,10 @@ // 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 Avalonia.Animation.Utils; using System; -namespace Avalonia.Animation +namespace Avalonia.Animation.Easings { /// /// Eases in a value @@ -15,7 +16,7 @@ namespace Avalonia.Animation public override double Ease(double progress) { double p = progress; - return Math.Sin(13d * EasingConstants.HALFPI * p) * Math.Pow(2d, 10d * (p - 1)); + return Math.Sin(13d * EasingUtils.HALFPI * p) * Math.Pow(2d, 10d * (p - 1)); } } diff --git a/src/Avalonia.Animation/Easing/ElasticEaseInOut.cs b/src/Avalonia.Animation/Easing/ElasticEaseInOut.cs index 85dcfbd618..6e443dc720 100644 --- a/src/Avalonia.Animation/Easing/ElasticEaseInOut.cs +++ b/src/Avalonia.Animation/Easing/ElasticEaseInOut.cs @@ -2,8 +2,9 @@ // Licensed under the MIT license. See licence.md file in the project root for full license information. using System; +using Avalonia.Animation.Utils; -namespace Avalonia.Animation +namespace Avalonia.Animation.Easings { /// /// Eases a value @@ -18,11 +19,11 @@ namespace Avalonia.Animation if (p < 0.5d) { - return 0.5d * Math.Sin(13d * EasingConstants.HALFPI * (2d * p)) * Math.Pow(2d, 10d * ((2d * p) - 1d)); + return 0.5d * Math.Sin(13d * EasingUtils.HALFPI * (2d * p)) * Math.Pow(2d, 10d * ((2d * p) - 1d)); } else { - return 0.5d * (Math.Sin(-13d * EasingConstants.HALFPI * ((2d * p - 1d) + 1d)) * Math.Pow(2d, -10d * (2d * p - 1d)) + 2d); + return 0.5d * (Math.Sin(-13d * EasingUtils.HALFPI * ((2d * p - 1d) + 1d)) * Math.Pow(2d, -10d * (2d * p - 1d)) + 2d); } } diff --git a/src/Avalonia.Animation/Easing/ElasticEaseOut.cs b/src/Avalonia.Animation/Easing/ElasticEaseOut.cs index 830198468d..fd8380ff39 100644 --- a/src/Avalonia.Animation/Easing/ElasticEaseOut.cs +++ b/src/Avalonia.Animation/Easing/ElasticEaseOut.cs @@ -1,9 +1,10 @@ // 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 Avalonia.Animation.Utils; using System; -namespace Avalonia.Animation +namespace Avalonia.Animation.Easings { /// /// Eases out a value @@ -15,7 +16,7 @@ namespace Avalonia.Animation public override double Ease(double progress) { double p = progress; - return Math.Sin(-13d * EasingConstants.HALFPI * (p + 1)) * Math.Pow(2d, -10d * p) + 1d; + return Math.Sin(-13d * EasingUtils.HALFPI * (p + 1)) * Math.Pow(2d, -10d * p) + 1d; } diff --git a/src/Avalonia.Animation/Easing/ExponentialEaseIn.cs b/src/Avalonia.Animation/Easing/ExponentialEaseIn.cs index 5b9154d29c..14977f6783 100644 --- a/src/Avalonia.Animation/Easing/ExponentialEaseIn.cs +++ b/src/Avalonia.Animation/Easing/ExponentialEaseIn.cs @@ -3,7 +3,7 @@ using System; -namespace Avalonia.Animation +namespace Avalonia.Animation.Easings { /// /// Eases in a value diff --git a/src/Avalonia.Animation/Easing/ExponentialEaseInOut.cs b/src/Avalonia.Animation/Easing/ExponentialEaseInOut.cs index 0eaef18464..a3ce11e3b9 100644 --- a/src/Avalonia.Animation/Easing/ExponentialEaseInOut.cs +++ b/src/Avalonia.Animation/Easing/ExponentialEaseInOut.cs @@ -3,7 +3,7 @@ using System; -namespace Avalonia.Animation +namespace Avalonia.Animation.Easings { /// /// Eases a value diff --git a/src/Avalonia.Animation/Easing/ExponentialEaseOut.cs b/src/Avalonia.Animation/Easing/ExponentialEaseOut.cs index c071d18d2e..b89f8e9fa2 100644 --- a/src/Avalonia.Animation/Easing/ExponentialEaseOut.cs +++ b/src/Avalonia.Animation/Easing/ExponentialEaseOut.cs @@ -3,7 +3,7 @@ using System; -namespace Avalonia.Animation +namespace Avalonia.Animation.Easings { /// /// Eases out a value diff --git a/src/Avalonia.Animation/Easing/LinearEasing.cs b/src/Avalonia.Animation/Easing/LinearEasing.cs index fcbf6053f4..99278cb1f7 100644 --- a/src/Avalonia.Animation/Easing/LinearEasing.cs +++ b/src/Avalonia.Animation/Easing/LinearEasing.cs @@ -1,7 +1,7 @@ // 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. -namespace Avalonia.Animation +namespace Avalonia.Animation.Easings { /// /// Linearly eases a value. diff --git a/src/Avalonia.Animation/Easing/QuadraticEaseIn.cs b/src/Avalonia.Animation/Easing/QuadraticEaseIn.cs index 641fba4892..6c29b31516 100644 --- a/src/Avalonia.Animation/Easing/QuadraticEaseIn.cs +++ b/src/Avalonia.Animation/Easing/QuadraticEaseIn.cs @@ -1,7 +1,7 @@ // 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. -namespace Avalonia.Animation +namespace Avalonia.Animation.Easings { /// /// Eases in a value diff --git a/src/Avalonia.Animation/Easing/QuadraticEaseInOut.cs b/src/Avalonia.Animation/Easing/QuadraticEaseInOut.cs index 384c3caf32..55c7e35cef 100644 --- a/src/Avalonia.Animation/Easing/QuadraticEaseInOut.cs +++ b/src/Avalonia.Animation/Easing/QuadraticEaseInOut.cs @@ -1,7 +1,7 @@ // 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. -namespace Avalonia.Animation +namespace Avalonia.Animation.Easings { /// /// Eases a value diff --git a/src/Avalonia.Animation/Easing/QuadraticEaseOut.cs b/src/Avalonia.Animation/Easing/QuadraticEaseOut.cs index 14fd877ad4..58092677f0 100644 --- a/src/Avalonia.Animation/Easing/QuadraticEaseOut.cs +++ b/src/Avalonia.Animation/Easing/QuadraticEaseOut.cs @@ -1,7 +1,7 @@ // 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. -namespace Avalonia.Animation +namespace Avalonia.Animation.Easings { /// /// Eases out a value diff --git a/src/Avalonia.Animation/Easing/QuarticEaseIn.cs b/src/Avalonia.Animation/Easing/QuarticEaseIn.cs index a5efa241be..fee78761ce 100644 --- a/src/Avalonia.Animation/Easing/QuarticEaseIn.cs +++ b/src/Avalonia.Animation/Easing/QuarticEaseIn.cs @@ -1,7 +1,7 @@ // 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. -namespace Avalonia.Animation +namespace Avalonia.Animation.Easings { /// /// Eases in a value diff --git a/src/Avalonia.Animation/Easing/QuarticEaseInOut.cs b/src/Avalonia.Animation/Easing/QuarticEaseInOut.cs index d5cd2622cb..227f0a89e7 100644 --- a/src/Avalonia.Animation/Easing/QuarticEaseInOut.cs +++ b/src/Avalonia.Animation/Easing/QuarticEaseInOut.cs @@ -1,7 +1,7 @@ // 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. -namespace Avalonia.Animation +namespace Avalonia.Animation.Easings { /// /// Eases a value diff --git a/src/Avalonia.Animation/Easing/QuarticEaseOut.cs b/src/Avalonia.Animation/Easing/QuarticEaseOut.cs index 5ecb3a7a0b..2a1e2ea92b 100644 --- a/src/Avalonia.Animation/Easing/QuarticEaseOut.cs +++ b/src/Avalonia.Animation/Easing/QuarticEaseOut.cs @@ -1,7 +1,7 @@ // 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. -namespace Avalonia.Animation +namespace Avalonia.Animation.Easings { /// /// Eases out a value diff --git a/src/Avalonia.Animation/Easing/QuinticEaseIn.cs b/src/Avalonia.Animation/Easing/QuinticEaseIn.cs index 8dc6ad6bc8..79cb607fd4 100644 --- a/src/Avalonia.Animation/Easing/QuinticEaseIn.cs +++ b/src/Avalonia.Animation/Easing/QuinticEaseIn.cs @@ -1,7 +1,7 @@ // 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. -namespace Avalonia.Animation +namespace Avalonia.Animation.Easings { /// /// Eases in a value diff --git a/src/Avalonia.Animation/Easing/QuinticEaseInOut.cs b/src/Avalonia.Animation/Easing/QuinticEaseInOut.cs index 87f3c38dad..d51f7cfefe 100644 --- a/src/Avalonia.Animation/Easing/QuinticEaseInOut.cs +++ b/src/Avalonia.Animation/Easing/QuinticEaseInOut.cs @@ -1,7 +1,7 @@ // 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. -namespace Avalonia.Animation +namespace Avalonia.Animation.Easings { /// /// Eases a value diff --git a/src/Avalonia.Animation/Easing/QuinticEaseOut.cs b/src/Avalonia.Animation/Easing/QuinticEaseOut.cs index f4054c8da2..63d45f3290 100644 --- a/src/Avalonia.Animation/Easing/QuinticEaseOut.cs +++ b/src/Avalonia.Animation/Easing/QuinticEaseOut.cs @@ -1,7 +1,7 @@ // 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. -namespace Avalonia.Animation +namespace Avalonia.Animation.Easings { /// /// Eases out a value diff --git a/src/Avalonia.Animation/Easing/SineEaseIn.cs b/src/Avalonia.Animation/Easing/SineEaseIn.cs index 49c633ec5c..31a8f0ca94 100644 --- a/src/Avalonia.Animation/Easing/SineEaseIn.cs +++ b/src/Avalonia.Animation/Easing/SineEaseIn.cs @@ -1,9 +1,10 @@ // 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 Avalonia.Animation.Utils; using System; -namespace Avalonia.Animation +namespace Avalonia.Animation.Easings { /// /// Eases in a value @@ -14,7 +15,7 @@ namespace Avalonia.Animation /// public override double Ease(double progress) { - return Math.Sin((progress - 1) * EasingConstants.HALFPI) + 1; + return Math.Sin((progress - 1) * EasingUtils.HALFPI) + 1; } } } diff --git a/src/Avalonia.Animation/Easing/SineEaseInOut.cs b/src/Avalonia.Animation/Easing/SineEaseInOut.cs index 94b528c728..51052575e2 100644 --- a/src/Avalonia.Animation/Easing/SineEaseInOut.cs +++ b/src/Avalonia.Animation/Easing/SineEaseInOut.cs @@ -3,7 +3,7 @@ using System; -namespace Avalonia.Animation +namespace Avalonia.Animation.Easings { /// /// Eases a value diff --git a/src/Avalonia.Animation/Easing/SineEaseOut.cs b/src/Avalonia.Animation/Easing/SineEaseOut.cs index 7d0658c389..ed5a998e30 100644 --- a/src/Avalonia.Animation/Easing/SineEaseOut.cs +++ b/src/Avalonia.Animation/Easing/SineEaseOut.cs @@ -1,9 +1,10 @@ // 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 Avalonia.Animation.Utils; using System; -namespace Avalonia.Animation +namespace Avalonia.Animation.Easings { /// /// Eases out a value @@ -15,7 +16,7 @@ namespace Avalonia.Animation /// public override double Ease(double progress) { - return Math.Sin(progress * EasingConstants.HALFPI); + return Math.Sin(progress * EasingUtils.HALFPI); } } } diff --git a/src/Avalonia.Animation/IAnimation.cs b/src/Avalonia.Animation/IAnimation.cs new file mode 100644 index 0000000000..4de7e46af5 --- /dev/null +++ b/src/Avalonia.Animation/IAnimation.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Avalonia.Animation +{ + /// + /// Interface for Animation objects + /// + public interface IAnimation + { + /// + /// Apply the animation to the specified control + /// + IDisposable Apply(Animatable control, IObservable match); + } +} diff --git a/src/Avalonia.Animation/IEasing.cs b/src/Avalonia.Animation/IEasing.cs index fc1f2efc18..3c954a8cab 100644 --- a/src/Avalonia.Animation/IEasing.cs +++ b/src/Avalonia.Animation/IEasing.cs @@ -1,7 +1,7 @@ // 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. -namespace Avalonia.Animation +namespace Avalonia.Animation.Easings { /// /// Defines the interface for easing classes. diff --git a/src/Avalonia.Animation/Keyframes/Cue.cs b/src/Avalonia.Animation/Keyframes/Cue.cs new file mode 100644 index 0000000000..3f120ef604 --- /dev/null +++ b/src/Avalonia.Animation/Keyframes/Cue.cs @@ -0,0 +1,88 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Globalization; +using System.Text; + +namespace Avalonia.Animation.Keyframes +{ + /// + /// A Cue object for . + /// + [TypeConverter(typeof(CueTypeConverter))] + public struct Cue : IEquatable, IEquatable + { + /// + /// The normalized percent value, ranging from 0.0 to 1.0 + /// + public double CueValue { get; } + + /// + /// Sets a new object. + /// + /// + public Cue(double value) + { + if (value <= 1 && value >= 0) + CueValue = value; + else + throw new ArgumentException($"This cue object's value should be within or equal to 0.0 and 1.0"); + } + + /// + /// Parses a string to a object. + /// + public static object Parse(string value, CultureInfo culture) + { + string v = value; + + if (value.EndsWith("%")) + { + v = v.TrimEnd('%'); + } + + if (double.TryParse(v, NumberStyles.Float, culture, out double res)) + { + return new Cue(res / 100d); + } + else + { + throw new FormatException($"Invalid Cue string \"{value}\""); + } + } + + /// + /// Checks for equality between two s. + /// + /// The second cue. + public bool Equals(Cue other) + { + return CueValue == other.CueValue; + } + + /// + /// Checks for equality between a + /// and a value. + /// + /// + /// + public bool Equals(double other) + { + return CueValue == other; + } + } + + public class CueTypeConverter : TypeConverter + { + public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType) + { + return sourceType == typeof(string); + } + + public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value) + { + return Cue.Parse((string)value, culture); + } + } + +} diff --git a/src/Avalonia.Animation/Keyframes/DoubleKeyFrames.cs b/src/Avalonia.Animation/Keyframes/DoubleKeyFrames.cs new file mode 100644 index 0000000000..00558430e0 --- /dev/null +++ b/src/Avalonia.Animation/Keyframes/DoubleKeyFrames.cs @@ -0,0 +1,87 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Linq; +using System.Reactive.Linq; +using System.Diagnostics; +using Avalonia.Animation.Utils; +using Avalonia.Data; + +namespace Avalonia.Animation.Keyframes +{ + /// + /// Key frames that handles properties. + /// + public class DoubleKeyFrames : KeyFrames + { + /// + public override IDisposable DoInterpolation(Animation animation, Animatable control, Dictionary sortedkeyValues) + { + var timer = Timing.GetTimer(animation.Duration, animation.Delay); + + + var interp = timer.Select(p => + { + // Handle the errors rather naively, for now. + try + { + var x = animation.Easing.Ease(p); + + // Get a pair of keyframes to make the interpolation. + KeyValuePair firstCue, lastCue; + + firstCue = sortedkeyValues.First(); + lastCue = sortedkeyValues.Last(); + + // This should be changed later for a much more efficient one + if (sortedkeyValues.Count() > 2) + { + bool isWithinRange_Start = DoubleUtils.AboutEqual(x, 0.0) || x > 0.0; + bool isWithinRange_End = DoubleUtils.AboutEqual(x, 1.0) || x < 1.0; + + if (isWithinRange_Start && isWithinRange_End) + { + + firstCue = sortedkeyValues.Where(j => j.Key <= x).Last(); + lastCue = sortedkeyValues.Where(j=> j.Key >= firstCue.Key).First(); + } + else if (!isWithinRange_Start) + { + firstCue = sortedkeyValues.First(); + lastCue = sortedkeyValues.Skip(1).First(); + } + else if (!isWithinRange_End) + { + firstCue = sortedkeyValues.Skip(sortedkeyValues.Count() - 1).First(); + lastCue = sortedkeyValues.Last(); + } + else + { + throw new InvalidOperationException + ($"Can't find KeyFrames within the specified Easing time {x}"); + } + } + + // Piecewise Linear interpolation, courtesy of wikipedia + var y0 = firstCue.Value; + var x0 = firstCue.Key; + var y1 = lastCue.Value; + var x1 = lastCue.Key; + var y = ((y0 * (x1 - x)) + (y1 * (x - x0))) / x1 - x0; + + return y; + } + catch (Exception e) + { + Debug.WriteLine(e); + return 1; + } + }); + + + return control.Bind(Property, interp.Select(p => (object)p), BindingPriority.Animation); + } + + + } +} diff --git a/src/Avalonia.Animation/Keyframes/IKeyFrames.cs b/src/Avalonia.Animation/Keyframes/IKeyFrames.cs new file mode 100644 index 0000000000..6c49063615 --- /dev/null +++ b/src/Avalonia.Animation/Keyframes/IKeyFrames.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Avalonia.Animation.Keyframes +{ + /// + /// Interface for Keyframe group object + /// + public interface IKeyFrames + { + /// + /// Applies the current KeyFrame group to the specified control. + /// + IDisposable Apply(Animation animation, Animatable control, IObservable obsMatch); + } +} diff --git a/src/Avalonia.Animation/Keyframes/KeyFrame.cs b/src/Avalonia.Animation/Keyframes/KeyFrame.cs new file mode 100644 index 0000000000..982997c5e3 --- /dev/null +++ b/src/Avalonia.Animation/Keyframes/KeyFrame.cs @@ -0,0 +1,80 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.ComponentModel; + +namespace Avalonia.Animation.Keyframes +{ + + /// + /// Stores data regarding a specific key + /// point and value in an animation. + /// + public class KeyFrame + { + internal bool timeSpanSet, cueSet; + + private TimeSpan _ktimeSpan; + private Cue _kCue; + + /// + /// Gets or sets the key time of this . + /// + /// The key time. + public TimeSpan KeyTime + { + get + { + + return _ktimeSpan; + } + set + { + if (cueSet) + { + throw new InvalidOperationException($"You can only set either {nameof(KeyTime)} or {nameof(Cue)}."); + } + timeSpanSet = true; + _ktimeSpan = value; + } + } + + /// + /// Gets or sets the cue of this . + /// + /// The cue. + public Cue Cue + { + get + { + + return _kCue; + } + set + { + if (timeSpanSet) + { + throw new InvalidOperationException($"You can only set either {nameof(KeyTime)} or {nameof(Cue)}."); + } + cueSet = true; + _kCue = value; + } + } + + + public object Value { get; set; } + + + ///// + ///// Initializes a new instance of the class. + ///// + //public KeyFrame() + //{ + + //} + + } + + + +} diff --git a/src/Avalonia.Animation/Keyframes/KeyFrames.cs b/src/Avalonia.Animation/Keyframes/KeyFrames.cs new file mode 100644 index 0000000000..923e6fcbc5 --- /dev/null +++ b/src/Avalonia.Animation/Keyframes/KeyFrames.cs @@ -0,0 +1,122 @@ +using System; +using System.Collections.Generic; +using System.Text; +using Avalonia.Collections; +using System.ComponentModel; +using Avalonia.Animation.Utils; +using System.Reactive.Linq; +using System.Linq; + +namespace Avalonia.Animation.Keyframes +{ + /// + /// Base class for KeyFrames + /// + public abstract class KeyFrames : AvaloniaList, IKeyFrames + { + + /// + /// Target property. + /// + public AvaloniaProperty Property { get; set; } + + /// Enable if the derived class will do the verification of + /// its keyframes. + internal bool IsVerfifiedAndConverted; + + /// + public virtual IDisposable Apply(Animation animation, Animatable control, IObservable obsMatch) + { + if(obsMatch == null) return null; + + if (!IsVerfifiedAndConverted) + VerifyKeyFrames(animation, typeof(T)); + + return obsMatch + .Where(p => p == true) + .Subscribe(_ => DoInterpolation(animation, control, ConvertedValues)); + } + + + /// + /// Interpolates the given keyframes to the control. + /// + public abstract IDisposable DoInterpolation(Animation animation, + Animatable control, + Dictionary keyValues); + + internal Dictionary ConvertedValues = new Dictionary(); + + /// + /// Verifies keyframe value types. + /// + private void VerifyKeyFrames(Animation animation, Type type) + { + var typeConv = TypeDescriptor.GetConverter(type); + + foreach (KeyFrame k in this) + { + if (k.Value == null) + { + throw new ArgumentNullException($"KeyFrame value can't be null."); + } + if (!typeConv.CanConvertTo(k.Value.GetType())) + { + throw new InvalidCastException($"KeyFrame value doesnt match property type."); + } + + T convertedValue = (T)typeConv.ConvertTo(k.Value, type); + + Cue _normalizedCue = k.Cue; + + if (k.timeSpanSet) + { + _normalizedCue = new Cue(k.KeyTime.Ticks / animation.Duration.Ticks); + } + + ConvertedValues.Add(_normalizedCue.CueValue, convertedValue); + + } + + // This can be optional if we ever try to make + // the default start and end values to be the + // property's prior value. + SortKeyFrameCues(ConvertedValues); + + IsVerfifiedAndConverted = true; + + } + + private void SortKeyFrameCues(Dictionary convertedValues) + { + SortKeyFrameCues(convertedValues.ToDictionary((k) => k.Key, (v) => (object)v.Value)); + } + + internal void SortKeyFrameCues(Dictionary convertedValues) + { + bool hasStartKey, hasEndKey; + hasStartKey = hasEndKey = false; + + foreach (var converted in ConvertedValues.Keys) + { + if (DoubleUtils.AboutEqual(converted, 0.0)) + { + hasStartKey = true; + } + else if (DoubleUtils.AboutEqual(converted, 1.0)) + { + hasEndKey = true; + } + } + + if (!hasStartKey && !hasEndKey) + throw new InvalidOperationException + ($"{this.GetType().Name} must have a starting (0% cue) and ending (100% cue) keyframe."); + + // Sort Cues, in case they don't order it by themselves. + ConvertedValues = ConvertedValues.OrderBy(p => p.Key) + .ToDictionary((k) => k.Key, (v) => v.Value); + + } + } +} diff --git a/src/Avalonia.Animation/Properties/AssemblyInfo.cs b/src/Avalonia.Animation/Properties/AssemblyInfo.cs index 7e4f2b340f..8234e75386 100644 --- a/src/Avalonia.Animation/Properties/AssemblyInfo.cs +++ b/src/Avalonia.Animation/Properties/AssemblyInfo.cs @@ -3,6 +3,11 @@ using Avalonia.Metadata; using System.Reflection; +using System.Runtime.CompilerServices; [assembly: AssemblyTitle("Avalonia.Animation")] -[assembly: XmlnsDefinition("https://github.com/avaloniaui", "Avalonia.Animation")] \ No newline at end of file +[assembly: InternalsVisibleTo("Avalonia.Visuals")] +[assembly: XmlnsDefinition("https://github.com/avaloniaui", "Avalonia.Animation")] +[assembly: XmlnsDefinition("https://github.com/avaloniaui", "Avalonia.Animation.Keyframes")] +[assembly: XmlnsDefinition("https://github.com/avaloniaui", "Avalonia.Animation.Transitions")] +[assembly: XmlnsDefinition("https://github.com/avaloniaui", "Avalonia.Animation.Easings")] \ No newline at end of file diff --git a/src/Avalonia.Animation/Timing.cs b/src/Avalonia.Animation/Timing.cs index a22dc1a6a7..d8ccafac62 100644 --- a/src/Avalonia.Animation/Timing.cs +++ b/src/Avalonia.Animation/Timing.cs @@ -88,6 +88,27 @@ namespace Avalonia.Animation .Concat(Observable.Return(1.0)); } + /// + /// Gets a timer that fires every frame for the specified duration with delay. + /// + /// + /// An observable that notifies the subscriber of the progress along the animation. + /// + /// + /// The parameter passed to the subscriber is the progress along the animation, with + /// 0 being the start and 1 being the end. The observable is guaranteed to fire 0 + /// immediately on subscribe and 1 at the end of the duration. + /// + public static IObservable GetTimer(TimeSpan duration, TimeSpan delay) + { + var startTime = Stopwatch.Elapsed.Ticks + delay.Ticks; + var endTime = startTime + duration.Ticks; + return Timer + .TakeWhile(x => x.Ticks < endTime) + .Select(x => (x.Ticks - startTime) / (double)duration.Ticks) + .StartWith(0.0) + .Concat(Observable.Return(1.0)); + } } } diff --git a/src/Avalonia.Animation/Transitions/DoubleTransition.cs b/src/Avalonia.Animation/Transitions/DoubleTransition.cs index 26f7f975e8..4af23b3219 100644 --- a/src/Avalonia.Animation/Transitions/DoubleTransition.cs +++ b/src/Avalonia.Animation/Transitions/DoubleTransition.cs @@ -5,7 +5,7 @@ using Avalonia.Metadata; using System; using System.Reactive.Linq; -namespace Avalonia.Animation +namespace Avalonia.Animation.Transitions { /// /// Transition class that handles with types. diff --git a/src/Avalonia.Animation/Transitions/FloatTransition.cs b/src/Avalonia.Animation/Transitions/FloatTransition.cs index b94295d311..c0664295a5 100644 --- a/src/Avalonia.Animation/Transitions/FloatTransition.cs +++ b/src/Avalonia.Animation/Transitions/FloatTransition.cs @@ -5,7 +5,7 @@ using Avalonia.Metadata; using System; using System.Reactive.Linq; -namespace Avalonia.Animation +namespace Avalonia.Animation.Transitions { /// /// Transition class that handles with types. diff --git a/src/Avalonia.Animation/ITransition.cs b/src/Avalonia.Animation/Transitions/ITransition.cs similarity index 89% rename from src/Avalonia.Animation/ITransition.cs rename to src/Avalonia.Animation/Transitions/ITransition.cs index f5dd91739a..b34ad2d4fe 100644 --- a/src/Avalonia.Animation/ITransition.cs +++ b/src/Avalonia.Animation/Transitions/ITransition.cs @@ -5,10 +5,10 @@ using Avalonia.Metadata; using System; using System.Reactive.Linq; -namespace Avalonia.Animation +namespace Avalonia.Animation.Transitions { /// - /// Interface for Property Transition objects. + /// Interface for Transition objects. /// public interface ITransition { diff --git a/src/Avalonia.Animation/Transitions/IntegerTransition.cs b/src/Avalonia.Animation/Transitions/IntegerTransition.cs index 200b93a3df..cbef37972a 100644 --- a/src/Avalonia.Animation/Transitions/IntegerTransition.cs +++ b/src/Avalonia.Animation/Transitions/IntegerTransition.cs @@ -5,7 +5,7 @@ using Avalonia.Metadata; using System; using System.Reactive.Linq; -namespace Avalonia.Animation +namespace Avalonia.Animation.Transitions { /// /// Transition class that handles with types. diff --git a/src/Avalonia.Animation/Transition.cs b/src/Avalonia.Animation/Transitions/Transition.cs similarity index 93% rename from src/Avalonia.Animation/Transition.cs rename to src/Avalonia.Animation/Transitions/Transition.cs index 808e7f69de..a93b129e75 100644 --- a/src/Avalonia.Animation/Transition.cs +++ b/src/Avalonia.Animation/Transitions/Transition.cs @@ -4,8 +4,9 @@ using Avalonia.Metadata; using System; using System.Reactive.Linq; +using Avalonia.Animation.Easings; -namespace Avalonia.Animation +namespace Avalonia.Animation.Transitions { /// /// Defines how a property should be animated using a transition. @@ -13,7 +14,7 @@ namespace Avalonia.Animation public abstract class Transition : ITransition { private AvaloniaProperty _prop; - private IEasing _easing; + private Easing _easing; /// /// Gets the duration of the animation. @@ -23,7 +24,7 @@ namespace Avalonia.Animation /// /// Gets the easing class to be used. /// - public IEasing Easing + public Easing Easing { get { diff --git a/src/Avalonia.Animation/Transitions.cs b/src/Avalonia.Animation/Transitions/Transitions.cs similarity index 93% rename from src/Avalonia.Animation/Transitions.cs rename to src/Avalonia.Animation/Transitions/Transitions.cs index 7e742fc0b4..14e5b4e6fd 100644 --- a/src/Avalonia.Animation/Transitions.cs +++ b/src/Avalonia.Animation/Transitions/Transitions.cs @@ -3,7 +3,7 @@ using Avalonia.Collections; -namespace Avalonia.Animation +namespace Avalonia.Animation.Transitions { /// /// A collection of definitions. diff --git a/src/Avalonia.Animation/Helpers/BounceEaseHelper.cs b/src/Avalonia.Animation/Utils/BounceEaseUtils.cs similarity index 94% rename from src/Avalonia.Animation/Helpers/BounceEaseHelper.cs rename to src/Avalonia.Animation/Utils/BounceEaseUtils.cs index b0da648fd3..cfb9c432ae 100644 --- a/src/Avalonia.Animation/Helpers/BounceEaseHelper.cs +++ b/src/Avalonia.Animation/Utils/BounceEaseUtils.cs @@ -1,12 +1,12 @@ // 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. -namespace Avalonia.Animation +namespace Avalonia.Animation.Utils { /// /// Helper static class for BounceEase classes. /// - internal static class BounceEaseHelper + internal static class BounceEaseUtils { /// /// Returns the consequent value of diff --git a/src/Avalonia.Animation/Helpers/DoubleHelper.cs b/src/Avalonia.Animation/Utils/DoubleUtils.cs similarity index 86% rename from src/Avalonia.Animation/Helpers/DoubleHelper.cs rename to src/Avalonia.Animation/Utils/DoubleUtils.cs index bc95a2bb14..67943b687d 100644 --- a/src/Avalonia.Animation/Helpers/DoubleHelper.cs +++ b/src/Avalonia.Animation/Utils/DoubleUtils.cs @@ -5,9 +5,9 @@ using System; using System.Collections.Generic; using System.Text; -namespace Avalonia.Animation +namespace Avalonia.Animation.Utils { - internal static class DoubleHelper + internal static class DoubleUtils { internal static bool AboutEqual(double x, double y) { diff --git a/src/Avalonia.Animation/Helpers/EasingConstants.cs b/src/Avalonia.Animation/Utils/EasingUtils.cs similarity index 85% rename from src/Avalonia.Animation/Helpers/EasingConstants.cs rename to src/Avalonia.Animation/Utils/EasingUtils.cs index b6a52ab4a4..d07ec3cdf4 100644 --- a/src/Avalonia.Animation/Helpers/EasingConstants.cs +++ b/src/Avalonia.Animation/Utils/EasingUtils.cs @@ -3,12 +3,12 @@ using System; -namespace Avalonia.Animation +namespace Avalonia.Animation.Utils { /// /// Helper static class for easing mathematical constants. /// - internal static class EasingConstants + internal static class EasingUtils { /// /// Half of diff --git a/src/Avalonia.Styling/Avalonia.Styling.csproj b/src/Avalonia.Styling/Avalonia.Styling.csproj index 81b2bfd88b..7f39da4801 100644 --- a/src/Avalonia.Styling/Avalonia.Styling.csproj +++ b/src/Avalonia.Styling/Avalonia.Styling.csproj @@ -34,6 +34,7 @@ + \ No newline at end of file diff --git a/src/Avalonia.Styling/Styling/Style.cs b/src/Avalonia.Styling/Styling/Style.cs index 4182ffada3..fe4b77322d 100644 --- a/src/Avalonia.Styling/Styling/Style.cs +++ b/src/Avalonia.Styling/Styling/Style.cs @@ -7,6 +7,7 @@ using System.Collections.Specialized; using System.Reactive.Linq; using Avalonia.Controls; using Avalonia.Metadata; +using Avalonia.Animation; namespace Avalonia.Styling { @@ -20,6 +21,8 @@ namespace Avalonia.Styling private IResourceNode _parent; private IResourceDictionary _resources; + private IList _animations; + /// /// Initializes a new instance of the class. /// @@ -78,6 +81,17 @@ namespace Avalonia.Styling [Content] public IList Setters { get; set; } = new List(); + public IList Animations + { + get + { + return _animations ?? (_animations = new List()); + } + set + { + _animations = value; + } + } /// IResourceNode IResourceNode.ResourceParent => _parent; @@ -91,7 +105,7 @@ namespace Avalonia.Styling /// /// The control that contains this style. May be null. /// - public void Attach(IStyleable control, IStyleHost container) + void IStyle.Attach(IStyleable control, IStyleHost container) { if (Selector != null) { @@ -101,6 +115,11 @@ namespace Avalonia.Styling { var subs = GetSubscriptions(control); + foreach (var animation in Animations) + { + subs.Add(animation.Apply((Animatable)control, match.ObservableResult)); + } + foreach (var setter in Setters) { var sub = setter.Apply(this, control, match.ObservableResult); diff --git a/src/Avalonia.Visuals/Animation/Keyframes/TransformKeyFrames.cs b/src/Avalonia.Visuals/Animation/Keyframes/TransformKeyFrames.cs new file mode 100644 index 0000000000..e8e5ff4d9a --- /dev/null +++ b/src/Avalonia.Visuals/Animation/Keyframes/TransformKeyFrames.cs @@ -0,0 +1,77 @@ +using System; +using System.Collections.Generic; +using System.Text; +using Avalonia.Collections; +using System.ComponentModel; +using Avalonia.Animation.Utils; +using System.Reactive.Linq; +using System.Linq; +using Avalonia.Media; + +namespace Avalonia.Animation.Keyframes +{ + /// + /// Key frames that handles properties. + /// + public class TransformKeyFrames : KeyFrames + { + DoubleKeyFrames childKeyFrames; + + /// + public override IDisposable Apply(Animation animation, Animatable control, IObservable obsMatch) + { + var ctrl = (Visual)control; + + // Check if the AvaloniaProperty is Transform derived. + if (typeof(Transform).IsAssignableFrom(Property.OwnerType)) + { + var renderTransformType = ctrl.RenderTransform.GetType(); + + // It's only 1 transform object so let's target that. + if (renderTransformType == Property.OwnerType) + { + var targetTransform = Convert.ChangeType(ctrl.RenderTransform, Property.OwnerType); + + if (childKeyFrames == null) + { + childKeyFrames = new DoubleKeyFrames(); + + foreach (KeyFrame k in this) + { + childKeyFrames.Add(k); + } + + childKeyFrames.Property = Property; + } + + return childKeyFrames.Apply(animation, ctrl.RenderTransform, obsMatch); + } + if (renderTransformType == typeof(TransformGroup)) + { + foreach (Transform t in ((TransformGroup)ctrl.RenderTransform).Children) + { + if (renderTransformType == Property.OwnerType) + { + + } + } + + // not existing in the transform + + } + } + else + { + throw new InvalidProgramException($"Unsupported property {Property}"); + } + + return null; + } + + /// + public override IDisposable DoInterpolation(Animation animation, Animatable control, Dictionary keyValues) + { + return Timing.GetTimer(animation.Duration, animation.Delay).Subscribe(); + } + } +} diff --git a/src/Avalonia.Visuals/Animation/PointTransition.cs b/src/Avalonia.Visuals/Animation/Transitions/PointTransition.cs similarity index 92% rename from src/Avalonia.Visuals/Animation/PointTransition.cs rename to src/Avalonia.Visuals/Animation/Transitions/PointTransition.cs index f29ae1b9eb..66082dca52 100644 --- a/src/Avalonia.Visuals/Animation/PointTransition.cs +++ b/src/Avalonia.Visuals/Animation/Transitions/PointTransition.cs @@ -5,10 +5,10 @@ using Avalonia.Metadata; using System; using System.Reactive.Linq; -namespace Avalonia.Animation +namespace Avalonia.Animation.Transitions { /// - /// Transition class that handles with types. + /// Transition class that handles with type. /// public class PointTransition : Transition { diff --git a/src/Avalonia.Visuals/Animation/ThicknessTransition.cs b/src/Avalonia.Visuals/Animation/Transitions/ThicknessTransition.cs similarity index 93% rename from src/Avalonia.Visuals/Animation/ThicknessTransition.cs rename to src/Avalonia.Visuals/Animation/Transitions/ThicknessTransition.cs index 4f31d3eb48..9c6d5cc327 100644 --- a/src/Avalonia.Visuals/Animation/ThicknessTransition.cs +++ b/src/Avalonia.Visuals/Animation/Transitions/ThicknessTransition.cs @@ -5,10 +5,10 @@ using Avalonia.Metadata; using System; using System.Reactive.Linq; -namespace Avalonia.Animation +namespace Avalonia.Animation.Transitions { /// - /// Transition class that handles with types. + /// Transition class that handles with type. /// public class ThicknessTransition : Transition { diff --git a/src/Avalonia.Visuals/Properties/AssemblyInfo.cs b/src/Avalonia.Visuals/Properties/AssemblyInfo.cs index 87347d64b1..5456576ef2 100644 --- a/src/Avalonia.Visuals/Properties/AssemblyInfo.cs +++ b/src/Avalonia.Visuals/Properties/AssemblyInfo.cs @@ -8,6 +8,8 @@ using Avalonia.Metadata; [assembly: AssemblyTitle("Avalonia.Visuals")] [assembly: InternalsVisibleTo("Avalonia.Visuals.UnitTests")] [assembly: XmlnsDefinition("https://github.com/avaloniaui", "Avalonia.Animation")] +[assembly: XmlnsDefinition("https://github.com/avaloniaui", "Avalonia.Animation.Transitions")] +[assembly: XmlnsDefinition("https://github.com/avaloniaui", "Avalonia.Animation.Keyframes")] [assembly: XmlnsDefinition("https://github.com/avaloniaui", "Avalonia.Media")] [assembly: InternalsVisibleTo("Avalonia.Direct2D1.RenderTests")] diff --git a/src/Markup/Avalonia.Markup.Xaml/Converters/EasingTypeConverter.cs b/src/Markup/Avalonia.Markup.Xaml/Converters/EasingTypeConverter.cs index 851d579581..2dfea4f176 100644 --- a/src/Markup/Avalonia.Markup.Xaml/Converters/EasingTypeConverter.cs +++ b/src/Markup/Avalonia.Markup.Xaml/Converters/EasingTypeConverter.cs @@ -1,7 +1,7 @@ // 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 Avalonia.Animation; +using Avalonia.Animation.Easings; using System; using System.ComponentModel; using System.Globalization; diff --git a/src/Markup/Avalonia.Markup.Xaml/PortableXaml/AvaloniaDefaultTypeConverters.cs b/src/Markup/Avalonia.Markup.Xaml/PortableXaml/AvaloniaDefaultTypeConverters.cs index 5f0f8d8d46..64a242cfc6 100644 --- a/src/Markup/Avalonia.Markup.Xaml/PortableXaml/AvaloniaDefaultTypeConverters.cs +++ b/src/Markup/Avalonia.Markup.Xaml/PortableXaml/AvaloniaDefaultTypeConverters.cs @@ -13,6 +13,7 @@ using Avalonia.Input; using Avalonia.Collections; using Avalonia.Controls.Templates; using Avalonia.Animation; +using Avalonia.Animation.Easings; namespace Avalonia.Markup.Xaml.PortableXaml { From 431afdebb10386783add0d358f2a3b14472dc3d3 Mon Sep 17 00:00:00 2001 From: Jumar Macato Date: Tue, 27 Mar 2018 00:16:33 +0800 Subject: [PATCH 011/149] * Fixed interpolation point selection when theres three or more keyframes. *Added new examples in RenderTest/AnimationsPage --- samples/RenderTest/Pages/AnimationsPage.xaml | 132 ++++++++++-------- .../Keyframes/DoubleKeyFrames.cs | 5 +- 2 files changed, 79 insertions(+), 58 deletions(-) diff --git a/samples/RenderTest/Pages/AnimationsPage.xaml b/samples/RenderTest/Pages/AnimationsPage.xaml index 5cd4e6e431..76e4ba0051 100644 --- a/samples/RenderTest/Pages/AnimationsPage.xaml +++ b/samples/RenderTest/Pages/AnimationsPage.xaml @@ -1,72 +1,94 @@ - - - - - - + + + + - + - - - - - + + + + + + + + + + + Hover to activate Transform Keyframe Animations. + + - + - + + + + + + - + + + \ No newline at end of file diff --git a/src/Avalonia.Animation/Keyframes/DoubleKeyFrames.cs b/src/Avalonia.Animation/Keyframes/DoubleKeyFrames.cs index 00558430e0..107b736b8b 100644 --- a/src/Avalonia.Animation/Keyframes/DoubleKeyFrames.cs +++ b/src/Avalonia.Animation/Keyframes/DoubleKeyFrames.cs @@ -40,10 +40,9 @@ namespace Avalonia.Animation.Keyframes bool isWithinRange_End = DoubleUtils.AboutEqual(x, 1.0) || x < 1.0; if (isWithinRange_Start && isWithinRange_End) - { - + { firstCue = sortedkeyValues.Where(j => j.Key <= x).Last(); - lastCue = sortedkeyValues.Where(j=> j.Key >= firstCue.Key).First(); + lastCue = sortedkeyValues.Where(j => j.Key >= x).First(); } else if (!isWithinRange_Start) { From 39fea68f745347966cb95541d35a0469810c8c51 Mon Sep 17 00:00:00 2001 From: Jumar Macato Date: Tue, 27 Mar 2018 00:45:06 +0800 Subject: [PATCH 012/149] --- samples/RenderTest/Pages/AnimationsPage.xaml | 7 ++++--- src/Avalonia.Styling/Styling/Style.cs | 2 +- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/samples/RenderTest/Pages/AnimationsPage.xaml b/samples/RenderTest/Pages/AnimationsPage.xaml index 76e4ba0051..5532383aac 100644 --- a/samples/RenderTest/Pages/AnimationsPage.xaml +++ b/samples/RenderTest/Pages/AnimationsPage.xaml @@ -36,10 +36,11 @@ - - - - - - - - Hover to activate Transform Keyframe Animations. + + + Hover to activate Transform Keyframe Animations. + diff --git a/src/Avalonia.Animation/Keyframes/DoubleKeyFrames.cs b/src/Avalonia.Animation/Keyframes/DoubleKeyFrames.cs index 107b736b8b..6c23658d40 100644 --- a/src/Avalonia.Animation/Keyframes/DoubleKeyFrames.cs +++ b/src/Avalonia.Animation/Keyframes/DoubleKeyFrames.cs @@ -30,43 +30,56 @@ namespace Avalonia.Animation.Keyframes // Get a pair of keyframes to make the interpolation. KeyValuePair firstCue, lastCue; - firstCue = sortedkeyValues.First(); - lastCue = sortedkeyValues.Last(); + // This should be changed later for a much more efficient one if (sortedkeyValues.Count() > 2) { - bool isWithinRange_Start = DoubleUtils.AboutEqual(x, 0.0) || x > 0.0; - bool isWithinRange_End = DoubleUtils.AboutEqual(x, 1.0) || x < 1.0; + bool OutsideRange_Start = DoubleUtils.AboutEqual(x, 0.0) || x < 0.0; + bool OutsideRange_End = DoubleUtils.AboutEqual(x, 1.0) || x > 1.0; + var kyF = sortedkeyValues.ToArray(); - if (isWithinRange_Start && isWithinRange_End) - { - firstCue = sortedkeyValues.Where(j => j.Key <= x).Last(); - lastCue = sortedkeyValues.Where(j => j.Key >= x).First(); - } - else if (!isWithinRange_Start) + if (OutsideRange_Start) { - firstCue = sortedkeyValues.First(); - lastCue = sortedkeyValues.Skip(1).First(); + firstCue = kyF[0]; + lastCue = kyF[1]; } - else if (!isWithinRange_End) + else if (OutsideRange_End) { - firstCue = sortedkeyValues.Skip(sortedkeyValues.Count() - 1).First(); - lastCue = sortedkeyValues.Last(); + var count = kyF.Count(); + firstCue = kyF[count - 2]; + lastCue = kyF[count - 1]; } else { - throw new InvalidOperationException - ($"Can't find KeyFrames within the specified Easing time {x}"); + firstCue = sortedkeyValues.Where(j => j.Key <= x).Last(); + lastCue = sortedkeyValues.Where(j => j.Key >= x).First(); } } + else + { + firstCue = sortedkeyValues.First(); + lastCue = sortedkeyValues.Last(); + } + + double x0, y0, x1, y1; - // Piecewise Linear interpolation, courtesy of wikipedia - var y0 = firstCue.Value; - var x0 = firstCue.Key; - var y1 = lastCue.Value; - var x1 = lastCue.Key; - var y = ((y0 * (x1 - x)) + (y1 * (x - x0))) / x1 - x0; + // Swap interpolants if its descending + // if (firstCue.Value > lastCue.Value) + // // { + // y1 = firstCue.Value; + // x1 = firstCue.Key; + // y0 = lastCue.Value; + // x0 = lastCue.Key; + // } + // else + // { + y0 = firstCue.Value; + x0 = firstCue.Key; + y1 = lastCue.Value; + x1 = lastCue.Key; + + var y = y0 + ((x - x0)/(x1 - x0)) * (y1-y0); return y; } From 6acc8f0551980d53feb34f2f2d7cac8426a4f303 Mon Sep 17 00:00:00 2001 From: Jumar Macato Date: Tue, 27 Mar 2018 22:54:14 +0800 Subject: [PATCH 014/149] * Transfer some keyframe handling responsibilites to the base class for maximum reusability on later custom derived classes. * Fix flickering issues on restart in TransformKeyFrames. *Add some syntactic sugar here and there. --- .../Keyframes/DoubleKeyFrames.cs | 96 ++++-------------- src/Avalonia.Animation/Keyframes/KeyFrames.cs | 99 +++++++++++++------ .../Animation/Keyframes/TransformKeyFrames.cs | 4 +- 3 files changed, 89 insertions(+), 110 deletions(-) diff --git a/src/Avalonia.Animation/Keyframes/DoubleKeyFrames.cs b/src/Avalonia.Animation/Keyframes/DoubleKeyFrames.cs index 6c23658d40..1360021ca3 100644 --- a/src/Avalonia.Animation/Keyframes/DoubleKeyFrames.cs +++ b/src/Avalonia.Animation/Keyframes/DoubleKeyFrames.cs @@ -14,86 +14,24 @@ namespace Avalonia.Animation.Keyframes /// public class DoubleKeyFrames : KeyFrames { - /// - public override IDisposable DoInterpolation(Animation animation, Animatable control, Dictionary sortedkeyValues) - { - var timer = Timing.GetTimer(animation.Duration, animation.Delay); - - - var interp = timer.Select(p => - { - // Handle the errors rather naively, for now. - try - { - var x = animation.Easing.Ease(p); - - // Get a pair of keyframes to make the interpolation. - KeyValuePair firstCue, lastCue; - - - - // This should be changed later for a much more efficient one - if (sortedkeyValues.Count() > 2) - { - bool OutsideRange_Start = DoubleUtils.AboutEqual(x, 0.0) || x < 0.0; - bool OutsideRange_End = DoubleUtils.AboutEqual(x, 1.0) || x > 1.0; - var kyF = sortedkeyValues.ToArray(); - - if (OutsideRange_Start) - { - firstCue = kyF[0]; - lastCue = kyF[1]; - } - else if (OutsideRange_End) - { - var count = kyF.Count(); - firstCue = kyF[count - 2]; - lastCue = kyF[count - 1]; - } - else - { - firstCue = sortedkeyValues.Where(j => j.Key <= x).Last(); - lastCue = sortedkeyValues.Where(j => j.Key >= x).First(); - } - } - else - { - firstCue = sortedkeyValues.First(); - lastCue = sortedkeyValues.Last(); - } - - double x0, y0, x1, y1; - - // Swap interpolants if its descending - // if (firstCue.Value > lastCue.Value) - // // { - // y1 = firstCue.Value; - // x1 = firstCue.Key; - // y0 = lastCue.Value; - // x0 = lastCue.Key; - // } - // else - // { - y0 = firstCue.Value; - x0 = firstCue.Key; - y1 = lastCue.Value; - x1 = lastCue.Key; - - var y = y0 + ((x - x0)/(x1 - x0)) * (y1-y0); - - return y; - } - catch (Exception e) - { - Debug.WriteLine(e); - return 1; - } - }); - - - return control.Bind(Property, interp.Select(p => (object)p), BindingPriority.Animation); - } + /// + public override IObservable DoInterpolation(Animation animation, Animatable control) + => GetKeyFramesTimer(animation) + .Select(t => + { + // Get a pair of keyframes to make the interpolation. + var pair = GetKeyFramePairByTime(t); + + // Do linear parametric interpolation + double y0 = pair.firstKF.Value; + double t0 = pair.firstKF.Key; + double y1 = pair.lastKF.Value; + double t1 = pair.lastKF.Key; + + // Calculate the final interpolated value + return y0 + ((t - t0) / (t1 - t0)) * (y1 - y0); + }); } } diff --git a/src/Avalonia.Animation/Keyframes/KeyFrames.cs b/src/Avalonia.Animation/Keyframes/KeyFrames.cs index 923e6fcbc5..6a580f3b96 100644 --- a/src/Avalonia.Animation/Keyframes/KeyFrames.cs +++ b/src/Avalonia.Animation/Keyframes/KeyFrames.cs @@ -6,6 +6,7 @@ using System.ComponentModel; using Avalonia.Animation.Utils; using System.Reactive.Linq; using System.Linq; +using Avalonia.Data; namespace Avalonia.Animation.Keyframes { @@ -20,42 +21,89 @@ namespace Avalonia.Animation.Keyframes /// public AvaloniaProperty Property { get; set; } - /// Enable if the derived class will do the verification of - /// its keyframes. - internal bool IsVerfifiedAndConverted; + /// + /// List of type-converted keyframes. + /// + public Dictionary ConvertedKeyframes = new Dictionary(); + private bool IsVerfifiedAndConverted; + /// public virtual IDisposable Apply(Animation animation, Animatable control, IObservable obsMatch) { - if(obsMatch == null) return null; - if (!IsVerfifiedAndConverted) - VerifyKeyFrames(animation, typeof(T)); + VerifyConvertKeyFrames(animation, typeof(T)); return obsMatch .Where(p => p == true) - .Subscribe(_ => DoInterpolation(animation, control, ConvertedValues)); + .Subscribe(_ => + { + var interp = DoInterpolation(animation, control) + .Select(p => (object)p); + control.Bind(Property, interp, BindingPriority.Animation); + }); } + /// + /// Get the nearest pair of cue-time ordered keyframes + /// according to the given time parameter. + /// + public (KeyValuePair firstKF, KeyValuePair lastKF) GetKeyFramePairByTime(double t) + { + KeyValuePair firstCue, lastCue; + int kvCount = ConvertedKeyframes.Count(); + if (kvCount > 2) + { + if (DoubleUtils.AboutEqual(t, 0.0) || t < 0.0) + { + firstCue = ConvertedKeyframes.First(); + lastCue = ConvertedKeyframes.Skip(1).First(); + } + else if (DoubleUtils.AboutEqual(t, 1.0) || t > 1.0) + { + firstCue = ConvertedKeyframes.Skip(kvCount - 2).First(); + lastCue = ConvertedKeyframes.Last(); + } + else + { + firstCue = ConvertedKeyframes.Where(j => j.Key <= t).Last(); + lastCue = ConvertedKeyframes.Where(j => j.Key >= t).First(); + } + } + else + { + firstCue = ConvertedKeyframes.First(); + lastCue = ConvertedKeyframes.Last(); + } + return (firstCue, lastCue); + } + + + /// + /// Returns an observable timer with the specific Animation + /// duration and delay and applies the Animation's easing function. + /// + public IObservable GetKeyFramesTimer(Animation animation) => + Timing.GetTimer(animation.Duration, animation.Delay) + .Select(t => animation.Easing.Ease(t)); /// /// Interpolates the given keyframes to the control. /// - public abstract IDisposable DoInterpolation(Animation animation, - Animatable control, - Dictionary keyValues); + public abstract IObservable DoInterpolation(Animation animation, + Animatable control); - internal Dictionary ConvertedValues = new Dictionary(); /// - /// Verifies keyframe value types. + /// Verifies and converts keyframe values according to this class type parameter. /// - private void VerifyKeyFrames(Animation animation, Type type) + private void VerifyConvertKeyFrames(Animation animation, Type type) { var typeConv = TypeDescriptor.GetConverter(type); foreach (KeyFrame k in this) { + if (k.Value == null) { throw new ArgumentNullException($"KeyFrame value can't be null."); @@ -74,30 +122,23 @@ namespace Avalonia.Animation.Keyframes _normalizedCue = new Cue(k.KeyTime.Ticks / animation.Duration.Ticks); } - ConvertedValues.Add(_normalizedCue.CueValue, convertedValue); + ConvertedKeyframes.Add(_normalizedCue.CueValue, convertedValue); } - // This can be optional if we ever try to make - // the default start and end values to be the - // property's prior value. - SortKeyFrameCues(ConvertedValues); - + SortKeyFrameCues(ConvertedKeyframes); IsVerfifiedAndConverted = true; } private void SortKeyFrameCues(Dictionary convertedValues) - { - SortKeyFrameCues(convertedValues.ToDictionary((k) => k.Key, (v) => (object)v.Value)); - } - - internal void SortKeyFrameCues(Dictionary convertedValues) { bool hasStartKey, hasEndKey; hasStartKey = hasEndKey = false; - foreach (var converted in ConvertedValues.Keys) + // this can be optional later, by making the default start/end keyframes + // to have a neutral value (a.k.a. the value prior to the animation). + foreach (var converted in ConvertedKeyframes.Keys) { if (DoubleUtils.AboutEqual(converted, 0.0)) { @@ -108,15 +149,15 @@ namespace Avalonia.Animation.Keyframes hasEndKey = true; } } - + if (!hasStartKey && !hasEndKey) throw new InvalidOperationException ($"{this.GetType().Name} must have a starting (0% cue) and ending (100% cue) keyframe."); // Sort Cues, in case they don't order it by themselves. - ConvertedValues = ConvertedValues.OrderBy(p => p.Key) - .ToDictionary((k) => k.Key, (v) => v.Value); - + ConvertedKeyframes = ConvertedKeyframes.OrderBy(p => p.Key) + .ToDictionary((k) => k.Key, (v) => v.Value); } + } } diff --git a/src/Avalonia.Visuals/Animation/Keyframes/TransformKeyFrames.cs b/src/Avalonia.Visuals/Animation/Keyframes/TransformKeyFrames.cs index e8e5ff4d9a..4ad7c39bed 100644 --- a/src/Avalonia.Visuals/Animation/Keyframes/TransformKeyFrames.cs +++ b/src/Avalonia.Visuals/Animation/Keyframes/TransformKeyFrames.cs @@ -69,9 +69,9 @@ namespace Avalonia.Animation.Keyframes } /// - public override IDisposable DoInterpolation(Animation animation, Animatable control, Dictionary keyValues) + public override IObservable DoInterpolation(Animation animation, Animatable control) { - return Timing.GetTimer(animation.Duration, animation.Delay).Subscribe(); + return null; } } } From c828437239d08dcd82ad3407667d11cf733a7a7d Mon Sep 17 00:00:00 2001 From: Jumar Macato Date: Sat, 31 Mar 2018 16:32:20 +0800 Subject: [PATCH 015/149] Minor fixes. --- samples/ControlCatalog/SideBar.xaml | 14 ++++- samples/RenderTest/MainWindow.xaml | 2 +- samples/RenderTest/Pages/AnimationsPage.xaml | 8 +-- src/Avalonia.Styling/Styling/Style.cs | 14 ++++- .../Animation/Keyframes/TransformKeyFrames.cs | 51 +++++++++++-------- 5 files changed, 60 insertions(+), 29 deletions(-) diff --git a/samples/ControlCatalog/SideBar.xaml b/samples/ControlCatalog/SideBar.xaml index 3013c5a80e..e5baf9e942 100644 --- a/samples/ControlCatalog/SideBar.xaml +++ b/samples/ControlCatalog/SideBar.xaml @@ -1,4 +1,5 @@ - + + + diff --git a/samples/RenderTest/MainWindow.xaml b/samples/RenderTest/MainWindow.xaml index 51da5f342a..dc0d7b1c17 100644 --- a/samples/RenderTest/MainWindow.xaml +++ b/samples/RenderTest/MainWindow.xaml @@ -1,6 +1,6 @@ diff --git a/samples/RenderTest/Pages/AnimationsPage.xaml b/samples/RenderTest/Pages/AnimationsPage.xaml index f8d486bfc4..21ecc3f0bb 100644 --- a/samples/RenderTest/Pages/AnimationsPage.xaml +++ b/samples/RenderTest/Pages/AnimationsPage.xaml @@ -1,7 +1,6 @@ - - - + + - + + diff --git a/src/Avalonia.Styling/Styling/Style.cs b/src/Avalonia.Styling/Styling/Style.cs index 9c6ed71781..5583cb229a 100644 --- a/src/Avalonia.Styling/Styling/Style.cs +++ b/src/Avalonia.Styling/Styling/Style.cs @@ -8,6 +8,7 @@ using System.Reactive.Linq; using Avalonia.Controls; using Avalonia.Metadata; using Avalonia.Animation; +using System.Diagnostics; namespace Avalonia.Styling { @@ -117,7 +118,18 @@ namespace Avalonia.Styling foreach (var animation in Animations) { - subs.Add(animation.Apply((Animatable)control, match.ObservableResult)); + // TODO: Needs more work in passing the appropriate + // observable. + IObservable obsMatch = match.ObservableResult; + + if (match.ImmediateResult == true) + { + obsMatch = Observable.Return(true); + } + + var sub = animation.Apply((Animatable)control, obsMatch); + subs.Add(sub); + } foreach (var setter in Setters) diff --git a/src/Avalonia.Visuals/Animation/Keyframes/TransformKeyFrames.cs b/src/Avalonia.Visuals/Animation/Keyframes/TransformKeyFrames.cs index 4ad7c39bed..814f51bde1 100644 --- a/src/Avalonia.Visuals/Animation/Keyframes/TransformKeyFrames.cs +++ b/src/Avalonia.Visuals/Animation/Keyframes/TransformKeyFrames.cs @@ -16,7 +16,7 @@ namespace Avalonia.Animation.Keyframes public class TransformKeyFrames : KeyFrames { DoubleKeyFrames childKeyFrames; - + /// public override IDisposable Apply(Animation animation, Animatable control, IObservable obsMatch) { @@ -27,45 +27,52 @@ namespace Avalonia.Animation.Keyframes { var renderTransformType = ctrl.RenderTransform.GetType(); + if (childKeyFrames == null) + { + InitializeInternalDoubleKeyFrames(); + } + // It's only 1 transform object so let's target that. if (renderTransformType == Property.OwnerType) { - var targetTransform = Convert.ChangeType(ctrl.RenderTransform, Property.OwnerType); - - if (childKeyFrames == null) - { - childKeyFrames = new DoubleKeyFrames(); - - foreach (KeyFrame k in this) - { - childKeyFrames.Add(k); - } - - childKeyFrames.Property = Property; - } - return childKeyFrames.Apply(animation, ctrl.RenderTransform, obsMatch); } + // TODO: Selection within TransformGroup is not working if (renderTransformType == typeof(TransformGroup)) { - foreach (Transform t in ((TransformGroup)ctrl.RenderTransform).Children) + foreach (Transform transform in ((TransformGroup)ctrl.RenderTransform).Children) { - if (renderTransformType == Property.OwnerType) + if (transform.GetType() == Property.OwnerType) { - + return childKeyFrames.Apply(animation, transform, obsMatch); } } + } + + return null; + + // // Throw exception when there is no appropriate transform object found. + // throw new Exception + // ($"TransformKeyFrame hasn't found an appropriate Transform object with type {Property.OwnerType} in target {control}."); - // not existing in the transform - } } else { - throw new InvalidProgramException($"Unsupported property {Property}"); + throw new Exception($"Unsupported property {Property}"); + } + } + + void InitializeInternalDoubleKeyFrames() + { + childKeyFrames = new DoubleKeyFrames(); + + foreach (KeyFrame keyframe in this) + { + childKeyFrames.Add(keyframe); } - return null; + childKeyFrames.Property = Property; } /// From 7693836c438cdfd093cb0efdfc74ab34518f9795 Mon Sep 17 00:00:00 2001 From: Jumar Macato Date: Tue, 3 Apr 2018 00:18:11 +0800 Subject: [PATCH 016/149] Minor Fixes pt. 2 --- samples/RenderTest/Pages/AnimationsPage.xaml | 26 ++++--- .../RenderTest/Pages/AnimationsPage.xaml.cs | 67 +------------------ src/Avalonia.Animation/Animatable.cs | 2 +- src/Avalonia.Animation/Keyframes/KeyFrame.cs | 15 +---- src/Avalonia.Animation/Keyframes/KeyFrames.cs | 42 ++++++++++-- 5 files changed, 57 insertions(+), 95 deletions(-) diff --git a/samples/RenderTest/Pages/AnimationsPage.xaml b/samples/RenderTest/Pages/AnimationsPage.xaml index 21ecc3f0bb..f2452df03b 100644 --- a/samples/RenderTest/Pages/AnimationsPage.xaml +++ b/samples/RenderTest/Pages/AnimationsPage.xaml @@ -1,11 +1,16 @@ - + + - - - - - - - + + + + + + + + + + + - - - - Hover to activate Transform Keyframe Animations. - - - - + + + + + Hover to activate Transform Keyframe Animations. + + public class Animatable : AvaloniaObject { + /// + /// Initializes this object. + /// + public Animatable() + { + Transitions = new Transitions.Transitions(); + } /// /// Defines the property. @@ -61,7 +68,7 @@ namespace Avalonia.Animation { get { return GetValue(TransitionsProperty); } set { SetValue(TransitionsProperty, value); } - } + } /// /// Reacts to a change in a value in diff --git a/src/Avalonia.Animation/Keyframes/KeyFrames.cs b/src/Avalonia.Animation/Keyframes/KeyFrames.cs index 4edb4969cd..b827cbbe21 100644 --- a/src/Avalonia.Animation/Keyframes/KeyFrames.cs +++ b/src/Avalonia.Animation/Keyframes/KeyFrames.cs @@ -7,6 +7,7 @@ using Avalonia.Animation.Utils; using System.Reactive.Linq; using System.Linq; using Avalonia.Data; +using System.Reactive.Disposables; namespace Avalonia.Animation.Keyframes { @@ -36,8 +37,11 @@ namespace Avalonia.Animation.Keyframes return obsMatch .Where(p => p == true) + .Where(p=> Timing.GetGlobalPlayState() != AnimationPlayState.Paused) .Subscribe(_ => { + + var interp = DoInterpolation(animation, control) .Select(p => (object)p); control.Bind(Property, interp, BindingPriority.Animation); @@ -115,7 +119,7 @@ namespace Avalonia.Animation.Keyframes /// public IObservable<(double Time, Animatable Target)> SetupAnimation(Animation animation, Animatable control) => - Timing.GetTimer(control, animation.Duration, animation.Delay) + Timing.GetAnimationsTimer(control, animation.Duration, animation.Delay) .Select(t => (animation.Easing.Ease(t), control)); /// diff --git a/src/Avalonia.Animation/Timing.cs b/src/Avalonia.Animation/Timing.cs index 09f55c0444..fc75e2c7c3 100644 --- a/src/Avalonia.Animation/Timing.cs +++ b/src/Avalonia.Animation/Timing.cs @@ -15,6 +15,9 @@ namespace Avalonia.Animation /// public static class Timing { + static ulong _animationsFrameCount, _transitionsFrameCount; + static AnimationPlayState _globalState = AnimationPlayState.Running; + /// /// The number of frames per second. /// @@ -25,55 +28,89 @@ namespace Avalonia.Animation /// private static readonly TimeSpan Tick = TimeSpan.FromSeconds(1.0 / FramesPerSecond); - static ulong _frameCount = 0; - /// /// Initializes static members of the class. /// static Timing() { - Stopwatch = new Stopwatch(); - Stopwatch.Start(); - Timer = Observable.Interval(Tick, AvaloniaScheduler.Instance) + AnimationTimer = Observable.Interval(Tick, AvaloniaScheduler.Instance) .Select(_ => { - _frameCount += 1; - return _frameCount; + switch (_globalState) + { + case AnimationPlayState.Paused: + break; + default: + _animationsFrameCount += 1; + break; + } + return _animationsFrameCount; }) .Publish() .RefCount(); + + TransitionsTimer = Observable.Interval(Tick, AvaloniaScheduler.Instance) + .Select(p => _transitionsFrameCount += 1) + .Publish() + .RefCount(); + } + + + /// + /// Sets the animation play state for all animations + /// + public static void SetGlobalPlayState(AnimationPlayState playState) + { + Dispatcher.UIThread.VerifyAccess(); + _globalState = playState; + } + + /// + /// Gets the animation play state for all animations + /// + public static AnimationPlayState GetGlobalPlayState() + { + Dispatcher.UIThread.VerifyAccess(); + return _globalState; } /// - /// The stopwatch used to track time. + /// Gets the animation timer. /// + /// + /// The animation timer increments usually 60 times per second as + /// defined in . + /// The parameter passed to a subsciber is the number of frames since the animation system was + /// initialized. + /// /// - /// The stopwatch used to track time. + /// The animation timer. /// - public static Stopwatch Stopwatch + public static IObservable AnimationTimer { get; } /// - /// Gets the animation timer. + /// Gets the transitions timer. /// /// - /// The animation timer ticks times per second. The - /// parameter passed to a subsciber is the number of frames since the animation system was + /// The transitions timer increments usually 60 times per second as + /// defined in . + /// The parameter passed to a subsciber is the number of frames since the animation system was /// initialized. /// /// /// The animation timer. /// - public static IObservable Timer + public static IObservable TransitionsTimer { get; } - /// /// Gets a timer that fires every frame for the specified duration with delay. + /// This timer's running state can be changed via method. /// /// /// An observable that notifies the subscriber of the progress along the animation. @@ -83,12 +120,36 @@ namespace Avalonia.Animation /// 0 being the start and 1 being the end. The observable is guaranteed to fire 0 /// immediately on subscribe and 1 at the end of the duration. /// - public static IObservable GetTimer(Animatable control, TimeSpan duration, TimeSpan delay) + public static IObservable GetAnimationsTimer(Animatable control, TimeSpan duration, TimeSpan delay) { - var startTime = _frameCount; + var startTime = _animationsFrameCount; var _duration = (ulong)(duration.Ticks/Tick.Ticks); var endTime = startTime + _duration; - return Timer + return AnimationTimer + .TakeWhile(x => x < endTime) + .Select(x => (double)(x - startTime) / _duration) + .StartWith(0.0) + .Concat(Observable.Return(1.0)); + } + + /// + /// Gets a timer that fires every frame for the specified duration with delay. + /// + /// + /// An observable that notifies the subscriber of the progress along the transition. + /// + /// + /// The parameter passed to the subscriber is the progress along the transition, with + /// 0 being the start and 1 being the end. The observable is guaranteed to fire 0 + /// immediately on subscribe and 1 at the end of the duration. + /// + public static IObservable GetTransitionsTimer(Animatable control, TimeSpan duration, TimeSpan delay) + { + var startTime = _transitionsFrameCount; + var _duration = (ulong)(duration.Ticks / Tick.Ticks); + var endTime = startTime + _duration; + + return TransitionsTimer .TakeWhile(x => x < endTime) .Select(x => (double)(x - startTime) / _duration) .StartWith(0.0) diff --git a/src/Avalonia.Animation/Transitions/Transition.cs b/src/Avalonia.Animation/Transitions/Transition.cs index f8f9acc80a..66fda063b6 100644 --- a/src/Avalonia.Animation/Transitions/Transition.cs +++ b/src/Avalonia.Animation/Transitions/Transition.cs @@ -61,7 +61,7 @@ namespace Avalonia.Animation.Transitions /// public IDisposable Apply(Animatable control, object oldValue, object newValue) { - var transition = DoTransition(Timing.GetTimer(control, Duration, TimeSpan.Zero), (T)oldValue, (T)newValue).Select(p => (object)p); + var transition = DoTransition(Timing.GetTransitionsTimer(control, Duration, TimeSpan.Zero), (T)oldValue, (T)newValue).Select(p => (object)p); return control.Bind(Property, transition, Data.BindingPriority.Animation); } From 4f929ac0a14ce3ad86fcfbaf5dab2cf696c29c07 Mon Sep 17 00:00:00 2001 From: Jumar Macato Date: Tue, 3 Apr 2018 19:52:35 +0800 Subject: [PATCH 019/149] Fix transitions --- src/Avalonia.Animation/Animatable.cs | 19 +++++++++++++------ src/Avalonia.Animation/Keyframes/KeyFrames.cs | 2 -- 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/src/Avalonia.Animation/Animatable.cs b/src/Avalonia.Animation/Animatable.cs index 26c35c5493..3b4f571d33 100644 --- a/src/Avalonia.Animation/Animatable.cs +++ b/src/Avalonia.Animation/Animatable.cs @@ -7,6 +7,7 @@ using System; using System.Reactive.Linq; using Avalonia.Collections; using Avalonia.Animation.Transitions; +using System.Collections.Generic; namespace Avalonia.Animation { @@ -58,17 +59,23 @@ namespace Avalonia.Animation /// /// Defines the property. /// - public static readonly StyledProperty TransitionsProperty = - AvaloniaProperty.Register(nameof(Transitions)); + public static readonly DirectProperty> TransitionsProperty = + AvaloniaProperty.RegisterDirect>( + nameof(Transitions), + o => o.Transitions, + (o, v) => o.Transitions = v); + + private IEnumerable _transitions = new AvaloniaList(); /// /// Gets or sets the property transitions for the control. /// - public Transitions.Transitions Transitions + public IEnumerable Transitions { - get { return GetValue(TransitionsProperty); } - set { SetValue(TransitionsProperty, value); } - } + get { return _transitions; } + set { SetAndRaise(TransitionsProperty, ref _transitions, value); } + } + /// /// Reacts to a change in a value in diff --git a/src/Avalonia.Animation/Keyframes/KeyFrames.cs b/src/Avalonia.Animation/Keyframes/KeyFrames.cs index b827cbbe21..a31becaccf 100644 --- a/src/Avalonia.Animation/Keyframes/KeyFrames.cs +++ b/src/Avalonia.Animation/Keyframes/KeyFrames.cs @@ -40,8 +40,6 @@ namespace Avalonia.Animation.Keyframes .Where(p=> Timing.GetGlobalPlayState() != AnimationPlayState.Paused) .Subscribe(_ => { - - var interp = DoInterpolation(animation, control) .Select(p => (object)p); control.Bind(Property, interp, BindingPriority.Animation); From bb5c5c0d3a513c4223cf89d8aa00ba34c9385210 Mon Sep 17 00:00:00 2001 From: Jumar Macato Date: Wed, 11 Apr 2018 01:08:48 +0800 Subject: [PATCH 020/149] TransitionKeyFrames transform selection logic are now fully functional. Trigger RaiseChanged() on TransformGroup class when a child transform was changed. Split the PlayState enum to a new file. --- samples/RenderTest/Pages/AnimationsPage.xaml | 223 +----------------- src/Avalonia.Animation/Animatable.cs | 17 -- src/Avalonia.Animation/Animation.cs | 12 - src/Avalonia.Animation/AnimationPlayState.cs | 12 + .../Animation/Keyframes/TransformKeyFrames.cs | 29 ++- src/Avalonia.Visuals/Media/TransformGroup.cs | 24 ++ 6 files changed, 62 insertions(+), 255 deletions(-) create mode 100644 src/Avalonia.Animation/AnimationPlayState.cs diff --git a/samples/RenderTest/Pages/AnimationsPage.xaml b/samples/RenderTest/Pages/AnimationsPage.xaml index f03f1a0c63..e478f45fef 100644 --- a/samples/RenderTest/Pages/AnimationsPage.xaml +++ b/samples/RenderTest/Pages/AnimationsPage.xaml @@ -5,7 +5,7 @@ + @@ -74,113 +79,14 @@ Hover to activate Transform Keyframe Animations.