Анимация
Наличие встроенных возможностей анимации представляют одну из ключевых особенностей платформы 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);
Анимация Opacity для DropShadowEffect
По идее, во время работы приложения у DropShadowEffect должен быть эффект плавного возникновения и исчезновения (пульсации). Однако такого эффекта не наблюдается. Что я упустил?
Отслеживать
задан 21 апр 2018 в 14:35
95 8 8 бронзовых знаков1 ответ 1
Сортировка: Сброс на вариант по умолчанию
Используйте для этого Storyboard, через код либо через XAML разметку.
С#
- Сама анимация тут сильно не изменится:
var animation = new DoubleAnimation < To = 0, AutoReverse = true, Duration = TimeSpan.FromMilliseconds(800), RepeatBehavior = RepeatBehavior.Forever >;Storyboard.SetTarget(animation, borderOne); Storyboard.SetTargetProperty(animation, new PropertyPath("Effect.Opacity"));var storyboard = new Storyboard >; storyboard.Begin();XAML
Через разметку XAML тоже довольно просто добавлять анимацию, все тоже самое. Нам нужно всего лишь указать внутри Border’a примерно такой код:
Вот собственно и все, ваша пульсация должна заработать 😉
Анимация полета ракеты на C# WPF
Приложение-шутка на платформе C# WPF запускает анимацию перемещения космической ракеты по экрану монитора. Это последовательное перемещение окна приложения, форму которого определяет анимированное изображение космической ракеты. Точки старта и финиша определяются случайным образом, создавая прикольный хаотичный полет мультяшной ракеты на экране.
Реалистичный полет ракеты создают совокупность нескольких видов анимации:
DoubleAnimation — изменение свойства Opacity и изменение свойств Left, Top главного окна;
ObjectAnimationUsingKeyFrames – покадровая анимация ракеты с работающим двигателем.Приложение может служит стартовым кодом создания программ-приколов или использоваться для тестирования WPF анимаций. Пример подобной программы-шутки — Анимация новогоднего поздравления.
Окно в форме ракеты
Чтобы создать фигурное окно приложения на платформе WPF необходимо сделать главное окно прозрачным, для чего требуется обязательное переопределение некоторых свойств главного окна:
AllowsTransparency — окно приложения может быть прозрачным только в случае определения этого свойства значением true .
WindowStyle — единственное допустимое значение для этого свойства WindowStyle.None , если AllowsTransparency имеет значение true .
Background — необходимо выбрать прозрачную кисть для фона окна.
Только после надлежащего определения этих свойств можно получить прозрачное главное окно приложения. Эти свойства можно определить непосредственно в разметке XAML или в программном коде, смотрите коды ниже.После создания прозрачного окна приложения любой дочерний визуальный элемент будет определять форму главного окна. В нашем случае это объект Image класса AnimRocket с анимацией последовательно показываемых файлов .png.
Разметка XAML WPF для создания окна фигурной формы:
Определение прозрачности главного окна в конструкторе приложения:
public MainWindow()
Класс бесконечной анимации AnimRocket
AnimRocket – класс бесконечной анимации ракеты с работающим двигателем. Анимация построена на функциональности объекта ObjectAnimationUsingKeyFrames, который показывает коллекцию ключевых кадров в течение определенного промежутка времени, заданного свойством Duration.
В качестве ключевых кадров используются файлы .png, определенные как ресурсы приложения. Плоскостью для последовательного показа изображений ресурсов служит элемент Image, в котором подготовленный объект ObjectAnimationUsingKeyFrames периодически изменяет источник изображения.
AnimRocket имеет метод Rotate(double angle) для ориентирования изображения ракеты вдоль оси перемещения. С помощью RotateTransform программным кодом обеспечивается вращение ракеты относительно своего центра.
Метод ComputeMaxImageSize() вычисляет максимальную длину из всех сторон изображений. ComputeMaxImageSize() помогает создавать квадратное главное окно приложения с соответствующими размерами для корректного вращения ракеты вокруг центральной точки.
Программный код класса бесконечной анимации космической ракеты с работающим двигателем:
class AnimRocket < // Размер массива адресных данных ключевых кадров. readonly int numberFrames = 0; // Адресные координаты изображений для ключевых кадров. readonly string[] uriSources = < "img/rocket/rocket-main-anim-frame0.png", "img/rocket/rocket-main-anim-frame1.png", "img/rocket/rocket-main-anim-frame2.png" >; public readonly Size maxImgSize = new(0, 0); private readonly Panel? _parent = null; // Инициализация полей и запуск анимации. public AnimRocket(Panel parent) < _parent = parent; numberFrames = uriSources.Length; maxImgSize = ComputeMaxImageSize(); Animation(); >// Вращение ракеты для расположения вдоль оси перемещения. public void Rotate(double angle) < if (_parent != null) < // Создаем трансформацию вращения. RotateTransform rt = new(angle); // Вращаем контейнер изображения. _parent.Children[0].RenderTransform = rt; >> // Метод бесконечной анимации по ключевым кадрам. private void Animation() < // Создание объекта анимации с заданными свойствами. ObjectAnimationUsingKeyFrames objectAnimation = new() < Duration = new Duration(TimeSpan.FromSeconds(0.3)), RepeatBehavior = RepeatBehavior.Forever >; // Создание ключевых кадров. for (int i = 0; i < numberFrames; i++) < // Количество времени на показ данного кадра. double quota = (double)i / numberFrames; // В коллекцию ключевых кадров добавляется кадр // со своим изображением и временем показа. objectAnimation.KeyFrames.Add( new DiscreteObjectKeyFrame() < Value = new BitmapImage(new Uri(uriSources[i], UriKind.Relative)), KeyTime = KeyTime.FromPercent(quota) >); > // Объект для анимации. Создается локально, // но жизненный цикл до закрытия приложения. Image image = new() < Margin = new Thickness(0, 0, 0, 0), HorizontalAlignment = HorizontalAlignment.Center, VerticalAlignment = VerticalAlignment.Center, Stretch = Stretch.None, // Изображение должно вращаться относительно // своей центральной точки. RenderTransformOrigin = new Point(0.5, 0.5) >; _parent?.Children.Add(image); // Непосредственно запуск анимации космической ракеты. image.BeginAnimation(Image.SourceProperty, objectAnimation); > // Получение максимальных размеров объекта private Size ComputeMaxImageSize() < ListmaxW = new(); List maxH = new(); // Вычисляем габаритный размер анимации for (int i = 0; i < numberFrames; i++) < BitmapImage bmp = new(new Uri(uriSources[i], UriKind.Relative)); maxW.Add(bmp.Width); maxH.Add(bmp.Height); >// Отправляем наибольший размер из всех полученных. return new Size(maxW.Max(x => x), maxH.Max(y => y)); > >Анимация перемещения ракеты DoubleAnimation
Перемещения ракеты, а точнее главного окна приложения, основано на объекте анимации типа DoubleAnimation, который обеспечивает поступательное движение ракеты между двумя указанными точками и автоматически вычисляет промежуточные положения методом линейной интерполяции. При этом позиция окна приложения, а значит и ракеты, вычисляется относительно его центра.
Движения ракеты происходят между двумя случайными точками периметра экрана. Для повышения разнообразия перемещений каждая сторона периметра экрана делится на определённое количество точек, в данном исходном коде на каждую сторону 10 точек. Метод ComputeCoordinates() выдает случайные координаты старта и финиша ракеты между случайно выбранными сторонами периметра экрана.
Перед началом движения вычисляется вектор направления и его угол относительно базового вектора. Далее, при помощи метода animObject.Rotate(angle) анимированное изображение космической ракеты разворачивается точно по оси движения между двумя точками.
После события окончания перемещения локальный таймер, через указанную паузу, запускает анимацию перемещения вновь с новыми, случайными координатами и так далее, вплоть до закрытия приложения. Ракета потешно летает поверх всех приложений радуя пользователя своим изяществом .
// Анимация перемещения окна private void AnimationWindow() < // Габариты перемещения основного окна double minX = 0; double maxX = SystemParameters.PrimaryScreenWidth; double minY = 0; double maxY = SystemParameters.PrimaryScreenHeight; // Получение координат перемещения объекта (double fromX, double toX, double fromY, double toY) = ComputeCoordinates(minX, maxX, minY, maxY); // Вычисляем угол поворота объекта относительно горизонтали. Vector vectorBase = new(30, 0); Vector vectorDirection = new(toX - fromX, toY - fromY); double angle = Vector.AngleBetween(vectorBase, vectorDirection); animObject.Rotate(angle); // Координата Х расположения окна на экране. DoubleAnimation posLeft = new(); // Координата Y расположения окна на экране. DoubleAnimation posTop = new(); // Характеристики поведения координат идентичны. posLeft.Duration = posTop.Duration = new Duration(TimeSpan.FromSeconds(_duration)); posLeft.FillBehavior = posTop.FillBehavior = FillBehavior.Stop; // Позиция окна вычисляется относительно его центра. posLeft.From = fromX - Width / 2; posLeft.To = toX - Width / 2; posTop.From = fromY - Height / 2; posTop.To = toY - Height / 2; // Анимация появления окна double opacityShow = 0.2; DoubleAnimation opacityAnimationShow = new() < From = 0.0, To = 1.0, Duration = new Duration(TimeSpan.FromSeconds(opacityShow)), FillBehavior = FillBehavior.Stop >; // Анимация исчезновения окна. double opacityHide = 0.2; // Защита от отрицательного временного промежутка. double beginTimeHide = _duration >= (opacityShow + opacityHide) ? (_duration - (opacityShow + opacityHide)) : 0.0; DoubleAnimation opacityAnimationHide = new() < From = 1.0, To = 0.0, Duration = new Duration(TimeSpan.FromSeconds(opacityHide)), BeginTime = TimeSpan.FromSeconds(beginTimeHide), FillBehavior = FillBehavior.Stop >; // Событие окончания перемещения posLeft.Completed += (sender, eArgs) => < // Запуск новой анимации DispatcherTimer timer = new() < Interval = TimeSpan.FromSeconds(2) >; timer.Tick += (sender, args) => < timer.Stop(); AnimationWindow(); >; timer.Start(); >; // Событие появления окна opacityAnimationShow.Completed += (sender, eArgs) => < this.Opacity = 1; // Повторная анимация прозрачности запускается точно после окончания первой, // иначе управление одним свойством двумя анимациями создает артефакты. BeginAnimation(OpacityProperty, opacityAnimationHide); >; // Окончание исчезновения. opacityAnimationHide.Completed += (sender, eArgs) => < Opacity = 0; >; // Запуск анимаций BeginAnimation(OpacityProperty, opacityAnimationShow); BeginAnimation(LeftProperty, posLeft); BeginAnimation(TopProperty, posTop); >Анимация появления и исчезновения ракеты
Начальная и конечная координата находятся на периметре экрана. Для плавного вхождения и исчезновения ракеты применяются две анимации прозрачности: при старте ракеты и симуляции выхода ракеты за пределы экрана.
Для появления и исчезновения ракеты применяется анимация свойства главного окна Opacity с помощью объектов DoubleAnimation. Анимации перемещения posLeft , posTop и появления ракеты opacityAnimationShow запускаются одновременно, смотрите код выше.
Анимация исчезновения opacityAnimationHide запускается только после завершения работы opacityAnimationShow . Запускается с отложенным началом, чтобы исчезание происходило логично в конце подлета ракеты к финишной точке. Можно было бы запустить все анимации одновременно, но параллельное управление свойством Opacity приложения двумя анимациями, даже если одна с отложенным началом, может вызвать нежелательные визуальные артефакты.
Вычисление случайных координат перемещения
Вычисление координат перемещения не такая уж и простая задача для такого шуточного приложения. Для большей забавности ракета должна всегда пересекать экран, каждый раз начиная полет с новыми координатами старта и финиша.
Для получения приемлемого результата периметр экрана виртуально делится на стороны, для каждой стороны выбирается определенное количество координатных точек, при запросе из этого количества выбирается случайная точка. Именно так работает метод
GetPointMove(double minX, double maxX, double minY, double maxY, int variant) .Затем в методе
ComputeCoordinates(double minX, double maxX, double minY, double maxY) указывается случайный номер варианта выбора стороны, и мы получаем случайные координаты перемещения ракеты между смежными или противоположными сторонами экрана, исключая выбор координат только на одной стороне.// Подготовка случайных координат для перемещения анимации объекта. private (double fromX, double toX, double fromY, double toY) ComputeCoordinates(double minX, double maxX, double minY, double maxY) < // Подготовка для выбора случайных координат начала и конца перемещения. ListrandPos = new() < 0, 1, 2, 3 >; randPos = randPos.OrderBy(a => random.Next()).ToList(); // Получение случайной координаты старта. Point start = GetPointMove(minX, maxX, minY, maxY, randPos[0]); double fromX = start.X; double fromY = start.Y; // Получение координаты финиша. Point end = GetPointMove(minX, maxX, minY, maxY, randPos[1]); double toX = end.X; double toY = end.Y; // Для получения длины расстояния перемещения Vector vecDirection = new() < X = toX - fromX, Y = toY - fromY, >; // Скорость перемещения будет всегда одинакова, // независимо от расстояния перемещения. _duration = vecDirection.Length / 1100; return (fromX, toX, fromY, toY); > // Получение одной случайной координаты перемещения private Point GetPointMove(double minX, double maxX, double minY, double maxY, int variant) < // Количество координатных точек на выбранную сторону экрана. int num = 10; // Список хранения точек выбранной стороны периметра. Listpoints = new(); // Варианты получения координатных точек. switch (variant) < case 0: // Верхняя сторона экрана. for (int i = 0; i ; points.Add(p); > break; case 1: // Нижняя сторона экрана for (int i = 0; i ; points.Add(p); > break; case 2: // Левая сторона экрана for (int i = 0; i ; points.Add(p); > break; case 3: // Правая сторона экрана for (int i = 0; i ; points.Add(p); > break; > // Случайный выбор одной точки для возрата. return points[random.Next(0, points.Count)]; >Доработка приложения
Для настоящей программы-шутки требуется небольшая доработка кода приложения. Для отложенного запуска анимации ракеты необходимо добавить еще один таймер. Кроме того, необходим начальный интерфейс запуска анимации, где можно настроить время до запуска анимации и количества повторений до закрытия программы. Также желательно предусмотреть механизм для нормального закрытия приложения: горячие клавиши или действия кнопками мыши.
Исходник C#
Прикрепленный архив содержит исходный код анимационной программы-шутки полета космической ракеты на экране монитора. Исходник написан на языке C# в MS Visual Studio 2022, программная платформа .NET6. Исходник включает исполнительный файл программы для быстрого тестирования.
Скачать исходник
Тема: «Анимация полета ракеты на C# WPF»
WpfAppRocket-vs17.zip Размер: 165 Кбайт Загрузки: 17
Экран заставки

