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>
<Styles.Resources> <Styles.Resources>
<Template x:Key="Acorn"> <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> </Template>
</Styles.Resources> </Styles.Resources>
<Style Selector="Border.Test"> <Style Selector="Border.Test">
@ -16,12 +32,16 @@
<Setter Property="Child" Value="{StaticResource Acorn}"/> <Setter Property="Child" Value="{StaticResource Acorn}"/>
</Style> </Style>
<Style Selector="Border.Rect1:pointerover"> <Style Selector="Border.Rect1:pointerover">
<Setter Property="Opacity" Value="0.5"/>
<Style.Animations> <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"> <TransformKeyFrames Property="RotateTransform.Angle">
<KeyFrame Cue="0%" Value="0"/> <KeyFrame Cue="0%" Value="45"/>
<KeyFrame Cue="100%" Value="360"/> <KeyFrame Cue="100%" Value="120"/>
</TransformKeyFrames> </TransformKeyFrames>
<TransformKeyFrames Property="ScaleTransform.ScaleX"> <TransformKeyFrames Property="ScaleTransform.ScaleX">
<KeyFrame Cue="0%" Value="1"/> <KeyFrame Cue="0%" Value="1"/>
@ -86,11 +106,11 @@
<ScaleTransform/> <ScaleTransform/>
</TransformGroup> </TransformGroup>
</Border.RenderTransform> </Border.RenderTransform>
<Border.Transitions> <!-- <Border.Transitions>
<Transitions> <Transitions>
<DoubleTransition Property="Border.Opacity" Duration="0:0:2.5"/> <DoubleTransition Property="Border.Opacity" Duration="0:0:2.5"/>
</Transitions> </Transitions>
</Border.Transitions> </Border.Transitions> -->
</Border> </Border>
<Border Classes="Test Rect2" Background="DarkMagenta"> <Border Classes="Test Rect2" Background="DarkMagenta">
<Border.RenderTransform> <Border.RenderTransform>

35
src/Avalonia.Animation/Animatable.cs

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

5
src/Avalonia.Animation/Animation.cs

@ -37,6 +37,11 @@ namespace Avalonia.Animation
/// </summary> /// </summary>
public PlaybackDirection PlaybackDirection { get; set; } public PlaybackDirection PlaybackDirection { get; set; }
/// <summary>
/// The value fill mode for this animation.
/// </summary>
public FillMode FillMode { get; set; }
/// <summary> /// <summary>
/// Number of repeat iteration for this animation. /// Number of repeat iteration for this animation.
/// </summary> /// </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 /// according to the given time parameter that is relative to
/// total animation time and the normalized intra-keyframe pair time /// total animation time and the normalized intra-keyframe pair time
/// (i.e., the normalized time between the selected keyframes, relative to the /// (i.e., the normalized time between the selected keyframes, relative to the
/// give time parameter). /// time parameter).
/// </summary> /// </summary>
/// <param name="t">The time parameter, relative to the total animation time</param> /// <param name="t">The time parameter, relative to the total animation time</param>
public (double IntraKFTime, KeyFramePair<T> KFPair) GetKFPairAndIntraKFTime(double t) public (double IntraKFTime, KeyFramePair<T> KFPair) GetKFPairAndIntraKFTime(double t)
@ -95,7 +95,7 @@ namespace Avalonia.Animation.Keyframes
public IDisposable RunKeyFrames(Animation animation, Animatable control) public IDisposable RunKeyFrames(Animation animation, Animatable control)
{ {
var _kfStateMach = new KeyFramesStateMachine<T>(); var _kfStateMach = new KeyFramesStateMachine<T>();
_kfStateMach.Initialize(animation, control); _kfStateMach.Initialize(animation, control, this);
Timing.AnimationStateTimer Timing.AnimationStateTimer
.TakeWhile(_ => !_kfStateMach._unsubscribe) .TakeWhile(_ => !_kfStateMach._unsubscribe)

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

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

Loading…
Cancel
Save