Спонсор: Microsoft
Опубликован: 23.01.2009 | Уровень: для всех | Доступ: платный
Лекция 7:

Анимация в XAML-графике

Анимация вдоль заданной траектории

Одна из самых интересных возможностей компьютерной анимации – это движение фигуры вдоль произвольно задаваемой траектории (рис. 7.9):

1.  Два отдельных объекта. 2. Эллипс становится траекторией движения. 3. Движение вдоль заданной траектории (иллюстрация из справки Microsoft Expression Blend)

Рис. 7.9. 1. Два отдельных объекта. 2. Эллипс становится траекторией движения. 3. Движение вдоль заданной траектории (иллюстрация из справки Microsoft Expression Blend)

В качестве траектории может использоваться не только замкнутая фигура, но и вообще, любая кривая, нарисованная, например, с помощью инструмента "Кисть". К сожалению, на момент написания этого курса, в Silverlight-проектах не поддерживается привязка движения к траектории. Однако эта возможность реализована в WPF-приложениях. Рассмотрим такой пример на практике. Создаем новый WPF – проект (рис. 7.10):

Создание нового WPF-приложения

Рис. 7.10. Создание нового WPF-приложения

Сначала нарисуем окружность, которая будет служить траекторией. Для этого выбираем инструмент эллипс и рисуем фигуру – можно даже без всякой заливки (рис. 7.11):

Рисование траектории

увеличить изображение
Рис. 7.11. Рисование траектории

Теперь добавим окружность, которая и будет двигаться. Ее расположение на холсте, цвет заливки и оформления могут быть совершенно произвольными. Но на панели "Properties", в поле "Name " введем имя фигуры, например, "myBall" (рис. 7.12):

Фигура, которая будет двигаться по траектории, должна иметь заполненное значение поля Name

увеличить изображение
Рис. 7.12. Фигура, которая будет двигаться по траектории, должна иметь заполненное значение поля Name

Выделяем исходный эллипс, который будет служить траекторией и в главном меню выбираем пункт "Object \ Path \ Convert to Motion Path" (рис. 7.13):

Пункт главного меню "Object \ Path \ Convert to Motion Path".

Рис. 7.13. Пункт главного меню "Object \ Path \ Convert to Motion Path".

При этом происходит преобразование текущей кривой в траекторию движения. В появившемся диалоговом окне "Convert to Motion Path" нам нужно выбрать объект, который будет привязан к создаваемой траектории. Здесь мы указываем объект "myBall" (рис. 7.14):

Выбор привязываемого объекта

Рис. 7.14. Выбор привязываемого объекта

Практически все готово. Вид редактора Microsoft Expression Blend меняется – шарик перескакивает на свою стартовую позицию, причем его центр совмещен с траекторией (рис. 7.15):

Вид редактора Microsoft Expression Blend после завершения привязки объекта к траектории

увеличить изображение
Рис. 7.15. Вид редактора Microsoft Expression Blend после завершения привязки объекта к траектории

Запускаем приложение, нажимая клавишу F5. Объект двигается по замкнутой траектории (рис. 7.16):

Готовое приложение

увеличить изображение
Рис. 7.16. Готовое приложение

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

<Window
  xmlns="http://schemas.microsoft.com/winfx/2006/
  xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  x:Class="Motion_Path.Window1"
  x:Name="Window"
  Title="Window1"
  Width="640" Height="480">
  <Window.Resources>
    <Storyboard x:Key="Storyboard1">
      <DoubleAnimationUsingPath BeginTime="00:00:00" 
Duration="00:00:02" Storyboard.TargetName="myBall" 
Storyboard.TargetProperty="(UIElement.RenderTransform).(
TransformGroup.Children)[3].
(TranslateTransform.X)" Source="X">
  <DoubleAnimationUsingPath.PathGeometry>
    <PathGeometry>
        <PathFigure IsClosed="True" 
        StartPoint="241.5,12">
          <BezierSegment Point1="241.5,53.1452138623941" 
Point2="167.179268471912,86.5" Point3="75.5,86.5" IsSmoothJoin="True"/>
          <BezierSegment Point1="-16.1792684719117,86.5"
 Point2="-90.5,53.1452138623941" Point3="-90.5,12" IsSmoothJoin="True"/>
          <BezierSegment Point1="-90.5,-29.1452138623941"
 Point2="-16.1792684719117,-62.5" Point3="75.5,-62.5" IsSmoothJoin="True"/>
          <BezierSegment Point1="167.179268471912,-62.5" 
Point2="241.5,-29.1452138623941" Point3="241.5,12" IsSmoothJoin="True"/>
          </PathFigure>
        </PathGeometry>
      </DoubleAnimationUsingPath.PathGeometry>
    </DoubleAnimationUsingPath>
    <DoubleAnimationUsingPath 
    BeginTime="00:00:00" Duration="00:00:02" 
    Storyboard.TargetName="myBall" Storyboard.
TargetProperty="(UIElement.RenderTransform).
(TransformGroup.Children)[3].(TranslateTransform.Y)" Source="Y">
    <DoubleAnimationUsingPath.PathGeometry>
      <PathGeometry>
        <PathFigure IsClosed="True" StartPoint="241.5,12">
          <BezierSegment Point1="241.5,53.1452138623941"
 Point2="167.179268471912,86.5" Point3="75.5,86.5" IsSmoothJoin="True"/>
          <BezierSegment Point1="-16.1792684719117,86.5" 
