Spec-Zone.ru › Angular.js 1.7

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

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-классе. Подробнее об этом см. здесь.

Обработчики анимаций для класса и ngClass

AngularJS также обращает внимание на изменения CSS-классов элементов, вызывая обработчики add и remove. Это означает, что если к элементу добавляется или удаляется CSS-класс, то в промежутке может быть выполнена анимация, до завершения добавления или удаления 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;
}

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

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

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

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

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

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

Дополнительная информация об анимациях

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

Для просмотра полного демо, обратитесь к шагу анимации в учебном пособии phonecat.

© 2010–2018 Google, Inc.
Licensed under the Creative Commons Attribution License 4.0.
https://code.angularjs.org/1.7.8/docs/guide/animations

Spec-Zone.ru

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