Главная » WPF » Время и временная шкала

0

Первое и самое главное, что нужно понимать, когда речь идет об анимации,  – это время. В реальном  мире идея времени  проста; оно началось давным давно и течет с постоянной  скоростью в одном направлении. В анимации  со временем все не так просто.

Время при анимации  всегда соотносится с некоторой  временной  шкалой (timeline). Точка отсчета (нулевой момент)  считается  началом шкалы, а «30 се кунд» всегда означает 30 секунд от начала шкалы. Временные шкалы организова ны иерархически; начало и конец каждой отсчитываются от начала родительской шкалы. На вершине иерархии находится «глобальная» шкала, начало которой по определению  совпадает с моментом создания  процесса.

Чтобы  лучше  понять  идею относительных временных  шкал,  предположим, что нужно анимировать свойство типа double, начиная  с момента 0:0:2, или 2 се кунды. Такая  анимация  начнется  спустя  2 секунды с момента  запуска  содержа щей ее раскадровки. Если продолжительность анимации  составляет  0:0:5, то за кончится  она через 7 секунд после начала раскадровки:

<Storyboard>

<DoubleAnimation BeginTime=»0:0:2» Duration=»0:0:5» />

</Storyboard>

Рис. 5.56. Временная иерархия; анимация значения типа double начинается через 2 секунды и продолжается 5 секунд

Этот пример изображен  на рис. 5.56. Продолжительность раскадровки мы не задавали,  поэтому она закончится,  когда мы дойдем до конца самой длинной  до черней шкалы, другими словами, на седьмой секунде. В WPF не существует спо соба объявить  анимацию относительно глобального  времени.

Время в WPF измеряется в часах, минутах, секундах и миллисекундах. Может, вам это кажется  очевидным, но в системах анимирования применяется и другой способ измерения времени: по числу кадров. WPF автоматически вычисляет частоту смены кадров для каждого приложения, поэтому на одних машинах (и при определенных ус ловиях) можно получить 120 кадров в секунду, а на других – всего 12. В любом слу чае продолжительность анимации определяется реальным истекшим временем.

И последнее, что нужно знать о времени, – это то, что оно изменчиво. В WPF можно изменить  темп для любой временной  шкалы, так чтобы время текло быст рее или медленнее. Кроме того, можно сдвигать время вперед или назад в объек те AnimationClock, который  отслеживает время  указанной анимации.  Програм мы конструкторы для создания  анимаций  часто позволяют  манипулировать ло кальным  временем  сцены, чтобы можно было подкорректировать значение  того или иного свойства в конкретный момент.

Определение анимации

Приручив время, мы можем перейти к определению  анимации.  Анимация  чем то напоминает градиент:  можно  определить  начальную  точку (опорную  точку со смещением 0), ряд промежуточных точек (остальные опорные точки) и что должно случиться  в конце. В области анимирования мы говорим о начальной точке (From), промежуточных точках (определенных с помощью ключевых кадров или свойства To) и о поведении в конце процесса (FillBehavior, RepeatBehavior, AutoReverse).

Чтобы понять, как все это работает совместно, определим анимацию для изме нения размера эллипса.  Нам понадобятся две анимации:  одна для ширины,  дру гая для высоты. Проще всего для этой цели воспользоваться анимацией  типа From/To. В данном случае мы хотим анимировать эллипс с именем _target:

<Storyboard>

<DoubleAnimation From=’5’ To=’45’ Duration=’0:0:12’ Storyboard.TargetName=’_target’

Storyboard.TargetProperty=’Width’ />

<DoubleAnimation From=’5’ To=’45’ Duration=’0:0:12’ Storyboard.TargetName=’_target’

Storyboard.TargetProperty=’Height’ />

</Storyboard>

Эта анимация длится 12 секунд, на протяжении которых ширина и высота эллипса плавно увеличиваются от 5 до 45. Когда анимация завершится, эллипс станет больше. А теперь заставим  эту анимацию  продолжаться бесконечно,  запустив  как фоновую. Свойство  RepeatBehavior объекта Storyboard применяется ко всей анимации,  опреде ленной в Storyboard, поэтому дочерние анимации тоже будут повторяться бесконечно:

<Storyboard RepeatBehavior=’Forever’>

<DoubleAnimation From=’5’ To=’45’ Duration=’0:0:12’ Storyboard.TargetName=’_target’

Storyboard.TargetProperty=’Width’ />

<DoubleAnimation From=’5’ To=’45’ Duration=’0:0:12’ Storyboard.TargetName=’_target’

Storyboard.TargetProperty=’Height’ />

</Storyboard>

