Модификация приложения Silverlight : работа с анимацией



 

Для работы с анимацией нужен объект анимации и объект, который будет анимирован. В нашем случаем таки объектом станет простой эллипс. Для «анимации» движения объекта в пространстве понадобится конечная точка движения, которую можно получить простым щелчком на странице в стороне от эллипса. Всё движение будет происходить от начальной до конечной точки, за время, определённое в коде.

Весь процесс анимации в основном в данной части работы, будет определять объект анимации:

 

<Storyboard>Дочерние анимации</Storyboard>

 

Storyboard — класс, шкала времени контейнера, которая предоставляет объект и свойство, содержащие сведения о дочерних анимациях.

Большая часть свойств шкалы времени могут иметь привязку данных или могут быть анимированы; тем не менее, в связи с особенностями действия системы расчета времени поведение привязанных к данным и анимированных шкал времени отличается от поведения других привязанных к данным или анимированных объектов. Чтобы понять их поведение, следует осознать, что означает активирование временной шкалы.

При активировании временной шкалы создаются копии временной шкалы и дочерних временных шкал. Эти копии фиксируются (устанавливаются в режим "только для чтения"), а на их базе создаются объекты Clock. Эти объекты используются для выполнения фактических действий по анимации целевых свойств. Если шкала времени является привязанной к данный или анимированной, при создании часов создается моментальный снимок ее текущих значений. Хотя исходная временная шкала может продолжить изменение, ее часы не будут меняться.

Для отражения в шкале времени привязки данных или изменений анимации необходимо повторно создать ее часы. Часы не воссоздаются автоматически. Ниже приведены несколько способов применения изменений шкалы времени:

  • Если шкала времени принадлежит Storyboard, можно заставить её отражать изменения посредством применения её раскадровки при помощи метода BeginStoryboard или Begin. Это имеет побочный эффект в виде перезапуска анимации. В коде можно использовать метод Seek для возврата раскадровки обратно в предыдущее положение.
  • Если анимация применяется непосредственно к свойству с помощью метода BeginAnimation, снова вызываем метод BeginAnimation и передаём ему измененную анимацию.
  • Если ведется работа непосредственно на уровне часов, создаём и применяем новый набор часов и используем их для замены предыдущего набора созданных часов.

 

