Browse Source

*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.
pull/1461/head
Jumar Macato 8 years ago
parent
commit
dd657e203d
  1. 3
      samples/RenderTest/MainWindow.xaml
  2. 70
      samples/RenderTest/Pages/AnimationsPage.xaml
  3. 2
      samples/RenderTest/Pages/AnimationsPage.xaml.cs
  4. 175
      samples/RenderTest/RenderTest.csproj
  5. 2
      samples/RenderTest/SideBar.xaml
  6. 2
      samples/RenderTest/ViewModels/MainWindowViewModel.cs
  7. 7
      src/Avalonia.Animation/Animatable.cs
  8. 65
      src/Avalonia.Animation/Animation.cs
  9. 2
      src/Avalonia.Animation/Easing.cs
  10. 2
      src/Avalonia.Animation/Easing/BackEaseIn.cs
  11. 2
      src/Avalonia.Animation/Easing/BackEaseOut.cs
  12. 6
      src/Avalonia.Animation/Easing/BounceEaseIn.cs
  13. 8
      src/Avalonia.Animation/Easing/BounceEaseInOut.cs
  14. 6
      src/Avalonia.Animation/Easing/BounceEaseOut.cs
  15. 2
      src/Avalonia.Animation/Easing/CircularEaseIn.cs
  16. 2
      src/Avalonia.Animation/Easing/CircularEaseInOut.cs
  17. 2
      src/Avalonia.Animation/Easing/CircularEaseOut.cs
  18. 2
      src/Avalonia.Animation/Easing/CubicEaseIn.cs
  19. 2
      src/Avalonia.Animation/Easing/CubicEaseInOut.cs
  20. 2
      src/Avalonia.Animation/Easing/CubicEaseOut.cs
  21. 5
      src/Avalonia.Animation/Easing/ElasticEaseIn.cs
  22. 7
      src/Avalonia.Animation/Easing/ElasticEaseInOut.cs
  23. 5
      src/Avalonia.Animation/Easing/ElasticEaseOut.cs
  24. 2
      src/Avalonia.Animation/Easing/ExponentialEaseIn.cs
  25. 2
      src/Avalonia.Animation/Easing/ExponentialEaseInOut.cs
  26. 2
      src/Avalonia.Animation/Easing/ExponentialEaseOut.cs
  27. 2
      src/Avalonia.Animation/Easing/LinearEasing.cs
  28. 2
      src/Avalonia.Animation/Easing/QuadraticEaseIn.cs
  29. 2
      src/Avalonia.Animation/Easing/QuadraticEaseInOut.cs
  30. 2
      src/Avalonia.Animation/Easing/QuadraticEaseOut.cs
  31. 2
      src/Avalonia.Animation/Easing/QuarticEaseIn.cs
  32. 2
      src/Avalonia.Animation/Easing/QuarticEaseInOut.cs
  33. 2
      src/Avalonia.Animation/Easing/QuarticEaseOut.cs
  34. 2
      src/Avalonia.Animation/Easing/QuinticEaseIn.cs
  35. 2
      src/Avalonia.Animation/Easing/QuinticEaseInOut.cs
  36. 2
      src/Avalonia.Animation/Easing/QuinticEaseOut.cs
  37. 5
      src/Avalonia.Animation/Easing/SineEaseIn.cs
  38. 2
      src/Avalonia.Animation/Easing/SineEaseInOut.cs
  39. 5
      src/Avalonia.Animation/Easing/SineEaseOut.cs
  40. 17
      src/Avalonia.Animation/IAnimation.cs
  41. 2
      src/Avalonia.Animation/IEasing.cs
  42. 88
      src/Avalonia.Animation/Keyframes/Cue.cs
  43. 87
      src/Avalonia.Animation/Keyframes/DoubleKeyFrames.cs
  44. 17
      src/Avalonia.Animation/Keyframes/IKeyFrames.cs
  45. 80
      src/Avalonia.Animation/Keyframes/KeyFrame.cs
  46. 122
      src/Avalonia.Animation/Keyframes/KeyFrames.cs
  47. 7
      src/Avalonia.Animation/Properties/AssemblyInfo.cs
  48. 21
      src/Avalonia.Animation/Timing.cs
  49. 2
      src/Avalonia.Animation/Transitions/DoubleTransition.cs
  50. 2
      src/Avalonia.Animation/Transitions/FloatTransition.cs
  51. 4
      src/Avalonia.Animation/Transitions/ITransition.cs
  52. 2
      src/Avalonia.Animation/Transitions/IntegerTransition.cs
  53. 7
      src/Avalonia.Animation/Transitions/Transition.cs
  54. 2
      src/Avalonia.Animation/Transitions/Transitions.cs
  55. 4
      src/Avalonia.Animation/Utils/BounceEaseUtils.cs
  56. 4
      src/Avalonia.Animation/Utils/DoubleUtils.cs
  57. 4
      src/Avalonia.Animation/Utils/EasingUtils.cs
  58. 1
      src/Avalonia.Styling/Avalonia.Styling.csproj
  59. 21
      src/Avalonia.Styling/Styling/Style.cs
  60. 77
      src/Avalonia.Visuals/Animation/Keyframes/TransformKeyFrames.cs
  61. 4
      src/Avalonia.Visuals/Animation/Transitions/PointTransition.cs
  62. 4
      src/Avalonia.Visuals/Animation/Transitions/ThicknessTransition.cs
  63. 2
      src/Avalonia.Visuals/Properties/AssemblyInfo.cs
  64. 2
      src/Markup/Avalonia.Markup.Xaml/Converters/EasingTypeConverter.cs
  65. 1
      src/Markup/Avalonia.Markup.Xaml/PortableXaml/AvaloniaDefaultTypeConverters.cs

