Как сделать анимацию исчезновения в wpf
Перейти к содержимому

Как сделать анимацию исчезновения в wpf

  • автор:

Практическое руководство. Анимация свойства «Прозрачность» элемента или кисти

Чтобы элемент платформы появлялся и исчезал, можно анимировать его свойство Opacity или анимировать свойство Opacity для Brush (или кистей), используемое при его рисовании. С помощью анимации непрозрачности можно сделать так, что элемент и его дочерние элементы будут появляться и исчезать, а анимация кисти, используемой при рисовании этого элемента, позволяет выбирать, какая часть элемента будет исчезать. Например, можно анимировать непрозрачность кисти, используемой при закрашивании фона кнопки. В результате фон кнопки будет исчезать и появляться, оставляя текст полностью непрозрачным.

Анимация OpacityBrush обеспечивает преимущества по сравнению с анимацией свойства Opacity элемента.

В следующем примере две кнопки анимируются так, чтобы они исчезали и появлялись. Непрозрачность первой Button анимируется от 1.0 до 0.0 на Duration пяти секунд. Вторая кнопка также анимируется, но при этом анимируется Непрозрачность кисти SolidColorBrush, использованной при рисовании ее Background, а не непрозрачность всей кнопки. При запуске примера первая кнопка полностью исчезает и появляется, а в случае второй кнопки исчезает и появляется только ее фон. Ее текст и граница остаются полностью непрозрачными.

Пример

          

Код в этом примере опущен. Полный пример также показывает, как анимировать непрозрачность Color в рамках LinearGradientBrush. Полный пример см. в разделе Анимация непрозрачности примера элемента.

Совместная работа с нами на GitHub

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

Маски непрозрачности

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

Свойство OpacityMask принимает любую кисть. Альфа-канал кисти определяет степень прозрачности. Например, если применить кисть SolidColorBrush с установленным прозрачным цветом для OpacityMask, то весь элемент полностью исчезнет. Если использовать SolidColorBrush с непрозрачным цветом, элемент останется полностью видимым. Прочие детали цвета (красный, зеленый и синий компоненты) не имеют значения и при установке свойства OpacityMask игнорируются.

Применение OpacityMask с SolidColorBrush не имеет особого смысла, поскольку тот же эффект может быть достигнут проще — через свойство Opacity. Однако OpacityMask становится более удобным при использовании экзотических типов кистей, таких как LinearGradient или RadialGradientBrush. Используя градиент, который выполняет переход от сплошного к прозрачному цвету, можно создать эффект постепенного «исчезновения» поверхности элемента, как в следующем примере с кнопкой:

 

Кнопка с переходом от сплошного цвета к прозрачности

Свойство OpacityMask можно также использовать в сочетании с VisualBrush для создания эффекта отражения. Например, следующий код разметки создает один из наиболее распространенных эффектов WPF — текстового поля с отраженным текстом. По мере набора текста кисть VisualBrush рисует ниже отражение введенного текста. VisualBrush рисует прямоугольник, использующий свойство OpacityMask для постепенного затухания отражения, что отличает его от реального элемента, находящегося выше:

 Here is some reflected text     

Эффект отражения

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

Наряду с градиентными кистями и VisualBrush свойство OpacityMask часто используется вместе с кистью DrawingBrush. Она позволяет применять к элементу прозрачную область определенной формы.

Анимация

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

  • Одна анимация выполняется только для одного свойства зависимостей
  • Для анимации свойства необходим класс анимации, который бы поддерживал тип этого свойства. Например, для изменения таких свойств, как длина, ширина, которые представляют тип double , предназначен класс DoubleAnimation . Для изменения цвета фона или шрифта — ColorAnimation , для изменения свойства Margin — ThiknessAnimation .