Текст выше является выдержкой из статьи Storyboard - класс (http :// msdn . microsoft . com / ru - ru / library / system . windows . media . animation . storyboard . aspx).

 

Подготовим «площадку» для реализации возможностей анимации. Откроем конструктор главной страницы MainPage . xaml и установим в единственную незанятую ячейку элемента Grid (строка: 4, столбец: 0) элемент TabControl ( ) со следующими свойствами, уже определёнными XAML-кодом:

 

   <sdk:TabControl Grid.Row="4" HorizontalAlignment="Stretch" Name="tabControl1" VerticalAlignment="Stretch">

       ...

   </sdk:TabControl>

 

Элемент TabControl будет растянут по всей ячейки элемента Grid. Что же касается кода на месте троеточия: там будет располагаться код определяющий закладки TabItem, но каждая закладка будет являться полноценной страницей XAML. Для первой закладки создаём эту страницу. Выделим имя WPF-проекта, далее выполним Проект ->  Добавить новый элемент...: в открывшемся окне в списке «Установленные шаблоны» выберем «Silverlight», далее справа найдём «Страница Silverlight»:

 

Рис. 5. 1. Добавление нового элемента – LWP 17 Silverlight: Страница Silverlight

 

Имя вводим как Page _ BasicAnimation . xaml (приставка Page нужна для группировки страниц в обозревателе решений). Жмём ОК. Страница добавлена. Откроем её в конструкторе и в коде XAML найдём строчки:

 

      d:Designd:DesignHeight="480"

      Title="Page1 Page">

 

Изменим эти строчки так (равносильно изменению свойств, как уже было сказано):

 

      d:Designd:DesignHeight="300"

      Title="Знакомство с Silverlight (C#) :: Основы анимации">

 

Теперь «запихнём» новую страницу в TabControl следующим образом. Откроем MainPage . xaml в конструкторе, найдём код добавления элемента TabControl и между тэгами добавим следующий XAML-код:

 

           <lwp17:Page_BasicAnimation></lwp17:Page_BasicAnimation>

 

Напоминаем, слово lwp 17 это доступ к пространству имён приложения (WPF-проекта) определённый в шапке страницы MainPage . xaml:

 

 xmlns:lwp17="clr-namespace:LWP17Silverlight"

 

Откроем XAML-код страницы Page_BasicAnimation.xaml, найдём:

 

<Grid x:Name="LayoutRoot">

   ...

</Grid>

 

За место троеточия вставим:

 

     <Rectangle x:Name="rectangle1" Fill="Turquoise"

       Canvas.Top="100" Canvas.Left="100"

       Height="100">

       <Rectangle.Triggers>

           <EventTrigger RoutedEvent="Rectangle.Loaded">

               <BeginStoryboard>

                   <Storyboard>

                       <DoubleAnimation RepeatBehavior="3x"              

                           Storyboard.TargetName="rectangle1"

                           Storyboard.TargetProperty="Height"

                           To="200" Duration="0:0:5"

                           AutoReverse="True" />

                   </Storyboard>

               </BeginStoryboard>

           </EventTrigger>

       </Rectangle.Triggers>

   </Rectangle>

 

Откомпилируем и увидим как ровный квадрат превращается в следующее:

 

Теперь немного теории:

BeginStoryboard («Начать раскадровку») — это действие триггера, содержащее объект Storyboard («Раскадровка»). Объекты раскадровки содержат определения анимации. При определении анимации эти объекты просто внедряются внутрь определения EventTrigger:

 

<Rectangle x:Name="rect" Fill="Red"

 Canvas.Top="100" Canvas.Left="100"

 Height="100">

<Rectangle.Triggers>

   <EventTrigger RoutedEvent="Rectangle.Loaded">

       <BeginStoryboard>

           <Storyboard>

           </Storyboard>

       </BeginStoryboard>

   </EventTrigger>

</Rectangle.Triggers>

</Rectangle>

 

Теперь, когда инфраструктура для анимации установлена, можно указать, какую анимацию следует выполнить. На самом базовом уровне, анимация определяет изменение свойства с течением времени. Можно анимировать три различных типа свойств. Каждый из этих типов свойств анимируется из значения, указанного в атрибуте From («От») (или, если оно не установлено, из его текущего значения), к значению, указанному в атрибуте To («К»), либо к значению, указанному в атрибуте By («По»).

Двойные типы анимируются с помощью DoubleAnimation или DoubleAnimationUsingKeyFrames. Этот метод используется для анимации свойств, содержащих двойное значение — например, измерений, вроде Canvas.Left или визуальных атрибутов, вроде Opacity.

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

Типы цветов анимируются с помощью ColorAnimation или типа ColorAnimationUsingKeyFrames. Этот метод используется для анимации свойств, содержащих значение цвета — фона или штриха элемента, например.

 

Чтобы определить, к какому объекту следует применить анимацию, на этих типах анимации используется свойство Storyboard.TargetName и ему необходимо передать имя такого объекта, устанавливаемое на объекте с помощью свойства x:Name. Вдобавок, свойство, которое будет анимировано, указывается с помощью Storyboard.TargetProperty. Учтите, что при указании сложного или присоединенного свойства (такого как Canvas.Left), его следует поместить в скобки. Так, для примера, чтобы нацелить двойную анимацию на Height прямоугольника, именуемого rect angle 1, XAML должен выглядеть следующим образом:

 

<DoubleAnimation Storyboard.TargetName="rectangle1"

 Storyboard.TargetProperty="Height" />

 

Чтобы определить, сколько времени займёт переход затрагиваемых свойств от одного значения к другому, используется свойство Duration («Продолжительность»). Отметьте, что оно определено в формате чч:мм:сс, где пятисекундная продолжительность анимации указывается как 00:00:05, сокращенно 0:0:5.

 

<DoubleAnimation Storyboard.TargetName="rectangle1"

 Storyboard.TargetProperty="Height" Duration="0:0:5" />

 

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

 

<DoubleAnimation Storyboard.TargetName="rectangle1"

 Storyboard.TargetProperty="Height" BeginTime="0:0:5" />

 

Можно также скорректировать поведение анимации, умножив продолжительность на коэффициент скорости. Это проделывается с помощью свойства SpeedRatio. Например, в предыдущем случае продолжительность была установлена на 5 секунд. Можно изменить коэффициент скорости, заставив анимацию длиться 10 секунд, установив SpeedRatio на 2, или, как вариант, можно ускорить анимацию до 1 секунды, установив SpeedRatio на 0.2.

 

<DoubleAnimation Storyboard.TargetName="rectangle1"

 Storyboard.TargetProperty="Height" SpeedRatio="2" Duration="0:0:5" />

 

Анимация Silverlight предоставляет средство для отмены изменений, внесённых как часть анимации. Например, если двойное значение переносится от 0 к 500 за определенный промежуток времени, AutoReverse заставит анимацию перейти от 500 обратно к нулю.

Если анимация установлена на работу в течении 5 секунд, как показано выше и AutoReverse установлен на true, то полная анимация займёт 10 секунд.

 

<DoubleAnimation Storyboard.TargetName="rectangle1"

 Storyboard.TargetProperty="Height" SpeedRatio="2" Duration="0:0:5" AutoReverse="True" />

 

Когда работа анимации завершена, можно применить ряд параметров, чтобы заставить её вести себя нужным образом. Они указываются с помощью свойства RepeatBehavior. Это свойство может принимать три различных типа значений:

  • Время, определённое в секундах. Временная шкала подождет это время и затем начнёт анимацию снова.
  • Установка RepeatBehavior на Forever («Постоянно») для постоянного повторения.
  • Определенное число повторений, установленное путем указанием числа, за которым следует x. Например, если анимация должна произойти трижды, указывается значение .

 

Анимация значения с помощью DoubleAnimation:

 

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

При анимации двойного значения, значение в начале анимации указывается с помощью значения From и затем изменяется либо на значение To, являющееся абсолютной точкой назначения, либо на значение By, являющееся относительной точкой назначения. Например, если свойство Canvas.Left элемента перемещается от 100 (рядом с левой стороной экрана) к 500, можно установить From на 100 и To на 500, либо By на 400. Если установить оба, свойство To будет иметь больший приоритет, а свойство By игнорируется. Также, если прямоугольник уже размещён в желаемой позиции From, указывать свойство From не нужно.

 

Анимация цвета с помощью ColorAnimation:

ColorAnimation работает как DoubleAnimation. Оно используется для определения того, как значение цвета элемента будет меняться со временем. Анимация, вычисляется как линейная интерполяция между значениями свойств цвета с течением указанного времени.

При анимации цвета, его значение в начале анимации указывается с использованием свойства From. Если не указать его, то используется текущий цвет. Желательный конечный цвет указывается с использованием атрибута To. Можно также указать атрибут By, который предоставит конечный цвет, являющийся продуктом добавления значений цвета From (или начального цвета) к цвету By.

При анимации свойства, основанного на цвете, содержимое свойства не анимируется напрямую, поскольку содержимым свойства обычно является кисть, а не цвет. Так что если необходимо анимировать цвет заливки прямоугольника, в качестве цели не следует использовать свойство Fill («Заливка»). Вместо этого, следует указать, что собираетесь анимировать свойство Color («Цвет») используемой для выполнения заливки SolidBrush.

Пример анимации цвета прямоугольника и изменения его с чёрного на белый в течении пяти секунд (затем возвращение и вечное повторение), с помощью анимации света. Как можно заметить в коде ниже, этот фрагмент XAML указывает свойство Color, относящееся к заливающей фигуру SolidColorBrush, как свое целевое свойство. Добавим этот кусок кода на страницу Page _ BasicAnimation . xaml после предыдущего фрагмента, закрывающего тэга прямоугольника:

 

   <Rectangle x:Name="rectangle2"

       Height="100"

       Fill="Black"

       HorizontalAlignment="Left" VerticalAlignment="Top">

       <Rectangle.Triggers>

           <EventTrigger RoutedEvent="Rectangle.Loaded">

               <BeginStoryboard>

                   <Storyboard>

                       <ColorAnimation Storyboard.TargetName="rectangle2"

                           Storyboard.TargetProperty=

                           "(Shape.Fill).(SolidColorBrush.Color)"

                           To="#00000000" Duration="0:0:5"

                           AutoReverse="True" RepeatBehavior="Forever"/>

                   </Storyboard>

               </BeginStoryboard>

           </EventTrigger>

       </Rectangle.Triggers>

   </Rectangle>

 

Анимация точки с помощью PointAnimation:

Чтобы изменять значение, определённое как точка, с течением времени, используется тип PointAnimation. Анимация вычисляется как линейная интерполяция между значениями свойств цвета с течением указанного времени.

Подобно анимации двойных значений и значений цвета, значение в начале анимации указывается с помощью значения From, а конечное значение указывается либо как относительное направление (с помощью By), либо как абсолютная точка (с помощью To). Следующий код показывает пример того, как можно анимировать конечную точку кривой Безье. В данном случае, кривая Безье определяется со начальной точкой в (100,100), конечной точкой в (300,100) и контрольной точкой в (200,0). Анимация устанавливается на запуск после загрузки пути и она анимирует конечную точку кривой (Point2) от (300,100) до (300,200) на протяжении пяти секунд. Код добавляем после закрывающего тэга предыдущего фрагмента (прямоугольника):

 

   <Path Stroke="Black">

       <Path.Data>

           <PathGeometry>

               <PathFigure StartPoint="100,100">

                   <QuadraticBezierSegment x:Name="seg"

                       Point1="200,0" Point2="300,100" />

               </PathFigure>

           </PathGeometry>

       </Path.Data>

       <Path.Triggers>

           <EventTrigger RoutedEvent="Path.Loaded">

               <BeginStoryboard>

                   <Storyboard>

                       <PointAnimation Storyboard.TargetName="seg"

                           Storyboard.TargetProperty="Point2"

                           From="300,100" To="300,200"

                           Duration="0:0:5" AutoReverse="True"

                           RepeatBehavior="Forever" />

                   </Storyboard>

               </BeginStoryboard>

           </EventTrigger>

       </Path.Triggers>

   </Path>

 

Использование опорных кадров:

Все три типа анимации, о которых только что было рассказано, ColorAnimation, DoubleAnimation и PointAnimation работают путём изменения определённого свойства с течением времени, используя линейную интерполяцию. Например, при переводе двойного значения от 100 к 500 в течение пяти секунд, оно будет изменяться на 80 каждую секунду.

В каждом из этих трёх типов анимации данный перенос может быть определён через набор «вех», именуемых опорными кадрами. Чтобы изменить линейное поведение анимации от начального свойства к конечному свойству, следует просто вставить один или несколько опорных кадров. Затем определяется желаемый стиль анимации между этими различными точками.

Опорные кадры определяются с помощью «опорных моментов». Это моменты, указываемые относительно момента начала анимации; они также указывают время окончания опорного кадра. Так что если, для примера, необходима девятисекундная анимация с тремя равномерно распределёнными опорными моментами, можно указать окончание первого опорного момента в 0:0:3, второго в 0:0:6 и третьего в 0:0:9.  Длина опорного момента не указывается: вместо этого указывается конечное время для каждого опорного кадра.

В качестве ещё одного примера, представим себе двойную анимацию, которая должна охватывать половину диапазона от 100 до 500. Анимация должна двигаться очень быстро в первой половине и очень медленно во второй. В целом, она будет требовать шести секунд переноса. Поскольку 350 – это середина между 100 и 500, опорный кадр следует определить как начинающийся в точке 350. Ему следует указать продолжаться одну секунду между начальной точкой и средней точкой (опорное время 0:0:1) и затем установить продолжительность опорного времени между средней точкой и конечной точкой в пять секунд, используя второе опорное время как 0:0:6. Теперь элемент установлен так, чтобы пролететь по экрану к средней точке и медленно ползти дальше.

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

Для указания опорных кадров на анимации используется постфикс UsingKeyFrames. То есть, для указания двойных анимаций и использования опорных кадров, следует использовать DoubleAnimationUsingKeyFrame, на котором указывается цель и свойство (тем же образом, что при использовании DoubleAnimation). DoubleAnimationUsingKeyFrames содержит определения опорных кадров. И то же самое относится к PointAnimationUsingKeyFrames или ColorAnimationUsingKeyFrames.

 

Использование линейных опорных кадров:

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

 

   <Rectangle Fill="#FFFF0000" Stroke="#FF000000"

       Height="40" x:Name="rectangle3">

       <Rectangle.Triggers>

           <EventTrigger RoutedEvent="Rectangle.Loaded">

               <BeginStoryboard>

                   <Storyboard>

                       <DoubleAnimationUsingKeyFrames Storyboard.TargetName="rectangle3"

                           Storyboard.TargetProperty="Width" AutoReverse="True" RepeatBehavior="5x" >

                           <LinearDoubleKeyFrame KeyTime="0:0:1" Value="100" />

                           <LinearDoubleKeyFrame KeyTime="0:0:9" Value="200" />

                       </DoubleAnimationUsingKeyFrames>

                   </Storyboard>

               </BeginStoryboard>

           </EventTrigger>

       </Rectangle.Triggers>

   </Rectangle>

 

Использование дискретных опорных кадров:

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

 

   <Rectangle Fill="#FFFF00FF" Stroke="#FF0000FF"

       HorizontalAlignment="Center" VerticalAlignment="Bottom"

       Height="20" x:Name="rectangle4">

       <Rectangle.Triggers>

           <EventTrigger RoutedEvent="Rectangle.Loaded">

               <BeginStoryboard>

                   <Storyboard>

                       <DoubleAnimationUsingKeyFrames Storyboard.TargetName="rectangle4"

                           Storyboard.TargetProperty="Width" AutoReverse="True" RepeatBehavior="5x">

                           <DiscreteDoubleKeyFrame KeyTime="0:0:1" Value="100" />

                           <DiscreteDoubleKeyFrame KeyTime="0:0:2" Value="200" />

                           <DiscreteDoubleKeyFrame KeyTime="0:0:3" />

                       </DoubleAnimationUsingKeyFrames>

                   </Storyboard>

               </BeginStoryboard>

           </EventTrigger>

       </Rectangle.Triggers>

   </Rectangle>

 

Использование опорных кадров сплайна :

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

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

Анимация, в этом случае — тень мяча, а сплайн — траектория полёта меча. Эта траектория, сплайн, определяется используя KeySpline. KeySpline определяет контрольные точки для квадратичной кривой Безье. Он нормализован так, что первой точкой кривой является 0, а второй 1. Для параболической дуги, по которой пролетел бы мяч, KeySpline будет содержать два нормализованных значения, разделенных запятыми.

Чтобы определить кривую, подобную полету мяча, можно определить сплайн, используя KeySpline, такой как 0.3, 0 0.6, 1. Это определит первую точку кривой как (0.3, 0), а вторую как (0.6, 1). Результатом этого будет быстрое ускорение анимации примерно до завершения одной трети полета мяча; затем она будет двигаться с единообразной скоростью, примерно до завершения второй трети; и наконец, будет уменьшаться в течение оставшейся части полета анимированного мяча, когда анимация имитирует падение мяча на землю. Код такой:

 

   <Ellipse Fill="Aqua" Stroke="#FFFF4444" HorizontalAlignment="Right"

       Height="20" x:Name="ball1">

       <Ellipse.Triggers>

           <EventTrigger RoutedEvent="Ellipse.Loaded">

               <BeginStoryboard>

                   <Storyboard>

                       <DoubleAnimationUsingKeyFrames

                           Storyboard.TargetName="ball1"

                           Storyboard.TargetProperty="Height" AutoReverse="True" RepeatBehavior="3x">

                            <SplineDoubleKeyFrame KeyTime="0:0:5"

         KeySpline="0.3,0 0.6,1" Value="200" />

                       </DoubleAnimationUsingKeyFrames>

                   </Storyboard>

               </BeginStoryboard>

           </EventTrigger>

       </Ellipse.Triggers>

   </Ellipse>

 

Теперь без долгих объяснений создаём «событийную» анимацию, о которой говорилось в начале данного пункта работы. Основу каждого последующего окна составит сетка, разделённая в соотношении 1:9 размеров окна. В верхней части сетки будет закреплена StackPanel с надписью, а в нижней будет создан объект Path с градиентом. Основу Path составит простой эллипс закрашенный этим градиентом. Для второго StackPanel будет инициализировано событие MouseLeftButtonDown для получения координат нажатия.

 

Создаём новую страницу с именем Page _ BasicPointAnimation . xaml и XAML-кодом:

 

<navigation:Page x:Class="LWP17Silverlight.Page_BasicPointAnimation"

      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

      xmlns:d="http://schemas.microsoft.com/expression/blend/2008"

      xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"

      mc:Ignorable="d"

      xmlns:navigation="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Navigation"

      d:Designd:DesignHeight="300"

      Title="Знакомство с Silverlight (C#) :: Базовая точечная анимация с событиями">

<navigation:Page.Resources>

   <Storyboard x:Name="MyStoryboard">

       <PointAnimation x:Name="MyPointAnimation" Duration="0:0:2"

           Storyboard.TargetProperty="Center"

           Storyboard.TargetName="MyAnimatedEllipseGeometry">

       </PointAnimation>

   </Storyboard>

</navigation:Page.Resources>

<Grid x:Name="LayoutRoot">

   <Grid.RowDefinitions>

       <RowDefinition Height="1*"></RowDefinition>

       <RowDefinition Height="9*"></RowDefinition>

   </Grid.RowDefinitions>

   <StackPanel Grid.Row="0">

       <TextBlock Text="Щёлкните левой кнопкой мыши на любой позиции на сером фоне" TextAlignment="Center"></TextBlock>

   </StackPanel>

  <StackPanel MouseLeftButtonDown="StackPanel_MouseLeftButtonDown" x:Name="MyStackPanel" Background="Gray" Grid.Row="1">

       <Path>

           <Path.Fill>

               <LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">

                   <GradientStop Color="#FFF1F7FB" Offset="0"/>

                   <GradientStop Color="#FF3794E4" Offset="1"/>

               </LinearGradientBrush>

           </Path.Fill>

           <Path.Data>

               <!-- Рисуем эллипс -->

               <EllipseGeometry x:Name="MyAnimatedEllipseGeometry" Center="50,80" RadiusX="15" RadiusY="15" />

           </Path.Data>

       </Path>

   </StackPanel>

</Grid>

</navigation:Page>

 

Код события MouseLeftButtonDown для файла Page_BasicPointAnimation.xaml.cs будет таким:

 

   /// <summary>

   /// Обработчик события изменяет свойство To объекта PointAnimation,

   /// и начинает проигрывание анимации Storyboard

   /// </summary>

   /// <param name="sender"></param>

   /// <param name="e"></param>

   private void StackPanel_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)

   {

       var targetpoint = e.GetPosition(this.MyStackPanel);

       this.MyPointAnimation.To = targetpoint;

       this.MyStoryboard.Begin();

   }

 