Свойство  RepeatBehavior определяет, как долго должна продолжаться анима ция  или  сколько  следует  выполнить итераций.  Значение Forever  – это особый случай, означающий, что повторять анимацию следует бесконечно. При выполне нии этой программы  в конце каждой итерации  происходит  мгновенный  переход с 45 на 5. Если задать еще и свойство AutoReverse, то объект Storyboard обратит все дочерние анимации  вспять (так что на полный обратный  переход потребует ся все те же 12 секунд), а затем начнет все сначала:

<Storyboard AutoReverse=’True’ RepeatBehavior=’Forever’>

<DoubleAnimation From=’5’ To=’45’ Duration=’0:0:12’ Storyboard.TargetName=’_target’

Storyboard.TargetProperty=’Width’ />

<DoubleAnimation From=’5’ To=’45’ Duration=’0:0:12’ Storyboard.TargetName=’_target’

Storyboard.TargetProperty=’Height’ />

</Storyboard>

Теперь анимация  идет плавно в обоих направлениях. Сделаем  еще одну вещь: определим набор опорных кадров для анимации.  Это способ задать несколько  мо ментов и значения  свойства, которые должны быть достигнуты  к каждому из них. Вместо того чтобы выполнять анимацию с постоянной скоростью от начала до кон ца, мы можем определить  несколько  «мини анимаций» скажем так, как показано на рис. 5.57, где ширина и высота изменяются с разной скоростью. Для этого надо вместо класса DoubleAnimation взять класс DoubleAnimationUsingKeyFrames, он то и позволит  задать последовательность опорных кадров:

<Storyboard AutoReverse=’True’ RepeatBehavior=’Forever’>

<DoubleAnimationUsingKeyFrames Storyboard.TargetName=’_target’ Storyboard.TargetProperty=’Width’>

<LinearDoubleKeyFrame KeyTime=’0:0:0’ Value=’5’ />

<LinearDoubleKeyFrame KeyTime=’0:0:2’ Value=’10’ />

<LinearDoubleKeyFrame KeyTime=’0:0:4’ Value=’15’ />

<LinearDoubleKeyFrame KeyTime=’0:0:12’ Value=’45’ />

</DoubleAnimationUsingKeyFrames>

<DoubleAnimationUsingKeyFrames Storyboard.TargetName=’_target’ Storyboard.TargetProperty=’Height’>

<LinearDoubleKeyFrame KeyTime=’0:0:0’ Value=’5’ />

<LinearDoubleKeyFrame KeyTime=’0:0:2’ Value=’5’ />

<LinearDoubleKeyFrame KeyTime=’0:0:4’ Value=’25’ />

<LinearDoubleKeyFrame KeyTime=’0:0:12’ Value=’45’ />

</DoubleAnimationUsingKeyFrames>

</Storyboard>

У системы анимирования в WPF есть и много других возможностей, не упо мянутых  выше. Например, кроме LinearDoubleKeyFrame, существуют  и другие типы опорных кадров. Определение анимации  может быть очень сложным; как и в случае двумерной или трехмерной  графики,  для создания  хорошей, реалистич ной анимации  необходим подходящий инструмент.

В примере с эллипсом  в одной раскадровке определено  две анимации,  то есть класс  Storyboard является производным не только  от Timeline,  но также  и от TimelineGroup.  Понятие  композиции,  которое  встречалось  нам  при  рассмотре

нии элементов  управления, присутствует также и в мире анимации.  С помощью композиции временных шкал мы можем вкладывать  анимации  друг в друга и за давать их порядок для достижения желаемого эффекта.

Интеграция анимации

Прежде чем покончить с анимацией, кратко остановимся на вопросе об интегра ции ее с прочими частями  платформы. Можно анимировать любое свойство, име ющее значащий тип (value type), но анимация  встроена и еще в несколько мест25.

Интеграция с управляющими шаблонами

Анимация – прекрасное средство для создания  мультимедийного содержимо го, но с точки зрения разработчика,  наверное, более интересна  возможность включения анимации  в элементы  управления. Представьте себе кнопку,  рамка которой меняет толщину  и цвет при наведении  на нее мыши (рис. 5.58). Вместо того чтобы зашивать  переход в сам элемент, мы можем определить  раскадровку как часть объекта ControlTemplate.

Применяя уже рассмотренные идеи, мы можем записать в свойство ControlTemplate.Triggers два объекта EventTrigger. Если ассоциировать элемент BeginStoryboard с подходящей раскадровкой, то анимация будет ассоциирована с шаблонным элементом управления. Отметим также, что частям дерева отображе ния внутри ControlTemplate можно присвоить  имена и обращаться  по этим име нам из Storyboard:

<Button>

<Button.Template>

<ControlTemplate TargetType=’{x:Type Button}’>

<Border Name=’_border’ CornerRadius=’4’ BorderBrush=’Black’ BorderThickness=’1’>

<ContentPresenter HorizontalAlignment=’Center’ VerticalAlignment=’Center’ />

</Border>

<ControlTemplate.Triggers>