3
samples/RenderTest/MainWindow.xaml

@ -1,6 +1,7 @@
<Window xmlns="https://github.com/avaloniaui"
Title="Avalonia Render Test"
xmlns:pages="clr-namespace:RenderTest.Pages;assembly=RenderTest">
xmlns:pages="clr-namespace:RenderTest.Pages;assembly=RenderTest" MinWidth="400" MinHeight="200"
>
<DockPanel>
<Menu DockPanel.Dock="Top">
<MenuItem Header="Rendering">

70
samples/RenderTest/Pages/AnimationsPage.xaml

@ -1,2 +1,72 @@
<UserControl xmlns="https://github.com/avaloniaui">
<Grid>
<Grid.Styles>
<Styles>
<Style Selector="Rectangle.Test">
<Setter Property="Fill" Value="Red"/>
<Setter Property="Margin" Value="15"/>
<Setter Property="Width" Value="100"/>
<Setter Property="Height" Value="100"/>
</Style>
<Style Selector="Rectangle.Rect1:pointerover">
<Style.Animations>
<Animation Duration="0:0:2.5" Easing="BounceEaseInOut">
<TransformKeyFrames Property="RotateTransform.Angle">
<KeyFrame Cue="0%" Value="0"/>
<KeyFrame Cue="100%" Value="360"/>
</TransformKeyFrames>
</Animation>
</Style.Animations>
</Style>
<Style Selector="Rectangle.Rect2:pointerover">
<Style.Animations>
<Animation Duration="0:0:0.5" Easing="SineEaseInOut">
<TransformKeyFrames Property="ScaleTransform.ScaleX">
<KeyFrame Cue="0%" Value="0.8"/>
<KeyFrame Cue="100%" Value="1"/>
</TransformKeyFrames>
<TransformKeyFrames Property="ScaleTransform.ScaleY">
<KeyFrame Cue="0%" Value="0.8"/>
<KeyFrame Cue="100%" Value="1"/>
</TransformKeyFrames>
</Animation>
</Style.Animations>
</Style>
<Style Selector="Rectangle.Rect3:pointerover">
<Style.Animations>
<Animation Duration="0:0:3" Easing="BounceEaseInOut">
<TransformKeyFrames Property="TranslateTransform.Y">
<KeyFrame Cue="0%" Value="0"/>
<KeyFrame Cue="50%" Value="-100"/>
<KeyFrame Cue="100%" Value="0"/>
</TransformKeyFrames>
</Animation>
</Style.Animations>
</Style>
</Styles>
</Grid.Styles>
<StackPanel VerticalAlignment="Center"
HorizontalAlignment="Center"
Orientation="Horizontal"
ClipToBounds="False">
<Rectangle Classes="Test Rect1">
<Rectangle.RenderTransform>
<RotateTransform/>
</Rectangle.RenderTransform>
</Rectangle>
<Rectangle Classes="Test Rect2">
<Rectangle.RenderTransform>
<ScaleTransform ScaleX="0.8" ScaleY="0.8"/>
</Rectangle.RenderTransform>
</Rectangle>
<Rectangle Classes="Test Rect3">
<Rectangle.RenderTransform>
<TranslateTransform/>
</Rectangle.RenderTransform>
</Rectangle>
</StackPanel>
</Grid>
</UserControl>

2
samples/RenderTest/Pages/AnimationsPage.xaml.cs

@ -15,7 +15,7 @@ namespace RenderTest.Pages
public AnimationsPage()
{
this.InitializeComponent();
this.CreateAnimations();
// this.CreateAnimations();
}
private void InitializeComponent()

175
samples/RenderTest/RenderTest.csproj