Код новой вкладки для TabControl:

 

       <sdk:TabItem Header="Базовыя точечная анимации с событиями" Name="BasicPointAnimation" DataContext="{Binding}">

           <lwp17:Page_BasicPointAnimation></lwp17:Page_BasicPointAnimation>

       </sdk:TabItem>

 

Следующая вкладка выполняет похожую функцию, только «анимация» на этот раз нужна не только для эллипса, но и для линии, которая проводится из центра эллипса к точке, в которую эллипс должен быть перемещён. Для реализации помимо новой страницы также нам понадобится ещё одна дополнительная страница с классом.

Создаём новую страницу с именем Page _ AimateDependencyProperty . xaml с XAML-кодом:

 

<navigation:Page x:Class="LWP17Silverlight.Page_AnimateDependencyProperty"

      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

      xmlns:d="http://schemas.microsoft.com/expression/blend/2008"

      xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"

      xmlns:c="clr-namespace:LWP17Silverlight;assembly=LWP17Silverlight"

      mc:Ignorable="d"

      xmlns:navigation="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Navigation"

      d:Designd:DesignHeight="300"

      Title="Знакомство с Silverlight (C#) :: Анимация свойства зависимостей">

<navigation:Page.Resources>

   <Storyboard x:Name="MyAnimationStoryboard">

       <PointAnimation x:Name="MyAnimation"

           Duration="0:0:2"

           Storyboard.TargetProperty="EllipseCenter"

           Storyboard.TargetName="MyAnimatedEllipseGeometry">

       </PointAnimation>

    </Storyboard>

