ГЛАВНАЯ     АРХИВ     НАПИСАТЬ АДМИНУ     ПОДПИСАТЬСЯ НА RSS     ВОЙТИ      

Поиск

Категории

Облако тегов

  << Предыдущий пост       Следующий пост >>  
От: peerAt
26. апреля 2011 02:00

Оригинал статьи взят отсюда: Сервелат, анимация и старый добрый code-behind

Автор (на хабре): ThePretender

Решил немножко покопаться в Silverlight, да смастерить на нём что-нибудь прикольное. Это прикольное, конечно, должно шевелиться, переливаться и плавно подёргиваться, ибо вебдваноль у нас или где? :). И вот тут мне пришлось столкнуться с неплохой, по сути, системой анимаций в WPF/Silverlight. Покурив MSDN, я бодренько приступил к написанию анимаций в XAML. Одну написал, вторую, третью… А потом мне захотелось сделать так, чтобы они шли в определённой последовательности. И вот тут-то я и понял, что XAML, зараза, очень избыточный. Для описания интерфейсов он подходит идеально: сразу видно, что к чему относится и надобность в визуальном редакторе отпадает чуть менее, чем полностью. Но вот когда пытаешься написать в этом XAMLе какую-то логику, начинает проявляться вся его несуразность. Покурив гугл, я был сильно удивлён тем, что большинство людей упорно пытаются впихнуть в XAML абсолютно всё. Ругаются, путаются в коде, плачут, но продолжают писать. Прямо как те мыши с кактусом, чесслово. И тут мне пришла идея аккуратно описать анимации обычным кодом на C#. Мы, так сказать, олдфаги, рисовали интерфейс прямыми вызовами к WinAPI, неужто нас какие-то анимации испугают? :)

В результате получился вот такой портабельный класс AnimationBag. Портабельный он потому, что его безо всяких изменений можно использовать как в WPF, так и в Silverlight.


public class AnimationItem
{
    public event EventHandler Completed;

    private void OnStoryboardComplete(object sender, EventArgs e)
    {
        if (Completed != null)
            Completed(this, EventArgs.Empty);
    }

    private Storyboard storyboard;
    public Storyboard Storyboard
    {
        get { return storyboard; }
        set
        {
            if (storyboard != null)
                storyboard.Completed -= OnStoryboardComplete;

            storyboard = value;
            storyboard.Completed += OnStoryboardComplete;
        }
    }

    public Action BeginAction { get; set; }
    public Action EndAction { get; set; }
}

public class AnimationBag
{
    private readonly Dictionary<string, AnimationItem> storyboards =
             new Dictionary<string, AnimationItem>();

    public void AddAnimation(string name, DependencyObject control,
             string propertyName, Timeline animation, Action beginAction = null, Action endAction = null)
    {
        Storyboard board = new Storyboard();
        AnimationItem item = new AnimationItem { BeginAction = beginAction, EndAction = endAction };

        Storyboard.SetTarget(animation, control);
        Storyboard.SetTargetProperty(animation, new PropertyPath(propertyName));
        board.Children.Add(animation);
        if (endAction != null)
            item.Completed += item_Completed;
        item.Storyboard = board;

        storyboards[name] = item;
    }

    private void item_Completed(object sender, EventArgs e)
    {
        KeyValuePair<string, AnimationItem> pair =
                     storyboards.Where(x => x.Value.Equals(sender)).FirstOrDefault();

        if (pair.Value != null && pair.Value.EndAction != null)
            pair.Value.EndAction.Invoke();
    }

    public void StartAnimation(string name)
    {
        if (!storyboards.ContainsKey(name))
            return;

        if (storyboards[name].BeginAction != null)
            storyboards[name].BeginAction.Invoke();

        storyboards[name].Storyboard.Begin();
    }

    public void StopAnimation(string name)
    {
        if (!storyboards.ContainsKey(name))
            return;

        storyboards[name].Storyboard.Stop();

        if (storyboards[name].EndAction != null)
            storyboards[name].EndAction.Invoke();
    }

