Browse Source

Implemented FillMode; This PR is now almost feature complete (that nasty Control.Class selector bug).

pull/1461/head
Jumar Macato 9 years ago
parent
commit
edf982b820
  1. 34
      samples/RenderTest/Pages/AnimationsPage.xaml
  2. 35
      src/Avalonia.Animation/Animatable.cs
  3. 5
      src/Avalonia.Animation/Animation.cs
  4. 4
      src/Avalonia.Animation/Keyframes/KeyFrames.cs
  5. 53
      src/Avalonia.Animation/Keyframes/KeyFramesStateMachine.cs

34
samples/RenderTest/Pages/AnimationsPage.xaml

@ -5,7 +5,23 @@
<Styles>
<Styles.Resources>
<Template x:Key="Acorn">
<Path Fill="White" Stretch="Uniform" Data="F1 M 16.6309,18.6563C 17.1309,8.15625 29.8809,14.1563 29.8809,14.1563C 30.8809,11.1563 34.1308,11.4063 34.1308,11.4063C 33.5,12 34.6309,13.1563 34.6309,13.1563C 32.1309,13.1562 31.1309,14.9062 31.1309,14.9062C 41.1309,23.9062 32.6309,27.9063 32.6309,27.9062C 24.6309,24.9063 21.1309,22.1562 16.6309,18.6563 Z M 16.6309,19.9063C 21.6309,24.1563 25.1309,26.1562 31.6309,28.6562C 31.6309,28.6562 26.3809,39.1562 18.3809,36.1563C 18.3809,36.1563 18,38 16.3809,36.9063C 15,36 16.3809,34.9063 16.3809,34.9063C 16.3809,34.9063 10.1309,30.9062 16.6309,19.9063 Z"/>
<Path Fill="White" Stretch="Uniform"
Data="F1 M 16.6309,18.6563C 17.1309,
8.15625 29.8809,14.1563 29.8809,
14.1563C 30.8809,11.1563 34.1308,
11.4063 34.1308,11.4063C 33.5,12
34.6309,13.1563 34.6309,13.1563C
32.1309,13.1562 31.1309,14.9062
31.1309,14.9062C 41.1309,23.9062
32.6309,27.9063 32.6309,27.9062C
24.6309,24.9063 21.1309,22.1562
16.6309,18.6563 Z M 16.6309,19.9063C
21.6309,24.1563 25.1309,26.1562
31.6309,28.6562C 31.6309,28.6562
26.3809,39.1562 18.3809,36.1563C
18.3809,36.1563 18,38 16.3809,36.9063C
15,36 16.3809,34.9063 16.3809,34.9063C
16.3809,34.9063 10.1309,30.9062 16.6309,19.9063 Z"/>
</Template>
</Styles.Resources>
<Style Selector="Border.Test">
@ -16,12 +32,16 @@
<Setter Property="Child" Value="{StaticResource Acorn}"/>
</Style>
<Style Selector="Border.Rect1:pointerover">
<Setter Property="Opacity" Value="0.5"/>
<Style.Animations>
<Animation Duration="0:0:2.5" Easing="SineEaseInOut" Delay="0:0:5">
<Animation Duration="0:0:2.5"
RepeatBehavior="Repeat"
RepeatCount="2"
FillMode="Both"
PlaybackDirection="AlternateReverse"
Easing="SineEaseInOut">
<TransformKeyFrames Property="RotateTransform.Angle">
<KeyFrame Cue="0%" Value="0"/>
<KeyFrame Cue="100%" Value="360"/>
<KeyFrame Cue="0%" Value="45"/>
<KeyFrame Cue="100%" Value="120"/>
</TransformKeyFrames>
<TransformKeyFrames Property="ScaleTransform.ScaleX">
<KeyFrame Cue="0%" Value="1"/>
@ -86,11 +106,11 @@
<ScaleTransform/>
</TransformGroup>
</Border.RenderTransform>
<Border.Transitions>
<!-- <Border.Transitions>
<Transitions>
<DoubleTransition Property="Border.Opacity" Duration="0:0:2.5"/>
</Transitions>
</Border.Transitions>
</Border.Transitions> -->
</Border>
<Border Classes="Test Rect2" Background="DarkMagenta">
<Border.RenderTransform>