</navigation:Page.Resources>

<Grid x:Name="LayoutRoot">

   <Grid.RowDefinitions>

       <RowDefinition Height="1*"></RowDefinition>

       <RowDefinition Height="9*"></RowDefinition>

   </Grid.RowDefinitions>

   <StackPanel Grid.Row="0">

       <TextBlock Text="Щёлкните левой кнопкой мыши на любой позиции на чёрном фоне" TextAlignment="Center"></TextBlock>

   </StackPanel>

   <StackPanel x:Name="MyStackPanel" MouseLeftButtonDown="MyStackPanel_MouseLeftButtonDown" Background="Black"  Grid.Row="1">

       <Canvas>

           <Line x:Name="MyLine" Fill="Red"  Stroke="Red"  Visibility="Collapsed" StrokeThickness="5" Canvas.ZIndex="1"></Line>

           <c:MyEllipse x:Name="MyAnimatedEllipseGeometry" EllipseCenterChanged="MyAnimatedEllipseGeometry_EllipseCenterChanged"></c:MyEllipse>

       </Canvas>

   </StackPanel>

</Grid>

</navigation:Page>

 

Код новой вкладки для TabControl:

 

       <sdk:TabItem Header="Анимация свойства зависимостей" Name="AnimateDependencyProperty" DataContext="{Binding}">

           <lwp17:Page_AimateDependencyProperty></lwp17:Page_AimateDependencyProperty>

       </sdk:TabItem>

 

