在WPF中拥有精美的动画非常好,所以我尝试为UIElements实现一个通用的淡入动画,这是我在应用程序中使用的弹出窗口。我在想,它是不是有什么问题,并考虑将它张贴在这里昨天。当时确实有很多错误,从那时起,我对代码做了很大的改进(我认为),但是现在可能仍然存在一些问题。
我发现的第一个缺陷是,通过暂时改变RenderTransform,如果启动相同的动画,而另一个动画还在播放,那么原版就会被搞砸,因为he的Completed事件将永远不会被调用,而原始的RenderTransform将与来自我被破坏的动画的半动画转换合并。
为了防止这一点,我现在有一个HashSet,它被检查为UIElement,如果另一个还在进行中,就不会启动动画,也不确定这是否是这里最好的方法。
namespace HB.Animation
{
public enum Direction { Up, Down, Left, Right }
public enum AnimationType { In, Out }
public abstract class UIElementAnimationBase
{
private static readonly HashSet<UIElement> _animatedElements = new HashSet<UIElement>();
private AnimationType _type = AnimationType.In;
/// <summary>
/// Gets or sets the type of the animation. Default is In.
/// </summary>
public AnimationType Type
{
get { return _type; }
set { _type = value; }
}
private UIElement _target = null;
/// <summary>
/// Gets or sets the target of the animation.
/// </summary>
public UIElement Target
{
get { return _target; }
set { _target = value; }
}
private TimeSpan _duration = TimeSpan.FromSeconds(0.3);
/// <summary>
/// Gets or sets the duration of the animation. Default is 0.3 seconds.
/// </summary>
public TimeSpan Duration
{
get { return _duration; }
set { _duration = value; }
}
public event EventHandler Completed;
protected void OnCompleted()
{
_animatedElements.Remove(Target);
if (Completed != null)
{
Completed(this, null);
}
}
public void BeginAnimation()
{
if (_animatedElements.Contains(Target))
{
return;
}
else
{
_animatedElements.Add(Target);
}
BeginAnimationDetail();
}
protected abstract void BeginAnimationDetail();
}
public class FadeAnimation : UIElementAnimationBase
{
private Direction _direction = Direction.Down;
/// <summary>
/// Gets or sets the direction of the animation. Default is Down.
/// </summary>
public Direction Direction
{
get { return _direction; }
set { _direction = value; }
}
private double _distance = 50;
/// <summary>
/// Gets or sets the distance of the target travels in the animation. Default is 50.
/// </summary>
public double Distance
{
get { return _distance; }
set { _distance = value; }
}
private FillBehavior _opacityBehavior = FillBehavior.HoldEnd;
/// <summary>
/// Gets or sets the fill behavior of the opacity sub-animation. Default is HoldEnd.
/// </summary>
public FillBehavior OpacityBehavior
{
get { return _opacityBehavior; }
set { _opacityBehavior = value; }
}
public FadeAnimation(UIElement target)
{
Target = target;
}
public FadeAnimation(UIElement target, AnimationType type, Direction direction)
: this(target)
{
Type = type;
Direction = direction;
}
protected override void BeginAnimationDetail()
{
Transform tempTrans = Target.RenderTransform;
TranslateTransform trans;
EasingMode easing;
double opacityTarget;
double translateOrigin;
double translateTarget;
DependencyProperty translateProperty;
switch (Type)
{
case AnimationType.In:
easing = EasingMode.EaseOut;
opacityTarget = 1;
translateTarget = 0;
switch (Direction)
{
case Direction.Up:
trans = new TranslateTransform(0, -Distance);
translateProperty = TranslateTransform.YProperty;
break;
case Direction.Down:
trans = new TranslateTransform(0, Distance);
translateProperty = TranslateTransform.YProperty;
break;
case Direction.Left:
trans = new TranslateTransform(-Distance, 0);
translateProperty = TranslateTransform.XProperty;
break;
case Direction.Right:
trans = new TranslateTransform(Distance, 0);
translateProperty = TranslateTransform.XProperty;
break;
default:
throw new InvalidOperationException();
}
break;
case AnimationType.Out:
easing = EasingMode.EaseIn;
opacityTarget = 0;
trans = new TranslateTransform(0, 0);
switch (Direction)
{
case Direction.Up:
translateTarget = -Distance;
translateProperty = TranslateTransform.YProperty;
break;
case Direction.Down:
translateTarget = Distance;
translateProperty = TranslateTransform.YProperty;
break;
case Direction.Left:
translateTarget = -Distance;
translateProperty = TranslateTransform.XProperty;
break;
case Direction.Right:
translateTarget = Distance;
translateProperty = TranslateTransform.XProperty;
break;
default:
throw new InvalidOperationException();
}
break;
default:
throw new InvalidOperationException();
}
TransformGroup group = new TransformGroup();
if (tempTrans != null) group.Children.Add(tempTrans);
group.Children.Add(trans);
Target.RenderTransform = group;
DoubleAnimation animTranslate = new DoubleAnimation(translateTarget, (Duration)Duration);
animTranslate.EasingFunction = new CubicEase() { EasingMode = easing };
DoubleAnimation animFade = new DoubleAnimation(opacityTarget, (Duration)Duration) { FillBehavior = OpacityBehavior };
animTranslate.Completed += delegate
{
Target.RenderTransform = tempTrans;
OnCompleted();
};
Target.BeginAnimation(UIElement.OpacityProperty, animFade);
trans.BeginAnimation(translateProperty, animTranslate);
}
}
}下面是一个用法示例:
private void DisplayMode_QuickSelect_Executed(object sender, ExecutedRoutedEventArgs e)
{
Popup popup = FindResource("DisplayModePopup") as Popup;
FadeAnimation anim = new FadeAnimation(popup.Child) { Duration = Settings.BalloonAnimationDuration };
if (popup.IsOpen)
{
anim.Type = HB.Animation.AnimationType.Out;
anim.Completed += delegate { popup.IsOpen = false; };
}
else
{
popup.Child.Opacity = 0;
popup.IsOpen = true;
}
anim.BeginAnimation();
}发布于 2011-04-01 22:50:37
我会专注于BeginAnimationDetail方法。
tempTrans和trans。tempTrans名称没有告诉我这个变量是关于什么的。例如,我把它命名为originalTransform --这就足以理解为什么要将它添加到另一个转换中,然后再将控件的转换重置到这个转换中。从tempTrans的名字来看,还不清楚你将如何使用它。switch语句)设置一些变量。除了代码的某些部分在代码中重复的事实之外,我发现在定义变量时,当变量被分配到远离行的位置时,很难读懂它们。为了避免这种情况,我会重写方法。算法可以是:in和out点的位置。In = {0, 0},Out依赖于Direction和Distance。结果(缩短约两倍):
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}});
}发布于 2015-12-18 09:50:58
现在,使用自动实现的属性是可行的。
/// <summary>
/// Gets or sets the type of the animation. Default is In.
/// </summary>
public AnimationType Type { get; set; } = AnimationType.In;看上去比
私有AnimationType _type = AnimationType.In;/ /获取或设置动画的类型。默认值在。/公共AnimationType类型{ get {返回_type;} set { _type =值;}
使用C# 6 (VS 2015),我们可以简化OnCompleted()方法中的空检查,如下所示
protected void OnCompleted()
{
_animatedElements.Remove(Target);
Completed?.Invoke(this, null);
} 如果我们利用HashSet.Add()方法的结果,我们可以将BeginAnimation()方法简化如下
public void BeginAnimation()
{
if (_animatedElements.Add(Target))
{
BeginAnimationDetail();
}
} 但是,如果是NullReferenceException,我将抛出一个Target == null,因此它将从public方法而不是protected abstract void BeginAnimationDetail()方法中抛出。海事组织,这是必要的,因为Target属性是公共的,因此它可以被错误地设置为null。
https://codereview.stackexchange.com/questions/1617
复制相似问题