@ -1,18 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{F1FDC5B0-4654-416F-AE69-E3E9BBD87801}</ProjectGuid>
<OutputType>WinExe</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp2.0</TargetFramework>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
<RootNamespace>RenderTest</RootNamespace>
<AssemblyName>RenderTest</AssemblyName>
<TargetFrameworkVersion>v4.7</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
<TargetFrameworkProfile />
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
@ -33,152 +25,33 @@
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup>
<StartupObject />
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" />
<Reference Include="System.Net.Http" />
<Reference Include="System.Xml" />
<Reference Include="WindowsBase" />
</ItemGroup>
<ItemGroup>
<Compile Include="App.xaml.cs">
<DependentUpon>App.xaml</DependentUpon>
</Compile>
<Compile Include="Pages\DrawingPage.xaml.cs">
<DependentUpon>DrawingPage.xaml</DependentUpon>
</Compile>
<Compile Include="Pages\ClippingPage.xaml.cs">
<DependentUpon>ClippingPage.xaml</DependentUpon>
</Compile>
<Compile Include="Pages\AnimationsPage.xaml.cs">
<DependentUpon>AnimationsPage.xaml</DependentUpon>
</Compile>
<Compile Include="Program.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="MainWindow.xaml.cs">
<DependentUpon>MainWindow.xaml</DependentUpon>
<Compile Update="**\*.xaml.cs">
<DependentUpon>%(Filename)</DependentUpon>
</Compile>
<Compile Include="ViewModels\MainWindowViewModel.cs" />
</ItemGroup>
<ItemGroup>
<None Include="App.config" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="App.xaml">
<SubType>Designer</SubType>
</EmbeddedResource>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\Avalonia.Animation\Avalonia.Animation.csproj">
<Project>{d211e587-d8bc-45b9-95a4-f297c8fa5200}</Project>
<Name>Avalonia.Animation</Name>
</ProjectReference>
<ProjectReference Include="..\..\src\Avalonia.Base\Avalonia.Base.csproj">
<Project>{b09b78d8-9b26-48b0-9149-d64a2f120f3f}</Project>
<Name>Avalonia.Base</Name>
</ProjectReference>
<ProjectReference Include="..\..\src\Avalonia.Controls\Avalonia.Controls.csproj">
<Project>{d2221c82-4a25-4583-9b43-d791e3f6820c}</Project>
<Name>Avalonia.Controls</Name>
</ProjectReference>
<ProjectReference Include="..\..\src\Avalonia.DesignerSupport\Avalonia.DesignerSupport.csproj">
<Project>{799a7bb5-3c2c-48b6-85a7-406a12c420da}</Project>
<Name>Avalonia.DesignerSupport</Name>
</ProjectReference>
<ProjectReference Include="..\..\src\Avalonia.Diagnostics\Avalonia.Diagnostics.csproj">
<Project>{7062ae20-5dcc-4442-9645-8195bdece63e}</Project>
<Name>Avalonia.Diagnostics</Name>
</ProjectReference>
<ProjectReference Include="..\..\src\Avalonia.DotNetFrameworkRuntime\Avalonia.DotNetFrameworkRuntime.csproj">
<Project>{4a1abb09-9047-4bd5-a4ad-a055e52c5ee0}</Project>
<Name>Avalonia.DotNetFrameworkRuntime</Name>
</ProjectReference>
<ProjectReference Include="..\..\src\Avalonia.Input\Avalonia.Input.csproj">
<Project>{62024b2d-53eb-4638-b26b-85eeaa54866e}</Project>
<Name>Avalonia.Input</Name>
</ProjectReference>
<ProjectReference Include="..\..\src\Avalonia.Interactivity\Avalonia.Interactivity.csproj">
<Project>{6b0ed19d-a08b-461c-a9d9-a9ee40b0c06b}</Project>
<Name>Avalonia.Interactivity</Name>
</ProjectReference>
<ProjectReference Include="..\..\src\Avalonia.Layout\Avalonia.Layout.csproj">
<Project>{42472427-4774-4c81-8aff-9f27b8e31721}</Project>
<Name>Avalonia.Layout</Name>
</ProjectReference>
<ProjectReference Include="..\..\src\Avalonia.Logging.Serilog\Avalonia.Logging.Serilog.csproj">
<Project>{b61b66a3-b82d-4875-8001-89d3394fe0c9}</Project>
<Name>Avalonia.Logging.Serilog</Name>
</ProjectReference>
<ProjectReference Include="..\..\src\Avalonia.ReactiveUI\Avalonia.ReactiveUI.csproj">
<Project>{6417b24e-49c2-4985-8db2-3ab9d898ec91}</Project>
<Name>Avalonia.ReactiveUI</Name>
</ProjectReference>
<ProjectReference Include="..\..\src\Avalonia.Visuals\Avalonia.Visuals.csproj">
<Project>{eb582467-6abb-43a1-b052-e981ba910e3a}</Project>
<Name>Avalonia.Visuals</Name>
</ProjectReference>
<ProjectReference Include="..\..\src\Avalonia.Styling\Avalonia.Styling.csproj">
<Project>{f1baa01a-f176-4c6a-b39d-5b40bb1b148f}</Project>
<Name>Avalonia.Styling</Name>
</ProjectReference>
<ProjectReference Include="..\..\src\Avalonia.Themes.Default\Avalonia.Themes.Default.csproj">
<Project>{3e10a5fa-e8da-48b1-ad44-6a5b6cb7750f}</Project>
<Name>Avalonia.Themes.Default</Name>
</ProjectReference>
<ProjectReference Include="..\..\src\Markup\Avalonia.Markup.Xaml\Avalonia.Markup.Xaml.csproj">
<Project>{3e53a01a-b331-47f3-b828-4a5717e77a24}</Project>
<Name>Avalonia.Markup.Xaml</Name>
</ProjectReference>
<ProjectReference Include="..\..\src\Markup\Avalonia.Markup\Avalonia.Markup.csproj">
<Project>{6417e941-21bc-467b-a771-0de389353ce6}</Project>
<Name>Avalonia.Markup</Name>
</ProjectReference>
<ProjectReference Include="..\..\src\Skia\Avalonia.Skia\Avalonia.Skia.csproj">
<Project>{7d2d3083-71dd-4cc9-8907-39a0d86fb322}</Project>
<Name>Avalonia.Skia</Name>
</ProjectReference>
<ProjectReference Include="..\..\src\Windows\Avalonia.Direct2D1\Avalonia.Direct2D1.csproj">
<Project>{3e908f67-5543-4879-a1dc-08eace79b3cd}</Project>
<Name>Avalonia.Direct2D1</Name>
</ProjectReference>
<ProjectReference Include="..\..\src\Windows\Avalonia.Win32\Avalonia.Win32.csproj">
<Project>{811a76cf-1cf6-440f-963b-bbe31bd72a82}</Project>
<Name>Avalonia.Win32</Name>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="MainWindow.xaml">
<SubType>Designer</SubType>
</EmbeddedResource>
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="SideBar.xaml">
<SubType>Designer</SubType>
</EmbeddedResource>
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="Pages\AnimationsPage.xaml">
<SubType>Designer</SubType>
</EmbeddedResource>
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="Pages\ClippingPage.xaml">
<EmbeddedResource Include="**\*.xaml">
<SubType>Designer</SubType>
</EmbeddedResource>
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="Pages\DrawingPage.xaml">
<SubType>Designer</SubType>
</EmbeddedResource>
<ProjectReference Include="..\..\src\Avalonia.DesignerSupport\Avalonia.DesignerSupport.csproj" />
<ProjectReference Include="..\..\src\Avalonia.DotNetCoreRuntime\Avalonia.DotNetCoreRuntime.csproj" />
<ProjectReference Include="..\..\src\Linux\Avalonia.LinuxFramebuffer\Avalonia.LinuxFramebuffer.csproj" />
<ProjectReference Include="..\..\src\Avalonia.Base\Avalonia.Base.csproj" />
<ProjectReference Include="..\..\src\Markup\Avalonia.Markup.Xaml\Avalonia.Markup.Xaml.csproj" />
<ProjectReference Include="..\..\src\Markup\Avalonia.Markup\Avalonia.Markup.csproj" />
<ProjectReference Include="..\..\src\Avalonia.Animation\Avalonia.Animation.csproj" />
<ProjectReference Include="..\..\src\Avalonia.Controls\Avalonia.Controls.csproj" />
<ProjectReference Include="..\..\src\Avalonia.Diagnostics\Avalonia.Diagnostics.csproj" />
<ProjectReference Include="..\..\src\Avalonia.Input\Avalonia.Input.csproj" />
<ProjectReference Include="..\..\src\Avalonia.Interactivity\Avalonia.Interactivity.csproj" />
<ProjectReference Include="..\..\src\Avalonia.Layout\Avalonia.Layout.csproj" />
<ProjectReference Include="..\..\src\Avalonia.ReactiveUI\Avalonia.ReactiveUI.csproj" />
<ProjectReference Include="..\..\src\Avalonia.Visuals\Avalonia.Visuals.csproj" />
<ProjectReference Include="..\..\src\Avalonia.Styling\Avalonia.Styling.csproj" />
<ProjectReference Include="..\..\src\Avalonia.Themes.Default\Avalonia.Themes.Default.csproj" />
<ProjectReference Include="..\..\src\Avalonia.Logging.Serilog\Avalonia.Logging.Serilog.csproj" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<Import Project="..\..\build\Serilog.props" />
<Import Project="..\..\build\Rx.props" />
<Import Project="..\..\build\ReactiveUI.props" />