Код файла Page_AnimateDependencyProperty.xaml.cs:

 

using System;

using System.Collections.Generic;

using System.Linq;

using System.Net;

using System.Windows;

using System.Windows.Controls;

using System.Windows.Documents;

using System.Windows.Input;

using System.Windows.Media;

using System.Windows.Media.Animation;

using System.Windows.Shapes;

using System.Windows.Navigation;

 

namespace LWP17Silverlight

{

public partial class Page_AnimateDependencyProperty : Page

{

   Point _currenttargetpoint;

 

   public Page_AnimateDependencyProperty()

   {

       InitializeComponent();

   }

 

   // Выполняется, когда пользователь переходит на эту страницу.

   protected override void OnNavigatedTo(NavigationEventArgs e)

   {

   }

 

   private void MyStackPanel_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)

   {

       _currenttargetpoint = e.GetPosition(this.MyStackPanel);

       this.MyAnimation.To = _currenttargetpoint;

       this.MyAnimationStoryboard.Begin();

   }

 

   /// <summary>

   /// Сихнронизация конечной точки объекта MyLine c последней точкой

   /// которую пользователь выбирает нажатием и текущей позицие объекта MyEllipse.

   /// Анимация создаётся для MyLine

   /// </summary>

   /// <param name="sender"></param>