35
src/Avalonia.Animation/Animatable.cs

@ -27,7 +27,7 @@ namespace Avalonia.Animation
AnimatableTimer = Timing.AnimationStateTimer
.Select(p =>
{
if (this._playState == PlayState.Pause)
if (PlayState == PlayState.Pause)
{
return PlayState.Pause;
}
@ -46,44 +46,31 @@ namespace Avalonia.Animation
/// <summary>
/// Defines the <see cref="PlayState"/> property.
/// </summary>
public static readonly DirectProperty<Animatable, PlayState> PlayStateProperty =
AvaloniaProperty.RegisterDirect<Animatable, PlayState>(
nameof(PlayState),
o => o.PlayState,
(o, v) => o.PlayState = v);
private PlayState _playState = PlayState.Run;
public static readonly StyledProperty<PlayState> PlayStateProperty =
AvaloniaProperty.Register<Animatable, PlayState>(nameof(PlayState), PlayState.Run);
/// <summary>
/// Gets or sets the state of the animation for this
/// control.
/// Gets or sets the property transitions for the control.
/// </summary>
public PlayState PlayState
{
get { return _playState; }
set { SetAndRaise(PlayStateProperty, ref _playState, value); }
get { return GetValue(PlayStateProperty); }
set { SetValue(PlayStateProperty, value); }
}
/// <summary>
/// Defines the <see cref="Transitions"/> property.
/// </summary>
public static readonly DirectProperty<Animatable, IEnumerable<ITransition>> TransitionsProperty =
AvaloniaProperty.RegisterDirect<Animatable, IEnumerable<ITransition>>(
nameof(Transitions),
o => o.Transitions,
(o, v) => o.Transitions = v);
private IEnumerable<ITransition> _transitions = new AvaloniaList<ITransition>();
/// </summary>
public static readonly StyledProperty<IEnumerable<ITransition>> TransitionsProperty =
AvaloniaProperty.Register<Animatable, IEnumerable<ITransition>>(nameof(Transitions));
/// <summary>
/// Gets or sets the property transitions for the control.
/// </summary>
public IEnumerable<ITransition> Transitions
{
get { return _transitions; }
set { SetAndRaise(TransitionsProperty, ref _transitions, value); }
get { return GetValue(TransitionsProperty); }
set { SetValue(TransitionsProperty, value); }
}
/// <summary>

5
src/Avalonia.Animation/Animation.cs

@ -37,6 +37,11 @@ namespace Avalonia.Animation
/// </summary>
public PlaybackDirection PlaybackDirection { get; set; }
/// <summary>
/// The value fill mode for this animation.
/// </summary>
public FillMode FillMode { get; set; }
/// <summary>
/// Number of repeat iteration for this animation.
/// </summary>

4
src/Avalonia.Animation/Keyframes/KeyFrames.cs

@ -51,7 +51,7 @@ namespace Avalonia.Animation.Keyframes
/// according to the given time parameter that is relative to
/// total animation time and the normalized intra-keyframe pair time
/// (i.e., the normalized time between the selected keyframes, relative to the
/// give time parameter).
/// time parameter).
/// </summary>
/// <param name="t">The time parameter, relative to the total animation time</param>
public (double IntraKFTime, KeyFramePair<T> KFPair) GetKFPairAndIntraKFTime(double t)
@ -95,7 +95,7 @@ namespace Avalonia.Animation.Keyframes
public IDisposable RunKeyFrames(Animation animation, Animatable control)
{
var _kfStateMach = new KeyFramesStateMachine<T>();
_kfStateMach.Initialize(animation, control);
_kfStateMach.Initialize(animation, control, this);
Timing.AnimationStateTimer
.TakeWhile(_ => !_kfStateMach._unsubscribe)

53
src/Avalonia.Animation/Keyframes/KeyFramesStateMachine.cs

@ -1,4 +1,6 @@
using System;
using System.Linq;
using Avalonia.Data;
namespace Avalonia.Animation.Keyframes
{
@ -7,9 +9,8 @@ namespace Avalonia.Animation.Keyframes
/// </summary>
internal class KeyFramesStateMachine<T> : IObservable<object>, IDisposable
{
T _lastInterpValue = default(T);
T _firstKFValue = default(T);
private ulong _delayTotalFrameCount,
_durationTotalFrameCount,
_delayFrameCount,
@ -17,10 +18,16 @@ namespace Avalonia.Animation.Keyframes
_repeatCount,
_currentIteration;
private bool _isLooping, _isRepeating, _isReversed, _checkLoopAndRepeat;
private bool _isLooping,
_isRepeating,
_isReversed,
_checkLoopAndRepeat,
_gotFirstKFValue;
private FillMode _fillMode;
private PlaybackDirection _animationDirection;
KeyFramesStates _currentState, _savedState;
private Animation _parentAnimation;
private KeyFramesStates _currentState, _savedState;
private KeyFrames<T> _parent;
private Animation _targetAnimation;
private Animatable _targetControl;
internal bool _unsubscribe = false;
double _outputTime = 0d;
@ -40,9 +47,10 @@ namespace Avalonia.Animation.Keyframes
DISPOSED
}
public void Initialize(Animation animation, Animatable control)
public void Initialize(Animation animation, Animatable control, KeyFrames<T> keyframes)
{
_parentAnimation = animation;
_parent = keyframes;
_targetAnimation = animation;
_targetControl = control;
_delayTotalFrameCount = (ulong)(animation.Delay.Ticks / Timing.FrameTick.Ticks);
@ -85,14 +93,14 @@ namespace Avalonia.Animation.Keyframes
break;
}
_animationDirection = _parentAnimation.PlaybackDirection;
_animationDirection = _targetAnimation.PlaybackDirection;
_fillMode = _targetAnimation.FillMode;
if (_durationTotalFrameCount > 0)
_currentState = KeyFramesStates.DO_DELAY;
else
_currentState = KeyFramesStates.DO_RUN;
}
public void Step(PlayState _playState, Func<double, T> Interpolator)
@ -109,6 +117,13 @@ namespace Avalonia.Animation.Keyframes
private void InternalStep(PlayState _playState, Func<double, T> Interpolator)
{
if (!_gotFirstKFValue)
{
_firstKFValue = (T)_parent.First().Value;
_gotFirstKFValue = true;
}
if (_currentState == KeyFramesStates.DISPOSED)
throw new InvalidProgramException("This KeyFrames Animation is already disposed.");
@ -132,22 +147,34 @@ namespace Avalonia.Animation.Keyframes
switch (_currentState)
{
case KeyFramesStates.DO_DELAY:
if (_currentIteration == 0
&& _fillMode == FillMode.Backward
|| _fillMode == FillMode.Both)
{
_targetObserver.OnNext(_firstKFValue);
}
if (_delayFrameCount > _delayTotalFrameCount)
{
_currentState = KeyFramesStates.DO_RUN;
goto checkstate;
}
_delayFrameCount++;
break;
case KeyFramesStates.DO_RUN:
if (_isReversed)
_currentState = KeyFramesStates.RUN_BACKWARDS;
else
_currentState = KeyFramesStates.RUN_FORWARDS;
goto checkstate;
case KeyFramesStates.RUN_FORWARDS:
if (_durationFrameCount > _durationTotalFrameCount)
{
_currentState = KeyFramesStates.RUN_COMPLETE;
@ -160,6 +187,7 @@ namespace Avalonia.Animation.Keyframes
goto checkstate;
case KeyFramesStates.RUN_BACKWARDS:
if (_durationFrameCount > _durationTotalFrameCount)
{
_currentState = KeyFramesStates.RUN_COMPLETE;
@ -173,7 +201,7 @@ namespace Avalonia.Animation.Keyframes
case KeyFramesStates.RUN_APPLYVALUE:
_easedTime = _parentAnimation.Easing.Ease(_tempDuration);
_easedTime = _targetAnimation.Easing.Ease(_tempDuration);
_durationFrameCount++;
_lastInterpValue = Interpolator(_easedTime);
@ -217,6 +245,11 @@ namespace Avalonia.Animation.Keyframes
goto checkstate;
case KeyFramesStates.STOP:
if (_fillMode == FillMode.Forward
|| _fillMode == FillMode.Both)
{
_targetControl.SetValue(_parent.Property, _lastInterpValue, BindingPriority.Animation);
}
_targetObserver.OnCompleted();
break;
}

Loading…
Cancel
Save