Класс Animation является строительным блоком всех анимаций Xamarin.Forms, а методы расширения в классе ViewExtensions создают один или несколько объектов Animation. В этой статье показано, как использовать класс Animation для создания и отмены анимации, синхронизации нескольких анимаций и создания пользовательских анимаций, которые анимируют свойства, не анимированные существующими методами анимации.
При создании объекта Animation необходимо указать ряд параметров, включая начальное и конечное значения анимируемого свойства, а также обратный вызов, изменяющий значение свойства. Объект Animation также может содержать коллекцию дочерних анимаций, которые можно запускать и синхронизировать.
Запуск анимации, созданной с помощью класса Animation, которая может включать или не включать дочерние анимации, осуществляется путем вызова метода Commit. В этом методе указывается продолжительность анимации и, помимо прочего, обратный вызов, который определяет, следует ли повторять анимацию.
Кроме того, класс Animation имеет свойство IsEnabled, которое можно проверить, чтобы определить, была ли анимация отключена операционной системой, например, когда активирован режим энергосбережения.
Создание анимации в Xamarin
При создании объекта Animation обычно требуется не менее трех параметров, как показано в следующем примере кода:
var animation = new Animation (v => image.Scale = v, 1, 2);
Этот код определяет анимацию свойства Scale экземпляра Image от значения 1 до значения 2. Анимированное значение, получаемое Xamarin.Forms, передается в обратный вызов, указанный в качестве первого аргумента, где оно используется для изменения значения свойства Scale.
Анимация запускается вызовом метода Commit, как показано в следующем примере кода:
animation.Commit (this, "SimpleAnimation", 16, 2000, Easing.Linear, (v, c) => image.Scale = 1, () => true);
Обратите внимание, что метод Commit не возвращает объект Task. Вместо этого уведомления предоставляются через методы обратного вызова.
В методе Commit указываются следующие аргументы:
- Первый аргумент (owner) определяет владельца анимации. Это может быть визуальный элемент, к которому применяется анимация, или другой визуальный элемент, например, страница.
- Второй аргумент (имя) идентифицирует анимацию с помощью имени. Имя в сочетании с владельцем позволяет однозначно идентифицировать анимацию. Эта уникальная идентификация может быть использована для определения того, запущена ли анимация (AnimationIsRunning), или для ее отмены (AbortAnimation).
- Третий аргумент (rate) указывает количество миллисекунд между каждым вызовом метода обратного вызова, определенного в конструкторе анимации.
- Четвертый аргумент (length) указывает продолжительность анимации в миллисекундах.
- Пятый аргумент (easing) определяет функцию смягчения, которая будет использоваться в анимации. В качестве альтернативы функцию смягчения можно указать как аргумент конструктора Animation. Более подробную информацию о функциях смягчения см. в разделе Функции смягчения.
- Шестой аргумент (finished) - это обратный вызов, который будет выполнен по завершении анимации. Этот обратный вызов принимает два аргумента, первый из которых указывает конечное значение, а второй - bool, который устанавливается в true, если анимация была отменена. В качестве альтернативы, обратный вызов завершения можно указать в качестве аргумента конструктора анимации. Однако при использовании одной анимации, если обратный вызов finished указан и в конструкторе анимации, и в методе Commit, будет выполнен только обратный вызов, указанный в методе Commit.
- Седьмой аргумент (repeat) - это обратный вызов, который позволяет повторять анимацию. Он вызывается в конце анимации, и возвращение true означает, что анимация должна быть повторена.
Общий эффект заключается в создании анимации, которая увеличивает свойство Scale изображения с 1 до 2 в течение 2 секунд (2000 миллисекунд), используя функцию линейного смягчения. Каждый раз, когда анимация завершается, свойство Scale сбрасывается на 1, и анимация повторяется.
Примечание. Параллельные анимации, которые запускаются независимо друг от друга, можно построить, создав объект Animation для каждой анимации, а затем вызвав метод Commit для каждой анимации.
Дочерние анимации
Класс Animation также поддерживает дочерние анимации, что предполагает создание объекта Animation, к которому добавляются другие объекты Animation. Это позволяет запускать и синхронизировать серию анимаций. Следующий пример кода демонстрирует создание и запуск дочерних анимаций:
var parentAnimation = new Animation (); var scaleUpAnimation = new Animation (v => image.Scale = v, 1, 2, Easing.SpringIn); var rotateAnimation = new Animation (v => image.Rotation = v, 0, 360); var scaleDownAnimation = new Animation (v => image.Scale = v, 2, 1, Easing.SpringOut); parentAnimation.Add (0, 0.5, scaleUpAnimation); parentAnimation.Add (0, 1, rotateAnimation); parentAnimation.Add (0.5, 1, scaleDownAnimation); parentAnimation.Commit (this, "ChildAnimations", 16, 4000, null, (v, c) => SetIsEnabledButtonState (true, false));
В качестве альтернативы, пример кода может быть написан более лаконично, как показано в следующем примере кода:
new Animation { { 0, 0.5, new Animation (v => image.Scale = v, 1, 2) }, { 0, 1, new Animation (v => image.Rotation = v, 0, 360) }, { 0.5, 1, new Animation (v => image.Scale = v, 2, 1) } }.Commit (this, "ChildAnimations", 16, 4000, null, (v, c) => SetIsEnabledButtonState (true, false));
В обоих примерах кода создается родительский объект Animation, к которому затем добавляются дополнительные объекты Animation. Первые два аргумента метода Add задают время начала и окончания дочерней анимации. Значения аргументов должны быть между 0 и 1 и представляют собой относительный период в родительской анимации, в течение которого указанная дочерняя анимация будет активна. Поэтому в данном примере scaleUpAnimation будет активна в течение первой половины анимации, scaleDownAnimation - в течение второй половины анимации, а rotateAnimation - в течение всего времени.
Общий эффект заключается в том, что анимация происходит в течение 4 секунд (4000 миллисекунд). Анимация scaleUpAnimation анимирует свойство Scale с 1 до 2 в течение 2 секунд. Затем анимация scaleDownAnimation анимирует свойство Scale с 2 до 1 в течение 2 секунд. Во время выполнения обеих анимаций масштабирования анимация rotateAnimation анимирует свойство Rotation от 0 до 360, в течение 4 секунд. Обратите внимание, что анимация масштабирования также использует функции смягчения. Функция смягчения SpringIn заставляет изображение сначала уменьшаться, а затем увеличиваться, а функция смягчения SpringOut заставляет изображение уменьшаться по сравнению с его реальным размером к концу всей анимации.
Существует ряд различий между объектом Animation, который использует дочерние анимации, и тем, который нет:
- При использовании дочерних анимаций обратный вызов finished для дочерней анимации указывает на ее завершение, а обратный вызов finished, передаваемый в метод Commit, указывает на завершение всей анимации.
- При использовании дочерних анимаций возврат true из обратного вызова repeat в методе Commit не приведет к повторению анимации, но она продолжит выполняться без новых значений.
- При включении функции ослабления в метод Commit, если функция ослабления возвращает значение больше 1, анимация будет прервана. Если эта функция возвращает значение меньше 0, то значение зажимается до 0. Чтобы использовать функцию ослабления, которая возвращает значение меньше 0 или больше 1, она должна быть указана в одной из дочерних анимаций, а не в методе Commit.
Класс Animation также включает методы WithConcurrent, которые можно использовать для добавления дочерних анимаций к родительскому объекту Animation. Однако значения их аргументов begin и finish не ограничиваются значениями от 0 до 1, а активной будет только та часть дочерней анимации, которая соответствует диапазону от 0 до 1. Например, если вызов метода WithConcurrent определяет дочернюю анимацию, нацеленную на свойство Scale от 1 до 6, но со значениями begin и finish -2 и 3, то значение begin -2 соответствует значению Scale 1, а значение finish 3 соответствует значению Scale 6. Поскольку значения вне диапазона 0 и 1 не играют никакой роли в анимации, свойство Scale будет анимированные от 3 до 6.
Отмена анимации
Приложение может отменить анимацию с помощью вызова метода расширения AbortAnimation, как показано в следующем примере кода:
this.AbortAnimation ("SimpleAnimation");
Обратите внимание, что анимации однозначно идентифицируются комбинацией владельца анимации и имени анимации. Поэтому для отмены анимации необходимо указать владельца и имя, указанные при запуске анимации. Поэтому в примере кода будет немедленно отменена анимация с именем SimpleAnimation, принадлежащая странице.
Создание пользовательской анимации в Xamarin
Примеры, приведенные здесь до сих пор, демонстрировали анимации, которые в равной степени могли быть достигнуты с помощью методов класса ViewExtensions. Однако преимущество класса Animation заключается в том, что он имеет доступ к методу обратного вызова, который выполняется при изменении значения анимации. Это позволяет реализовать в обратном вызове любую желаемую анимацию. Например, следующий пример кода анимирует свойство BackgroundColor страницы, устанавливая его в значения Color, созданные методом Color.FromHsla, со значениями оттенка от 0 до 1:
new Animation (callback: v => BackgroundColor = Color.FromHsla (v, 1, 0.5), start: 0, end: 1).Commit (this, "Animation", 16, 4000, Easing.Linear, (v, c) => BackgroundColor = Color.Default);
Полученная анимация создает впечатление продвижения фона страницы по цветам радуги.
Создание пользовательского метода расширения анимации
Методы расширения в классе ViewExtensions анимируют свойство от его текущего значения до заданного. Это затрудняет создание, например, метода анимации ColorTo, который можно использовать для анимации цвета от одного значения к другому, потому что:
- Единственным свойством Color, определенным в классе VisualElement, является BackgroundColor, которое не всегда является желаемым свойством Color для анимации.
- Часто текущим значением свойства Color является Color.Default, который не является реальным цветом и не может быть использован в расчетах интерполяции.
Решение этой проблемы заключается в том, чтобы метод ColorTo не нацеливался на конкретное свойство Color. Вместо этого можно написать метод обратного вызова, который передает интерполированное значение Color обратно вызывающей стороне. Кроме того, метод будет принимать начальный и конечный аргументы Color.
Метод ColorTo может быть реализован как метод расширения, который использует метод Animate в классе AnimationExtensions для обеспечения своей функциональности. Это связано с тем, что метод Animate можно использовать для воздействия на свойства, которые не относятся к типу double, как показано в следующем примере кода:
public static class ViewExtensions { public static Task ColorTo(this VisualElement self, Color fromColor, Color toColor, Action callback, uint length = 250, Easing easing = null) { Func<_double2c_ Color=""> transform = (t) => Color.FromRgba(fromColor.R + t * (toColor.R - fromColor.R), fromColor.G + t * (toColor.G - fromColor.G), fromColor.B + t * (toColor.B - fromColor.B), fromColor.A + t * (toColor.A - fromColor.A)); return ColorAnimation(self, "ColorTo", transform, callback, length, easing); } public static void CancelAnimation(this VisualElement self) { self.AbortAnimation("ColorTo"); } static Task ColorAnimation(VisualElement element, string name, Func<_double2c_ Color=""> transform, Action callback, uint length, Easing easing) { easing = easing ?? Easing.Linear; var taskCompletionSource = new TaskCompletionSource(); element.Animate(name, transform, callback, 16, length, easing, (v, c) => taskCompletionSource.SetResult(c)); return taskCompletionSource.Task; } }
Метод Animate требует аргумента transform, который является методом обратного вызова. Входом для этого обратного вызова всегда является двойное значение в диапазоне от 0 до 1. Поэтому метод ColorTo определяет свою собственную функцию преобразования Func, которая принимает двойное значение в диапазоне от 0 до 1 и возвращает значение Color, соответствующее этому значению. Значение Color вычисляется путем интерполяции значений R, G, B и A двух заданных аргументов Color. Затем значение Color передается в метод обратного вызова для применения к определенному свойству.
Такой подход позволяет методу ColorTo анимировать любое свойство Color, как показано в следующем примере кода:
await Task.WhenAll( label.ColorTo(Color.Red, Color.Blue, c => label.TextColor = c, 5000), label.ColorTo(Color.Blue, Color.Red, c => label.BackgroundColor = c, 5000)); await this.ColorTo(Color.FromRgb(0, 0, 0), Color.FromRgb(255, 255, 255), c => BackgroundColor = c, 5000); await boxView.ColorTo(Color.Blue, Color.Red, c => boxView.Color = c, 4000);
В этом примере кода метод ColorTo анимирует свойства TextColor и BackgroundColor ярлыка, свойство BackgroundColor страницы и свойство Color BoxView.