   /// <param name="e"></param>

   private void MyAnimatedEllipseGeometry_EllipseCenterChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)

   {

       this.MyLine.Visibility = Visibility.Visible;

         this.MyLine.X1 = this.MyAnimatedEllipseGeometry.EllipseCenter.X;

       this.MyLine.Y1 = this.MyAnimatedEllipseGeometry.EllipseCenter.Y;

       this.MyLine.X2 = this._currenttargetpoint.X;

       this.MyLine.Y2 = this._currenttargetpoint.Y;

   }

}

}

 

Теперь создаём ещё одну страницу с именем MyEllipse . xaml и XAML-кодом:

 

<navigation:Page x:Class="LWP17Silverlight.MyEllipse"

      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

      xmlns:d="http://schemas.microsoft.com/expression/blend/2008"

      xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"

      xmlns:c="clr-namespace:LWP17Silverlight;assembly=LWP17Silverlight"

      mc:Ignorable="d"

      xmlns:navigation="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Navigation"

      d:Designd:DesignHeight="300">

<Grid x:Name="LayoutRoot" Background="Transparent" IsHitTestVisible="False">

   <Path>

       <Path.Fill>

           <LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">

               <GradientStop Color="#FFF1F7FB" Offset="0"/>

               <GradientStop Color="#FF3794E4" Offset="1"/>

           </LinearGradientBrush>

       </Path.Fill>

       <Path.Data>

           <EllipseGeometry x:Name="MyAnimatedEllipseGeometry"

           Center="50,50" RadiusX="15" RadiusY="15" />

       </Path.Data>

   </Path>

</Grid>

</navigation:Page>

 

Код файла MyEllipse.xaml.cs:

 

using System;

using System.Collections.Generic;

using System.Linq;

using System.Net;

using System.Windows;

using System.Windows.Controls;

using System.Windows.Documents;

using System.Windows.Input;

using System.Windows.Media;

using System.Windows.Media.Animation;

using System.Windows.Shapes;

using System.Windows.Navigation;

 

namespace LWP17Silverlight

{

public delegate void EllipseCenterChangedEventHandler(DependencyObject sender, DependencyPropertyChangedEventArgs e);

 

public partial class MyEllipse : Page

{

   public static readonly DependencyProperty EllipseCenterProperty =

       DependencyProperty.Register("EllipseCenter", typeof(Point), typeof(MyEllipse),

       new PropertyMetadata(new PropertyChangedCallback((obj, e) => { MyHandler(obj, e); })));

   EllipseCenterChangedEventHandler _myDelegate;

 

   public event EllipseCenterChangedEventHandler EllipseCenterChanged

   {

       add

       {

           if (_myDelegate == null) _myDelegate = value;

           else { Delegate.Combine(_myDelegate, value); }

       }

       remove { Delegate.Remove(_myDelegate, value); }

   }

 

   public Point EllipseCenter

   {

       get { return (Point)GetValue(EllipseCenterProperty); }

       set { SetValue(EllipseCenterProperty, value); }

   }

 

   public MyEllipse()

   {

       InitializeComponent();

       EllipseCenter = this.MyAnimatedEllipseGeometry.Center;

   }

 

