Spec-Zone.ru › Angular.js 1.8

Улучшить эту документациюАнимации

AngularJS предоставляет крючки для анимации для таких общих директив, как ngRepeat, ngSwitch и ngView, а также для пользовательских директив через сервис $animate. Эти крючки для анимации устанавливаются для запуска анимаций во время жизненного цикла различных директив и, при срабатывании, попытаются выполнить CSS-переход, CSS-анимацию с ключевыми кадрами или анимацию с обратным вызовом JavaScript (в зависимости от того, назначена ли анимация данной директиве). Анимации могут быть добавлены с помощью обычного CSS, следуя соглашениям об именовании, установленным AngularJS, или с помощью кода JavaScript, определенного как фабрика.

Обратите внимание, что в наших примерах мы использовали CSS-свойства переходов без префиксов, так как большинство современных браузеров теперь поддерживают свойства без префиксов. Если вам необходимо поддерживать более старые браузеры или определённые мобильные браузеры, вам нужно будет включить префиксные версии свойств перехода. Обратитесь к http://caniuse.com/#feat=css-transitions, чтобы узнать, какие браузеры требуют префиксы, и к https://github.com/postcss/autoprefixer для получения инструмента, который может автоматически генерировать префиксы.

Анимации недоступны, если вы не включите модуль ngAnimate в качестве зависимости вашего приложения.

Ниже приведен быстрый пример включения анимаций для ngShow и ngHide.

Установка

См. документацию API для ngAnimate для получения инструкций по установке модуля.

Вы также можете настроить отдельный CSS-файл для определения анимаций на основе CSS.

Как это работает

Анимации в AngularJS полностью основаны на CSS-классах. Если у вас есть CSS-класс, прикреплённый к элементу HTML в вашем приложении, вы можете применить к нему анимацию. Предположим, например, что у нас есть шаблон HTML с повторением, как показано ниже:

<div ng-repeat="item in items" class="repeated-item">
  {{ item.id }}
</div>

Как видите, класс repeated-item присутствует в элементе, который будет повторяться, и этот класс будет использоваться в CSS и/или JavaScript-коде анимации вашего приложения в качестве ссылки, чтобы сообщить AngularJS о выполнении анимации.

Когда ngRepeat выполняет свою работу, каждый раз, когда в список добавляется новый элемент, ngRepeat добавляет класс ng-enter к добавляемому элементу. При удалении он применяет класс ng-leave, а при перемещении — класс ng-move.

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

/*
  We are using CSS transitions for when the enter and move events
  are triggered for the element that has the `repeated-item` class
*/
.repeated-item.ng-enter, .repeated-item.ng-move {
  transition: all 0.5s linear;
  opacity: 0;
}

/*
  `.ng-enter-active` and `.ng-move-active` are where the transition destination
  properties are set so that the animation knows what to animate
*/
.repeated-item.ng-enter.ng-enter-active,
.repeated-item.ng-move.ng-move-active {
  opacity: 1;
}

/*
  We are using CSS keyframe animations for when the `leave` event
  is triggered for the element that has the `repeated-item` class
*/
.repeated-item.ng-leave {
  animation: 0.5s my_animation;
}

@keyframes my_animation {
  from { opacity: 1; }
  to   { opacity: 0; }
}

Тот же подход к анимации можно использовать с помощью кода JavaScript (для простоты мы используем jQuery для выполнения анимаций здесь):

myModule.animation('.repeated-item', function() {
  return {
    enter: function(element, done) {
      // Initialize the element's opacity
      element.css('opacity', 0);

      // Animate the element's opacity
      // (`element.animate()` is provided by jQuery)
      element.animate({opacity: 1}, done);

      // Optional `onDone`/`onCancel` callback function
      // to handle any post-animation cleanup operations
      return function(isCancelled) {
        if (isCancelled) {
          // Abort the animation if cancelled
          // (`element.stop()` is provided by jQuery)
          element.stop();
        }
      };
    },
    leave: function(element, done) {
      // Initialize the element's opacity
      element.css('opacity', 1);

      // Animate the element's opacity
      // (`element.animate()` is provided by jQuery)
      element.animate({opacity: 0}, done);

      // Optional `onDone`/`onCancel` callback function
      // to handle any post-animation cleanup operations
      return function(isCancelled) {
        if (isCancelled) {
          // Abort the animation if cancelled
          // (`element.stop()` is provided by jQuery)
          element.stop();
        }
      };
    },

    // We can also capture the following animation events:
    move: function(element, done) {},
    addClass: function(element, className, done) {},
    removeClass: function(element, className, done) {}
  }
});

При наличии этих сгенерированных имён CSS-классов в элементе AngularJS автоматически определяет, нужно ли выполнять CSS- и/или JavaScript-анимацию. Обратите внимание, что вы не можете иметь как CSS-, так и JavaScript-анимации, основанные на одном CSS-классе. Подробнее см. здесь.

Крючки анимации для Class и ngClass