За анимацию в WPF отвечает пространство имен System.Windows.Media.Animation . Оно содержит довольно большой набор классов, позволяющих анимировать различные свойства. Но в реальности все классы анимаций можно разделить условно на три группы:

  • Классы, которые производят линейную интерполяцию значений, благодаря чему при анимации свойство плавно изменяет свое значение. Как правило, такие классы называются по шаблону TypeAnimation , где Type — тип данных, которое представляет анимируемое свойства, например, DoubleAnimation :
    • ByteAnimation
    • ColorAnimation
    • DecimalAnimation
    • DoubleAnimation
    • Int16Animation
    • Int32Animation
    • Int64Animation
    • PointAnimation
    • Point3DAnimation
    • QuarternionAnimation
    • RectAnimation
    • Rotation3DAnimation
    • SingleAnimation
    • SizeAnimation
    • ThicknessAnimation
    • VectorAnimation
    • Vector3DAnimation
    • BooleanAnimationUsingKeyFrames
    • ByteAnimationUsingKeyFrames
    • CharAnimationUsingKeyFrames
    • ColorAnimationUsingKeyFrames
    • DecimalAnimationUsingKeyFrames
    • DoubleAnimationUsingKeyFrames
    • Int16AnimationUsingKeyFrames
    • Int32AnimationUsingKeyFrames
    • Int64AnimationUsingKeyFrames
    • MatrixAnimationUsingKeyFrames
    • ObjectAnimationUsingKeyFrames
    • PointAnimationUsingKeyFrames
    • Point3DAnimationUsingKeyFrames
    • QuarternionAnimationUsingKeyFrames
    • RectAnimationUsingKeyFrames
    • Rotation3DAnimationUsingKeyFrames
    • SingleAnimationUsingKeyFrames
    • SizeAnimationUsingKeyFrames
    • StringAnimationUsingKeyFrames
    • ThicknessAnimationUsingKeyFrames
    • VectorAnimationUsingKeyFrames
    • Vector3DAnimationUsingKeyFrames
    • DoubleAnimationUsingPath
    • MatrixAnimationUsingPath
    • PointAnimationUsingPath

    Анимацию можно создать и использовать как декларативно в коде XAML, так и программно в коде C#.

    Программная анимация

    Пусть в XAML определена кнопка:

    Проанимируем ее свойство Width :

    using System; using System.Windows; using System.Windows.Controls; using System.Windows.Media.Animation; namespace AnimationApp < public partial class MainWindow : Window < public MainWindow() < InitializeComponent(); DoubleAnimation buttonAnimation = new DoubleAnimation(); buttonAnimation.From = helloButton.ActualWidth; buttonAnimation.To = 150; buttonAnimation.Duration = TimeSpan.FromSeconds(3); helloButton.BeginAnimation(Button.WidthProperty, buttonAnimation); >> >

    В данном случае мы сначала задаем тип анимации и создаем ее. Поскольку изменяется свойство Width, это будет DoubleAnimation .

    У любого класса линейной анимации есть набор свойств, с помощью которых мы можем управлять анимацией:

    • From : начальное значение, с которого будет начинается анимация
    • To : конечное значение
    • Duration : время анимации в виде объекта TimeSpan
    • By : значение, которое указывает, насколько должно увеличиться анимируемое свойство. Свойство By используется вместо свойства To
    • RepeatBehavior : позволяет установить, как анимация будет повторяться
    • AccelerationRatio : задает ускорение анимации
    • DecelerationRatio : устанавливает замедление анимации
    • SpeedRatio : устанавливает скорость анимации. По умолчанию значение 1.0
    • AutoReverse : при значении true анимация выполняется в противоположную сторону
    • FillBehavior : определеяет поведение после окночания анимации. Если оно имеет значение Stop , то после окончания анимации объект возвращает прежние значения: buttonAnimation.FillBehavior = FillBehavior.Stop . Если же оно имеет значение HoldEnd , то анимация присваивает анимируемому свойству новое значение.

    Чтобы запустить анимацию, у элемента вызывается метод BeginAnimation() . В этот метод передается свойство зависимости, которое надо анимировать, и сам объект анимации.

    В данном случае произойдет изменение ширины кнопки от текущего значения до 150 пикселей. И данное изменение будет длиться 3 секунды.

    Если мы хотим повторения анимации, можно использовать свойство RepeatBehavior:

    DoubleAnimation buttonAnimation = new DoubleAnimation(); buttonAnimation.From = helloButton.ActualWidth; buttonAnimation.To = 150; buttonAnimation.Duration = TimeSpan.FromSeconds(3); buttonAnimation.RepeatBehavior = new RepeatBehavior(2); helloButton.BeginAnimation(Button.WidthProperty, buttonAnimation);

    В данном случае анимация будет повторяться два раза. Также мы можем задать время повторения:

    buttonAnimation.Duration = TimeSpan.FromSeconds(3); buttonAnimation.RepeatBehavior = new RepeatBehavior(TimeSpan.FromSeconds(7));

    Здесь время повторения — 7 секунд. Анимация длится 3 секунды, а это значит, что будет 7 / 3 повторений: два полноценных повторения и в последнем случае ширина увелится только до трети требуемой ширины.

    Чтобы задать плавное изменение свойства в обратную сторону, применим свойство AutoReverse:

    buttonAnimation.AutoReverse = true; buttonAnimation.RepeatBehavior = new RepeatBehavior(5);

    Событие Completed

    При завершении анимации генерируется событие Completed, которое мы можем обработать:

    public MainWindow() < InitializeComponent(); DoubleAnimation buttonAnimation = new DoubleAnimation(); buttonAnimation.From = helloButton.ActualWidth; buttonAnimation.To = 150; buttonAnimation.Duration = TimeSpan.FromSeconds(5); buttonAnimation.Completed += ButtonAnimation_Completed; helloButton.BeginAnimation(Button.WidthProperty, buttonAnimation); >private void ButtonAnimation_Completed(object sender, EventArgs e)

    Анимация нескольких свойств

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

    // анимация для ширины DoubleAnimation widthAnimation = new DoubleAnimation(); widthAnimation.From = helloButton.ActualWidth; widthAnimation.To = 150; widthAnimation.Duration = TimeSpan.FromSeconds(5); // анимация для высоты DoubleAnimation heightAnimation = new DoubleAnimation(); heightAnimation.From = helloButton.ActualHeight; heightAnimation.To = 60; heightAnimation.Duration = TimeSpan.FromSeconds(5); helloButton.BeginAnimation(Button.WidthProperty, widthAnimation); helloButton.BeginAnimation(Button.HeightProperty, heightAnimation);

    Свойства линейной анимации

    Свойство From задает начальное значение для свойства Width. Если щелкать на кнопке несколько раз, то всякий раз ширина кнопки будет сбрасываться в 150, и анимация запустится вновь. Так будет, даже если щелкнуть на кнопке в процессе уже запущенной анимации.

    В этом примере раскрывается еще одна сторона анимации WPF, а именно: всякое свойство зависимости должно обрабатываться только одной анимацией в каждый момент времени. Запуск второй анимации приводит к автоматической отмене первой.

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

    • Есть анимация, которая может быть запущена многократно и при этом давать совокупный эффект. Например, требуется создать кнопку, которая становится чуть больше при каждом щелчке.
    • Есть анимации, которые могут перекрываться. Например, событие MouseEnter может использоваться для запуска анимации, расширяющей кнопку, а событие MouseLeave — для активизации дополняющей анимации, которая вернет кнопку к исходному размеру. (Это часто называют эффектом «рыбьего глаза».) Если многократно быстро перемещать курсор мыши на такую кнопку и обратно, то каждая анимация будет прерывать предыдущую, заставляя кнопку «прыгать» обратно к размеру, заданному в свойстве From.

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

    DoubleAnimation da = new DoubleAnimation(); da.To = this.Width - 20; da.Duration = TimeSpan.FromSeconds(5); btn.BeginAnimation(Button.WidthProperty, da);

    Существует одна сложность. Чтобы такой прием работал, анимируемое свойство должно иметь ранее установленное значение. В данном примере это означает, что кнопка должна иметь жестко закодированную ширину (заданную либо в дескрипторе кнопки, либо примененную установкой стиля). Проблема в том, что во многих контейнерах компоновки принято не указывать ширину, а позволять контейнеру управлять ею на основе свойств выравнивания элементов. В рассматриваемом случае применяется значение ширины по умолчанию, которое равно специальному значению Double.NaN (где NaN означает «not a number» — «не число»). Выполнять анимацию свойства, имеющего такое значение, с применением линейной интерполяции нельзя.

    Итак, каково же решение? Во многих случаях оно сводится к жесткому кодированию ширины кнопки. Как вскоре можно будет убедиться, анимация часто требует более точного управления размерами элементов и их позиционированием. Фактически, наиболее часто используемым контейнером компоновки для «анимируемого» содержимого является Canvas, поскольку он позволяет легко перемещать содержимое по своей поверхности (с возможностью перекрытия) и изменять его размер. Canvas также наиболее облегченный контейнер компоновки, поскольку ему не приходится выполнять никакой дополнительной работы по компоновке при изменении свойства, подобного Width.

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

    da.From = btn.ActualWidth;

    Такой прием работает как с анимацией на основе кода (показанной в примере), так и с декларативной анимацией, которая рассматривается позже (что потребует применения выражения привязки для получения значения ActualWidth).

    В этом примере важно пользоваться именно свойством ActualWidth, а не Width. Это связано с тем, что Width отражает желаемую ширину, которая была выбрана, а ActualWidth — используемую в данный момент текущую отображаемую ширину. В случае применения автоматической компоновки, скорее всего, значение Width не будет жестко кодироваться, так что свойство Width просто вернет Double.NaN, и при попытке запуска анимации возникнет исключение.

    При использовании текущего значения в качестве начальной точки анимации следует помнить об еще одной проблеме: это может изменить скорость анимации. Причина в том, что длительность анимации не подстраивается, чтобы учесть меньшую дистанцию между начальным и конечным значением. Например, предположим, что создается кнопка, которая не использует значение From и выполняет анимацию, начиная с текущей позиции. Если щелкнуть на кнопке в момент, когда она почти достигла своей максимальной ширины, начнется новая анимация. Эта анимация сконфигурирована так, чтобы длиться пять секунд (задано в свойстве Duration), и это несмотря на то, что до максимальной ширины ей останется несколько пикселей. В результате рост кнопки тут же замедлится.

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

    Упомянутую проблему можно скомпенсировать, написав некоторую специальную логику, которая будет модифицировать длительность анимации, хотя это редко стоит потраченных усилий. Чтобы сделать это, необходимо сделать предположение относительно стандартного размера кнопки (что ограничит возможность повторного использования кода). Кроме того, для запуска этого кода анимация должна быть описана программно (а не декларативно, что общепринято, как вы вскоре убедитесь).

    Свойство To

    Точно так же, как можно опустить свойство From, можно не указывать и свойство То. Фактически можно не задавать оба свойства — и From, и То, — создав анимацию вроде следующей:

    DoubleAnimation da = new DoubleAnimation(); da.Duration = TimeSpan.FromSeconds(5); btn.BeginAnimation(Button.WidthProperty, da);

    На первый взгляд эта анимация выглядит как сложный способ не делать вообще ничего. Логично предположить, что поскольку опущены оба свойства From и То, они будут использовать одно и то же значение. Но между ними есть одно тонкое, однако, существенное отличие.

    В случае отсутствия From анимация использует текущее значение и учитывает состояние анимации. Например, если кнопка находится в процессе роста, значение From использует увеличенную ширину. Однако если не указано свойство То, анимация использует текущее значение, не принимая во внимание саму анимацию. По сути, это означает, что То принимает первоначальное значение — то, которое было последний раз установлено в коде, в дескрипторе элемента или с помощью стиля. (Это работает благодаря системе разрешения свойств WPF, которая в состоянии вычислить значение свойства на основе нескольких перекрывающихся поставщиков свойств, не отбрасывая никакой информации.)

    В примере с кнопкой это означает, что если запустить анимацию роста, а затем прервать ее анимацией, показанной ранее (возможно, щелчком на другой кнопке), то кнопка станет уменьшаться от того размера, до которого успела вырасти, и будет уменьшаться, пока не достигнет исходной ширины, указанной в разметке XAML. С другой стороны, если запустить этот код, когда никакая другая анимация не выполняется, то ничего не произойдет. Это объясняется тем, что значение From (анимируемая ширина) и значение То (исходная ширина) совпадают.

    Свойство By

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

    DoubleAnimation da = new DoubleAnimation(); da.By = 10; da.Duration = TimeSpan.FromSeconds(1); btn.BeginAnimation(Button.WidthProperty, da);

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

    da.То = btn.Width + 10;

    Однако значение By более осмысленно, когда анимация определяется в XAML-разметке, поскольку XAML не позволяет выполнять простые вычисления.

    Значения By и From можно использовать совместно, но это не сократит объем работы. Значение By просто добавляется к значению From для достижения значения То. Свойство By предоставляется большинством, хотя и не всеми классами, использующими интерполяцию. Например, оно не имеет смысла для нечисловых типов данных, таких как структура Color (применяемая в ColorAnimation).

    Есть только один способ получить аналогичное поведение без применения By — можно создать аддитивную анимацию, установив свойство IsAdditive. После этого текущее значение будет автоматически добавляться к обоим значениям — From и То.

    Свойство Duration

    Свойство Duration достаточно очевидно — оно принимает временной интервал (в миллисекундах, минутах, часах или любых других желаемых единицах) между моментом запуска анимации и временем ее завершения. Хотя длительность анимации в предыдущих примерах установлена с использованием TimeSpan, свойство Duration на самом деле требует объекта Duration. К счастью, Duration и TimeSpan достаточно похожи, и структура Duration предусматривает неявное приведение, с помощью которого при необходимости можно преобразовать System.TimeSpan в System.Windows.Duration.

    Вот почему следующая строка кода вполне законна:

    da.Duration = TimeSpan.FromSeconds(5);

    Так зачем вводить целый новый тип? Duration также включает два специальных значения, которые не могут быть представлены объектом TimeSpan. Это Duration.Automatic и Duration.Forever. Ни одно из этих значений не применимо в текущем примере. (Automatic просто устанавливает анимацию в односекундную длительность, a Forever задает бесконечную длительность анимации, что предотвращает проявление какого-либо эффекта.) Однако эти значения могут оказаться удобными при создании более сложной анимации.

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

Ваш адрес email не будет опубликован. Обязательные поля помечены *