   /// <summary>

   /// Это метод обратного вызова, который вызывает метод OnEllipseCenterChanged

   /// объекта MyEllipse, свойство EllipseCenter которого было изменено

   /// </summary>

   /// <param name="obj"></param>

   /// <param name="e"></param>

   static void MyHandler(DependencyObject obj, DependencyPropertyChangedEventArgs e)

   {

       MyEllipse ellipse = obj as MyEllipse;

       if (ellipse != null) { ellipse.OnEllipseCenterChanged(obj, e); }

   }

 

   /// <summary>

   /// Этот метод вызывается методом свойства EllipseCenterProperty.

   /// Это обновляет свойство Center объекта MyAnimatedEllipseGeometry интерфейса,

  /// когда срабатывает событие EllipseCenterChanged

   /// </summary>

   /// <param name="obj"></param>

   /// <param name="e"></param>

   void OnEllipseCenterChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)

   {

       MyEllipse ellipse = obj as MyEllipse;

       if (ellipse != null) { ellipse.MyAnimatedEllipseGeometry.Center = ellipse.EllipseCenter; }

       if (_myDelegate != null) _myDelegate(obj, e);

   }

}

}

 

Применение специально функции EasingFunction, позволяющей «настроить» анимацию различными способами:

 

<DoubleAnimation>

  <DoubleAnimation.EasingFunction>

    Функции

  </DoubleAnimation.EasingFunction>

</DoubleAnimation>

 

Создаём последнюю страницу с именем Page_EasingFunction.xaml с XAML-кодом:

 

<navigation:Page x:Class="LWP17Silverlight.Page_EasingFunction"

      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

      xmlns:d="http://schemas.microsoft.com/expression/blend/2008"

      xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"

      xmlns:c="clr-namespace:LWP17Silverlight;assembly=LWP17Silverlight"

      mc:Ignorable="d"

      xmlns:navigation="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Navigation"

      d:Designd:DesignHeight="300"

      Title="Знакомство с Silverlight (C#) :: Применение EasingFunction">

<navigation:Page.Resources>

   <BackEase x:Name="BackEase" Amplitude="1" EasingMode="EaseIn"></BackEase>

   <c:MyEase x:Name="MyEase"></c:MyEase>

   <Storyboard x:Name="MyAnimationStoryboard">

       <PointAnimation x:Name="MyAnimation"

                       Duration="0:0:2"

                       Storyboard.TargetProperty="Center"

                       Storyboard.TargetName="MyAnimatedEllipseGeometry"

                       EasingFunction="{StaticResource BackEase}">

       </PointAnimation>

   </Storyboard>

</navigation:Page.Resources>

<Grid x:Name="LayoutRoot" Background="White">

   <Grid.RowDefinitions>

       <RowDefinition Height="3*"></RowDefinition>

       <RowDefinition Height="7*"></RowDefinition>

   </Grid.RowDefinitions>

   <StackPanel Grid.Row="0">

       <RadioButton GroupName="g1" Content="Применяем BackEase" IsChecked="true" x:Name="BackEaseRadioButton" Click="BackEaseRadioButton_Click"></RadioButton>

       <RadioButton GroupName="g1" Content="Применяем MyEase" IsChecked="false" x:Name="MyEaseRadioButton" Click="MyEaseRadioButton_Click"></RadioButton>

       <TextBlock Text="Щёлкните левой кнопкой мыши на любой позиции на розовом фоне" TextAlignment="Center"></TextBlock>

   </StackPanel>

   <StackPanel MouseLeftButtonDown="MyStackPanel_MouseLeftButtonDown" x:Name="MyStackPanel" Background="Pink" Grid.Row="1">

       <Path>

           <Path.Fill>

               <LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">

                   <GradientStop Color="#FFF1F7FB" Offset="0"/>

                   <GradientStop Color="#FF3794E4" Offset="1"/>

               </LinearGradientBrush>

           </Path.Fill>

           <Path.Data>

               <EllipseGeometry x:Name="MyAnimatedEllipseGeometry"

                   Center="50,50" RadiusX="15" RadiusY="15" />

           </Path.Data>

       </Path>

   </StackPanel>

</Grid>

</navigation:Page>

 

Код события Click нажатия кнопки для файла Page_EasingFunction.xaml.cs, нажатия на одну из RadioButton, а также специальный класс MyEase будет таким (в одно файле):

 

   private void MyStackPanel_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)

     {

       var targetpoint = e.GetPosition(this.MyStackPanel);

       this.MyAnimation.To = targetpoint;

       this.MyAnimationStoryboard.Begin();

   }

 

   private void MyEaseRadioButton_Click(object sender, RoutedEventArgs e)

   {

       this.MyAnimation.EasingFunction = this.Resources["MyEase"] as IEasingFunction;

   }

 

   private void BackEaseRadioButton_Click(object sender, RoutedEventArgs e)

   {

       this.MyAnimation.EasingFunction = this.Resources["BackEase"] as IEasingFunction;

   }

}

 

/// <summary>

/// Класс MyEase

/// </summary>

public class MyEase : EasingFunctionBase

{

   protected override double EaseInCore(double normalizedTime)

   {

       return normalizedTime / 5;

   }

}

 