Какими бы быстрыми ни были приложения WPF, они не стартуют мгновенно. При первом запуске приложения происходит задержка, пока общеязыковая исполняющая среда (CLR) инициализирует .NET и затем запускает приложение.
Эта задержка не обязательно является проблемой. Обычно проходит небольшое время и первое окно появляется. Однако если приходится выполнять шаги инициализации, требующие большего времени, или необходимо добиться более профессионального открытия и отображения графики, можно воспользоваться простым средством , доступным в WPF.
Ниже перечислены шаги по добавлению экрана заставки:
- Добавьте в проект файл изображения (обычно .bmp, .png или .jpg).
- Выберите этот файл в Solution Explorer.
- Установите Build Action (Действие при построении) в SplashScreen.
Теперь при следующем запуске приложения это изображение будет показано немедленно, в центре экрана. Как только исполняющая среда будет готова, после завершения метода Application.Startup появится первое окно приложения, а экран заставки постепенно исчезнет (примерно за 300 миллисекунд).
Это очень простое средство. Но имейте в виду, что экран заставки отображается без каких-либо украшений. Вокруг него не рисуется рамка, так что если она нужна, то следует поместить ее в само изображение. Не предусмотрена также возможность показа последовательности изображений либо анимации. Если же это необходимо, то должен применяться традиционный подход: создание стартового окна, которое выполняет код инициализации одновременно с отображением нужного графического содержимого.
При добавлении экрана заставки компилятор WPF добавляет в автоматически сгенерированный файл App.g.i.cs код вроде следующего:
[System.STAThreadAttribute()] [System.Diagnostics.DebuggerNonUserCodeAttribute()] public static void Main()
Этот код можно написать и самостоятельно, не пользуясь действием сборки SplashScreen. Единственной деталью, которую можно изменить, является скорость исчезновения экрана заставки. Для этого методу SpashScreen.Show() понадобится передать значение false (чтобы экран заставки не исчезал автоматически). Экран заставки затем можно закрыть за нужное время, вызвав метод SplashScreen.Close() и передав ему значение TimeSpan, указывающее длительность процесса угасания.