AngularJS также обращает внимание на изменения CSS-классов в элементах, вызывая крючки add и remove. Это означает, что если CSS-класс добавляется к элементу или удаляется из него, то между этим и окончательным добавлением или удалением класса может быть выполнена анимация. (Помните, что AngularJS сможет захватывать изменения классов только в том случае, если для элемента используется интерполированное выражение или директива ng-class.)

В приведенном ниже примере показано, как выполнять анимации во время изменений класса:

Хотя CSS немного отличается от того, что мы видели раньше, идея та же.

Какие директивы поддерживают анимации?

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

Директива Поддерживаемые анимации
form / ngForm add и remove (различные классы)
ngAnimateSwap enter и leave
ngClass / {{class}​} add и remove
ngClassEven add и remove
ngClassOdd add и remove
ngHide add и remove (класс ng-hide)
ngIf enter и leave
ngInclude enter и leave
ngMessage / ngMessageExp enter и leave
ngMessages add и remove (классы ng-active/ng-inactive)
ngModel add и remove (различные классы)
ngRepeat enter, leave и move
ngShow add и remove (класс ng-hide)
ngSwitch enter и leave
ngView enter и leave

(Дополнительную информацию можно найти в документации, связанной с каждой директивой.)

Для полного описания шагов, выполняемых во время каждого события анимации, обратитесь к $animate документации API.

Как использовать анимации в собственных директивах?

Анимации в пользовательских директивах также можно создать, введя $animate напрямую в вашу директиву и вызывая её при необходимости.

myModule.directive('my-directive', ['$animate', function($animate) {
  return function(scope, element) {
    element.on('click', function() {
      if (element.hasClass('clicked')) {
        $animate.removeClass(element, 'clicked');
      } else {
        $animate.addClass(element, 'clicked');
      }
    });
  };
}]);

Анимации при запуске приложения/загрузке страницы

По умолчанию анимации отключены при загрузке приложения AngularJS запуска приложения. Если вы используете директиву ngApp, это происходит в событии DOMContentLoaded, сразу после загрузки страницы. Анимации отключены, чтобы пользовательский интерфейс и содержимое были немедленно видны. В противном случае, при наличии многих анимаций на странице процесс загрузки может стать слишком визуально перегруженным, и пострадает производительность.

Внутренне ngAnimate ожидает, пока завершатся все загрузки шаблонов, начатые сразу после запуска. Затем он ожидает завершения текущего $digest и ещё одного после него. Это гарантирует, что всё приложение будет полностью скомпилировано, прежде чем будут выполняться анимации.

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

myModule.run(function($animate) {
  $animate.enabled(true);
});

Как (выборочно) включать, отключать и пропускать анимации

Существует несколько способов отключения анимаций, как глобально, так и для конкретных анимаций. Отключение конкретных анимаций может помочь ускорить производительность рендеринга, например, для больших списков ngRepeat, в которых анимации не нужны. Поскольку ngAnimate проверяет во время выполнения наличие анимаций, производительность пострадает даже если у элемента нет анимации.

Во время конфигурации: $animateProvider.customFilter()

Эта функция может быть вызвана на стадии config приложения. Она принимает функцию фильтра в качестве единственного аргумента, которая затем будет использоваться для «фильтрации» анимаций (на основе анимированного элемента, типа события и параметров анимации). Анимация будет выполнена только тогда, когда функция фильтра вернёт true. Это обеспечивает большую гибкость — вы можете легко создавать сложные правила, такие как разрешение только определённых событий или включение анимаций только на определённых поддеревьях DOM, и динамически изменять их, например, отключая анимации в определённые моменты времени или при определённых обстоятельствах.

app.config(function($animateProvider) {
  $animateProvider.customFilter(function(node, event, options) {
    // Example: Only animate `enter` and `leave` operations.
    return event === 'enter' || event === 'leave';
  });
});

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

Лучшая практика: Сделайте функцию фильтрации как можно более простой, так как она будет вызываться для каждого действия с DOM (например, вставки, удаления, изменения класса), выполняемого «осознающими анимацию» директивами. См. здесь для списка встроенных директив, поддерживающих анимации. Выполнение вычислений или операций, занимающих много времени, при каждом вызове функции фильтрации может привести к замедлению анимаций.

Во время конфигурации: $animateProvider.classNameFilter()

Эта функция также может быть вызвана на стадии config приложения. Она принимает регулярное выражение в качестве единственного аргумента, которое затем будет сопоставляться с классами любого элемента, который собирается быть анимированным. Регулярное выражение позволяет получить много гибкости — вы можете разрешить анимации только для определённых классов (полезно, если вы работаете с анимациями сторонних разработчиков) или исключить определённые классы из анимации.

app.config(function($animateProvider) {
  $animateProvider.classNameFilter(/animate-/);
});
/* prefixed with `animate-` */
.animate-fade-add.animate-fade-add-active {
  transition: all 1s linear;
  opacity: 0;
}

Подход classNameFilter обычно обеспечивает значительный прирост скорости по сравнению с другими стратегиями, поскольку сопоставление выполняется до проверки других стратегий отключения анимации. Однако это также означает, что невозможно переопределить сопоставление по имени класса с помощью двух следующих стратегий. Конечно, по-прежнему возможно включить/отключить анимацию, изменив имя класса элемента во время выполнения.