2
samples/RenderTest/SideBar.xaml

@ -37,7 +37,7 @@
<Setter Property="Opacity" Value="0.5"/>
<Setter Property="Transitions">
<Transitions>
<DoubleTransition Property="Opacity" Easing="CircularEaseIn" Duration="0:0:0.5"/>
<DoubleTransition Property="Opacity" Duration="0:0:0.5"/>
</Transitions>
</Setter>
</Style>

2
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()

7
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
/// <summary>
///
/// </summary>
public static readonly StyledProperty<AvaloniaList<ITransition>> TransitionsProperty =
AvaloniaProperty.Register<Animatable, AvaloniaList<ITransition>>(nameof(Transitions));
public static readonly StyledProperty<Transitions.Transitions> TransitionsProperty =
AvaloniaProperty.Register<Animatable, Transitions.Transitions>(nameof(Transitions));
/// <summary>
/// Gets or sets the property transitions for the control.
/// </summary>
public AvaloniaList<ITransition> Transitions
public Transitions.Transitions Transitions
{
get { return GetValue(TransitionsProperty); }
set { SetValue(TransitionsProperty, value); }

65
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
{
/// <summary>
/// Tracks the progress of an animation.
/// </summary>
public class Animation : IObservable<object>, IDisposable
public class Animation : IDisposable, IAnimation
{
private List<IDisposable>_subscription = new List<IDisposable>();
/// <summary>
/// The animation being tracked.
/// Run time of this animation.
/// </summary>
private readonly IObservable<object> _inner;
public TimeSpan Duration { get; set; }
/// <summary>
/// The disposable used to cancel the animation.
/// Delay time for animation.
/// </summary>
private readonly IDisposable _subscription;
public TimeSpan Delay { get; set; }
/// <summary>
/// Easing function to be used.
/// </summary>
public Easing Easing { get; set; } = new LinearEasing();
/// <summary>
/// Initializes a new instance of the <see cref="Animation"/> class.
/// A list of <see cref="IKeyFrames"/> objects.
/// </summary>
/// <param name="inner">The animation observable being tracked.</param>
/// <param name="subscription">A disposable used to cancel the animation.</param>
public Animation(IObservable<object> inner, IDisposable subscription)
{
_inner = inner;
_subscription = subscription;
}
[Content]
public AvaloniaList<IKeyFrames> Children { get; set; } = new AvaloniaList<IKeyFrames>();
/// <summary>
/// Cancels the animation.
/// </summary>
public void Dispose()
{
_subscription.Dispose();
foreach(var sub in _subscription) sub.Dispose();
}
/// <summary>
/// Notifies the provider that an observer is to receive notifications.
/// </summary>
/// <param name="observer">The observer.</param>
/// <returns>
/// A reference to an interface that allows observers to stop receiving notifications
/// before the provider has finished sending them.
/// </returns>
public IDisposable Subscribe(IObserver<object> observer)
/// <inheritdocs/>
public IDisposable Apply(Animatable control, IObservable<bool> matchObs)
{
return _inner.Subscribe(observer);
foreach (IKeyFrames keyframes in Children)
{
_subscription.Add(keyframes.Apply(this, control, matchObs));
}
return this;
}
///// <summary>
///// Notifies the provider that an observer is to receive notifications.
///// </summary>
///// <param name="observer">The observer.</param>
///// <returns>
///// A reference to an interface that allows observers to stop receiving notifications
///// before the provider has finished sending them.
///// </returns>
//public IDisposable Subscribe(IObserver<object> observer)
//{
// return _inner.Subscribe(observer);
//}
}
}

2
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
{
/// <summary>
/// Base class for all Easing classes.

2
src/Avalonia.Animation/Easing/BackEaseIn.cs

@ -3,7 +3,7 @@
using System;
namespace Avalonia.Animation
namespace Avalonia.Animation.Easings
{
/// <summary>
/// Eases in a <see cref="double"/> value

2
src/Avalonia.Animation/Easing/BackEaseOut.cs

@ -3,7 +3,7 @@
using System;
namespace Avalonia.Animation
namespace Avalonia.Animation.Easings
{
/// <summary>
/// Eases out a <see cref="double"/> value

6
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
{
/// <summary>
/// Eases in a <see cref="double"/> value
@ -13,7 +15,7 @@ namespace Avalonia.Animation
/// <inheritdoc/>
public override double Ease(double progress)
{
return 1 - BounceEaseHelper.Bounce(1 - progress);
return 1 - BounceEaseUtils.Bounce(1 - progress);
}
}

8
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
{
/// <summary>
/// Eases a <see cref="double"/> 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;
}
}

6
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
{
/// <summary>
/// Eases out a <see cref="double"/> value
@ -13,7 +13,7 @@ namespace Avalonia.Animation
/// <inheritdoc/>
public override double Ease(double progress)
{
return BounceEaseHelper.Bounce(progress);
return BounceEaseUtils.Bounce(progress);
}
}
}

2
src/Avalonia.Animation/Easing/CircularEaseIn.cs

@ -3,7 +3,7 @@
using System;
namespace Avalonia.Animation
namespace Avalonia.Animation.Easings
{
/// <summary>
/// Eases in a <see cref="double"/> value

2
src/Avalonia.Animation/Easing/CircularEaseInOut.cs

@ -3,7 +3,7 @@
using System;
namespace Avalonia.Animation
namespace Avalonia.Animation.Easings
{
/// <summary>
/// Eases a <see cref="double"/> value

2
src/Avalonia.Animation/Easing/CircularEaseOut.cs

@ -3,7 +3,7 @@
using System;
namespace Avalonia.Animation
namespace Avalonia.Animation.Easings
{
/// <summary>
/// Eases out a <see cref="double"/> value

2
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
{
/// <summary>
/// Eases in a <see cref="double"/> value

2
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
{
/// <summary>
/// Eases a <see cref="double"/> value

2
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
{
/// <summary>
/// Eases out a <see cref="double"/> value

5
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
{
/// <summary>
/// Eases in a <see cref="double"/> 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));
}
}

7
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
{
/// <summary>
/// Eases a <see cref="double"/> 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);
}
}

5
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
{
/// <summary>
/// Eases out a <see cref="double"/> 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;
}

2
src/Avalonia.Animation/Easing/ExponentialEaseIn.cs

@ -3,7 +3,7 @@
using System;
namespace Avalonia.Animation
namespace Avalonia.Animation.Easings
{
/// <summary>
/// Eases in a <see cref="double"/> value

2
src/Avalonia.Animation/Easing/ExponentialEaseInOut.cs

@ -3,7 +3,7 @@
using System;
namespace Avalonia.Animation
namespace Avalonia.Animation.Easings
{
/// <summary>
/// Eases a <see cref="double"/> value

2
src/Avalonia.Animation/Easing/ExponentialEaseOut.cs

@ -3,7 +3,7 @@
using System;
namespace Avalonia.Animation
namespace Avalonia.Animation.Easings
{
/// <summary>
/// Eases out a <see cref="double"/> value

2
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
{
/// <summary>
/// Linearly eases a <see cref="double"/> value.

2
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
{
/// <summary>
/// Eases in a <see cref="double"/> value

2
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
{
/// <summary>
/// Eases a <see cref="double"/> value

2
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
{
/// <summary>
/// Eases out a <see cref="double"/> value

2
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
{
/// <summary>
/// Eases in a <see cref="double"/> value

2
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
{
/// <summary>
/// Eases a <see cref="double"/> value

2
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
{
/// <summary>
/// Eases out a <see cref="double"/> value

2
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
{
/// <summary>
/// Eases in a <see cref="double"/> value

2
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
{
/// <summary>
/// Eases a <see cref="double"/> value

2
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
{
/// <summary>
/// Eases out a <see cref="double"/> value

5
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
{
/// <summary>
/// Eases in a <see cref="double"/> value
@ -14,7 +15,7 @@ namespace Avalonia.Animation
/// <inheritdoc/>
public override double Ease(double progress)
{
return Math.Sin((progress - 1) * EasingConstants.HALFPI) + 1;
return Math.Sin((progress - 1) * EasingUtils.HALFPI) + 1;
}
}
}

2
src/Avalonia.Animation/Easing/SineEaseInOut.cs

@ -3,7 +3,7 @@
using System;
namespace Avalonia.Animation
namespace Avalonia.Animation.Easings
{
/// <summary>
/// Eases a <see cref="double"/> value

5
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
{
/// <summary>
/// Eases out a <see cref="double"/> value
@ -15,7 +16,7 @@ namespace Avalonia.Animation
/// <inheritdoc/>
public override double Ease(double progress)
{
return Math.Sin(progress * EasingConstants.HALFPI);
return Math.Sin(progress * EasingUtils.HALFPI);
}
}
}

17
src/Avalonia.Animation/IAnimation.cs

@ -0,0 +1,17 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace Avalonia.Animation
{
/// <summary>
/// Interface for Animation objects
/// </summary>
public interface IAnimation
{
/// <summary>
/// Apply the animation to the specified control
/// </summary>
IDisposable Apply(Animatable control, IObservable<bool> match);
}
}

2
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
{
/// <summary>
/// Defines the interface for easing classes.

88
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
{
/// <summary>
/// A Cue object for <see cref="KeyFrame"/>.
/// </summary>
[TypeConverter(typeof(CueTypeConverter))]
public struct Cue : IEquatable<Cue>, IEquatable<double>
{
/// <summary>
/// The normalized percent value, ranging from 0.0 to 1.0
/// </summary>
public double CueValue { get; }
/// <summary>
/// Sets a new <see cref="Cue"/> object.
/// </summary>
/// <param name="value"></param>
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");
}
/// <summary>
/// Parses a string to a <see cref="Cue"/> object.
/// </summary>
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}\"");
}
}
/// <summary>
/// Checks for equality between two <see cref="Cue"/>s.
/// </summary>
/// <param name="other">The second cue.</param>
public bool Equals(Cue other)
{
return CueValue == other.CueValue;
}
/// <summary>
/// Checks for equality between a <see cref="Cue"/>
/// and a <see cref="double"/> value.
/// </summary>
/// <param name="other"></param>
/// <returns></returns>
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);
}
}
}

87
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
{
/// <summary>
/// Key frames that handles <see cref="double"/> properties.
/// </summary>
public class DoubleKeyFrames : KeyFrames<double>
{
/// <inheritdocs/>
public override IDisposable DoInterpolation(Animation animation, Animatable control, Dictionary<double, double> 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<double, double> 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);
}
}
}

17
src/Avalonia.Animation/Keyframes/IKeyFrames.cs

@ -0,0 +1,17 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace Avalonia.Animation.Keyframes
{
/// <summary>
/// Interface for Keyframe group object
/// </summary>
public interface IKeyFrames
{
/// <summary>
/// Applies the current KeyFrame group to the specified control.
/// </summary>
IDisposable Apply(Animation animation, Animatable control, IObservable<bool> obsMatch);
}
}

80
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
{
/// <summary>
/// Stores data regarding a specific key
/// point and value in an animation.
/// </summary>
public class KeyFrame
{
internal bool timeSpanSet, cueSet;
private TimeSpan _ktimeSpan;
private Cue _kCue;
/// <summary>
/// Gets or sets the key time of this <see cref="KeyFrame"/>.
/// </summary>
/// <value>The key time.</value>
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;
}
}
/// <summary>
/// Gets or sets the cue of this <see cref="KeyFrame"/>.
/// </summary>
/// <value>The cue.</value>
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; }
///// <summary>
///// Initializes a new instance of the <see cref="KeyFrame"/> class.
///// </summary>
//public KeyFrame()
//{
//}
}
}

122
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
{
/// <summary>
/// Base class for KeyFrames
/// </summary>
public abstract class KeyFrames<T> : AvaloniaList<KeyFrame>, IKeyFrames
{
/// <summary>
/// Target property.
/// </summary>
public AvaloniaProperty Property { get; set; }
/// Enable if the derived class will do the verification of
/// its keyframes.
internal bool IsVerfifiedAndConverted;
/// <inheritdoc/>
public virtual IDisposable Apply(Animation animation, Animatable control, IObservable<bool> obsMatch)
{
if(obsMatch == null) return null;
if (!IsVerfifiedAndConverted)
VerifyKeyFrames(animation, typeof(T));
return obsMatch
.Where(p => p == true)
.Subscribe(_ => DoInterpolation(animation, control, ConvertedValues));
}
/// <summary>
/// Interpolates the given keyframes to the control.
/// </summary>
public abstract IDisposable DoInterpolation(Animation animation,
Animatable control,
Dictionary<double, T> keyValues);
internal Dictionary<double, T> ConvertedValues = new Dictionary<double, T>();
/// <summary>
/// Verifies keyframe value types.
/// </summary>
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<double, T> convertedValues)
{
SortKeyFrameCues(convertedValues.ToDictionary((k) => k.Key, (v) => (object)v.Value));
}
internal void SortKeyFrameCues(Dictionary<double, object> 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);
}
}
}

7
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")]
[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")]

21
src/Avalonia.Animation/Timing.cs

@ -88,6 +88,27 @@ namespace Avalonia.Animation
.Concat(Observable.Return(1.0));
}
/// <summary>
/// Gets a timer that fires every frame for the specified duration with delay.
/// </summary>
/// <returns>
/// An observable that notifies the subscriber of the progress along the animation.
/// </returns>
/// <remarks>
/// 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.
/// </remarks>
public static IObservable<double> 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));
}
}
}

2
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
{
/// <summary>
/// Transition class that handles <see cref="AvaloniaProperty"/> with <see cref="double"/> types.

2
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
{
/// <summary>
/// Transition class that handles <see cref="AvaloniaProperty"/> with <see cref="float"/> types.

4
src/Avalonia.Animation/ITransition.cs → 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
{
/// <summary>
/// Interface for Property Transition objects.
/// Interface for Transition objects.
/// </summary>
public interface ITransition
{

2
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
{
/// <summary>
/// Transition class that handles <see cref="AvaloniaProperty"/> with <see cref="int"/> types.

7
src/Avalonia.Animation/Transition.cs → 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
{
/// <summary>
/// Defines how a property should be animated using a transition.
@ -13,7 +14,7 @@ namespace Avalonia.Animation
public abstract class Transition<T> : ITransition
{
private AvaloniaProperty _prop;
private IEasing _easing;
private Easing _easing;
/// <summary>
/// Gets the duration of the animation.
@ -23,7 +24,7 @@ namespace Avalonia.Animation
/// <summary>
/// Gets the easing class to be used.
/// </summary>
public IEasing Easing
public Easing Easing
{
get
{

2
src/Avalonia.Animation/Transitions.cs → src/Avalonia.Animation/Transitions/Transitions.cs

@ -3,7 +3,7 @@
using Avalonia.Collections;
namespace Avalonia.Animation
namespace Avalonia.Animation.Transitions
{
/// <summary>
/// A collection of <see cref="ITransition"/> definitions.

4
src/Avalonia.Animation/Helpers/BounceEaseHelper.cs → 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
{
/// <summary>
/// Helper static class for BounceEase classes.
/// </summary>
internal static class BounceEaseHelper
internal static class BounceEaseUtils
{
/// <summary>
/// Returns the consequent <see cref="double"/> value of

4
src/Avalonia.Animation/Helpers/DoubleHelper.cs → 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)
{

4
src/Avalonia.Animation/Helpers/EasingConstants.cs → src/Avalonia.Animation/Utils/EasingUtils.cs

@ -3,12 +3,12 @@
using System;
namespace Avalonia.Animation
namespace Avalonia.Animation.Utils
{
/// <summary>
/// Helper static class for easing mathematical constants.
/// </summary>
internal static class EasingConstants
internal static class EasingUtils
{
/// <summary>
/// Half of <see cref="Math.PI"/>

1
src/Avalonia.Styling/Avalonia.Styling.csproj

@ -34,6 +34,7 @@
<ItemGroup>
<ProjectReference Include="..\Avalonia.Base\Avalonia.Base.csproj" />
<ProjectReference Include="..\Avalonia.Visuals\Avalonia.Visuals.csproj" />
<ProjectReference Include="..\Avalonia.Animation\Avalonia.Animation.csproj" />
</ItemGroup>
<Import Project="..\..\build\Rx.props" />
</Project>

21
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<IAnimation> _animations;
/// <summary>
/// Initializes a new instance of the <see cref="Style"/> class.
/// </summary>
@ -78,6 +81,17 @@ namespace Avalonia.Styling
[Content]
public IList<ISetter> Setters { get; set; } = new List<ISetter>();
public IList<IAnimation> Animations
{
get
{
return _animations ?? (_animations = new List<IAnimation>());
}
set
{
_animations = value;
}
}
/// <inheritdoc/>
IResourceNode IResourceNode.ResourceParent => _parent;
@ -91,7 +105,7 @@ namespace Avalonia.Styling
/// <param name="container">
/// The control that contains this style. May be null.
/// </param>
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);

77
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
{
/// <summary>
/// Key frames that handles <see cref="double"/> properties.
/// </summary>
public class TransformKeyFrames : KeyFrames<double>
{
DoubleKeyFrames childKeyFrames;
/// <inheritdoc/>
public override IDisposable Apply(Animation animation, Animatable control, IObservable<bool> 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;
}
/// <inheritdocs/>
public override IDisposable DoInterpolation(Animation animation, Animatable control, Dictionary<double, double> keyValues)
{
return Timing.GetTimer(animation.Duration, animation.Delay).Subscribe();
}
}
}

4
src/Avalonia.Visuals/Animation/PointTransition.cs → 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
{
/// <summary>
/// Transition class that handles <see cref="AvaloniaProperty"/> with <see cref="Point"/> types.
/// Transition class that handles <see cref="AvaloniaProperty"/> with <see cref="Point"/> type.
/// </summary>
public class PointTransition : Transition<Point>
{

4
src/Avalonia.Visuals/Animation/ThicknessTransition.cs → 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
{
/// <summary>
/// Transition class that handles <see cref="AvaloniaProperty"/> with <see cref="Thickness"/> types.
/// Transition class that handles <see cref="AvaloniaProperty"/> with <see cref="Thickness"/> type.
/// </summary>
public class ThicknessTransition : Transition<Thickness>
{

2
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")]

2
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;

1
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
{

Loading…
Cancel
Save