patterncsharpMinor
Custom UIElement Animations
Viewed 0 times
animationscustomuielement
Problem
Having fancy animations in WPF is quite nice so I tried to implement a generic fading animation for
One of the first flaws I came across was that by temporarily changing the
To prevent this I now have a
```
namespace HB.Animation
{
public enum Direction { Up, Down, Left, Right }
public enum AnimationType { In, Out }
public abstract class UIElementAnimationBase
{
private static readonly HashSet _animatedElements = new HashSet();
private AnimationType _type = AnimationType.In;
///
/// Gets or sets the type of the animation. Default is In.
///
public AnimationType Type
{
get { return _type; }
set { _type = value; }
}
private UIElement _target = null;
///
/// Gets or sets the target of the animation.
///
public UIElement Target
{
get { return _target; }
set { _target = value; }
}
private TimeSpan _duration = TimeSpan.FromSeconds(0.3);
///
/// Gets or sets the duration of the animation. Default is 0.3 seconds.
///
public TimeSpan Duration
{
get {
UIElements which I use for the Popups in my application. I was wondering if there was anything wrong with it, and thought about posting it here yesterday. There were indeed many things wrong at that time and I have improved the code greatly since then (I think) but there probably are still some issues with it now.One of the first flaws I came across was that by temporarily changing the
RenderTransform the original can get messed up if the same animation is started while another one is still playing because the Completed event of he first would never be called and the original RenderTransform is merged with a half-animated transform that comes from my disrupted animation.To prevent this I now have a
HashSet which is checked for the UIElement in question and no animation will be started if another one is still in progress, not sure if that is the best approach here.```
namespace HB.Animation
{
public enum Direction { Up, Down, Left, Right }
public enum AnimationType { In, Out }
public abstract class UIElementAnimationBase
{
private static readonly HashSet _animatedElements = new HashSet();
private AnimationType _type = AnimationType.In;
///
/// Gets or sets the type of the animation. Default is In.
///
public AnimationType Type
{
get { return _type; }
set { _type = value; }
}
private UIElement _target = null;
///
/// Gets or sets the target of the animation.
///
public UIElement Target
{
get { return _target; }
set { _target = value; }
}
private TimeSpan _duration = TimeSpan.FromSeconds(0.3);
///
/// Gets or sets the duration of the animation. Default is 0.3 seconds.
///
public TimeSpan Duration
{
get {
Solution
I would focus on
-
Variable names. I would rename at least these variables -
-
In the beginning of that method you have several variables defined and then for ~60 lines (two
-
Calculate positions of
-
Decide at which point animation will start and at which point it will finish.
-
Create animations and start them.
Result (it is around two times shorter):
BeginAnimationDetail method. -
Variable names. I would rename at least these variables -
tempTrans and trans. tempTrans name doesn't give me any idea what is this variable about. I would name it originalTransform for example - it will be enough to understand why do you add it to another transform and later resetting control's transform to this one. From tempTrans name it is unclear how will you use it. -
In the beginning of that method you have several variables defined and then for ~60 lines (two
switch statements) you're setting some values to those variables. Besides the fact that some part of code is repeated there, I find it very difficult to read when variables are assigned far from the line when they were defined. I would rewrite method in order to avoid it. Algorithm could be: -
Calculate positions of
in and out points. In = {0, 0}, Out depends on Direction and Distance. -
Decide at which point animation will start and at which point it will finish.
-
Create animations and start them.
Result (it is around two times shorter):
protected override void BeginAnimationDetail()
{
var inPoint = new Point(0, 0);
Point outPoint;
switch (Direction)
{
case Direction.Up:
outPoint = new Point(0, -Distance);
break;
case Direction.Down:
outPoint = new Point(0, Distance);
break;
case Direction.Left:
outPoint = new Point(-Distance, 0);
break;
case Direction.Right:
outPoint = new Point(Distance, 0);
break;
default:
throw new InvalidOperationException();
}
Transform originalTransform = Target.RenderTransform;
var easing = Type == AnimationType.In ? EasingMode.EaseOut : EasingMode.EaseIn;
double opacityTarget = Type == AnimationType.In ? 1 : 0;
Point from = Type == AnimationType.In ? outPoint : inPoint;
Point to = Type == AnimationType.In ? inPoint : outPoint;
var animatedTranslate = new TranslateTransform(from.X, from.Y);
var group = new TransformGroup();
if (originalTransform != null) group.Children.Add(originalTransform);
group.Children.Add(animatedTranslate);
Target.RenderTransform = group;
var animFade = new DoubleAnimation(opacityTarget, Duration) {FillBehavior = OpacityBehavior};
animFade.Completed += delegate
{
Target.RenderTransform = originalTransform;
OnCompleted();
};
Target.BeginAnimation(UIElement.OpacityProperty, animFade);
animatedTranslate.BeginAnimation(TranslateTransform.XProperty, new DoubleAnimation(to.X, Duration) {EasingFunction = new CubicEase {EasingMode = easing}});
animatedTranslate.BeginAnimation(TranslateTransform.YProperty, new DoubleAnimation(to.Y, Duration) {EasingFunction = new CubicEase {EasingMode = easing}});
}Code Snippets
protected override void BeginAnimationDetail()
{
var inPoint = new Point(0, 0);
Point outPoint;
switch (Direction)
{
case Direction.Up:
outPoint = new Point(0, -Distance);
break;
case Direction.Down:
outPoint = new Point(0, Distance);
break;
case Direction.Left:
outPoint = new Point(-Distance, 0);
break;
case Direction.Right:
outPoint = new Point(Distance, 0);
break;
default:
throw new InvalidOperationException();
}
Transform originalTransform = Target.RenderTransform;
var easing = Type == AnimationType.In ? EasingMode.EaseOut : EasingMode.EaseIn;
double opacityTarget = Type == AnimationType.In ? 1 : 0;
Point from = Type == AnimationType.In ? outPoint : inPoint;
Point to = Type == AnimationType.In ? inPoint : outPoint;
var animatedTranslate = new TranslateTransform(from.X, from.Y);
var group = new TransformGroup();
if (originalTransform != null) group.Children.Add(originalTransform);
group.Children.Add(animatedTranslate);
Target.RenderTransform = group;
var animFade = new DoubleAnimation(opacityTarget, Duration) {FillBehavior = OpacityBehavior};
animFade.Completed += delegate
{
Target.RenderTransform = originalTransform;
OnCompleted();
};
Target.BeginAnimation(UIElement.OpacityProperty, animFade);
animatedTranslate.BeginAnimation(TranslateTransform.XProperty, new DoubleAnimation(to.X, Duration) {EasingFunction = new CubicEase {EasingMode = easing}});
animatedTranslate.BeginAnimation(TranslateTransform.YProperty, new DoubleAnimation(to.Y, Duration) {EasingFunction = new CubicEase {EasingMode = easing}});
}Context
StackExchange Code Review Q#1617, answer score: 5
Revisions (0)
No revisions yet.