Код новой вкладки для TabControl:

 

       <sdk:TabItem Header="Применение EasingFunction" Name="EasingFunction" DataContext="{Binding}">

           <lwp17:Page_EasingFunction></lwp17:Page_EasingFunction>

       </sdk:TabItem>

 

Следующая последняя вкладка будет запускать по нажатию кнопки анимацию «падения шарика на пол».

Создаём последнюю страницу с именем Page _ UsingKeyFrames . xaml с XAML-кодом:

 

<navigation:Page x:Class="LWP17Silverlight.Page_UsingKeyFrames"

      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

      xmlns:d="http://schemas.microsoft.com/expression/blend/2008"

      xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"

      mc:Ignorable="d"

      xmlns:navigation="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Navigation"

      d:Designd:DesignHeight="300"

      Title="Знакомство с Silverlight (C#) :: Покадровая анимация">

<navigation:Page.Resources>

   <Storyboard x:Name="MyAnimationStoryboard">

       <DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="path" Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[3].(TranslateTransform.X)">

           <EasingDoubleKeyFrame KeyTime="00:00:00" Value="0"/>

           <SplineDoubleKeyFrame KeyTime="00:00:00.8000000" Value="-4" KeySpline="1,0,1,1"/>

           <SplineDoubleKeyFrame KeyTime="00:00:01" Value="-5" KeySpline="0,0,0,1"/>

           <SplineDoubleKeyFrame KeyTime="00:00:01.2000000" Value="-4" KeySpline="1,0,1,1"/>

       </DoubleAnimationUsingKeyFrames>

       <DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="path" Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[3].(TranslateTransform.Y)">

           <EasingDoubleKeyFrame KeyTime="00:00:00" Value="0"/>

             <SplineDoubleKeyFrame KeyTime="00:00:00.8000000" Value="127" KeySpline="1,0,1,1"/>

           <SplineDoubleKeyFrame KeyTime="00:00:01" Value="69" KeySpline="0,0,0,1"/>

           <SplineDoubleKeyFrame KeyTime="00:00:01.2000000" Value="127" KeySpline="1,0,1,1"/>

       </DoubleAnimationUsingKeyFrames>

   </Storyboard>

</navigation:Page.Resources>

<Grid x:Name="LayoutRoot">

   <Grid.RowDefinitions>

       <RowDefinition Height="1*"></RowDefinition>

       <RowDefinition Height="9*"></RowDefinition>

   </Grid.RowDefinitions>

   <StackPanel Grid.Row="0">

       <Button Content="Нажмите кнопку для начала анимации" Click="Button_Click"></Button>

   </StackPanel>

   <StackPanel x:Name="MyStackPanel" Background="WhiteSmoke"  Grid.Row="1">

       <Path x:Name="path" RenderTransformOrigin="0.5,0.5">

           <Path.RenderTransform>

               <TransformGroup>

                   <ScaleTransform/>

                   <SkewTransform/>

                   <RotateTransform/>

                   <TranslateTransform/>

               </TransformGroup>

           </Path.RenderTransform>

           <Path.Fill>

               <LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">

                   <GradientStop Color="#FFF1F7FB" Offset="0"/>

                   <GradientStop Color="#FF3794E4" Offset="1"/>

               </LinearGradientBrush>

           </Path.Fill>

           <Path.Data>

               <EllipseGeometry x:Name="MyAnimatedEllipseGeometry"

                   Center="50,50" RadiusX="15" RadiusY="15" />

           </Path.Data>

       </Path>

   </StackPanel>

</Grid>

</navigation:Page>

 

Код события Click нажатия кнопки для файла Page_UsingKeyFrames.xaml.cs будет таким:

 

   private void Button_Click(object sender, RoutedEventArgs e)

   {

       this.MyAnimationStoryboard.Begin();

   }

 

Код новой вкладки для TabControl:

 

       <sdk:TabItem Header="Покадровая анимация" Name="UsingKeyFrames" DataContext="{Binding}">

           <lwp17:Page_UsingKeyFrames></lwp17:Page_UsingKeyFrames>

       </sdk:TabItem>

 

Готово. Компилируем, проверяем работоспособность. Если требуется поменять заголовок страницы с приложением при запуске, открываем файлы LWP17SilverlightTestPage.aspx или LWP17SilverlightTestPage. html, и ищем так строку:

 

<title>LWP17Silverlight</title>

 

Завершающая часть

 

Компилируем приложения (Release) и запускаем.

 

Рис. 6. 1. Результат работы приложения Silverlight: анимация на вкладке «Основы анимации»

 

Рис. 6. 2. Результат работы приложения Silverlight: анимация на вкладке «Базовая точечная анимация с событиями»

 

Рис. 6. 3. Результат работы приложения Silverlight: анимация на вкладке «Анимация свойства зависимостей»

 

Рис. 6. 4. Результат работы приложения Silverlight: анимация на вкладке «Покадровая анимация»

 

Рис. 6. 5. Результат работы приложения Silverlight: анимация на вкладке «Применение EasingFunction»

 


Дата добавления: 2019-09-13; просмотров: 165; Мы поможем в написании вашей работы!

Поделиться с друзьями:






Мы поможем в написании ваших работ!