    public static DoubleAnimation CreateDoubleAnimation(double? to, long durationMs,
            double? from = null, bool repeat = false)
    {
        DoubleAnimation ret = new DoubleAnimation { To = to, From = from,
                    Duration = new Duration(TimeSpan.FromMilliseconds(durationMs)) };

        if (repeat)
            ret.RepeatBehavior = RepeatBehavior.Forever;

        return ret;
    }
}

Как видно из кода, класс предельно простой. Вот пример использования.

XAML:


<Window
x:Class="Animation.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow"
Height="350"
Width="525"
>
<Grid>
<Rectangle Height="110" HorizontalAlignment="Left"
Margin="55,71,0,0" Name="rectangle1" Stroke="Black"
VerticalAlignment="Top" Width="181" Fill="#FF9D3434"
/>
<Button Content="Button" Height="36" HorizontalAlignment="Left"
Margin="286,93,0,0" Name="button1" VerticalAlignment="Top"
Width="149" Click="button1_Click"
/>
</Grid>
</Window>

Code-behind:


public partial class MainWindow : Window
{
    private readonly AnimationBag animations = new AnimationBag();

    public MainWindow()
    {
        InitializeComponent();
        InitAnimations();
    }

    private void InitAnimations()
    {
        animations.AddAnimation(
            "fadeOut",
            rectangle1,
            "Opacity",
            AnimationBag.CreateDoubleAnimation(0, 500),
            null,
            () => animations.StartAnimation("fadeIn"));

        animations.AddAnimation(
            "fadeIn",
            rectangle1,
            "Opacity",
            AnimationBag.CreateDoubleAnimation(1, 500));
    }

    private void button1_Click(object sender, RoutedEventArgs e)
    {
        animations.StartAnimation("fadeOut");
    }
}

Сам класс представляет собой что-то вроде словаря с ключами — именами анимаций и значениями — анимациями. В методе InitAnimations в коллекцию добавляются две анимации, при этом указывается имя, контрол, над которым будет производится действо, свойство этого контрола и сам объект анимации. Его можно создавать ручками, а можно добавить статические хэлперы к уже имеющемуся методу для DoubleAnimation. Кроме всего прочего, метод AddAnimation может принимать два делегата, которые будут выполняться до и после самой анимации. Например, здесь после завершения анимации “fadeOut” сразу запускается “fadeIn”.

В итоге, получился довольно удобный механизм, позволяющий описывать анимации одной строкой кода вместо килобайтов перегруженного XAML.

Скачать исходники с тестовыми проектами

Похожие записи


Новый Старый Бойцовский клуб
Если у вас появилось немного свободного времени, то хорошим вариантом будет попробовать свои силы в какой-то онлайн игре. Компьютерные онлайн игры – это всегда залог веселья, новых знакомств и впечатлений. Для тех, кому лень возиться с установкой массивного клиента игры, существуют не менее интересные и захватывающие браузерные игры. И если кто-то д...

Irrlicht - это графический 3D движок с открытым исходным кодом
Оригинал cтатьи взят отсюда: www.irrlicht.ru Irrlicht - это графический 3D движок с открытым исходным кодом и высоким быстродействием, написанный на C++. Он являет собой готовый кросс-платформенный продукт, использующий как D3D, OpenGL так и собственный рендерер. В движке присутствуют все возможности коммерческих продуктов. Движок, за время существования, обрел огро...

Ianprime XNA Engine (IXNAE) - 2D игровой движек под XNA
IXNAE проект - это 2D движок XNA. Он создан, чтобы помочь разработчикам быстро создавать игры под XNA, не беспокоясь о простых, повторяющихся задачах. IXNAE упрощает анимацию спрайтов, а также создание игровых объектов. Области, в которых IXNAE упрощает жизнь разработчика игр: 1. Спрайты (анимация, проигрывание последовательностей, масштабирование, раскрашиван...

Добавить комментарий




biuquote
  • Комментарий
  • Предпросмотр
Loading


  Сохранить комментарий