<EventTrigger RoutedEvent=’Button.MouseEnter’>

25 Хотя тип анимируемого свойства может быть любым, но само свойство должно быть реализо

вано с помощью DependencyProperty.

<EventTrigger.Actions>

<BeginStoryboard>

<Storyboard>

<ColorAnimation To=’Red’ Storyboard.TargetName=’_border’ Storyboard.TargetProperty=

‘BorderBrush.Color’ />

<ThicknessAnimation To=’5’ Storyboard.TargetName=’_border’ Storyboard.TargetProperty=

‘BorderThickness’ />

</Storyboard>

</BeginStoryboard>

</EventTrigger.Actions>

</EventTrigger>

<EventTrigger RoutedEvent=’Button.MouseLeave’>

<EventTrigger.Actions>

<BeginStoryboard>

<Storyboard>

<ColorAnimation Storyboard.TargetName=’_border’ Storyboard.TargetProperty=

‘BorderBrush.Color’ />

<ThicknessAnimation Storyboard.TargetName=’_border’ Storyboard.TargetProperty=

‘BorderThickness’ />

</Storyboard>

</BeginStoryboard>

</EventTrigger.Actions>

</EventTrigger>

</ControlTemplate.Triggers>

</ControlTemplate>

</Button.Template> Hello World

</Button>

Интеграция с текстом

Дальнейшие эксперименты с анимацией  могут поставить  нас перед любопыт ной проблемой:  как сделать объектом  анимации  отдельные  символы  в сегменте текста. Из за сложности  системы  размещения (переносы,  форма  глифов  и т.д.) текст невозможно размещать  посимвольно,  то есть нельзя  просто разбить  текст на элементы  Span, содержащие  по одному символу.  WPF поддерживает посим вольную верстку с помощью специального  свойства текста TextEffects.  Это свой ство, имеющееся у всех текстовых элементов, позволяет  разработчику или дизай неру применить  некое преобразование к отдельным  символам.  Каждый  объект типа  TextEffect   содержит   экземпляры  объектов   Transform,   PositionStart  и

PositionCount, которые определяют,  на какие символы эффект  распространяется.

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

<Window x:Class=’EssentialWPF.TextEffectTest’ xmlns=’http://schemas.microsoft.com/winfx/2006/xaml/presentation’ xmlns:x=’http://schemas.microsoft.com/winfx/2006/xaml’ Title=’TextEffects’

<TextBlock FontSize=’72pt’ Name=’_text’ > This is animated text

</TextBlock>

</Window>

Далее нужно определиться с базовой структурой  программы. Мы собираемся на  каждой  итерации  обрабатывать  один  символ  внутри  блока,  поскольку  для каждого символа нам нужен свой эффект:

public partial class TextEffectTest : Window {

public TextEffectTest() { InitializeComponent();

for (int i = 0; i < _text.Text.Length; i++) {

}

}

}

Сначала построим объекты TextEffect. С каждым эффектом ассоциируем начальную позицию и счетчик символов, а также преобразование, которое будем анимировать:

public partial class TextEffectTest : Window {

public TextEffectTest() { InitializeComponent();

_text.TextEffects = new TextEffectCollection();

for (int i = 0; i < _text.Text.Length; i++) { TextEffect effect = new TextEffect(); effect.Transform = new TranslateTransform(); effect.PositionStart = i; effect.PositionCount = 1;

_text.TextEffects.Add(effect);

}

}

}

Наконец, создадим раскадровку, которая будет применять анимацию к преоб

разованиям в созданных объектах TextEffect:

public partial class TextEffectTest : Window {

public TextEffectTest() { InitializeComponent();

Storyboard perChar = new Storyboard();

_text.TextEffects = new TextEffectCollection();

for (int i = 0; i < _text.Text.Length; i++) { TextEffect effect = new TextEffect(); effect.Transform = new TranslateTransform(); effect.PositionStart = i; effect.PositionCount = 1;

_text.TextEffects.Add(effect);

DoubleAnimation anim = new DoubleAnimation();

anim.To = 5; anim.AccelerationRatio = .2; anim.DecelerationRatio = .2; anim.RepeatBehavior = RepeatBehavior.Forever; anim.AutoReverse = true;

anim.Duration = TimeSpan.FromSeconds(2); anim.BeginTime = TimeSpan.FromMilliseconds(250 * i); Storyboard.SetTargetProperty(anim,

new PropertyPath(«TextEffects[« + i + «].Transform.Y»));

Storyboard.SetTargetName(anim, _text.Name);

perChar.Children.Add(anim);

}

perChar.Begin(this);

}

}

Что получилось,  показано на рис. 5.59.

Источник: К. Андерсон  Основы  Windows Presentation Foundation. Пер. с англ. А. Слинкина — М.: ДМК Пресс, 2008 — 432 с.: ил.

По теме:

  • Комментарии