Анимации Объекта UIElement


Имеющие причудливые анимации в WPF является достаточно хорошим, поэтому я попытался реализовать универсальный замирания анимации для UIElements , который я использую для окна в моем приложении. Мне было интересно, если там было что-то неправильно с ним, и думал о публикации его здесь вчера. Есть действительно много вещей, в то время не так, и я значительно улучшил код с тех пор (как мне кажется) но там наверное еще какие-то проблемы с ним сейчас.

Одним из первых недостатков я наткнулся, что по временным изменением RenderTransform оригинал можно запутаться, если же, запускается анимация, а еще один до сих пор играет, потому что завершен случае он прежде никогда бы не позвонил и оригинальный RenderTransform объединяется с пол-анимированные преобразование, которое исходит из моего сорвана анимации.

Для предотвращения этого у меня сейчас для поиска HashSet , который проверяется на произведенные и никакая анимация будет запускаться, если другой еще в процессе, а не уверен, если это лучший подход здесь.

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();
}


955
3
задан 1 апреля 2011 в 09:04 Источник Поделиться
Комментарии
1 ответ

Я бы сосредоточилась на BeginAnimationDetail способ.


  • Имена переменных. Я бы переименовать хоть эти переменные - tempTrans и транс. tempTrans имя не дает мне никакого представления, что это переменная об. Я бы назвал это originalTransform например - этого будет достаточно, чтобы понять, почему вы добавляете его в другую преобразование и позже сброс управления преобразование к этим. От tempTrans имя непонятно, как вы будете его использовать.

  • В начале этого метода есть несколько определенных переменных, а затем на ~60 строк (два выключателя заявления) вы устанавливаете какие-то значения этих переменных. Кроме того, что какая-то часть кода повторяется там, я найти его очень трудно читать, когда переменные назначаются далеко от линии, когда они были определены. Я бы переписать метод для того, чтобы избежать этого. Алгоритм может быть следующим:


    • Рассчитать позиции в и из точек. В = {0, 0}, вне зависимости от направления и расстояния.

    • Решите, в какой момент анимации начнется и в какой момент оно закончится.

    • Создавать анимацию и запустить их.


Результат (это примерно в два раза короче):

   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}});
}

4
ответ дан 1 апреля 2011 в 10:04 Источник Поделиться