Point2="-90.5,53.1452138623941" Point3="-90.5,12" IsSmoothJoin="True"/>
          <BezierSegment Point1="-90.5,-29.1452138623941"
 Point2="-16.1792684719117,-62.5" Point3="75.5,-62.5" IsSmoothJoin="True"/>
          <BezierSegment Point1="167.179268471912,-62.5" 
Point2="241.5,-29.1452138623941" Point3="241.5,12" IsSmoothJoin="True"/>
        </PathFigure>
      </PathGeometry>
    </DoubleAnimationUsingPath.PathGeometry>
  </DoubleAnimationUsingPath>
  </Storyboard>
</Window.Resources>
<Window.Triggers>
<EventTrigger RoutedEvent="FrameworkElement.Loaded">
  <BeginStoryboard Storyboard="{StaticResource Storyboard1}"/>
  </EventTrigger>
</Window.Triggers>

<Grid x:Name="LayoutRoot">
  <Ellipse Margin="144,0,147,53" VerticalAlignment="Bottom" 
Height="150" Fill="#FFFFFFFF" Stroke="#FF000000"/>
  <Ellipse HorizontalAlignment="Left" Margin="210,0,0,115" 
  VerticalAlignment="Bottom" Width="50" Height="50" 
Fill="#FFFF0000" 
Stroke="#FF000000" x:Name="myBall" RenderTransformOrigin="0.5,0.5">
  <Ellipse.RenderTransform>
    <TransformGroup>
      <ScaleTransform ScaleX="1" ScaleY="1"/>
      <SkewTransform AngleX="0" AngleY="0"/>
      <RotateTransform Angle="0"/>
      <TranslateTransform X="0" Y="0"/>
    </TransformGroup>
  </Ellipse.RenderTransform>
</Ellipse>
</Grid>
</Window>
7.1.

Его можно упростить, если убрать дробные значения в координатах, а также удалить ненужные команды трансформации:

<Window
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  x:Class="Motion_Path.Window1"
  x:Name="Window"
  Title="Window1"
  Width="640" Height="480">
  <Window.Resources>
  <Storyboard x:Key="Storyboard1">
    <DoubleAnimationUsingPath BeginTime="00:00:00"
 Duration="00:00:02" Storyboard.TargetName="myBall" 
 Storyboard.TargetProperty="(UIElement.RenderTransform).(TranslateTransform.X)" Source="X">
    <DoubleAnimationUsingPath.PathGeometry>
      <PathGeometry>
        <PathFigure IsClosed="True" StartPoint="241.5,12">
          <BezierSegment Point1="241.5,53.1" 
Point2="167.1,86.5" Point3="75.5,86.5" />
          <BezierSegment Point1="-16.1,86.5" 
Point2="-90.5,53.1" Point3="-90.5,12" />
          <BezierSegment Point1="-90.5,-29.1" 
          Point2="-16.1,-62.5" Point3="75.5,-62.5" />
          <BezierSegment Point1="167.179268471912,-62.5" 
Point2="241.5,-29.1" Point3="241.5,12" />
        </PathFigure>
      </PathGeometry>
    </DoubleAnimationUsingPath.PathGeometry>
  </DoubleAnimationUsingPath>
  <DoubleAnimationUsingPath BeginTime="00:00:00" 
Duration="00:00:02" Storyboard.TargetName="myBall" Storyboard.
TargetProperty="(UIElement.RenderTransform).(TranslateTransform.Y)" Source="Y">
  <DoubleAnimationUsingPath.PathGeometry>
    <PathGeometry>
      <PathFigure IsClosed="True" StartPoint="241.5,12">
        <BezierSegment Point1="241.5,53.1" 
Point2="167.1,86.5" Point3="75.5,86.5" />
        <BezierSegment Point1="-16.1,86.5" 
Point2="-90.5,53.1" Point3="-90.5,12" />
        <BezierSegment Point1="-90.5,-29.1" 
Point2="-16.1,-62.5" Point3="75.5,-62.5" />
        <BezierSegment Point1="167.179268471912,-62.5" 
Point2="241.5,-29.1" Point3="241.5,12" />    
      </PathFigure>
    </PathGeometry>
  </DoubleAnimationUsingPath.PathGeometry>
</DoubleAnimationUsingPath>
</Storyboard>
</Window.Resources>
<Window.Triggers>
<EventTrigger RoutedEvent="FrameworkElement.Loaded">
  <BeginStoryboard Storyboard="{StaticResource Storyboard1}"/>
  </EventTrigger>
</Window.Triggers>

<Grid x:Name="LayoutRoot">
  <Ellipse Margin="144,0,147,53" VerticalAlignment="Bottom" 
Height="150" Fill="Transparent" Stroke="Black"/>
  <Ellipse HorizontalAlignment="Left" Margin="210,0,0,115"
 VerticalAlignment="Bottom" Width="50" Height="50"
 Fill="Red" x:Name="myBall" RenderTransformOrigin="0.5,0.5">
    <Ellipse.RenderTransform>    
      
      <TranslateTransform X="0" Y="0"/>

    </Ellipse.RenderTransform>
  </Ellipse>
</Grid>
</Window>
7.2.

Здесь мы видим, что привязка к траектории, описываемая объектом Path, осуществляется дважды – для команды TranslateTransform.X и TranslateTransform.Y. Вместо данных, подставляемых в объект Path, можно подставлять свои значения, созданные без всяких визуальных средств.

Завершив изучение этой лекции, полезно прочитать статью Деклана Бреннана (Declan Brennan) "Создание сложной трехмерной анимации с помощью Silverlight 2.0" http://msdn.microsoft.com/ru-ru/magazine/cc500570.aspx.

Илья Столупин
Илья Столупин
Россия
Олег Борхаленко
Олег Борхаленко
Украина