Во время выполнения: $animate.enabled()

Эта функция может использоваться для включения/отключения анимаций двумя различными способами:

С единственным аргументом boolean, она включает/отключает анимации глобально: $animate.enabled(false) отключает все анимации в вашем приложении.

Когда первым аргументом является родной DOM-элемент или jqLite/jQuery-элемент, функция включает/отключает анимации на этом элементе и всех его дочерних элементов: $animate.enabled(myElement, false). Вы по-прежнему можете использовать её для повторного включения анимаций для дочернего элемента, даже если вы отключили их на родительском элементе. И по сравнению с classNameFilter, вы можете изменить состояние анимации во время выполнения вместо конфигурации.

Однако обратите внимание, что состояние $animate.enabled() для отдельных элементов не переопределяет правила отключения, которые были установлены в classNameFilter.

Через стили CSS: переопределение стилей в классе ng-animate CSS

Всякий раз, когда запускается анимация, ngAnimate применяет класс ng-animate к элементу на всё время анимации. Применяя стили CSS-переходов/анимаций к этому классу, вы можете пропустить анимацию:

.my-class {
  transition: transform 2s;
}

.my-class:hover {
  transform: translateX(50px);
}

my-class.ng-animate {
  transition: 0s;
}

Установив transition: 0s, ngAnimate проигнорирует существующие стили перехода и не будет пытаться анимировать их (анимации на JavaScript всё равно будут выполняться). Это можно использовать для предотвращения проблем с существующими анимациями, конфликтующими с ngAnimate.

Предотвращение мерцания перед началом анимации

При вложенности элементов со структурными анимациями, такими как ngIf, в элементы с анимациями на основе классов, такие как ngClass, иногда происходит кратковременное мерцание или вспышка содержимого, где анимированный элемент ненадолго становится видимым перед началом фактической анимации.

Чтобы предотвратить это, вы можете применить стили к классу ng-[event]-prepare, который добавляется как только инициализируется анимация, но удаляется перед фактическим началом анимации (после ожидания $digest). Этот класс добавляется только для структурных анимаций (enter, move, и leave).

Вот пример, где вы можете увидеть мерцание:

<div ng-class="{red: myProp}">
  <div ng-class="{blue: myProp}">
    <div class="message" ng-if="myProp"></div>
  </div>
</div>

Возможны случаи, когда во время события enter div с классом .message будет ненадолго виден, прежде чем начнётся анимация. В таком случае вы можете добавить стили в CSS, чтобы убедиться, что элемент остаётся скрытым до начала анимации:

.message.ng-enter-prepare {
  opacity: 0;
}

/* Other animation styles ... */

Предотвращение конфликтов с существующими анимациями и сторонними библиотеками

По умолчанию любые директивы, использующие ngAnimate, будут предполагать, что стили transition / animation на элементе являются частью анимации ngAnimate. Это может привести к проблемам, если стили предназначены для анимаций, независимых от ngAnimate.

Например, элемент выступает в роли загрузчика. На нём есть бесконечная CSS-анимация, а также директива ngIf, для которой анимации не определены:

.spinner {
  animation: rotating 2s linear infinite;
}

@keyframes rotating {
  from { transform: rotate(0deg); }
  to { transform: rotate(360deg); }
}

Теперь, когда выражение ngIf меняется, ngAnimate увидит анимацию загрузчика и использует её для анимации события enter/leave, что не работает, потому что анимация бесконечная. Элемент всё равно будет добавлен/удален после таймаута, но будет заметная задержка.

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

Вы можете предотвратить это нежелательное поведение, добавив в CSS стили к классу .ng-animate, который добавляется на всё время каждой анимации. Просто переопределите продолжительность перехода/анимации. В случае с загрузчиком это будет:

.spinner.ng-animate {
  animation: 0s none;
  transition: 0s none;
}

Если у вас определены CSS-переходы/анимации для событий анимации, убедитесь, что у них более высокий приоритет, чем любые стили, не связанные с ngAnimate.

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

Включение анимаций за пределами DOM-дерева приложения: $animate.pin()

Перед анимацией ngAnimate проверяет, находится ли анимированный элемент внутри DOM-дерева приложения. Если нет, анимация не запускается. Обычно это не проблема, поскольку большинство приложений используют элементы html или body в качестве корневого элемента.

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

Вы можете использовать $animate.pin(element, parentHost), чтобы связать элемент с другим элементом, который принадлежит вашему приложению. Просто вызовите её перед добавлением элемента в DOM/перед началом анимации с элементом, который вы хотите анимировать, и элементом, который должен быть его предполагаемым родителем.

Подробнее об анимациях

Для полного описания каждой доступной функции $animate, см. документацию API.

Чтобы увидеть полную демонстрацию, см. шаг анимации в руководстве по туториалу phonecat.

© 2010–2020 Google, Inc.
Licensed under the Creative Commons Attribution License 3.0.
https://code.angularjs.org/1.8.2/docs/guide/animations

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API