Улучшить эту документацию ngAnimate
Установка
Сначала получите файл:
- Google CDN например,
"//ajax.googleapis.com/ajax/libs/angularjs/X.Y.Z/angular-animate.js"
- NPM например,
npm install --save angular-animate@X.Y.Z
илиyarn add angular-animate@X.Y.Z
- Bower например,
bower install angular-animate#X.Y.Z
- code.angularjs.org (не рекомендуется для использования в производстве) например,
"//code.angularjs.org/X.Y.Z/angular-animate.js"
где X.Y.Z — версия AngularJS, которую вы используете.
Затем включите angular-animate.js в свой HTML:
<script src="path/to/angular.js"></script> <script src="path/to/angular-animate.js"></script>
Наконец, загрузите модуль в своё приложение, добавив его в качестве зависимого модуля:
angular.module('app', ['ngAnimate']); Теперь вы готовы приступить к работе!
Модуль ngAnimate предоставляет поддержку анимаций на основе CSS (ключевые кадры и переходы), а также анимаций на основе JavaScript через обратные вызовы. Анимации по умолчанию не включены, однако, включив ngAnimate, вы активируете анимационные крючки для приложения AngularJS.
Использование
Проще говоря, есть два способа использования анимаций при использовании ngAnimate: с помощью CSS и JavaScript. Первый способ работает исключительно на основе CSS (используя соответствующие селекторы/стили CSS), а второй запускает анимации, зарегистрированные через module.animation(). Для анимаций как на основе CSS, так и на основе JS единственное требование — наличие соответствующего CSS class, которое существует как в зарегистрированной анимации, так и в HTML-элементе, на котором будет происходить анимация.
Поддержка директив
Следующие директивы «осознают» анимацию:
| Директива | Поддерживаемые анимации |
|---|---|
| form / ngForm | добавление и удаление (различные классы) |
| ngAnimateSwap | вход и выход |
| ngClass / {{class}} | добавление и удаление |
| ngClassEven | добавление и удаление |
| ngClassOdd | добавление и удаление |
| ngHide | добавление и удаление (класс ng-hide) |
| ngIf | вход и выход |
| ngInclude | вход и выход |
| ngMessage / ngMessageExp | вход и выход |
| ngMessages | добавление и удаление (классы ng-active/ng-inactive) |
| ngModel | добавление и удаление (различные классы) |
| ngRepeat | вход, выход и перемещение |
| ngShow | добавление и удаление (класс ng-hide) |
| ngSwitch | вход и выход |
| ngView | вход и выход |
(Более подробную информацию можно найти в документации, связанной с каждой директивой.)
Для полного разбора шагов, вовлеченных в каждом анимационном событии, обратитесь к $animate документации API.
Анимации на основе CSS
Анимации на основе CSS с ngAnimate уникальны тем, что для них вообще не требуется код JavaScript. Используя CSS-класс, на который мы ссылаемся в HTML и CSS-коде, мы можем создать анимацию, которая будет распознана AngularJS, когда подлежащая директива выполнит операцию.
Пример ниже демонстрирует, как можно сделать анимацию enter элемента, используя ng-if:
<div ng-if="bool" class="fade"> Fade me in out </div> <button ng-click="bool=true">Fade In!</button> <button ng-click="bool=false">Fade Out!</button>
Обратите внимание на CSS-класс fade? Теперь мы можем создать CSS-код перехода, который ссылается на этот класс:
/* The starting CSS styles for the enter animation */
.fade.ng-enter {
transition:0.5s linear all;
opacity:0;
}
/* The finishing CSS styles for the enter animation */
.fade.ng-enter.ng-enter-active {
opacity:1;
}
Главное, помнить, что в зависимости от анимационного события (которое каждая из приведенных выше директив запускает в зависимости от происходящего) к элементу будут применены два сгенерированных CSS-класса; в примере выше у нас есть .ng-enter и .ng-enter-active. Для CSS-переходов код перехода должен быть определён внутри начального CSS-класса (в этом случае .ng-enter). Класс назначения — это то, к чему будет анимироваться переход.
Например, если мы хотели создать анимации для leave и move (ngRepeat запускает перемещение), то мы можем сделать это, используя те же соглашения об именовании CSS:
/* now the element will fade out before it is removed from the DOM */
.fade.ng-leave {
transition:0.5s linear all;
opacity:1;
}
.fade.ng-leave.ng-leave-active {
opacity:0;
}
Мы также можем использовать CSS-ключевые кадры, сославшись на анимацию ключевых кадров внутри начального CSS-класса:
/* there is no need to define anything inside of the destination
CSS class since the keyframe will take charge of the animation */
.fade.ng-leave {
animation: my_fade_animation 0.5s linear;
-webkit-animation: my_fade_animation 0.5s linear;
}
@keyframes my_fade_animation {
from { opacity:1; }
to { opacity:0; }
}
@-webkit-keyframes my_fade_animation {
from { opacity:1; }
to { opacity:0; }
}
Вы можете смешивать переходы и ключевые кадры, а также любые другие CSS-классы на одном элементе.
Анимации на основе CSS-классов
Анимации на основе классов (анимации, которые запускаются через ngClass, ngShow, ngHide и некоторые другие директивы) имеют несколько иную систему именования. Анимации на основе классов достаточно просты, чтобы стандартный переход или ключевой кадр можно было использовать в классе, добавляемом и удаляемом.
Например, если мы хотели выполнить CSS-анимацию для ngHide, то мы поместим анимацию в CSS-класс .ng-hide:
<div ng-show="bool" class="fade">
Show and hide me
</div>
<button ng-click="bool=!bool">Toggle</button>
<style>
.fade.ng-hide {
transition:0.5s linear all;
opacity:0;
}
</style>
Всё, что происходит здесь с ngShow/ngHide за кулисами, — это добавление/удаление класса .ng-hide (когда состояние скрытия является действительным). Так как ngShow и ngHide «осознают» анимацию, то мы можем сопоставить переход, а ngAnimate позаботится о остальном.
Кроме того, помимо добавления и удаления CSS-класса, ngAnimate также предоставляет два вспомогательных метода, которые можно использовать для дальнейшей настройки анимации с помощью CSS-стилей.
<div ng-class="{on:onOff}" class="highlight">
Highlight this box
</div>
<button ng-click="onOff=!onOff">Toggle</button>
<style>
.highlight {
transition:0.5s linear all;
}
.highlight.on-add {
background:white;
}
.highlight.on {
background:yellow;
}
.highlight.on-remove {
background:black;
}
</style>
Мы также можем использовать CSS-ключевые кадры, поместив их в CSS-классы.
CSS-анимации со смещением
Анимация со смещением — это набор анимаций, выполняемых с небольшой задержкой между каждым последующим действием, что создаёт эффект штор. Модуль ngAnimate (версии >=1.2) поддерживает анимации со смещением, и эффект смещения можно выполнить, создав CSS-класс ng-EVENT-stagger и прикрепив его к основному CSS-классу, используемому для анимации. Свойство стиля, ожидаемое в классе смещения, может быть свойством transition-delay или animation-delay (или обоими, если ваша анимация содержит как переходы, так и анимации ключевых кадров).
.my-animation.ng-enter {
/* standard transition code */
transition: 1s linear all;
opacity:0;
}
.my-animation.ng-enter-stagger {
/* this will have a 100ms delay between each successive leave animation */
transition-delay: 0.1s;
/* As of 1.4.4, this must always be set: it signals ngAnimate
to not accidentally inherit a delay property from another CSS class */
transition-duration: 0s;
/* if you are using animations instead of transitions you should configure as follows:
animation-delay: 0.1s;
animation-duration: 0s; */
}
.my-animation.ng-enter.ng-enter-active {
/* standard transition styles */
opacity:1;
}
Анимации со смещением по умолчанию работают в ngRepeat (если CSS-класс определён). Вне ngRepeat для использования анимаций со смещением самостоятельно можно вызывать несколько вызовов одного и того же события в $animate. Однако ограничения заключаются в том, что каждый из элементов должен иметь одинаковое значение CSS className и тот же родительский элемент. Операция смещения также будет сброшена, если с момента многократных вызовов $animate прошло одно или несколько кадров анимации.
Следующий код запустит событие ng-leave-stagger для заданного элемента:
var kids = parent.children();
$animate.leave(kids[0]); //stagger index=0
$animate.leave(kids[1]); //stagger index=1
$animate.leave(kids[2]); //stagger index=2
$animate.leave(kids[3]); //stagger index=3
$animate.leave(kids[4]); //stagger index=4
window.requestAnimationFrame(function() {
//stagger has reset itself
$animate.leave(kids[5]); //stagger index=0
$animate.leave(kids[6]); //stagger index=1
$scope.$digest();
});
Анимации со смещением в настоящее время поддерживаются только в CSS-анимациях.
CSS-класс ng-animate
При анимации элемента ngAnimate применяет CSS-класс ng-animate к элементу на всё время анимации. Это временный CSS-класс, и он будет удалён, как только анимация закончится (как для анимаций на JavaScript, так и на основе CSS).
Таким образом, анимации можно применять к элементу непосредственно с помощью CSS, используя этот временный класс.
.zipper.ng-animate {
transition:0.5s linear all;
}
.zipper.ng-enter {
opacity:0;
}
.zipper.ng-enter.ng-enter-active {
opacity:1;
}
.zipper.ng-leave {
opacity:1;
}
.zipper.ng-leave.ng-leave-active {
opacity:0;
}
(Обратите внимание, что CSS-класс ng-animate зарезервирован и не может быть применён к элементу напрямую, так как ngAnimate всегда удалит CSS-класс после завершения анимации.)
Класс ng-[event]-prepare
Это специальный класс, который можно использовать для предотвращения нежелательного мерцания/мигания содержимого перед началом фактической анимации. Класс добавляется как только инициализируется анимация, но удаляется перед фактическим началом анимации (после ожидания $digest). Он также добавляется только для структурных анимаций (enter, move, и leave).
На практике мерцание может появляться при вложенных элементах со структурными анимациями, такими как ngIf в элементы с анимациями на основе классов, такие как ngClass.
<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;
}
Анимация между изменениями значений
Иногда вам нужно выполнить анимацию между различными состояниями выражений, значения которых не обязательно нужно знать или использовать в CSS-стилях. Если это возможно с другой «анимационной директивой», тот конкретный случай всегда можно покрыть с помощью ngAnimateSwap, как показано в этом примере.
Обратите внимание, что ngAnimateSwap — это структурная директива, что означает, что она создаёт новую копию элемента (включая все другие/вложенные директивы, которые могут у него быть) и привязывает её к новому контексту каждый раз, когда происходит swap. В некоторых случаях это может быть нежелательным (например, по причинам производительности или когда необходимо сохранить внутреннее состояние на исходном экземпляре элемента).
Анимации на основе JavaScript
ngAnimate также позволяет использовать анимации, обрабатываемые кодом JavaScript. Подход аналогичен анимациям на основе CSS (где используется общий CSS-класс, на который ссылается наш HTML-код), но помимо этого нам нужно зарегистрировать JavaScript-анимацию в модуле. С помощью функции модуля module.animation() мы можем зарегистрировать анимацию.
Давайте рассмотрим пример анимации входа/выхода с помощью ngRepeat:
<div ng-repeat="item in items" class="slide">
{{ item }}
</div>
Видите CSS-класс slide? Давайте используем этот класс для определения анимации, которую мы настроим в нашем коде модуля, используя module.animation:
myModule.animation('.slide', [function() {
return {
// make note that other events (like addClass/removeClass)
// have different function input parameters
enter: function(element, doneFn) {
jQuery(element).fadeIn(1000, doneFn);
// remember to call doneFn so that AngularJS
// knows that the animation has concluded
},
move: function(element, doneFn) {
jQuery(element).fadeIn(1000, doneFn);
},
leave: function(element, doneFn) {
jQuery(element).fadeOut(1000, doneFn);
}
}
}]);
Преимущество JS-анимаций заключается в возможности подключения других сервисов и использования расширенных библиотек анимации, таких как greensock.js и velocity.js.
Если наш код анимации основан на классах (то есть, что-то вроде ngClass, ngHide и ngShow его запускает), то мы все равно можем определять анимации внутри зарегистрированной анимации, однако входные аргументы функций немного отличаются:
<div ng-class="color" class="colorful"> this box is moody </div> <button ng-click="color='red'">Change to red</button> <button ng-click="color='blue'">Change to blue</button> <button ng-click="color='green'">Change to green</button>
myModule.animation('.colorful', [function() {
return {
addClass: function(element, className, doneFn) {
// do some cool animation and call the doneFn
},
removeClass: function(element, className, doneFn) {
// do some cool animation and call the doneFn
},
setClass: function(element, addedClass, removedClass, doneFn) {
// do some cool animation and call the doneFn
}
}
}]);
CSS + JS анимации вместе
AngularJS 1.4 и выше предпринял шаги, чтобы сделать объединение CSS и JS анимаций более гибким. Однако, в отличие от более ранних версий AngularJS, определение CSS и JS анимаций для работы с одним и тем же классом CSS больше не работает. Поэтому пример ниже будет только JS анимации будут управлять анимацией:
<div ng-if="bool" class="slide"> Slide in and out </div>
myModule.animation('.slide', [function() {
return {
enter: function(element, doneFn) {
jQuery(element).slideIn(1000, doneFn);
}
}
}]);
.slide.ng-enter {
transition:0.5s linear all;
transform:translateY(-100px);
}
.slide.ng-enter.ng-enter-active {
transform:translateY(0);
}
Значит ли это, что CSS и JS анимации не могут использоваться вместе? У JS-анимаций всегда более высокий приоритет? Мы можем компенсировать отсутствие CSS-анимаций, используя сервис $animateCss, чтобы запускать собственные настроенные CSS-анимации непосредственно из нашего собственного JS-кода анимации:
myModule.animation('.slide', ['$animateCss', function($animateCss) {
return {
enter: function(element) {
// this will trigger `.slide.ng-enter` and `.slide.ng-enter-active`.
return $animateCss(element, {
event: 'enter',
structural: true
});
}
}
}]);
Преимущество здесь в том, что мы можем сохранить пропускную способность, придерживаясь нашего CSS-кода анимации и не полагаясь на стороннюю библиотеку анимации.
Сервис $animateCss очень мощный, поскольку мы можем передавать любые дополнительные свойства, которые будут оценены и переданы в CSS-переход или анимацию ключевых кадров. Например, если мы хотим анимировать высоту элемента, добавляя и удаляя классы, то мы можем сделать это, передав эти данные в $animateCss напрямую:
myModule.animation('.slide', ['$animateCss', function($animateCss) {
return {
enter: function(element) {
return $animateCss(element, {
event: 'enter',
structural: true,
addClass: 'maroon-setting',
from: { height:0 },
to: { height: 200 }
});
}
}
}]);
Теперь мы можем заполнить остальное с помощью нашего CSS-кода перехода:
/* the transition tells ngAnimate to make the animation happen */
.slide.ng-enter { transition:0.5s linear all; }
/* this extra CSS class will be absorbed into the transition
since the $animateCss code is adding the class */
.maroon-setting { background:red; }
И $animateCss рассчитает остальное. Просто убедитесь, что обратный вызов done() запускает функцию doneFn, чтобы сигнализировать о завершении анимации.
Чтобы узнать больше о возможностях, посетите $animateCss сервис.
Привязка анимации (через ng-animate-ref)
ngAnimate в AngularJS 1.4 поставляется со способностью анимировать элементы между структурными областями приложения (например, представлениями), связывая элементы с помощью атрибута, называемого ng-animate-ref.
Предположим, например, что у нас есть два представления, управляемые ng-view, и мы хотим показать, что существует взаимосвязь между двумя компонентами, расположенными в этих представлениях. Используя атрибут ng-animate-ref, мы можем определить, что два компонента связаны вместе, и затем мы можем прикрепить анимацию, которая запускается при изменении представления.
Предположим, например, что у нас есть следующий шаблон кода:
<!-- index.html --> <div ng-view class="view-animation"> </div> <!-- home.html --> <a href="#/banner-page"> <img src="./banner.jpg" class="banner" ng-animate-ref="banner"> </a> <!-- banner-page.html --> <img src="./banner.jpg" class="banner" ng-animate-ref="banner">
Теперь, когда представление меняется (после нажатия ссылки), ngAnimate проверит содержимое HTML, чтобы увидеть, есть ли соответствие ссылки между компонентами в покидающем представлении и входящем представлении. Он просканирует как удаляемое (leave), так и вставляемое (enter) представление, чтобы увидеть, есть ли какие-либо спаренные DOM-элементы, содержащие соответствующее значение ref.
Два изображения совпадают, так как они имеют одинаковое значение ref. Теперь ngAnimate создаст элемент транспортировки (который является копией первого элемента изображения) и попытается анимировать его к позиции второго элемента изображения в следующем представлении. Для работы анимации специальный CSS-класс, называемый ng-anchor, будет добавлен к элементу транспортировки.
Теперь мы можем прикрепить переход к CSS-классу .banner.ng-anchor, и тогда ngAnimate обработает весь переход за нас, а также добавление и удаление любых изменений CSS-классов между элементами:
.banner.ng-anchor {
/* this animation will last for 1 second since there are
two phases to the animation (an `in` and an `out` phase) */
transition:0.5s linear all;
}
Мы также должны включить анимации для вставляемых и удаляемых представлений (иначе привязка не будет возможна, так как новое представление будет вставлено сразу).
.view-animation.ng-enter, .view-animation.ng-leave {
transition:0.5s linear all;
position:fixed;
left:0;
top:0;
width:100%;
}
.view-animation.ng-enter {
transform:translateX(100%);
}
.view-animation.ng-leave,
.view-animation.ng-enter.ng-enter-active {
transform:translateX(0%);
}
.view-animation.ng-leave.ng-leave-active {
transform:translateX(-100%);
}
Теперь мы можем вернуться к анимации привязки. При выполнении анимации происходят два этапа: этап out и этап in. Этап out происходит первым, и это когда элемент анимируется от своего источника. После завершения этой анимации происходит этап in, который анимирует элемент до его пункта назначения. Причина, по которой существует две анимации, заключается в том, чтобы дать достаточно времени для готовности анимации вставки нового элемента.
В приведенном выше примере настраивается переход для обоих фаз (вход и выход), но мы также можем напрямую нацеливаться на фазы выхода или входа с помощью ng-anchor-out и ng-anchor-in.
.banner.ng-anchor-out {
transition: 0.5s linear all;
/* the scale will be applied during the out animation,
but will be animated away when the in animation runs */
transform: scale(1.2);
}
.banner.ng-anchor-in {
transition: 1s linear all;
}
Демонстрация привязки
Как осуществляется транспортировка элемента?
При выполнении анимации привязки ngAnimate клонирует исходный элемент и помещает его точно в то место, где находится исходный элемент на экране, используя абсолютное позиционирование. Клонированный элемент будет размещен внутри корневого элемента приложения (где был определен ng-app), и все CSS-классы исходного элемента будут применены. Затем элемент будет анимирован в анимации out и in, и, в конечном итоге, достигнет координат и будет соответствовать размерам целевого элемента. В течение всей анимации к исходному и целевому элементам будет применен CSS-класс .ng-animate-shim для их скрытия (CSS-стили для класса: visibility:hidden). После достижения якорем пункта назначения он будет удален, и целевой элемент станет видимым, так как класс маски будет удален.
Как обрабатывается морфинг?
CSS-привязка опирается на переходы и ключевые кадры, и внутренний код достаточно интеллектуален, чтобы определить, какие CSS-классы отличаются между исходным и целевым элементами. Эти отличающиеся CSS-классы будут добавлены/удалены в элементе якоря, и будет применен переход (переход, предоставленный в классе якоря). Короче говоря, ngAnimate определит, какие классы добавить и удалить, что сделает переход элемента максимально плавным и автоматизированным. Используйте простые CSS-классы, которые не полагаются на структуру вложенности DOM, чтобы элемент якоря выглядел так же, как и исходный элемент (так как клонированный элемент размещается внутри корневого элемента, который, скорее всего, близок к тегу body).
Обратите внимание, что если корневой элемент находится в элементе <html>, то клонированный узел будет помещен внутрь тега body.
Использование $animate в вашем директиве кода
До сих пор мы изучали, как передавать анимации в приложение AngularJS, но как мы запускаем анимации внутри собственных директив в нашем приложении? Введя сервис $animate в наш код директивы, мы можем запустить структурные и основанные на классах крючки, которые затем могут быть использованы анимациями. Представьте себе поле приветствия, которое отображается и скрывается при изменении данных
<greeting-box active="onOrOff">Hi there</greeting-box>
ngModule.directive('greetingBox', ['$animate', function($animate) {
return function(scope, element, attrs) {
attrs.$observe('active', function(value) {
value ? $animate.addClass(element, 'on') : $animate.removeClass(element, 'on');
});
});
}]);
Теперь CSS-класс on добавляется и удаляется из компонента поля приветствия. Теперь, если мы добавим CSS-класс поверх элемента поля приветствия в наш HTML-код, мы можем запустить CSS или JS анимацию.
/* normally we would create a CSS class to reference on the element */
greeting-box.on { transition:0.5s linear all; background:green; color:white; }
Сервис $animate содержит множество других методов, таких как enter, leave, animate и setClass. Чтобы узнать больше о возможностях, посетите $animate сервис API страницы.
Обратные вызовы и обещания
Когда $animate вызывается, возвращается обещание, которое можно использовать для захвата момента завершения анимации. Таким образом, если мы запустим анимацию (внутри нашего кода директивы), мы можем продолжить выполнение действий, относящихся к директивам и области видимости, после завершения анимации, подключившись к возвращаемому обещанию метода анимации.
// somewhere within the depths of the directive
$animate.enter(element, parent).then(function() {
//the animation has completed
});
(Обратите внимание, что в более ранних версиях AngularJS до v1.4 код обещания требовал обернуть его с помощью $scope.$apply(...). Это больше не так.)
В дополнение к обещанию анимации мы также можем использовать обратные вызовы, связанные с анимацией, в наших директивах и контроллерах, зарегистрировав обработчик событий с помощью сервиса $animate. Предположим, например, что анимация была запущена в нашем контроллере маршрутизации представления, чтобы подключиться к этому:
ngModule.controller('HomePageController', ['$animate', function($animate) {
$animate.on('enter', ngViewElement, function(element) {
// the animation for this route has completed
}]);
}])
(Обратите внимание, что вам необходимо запустить переваривание внутри обратного вызова, чтобы AngularJS заметил любые изменения, связанные с областью видимости.)
Компоненты модуля
Директива
| Название | Описание |
|---|---|
| ngAnimateChildren |
ngAnimateChildren позволяет указать, что дочерние элементы этого элемента должны анимироваться, даже если у одного из родителей детей активна анимация. По умолчанию, когда у элемента есть активная |
| ngAnimateSwap |
ngAnimateSwap — это директива, ориентированная на анимацию, которая позволяет контейнеру удаляться и вставляться всякий раз, когда связанное выражение меняется. Распространенное использование этой директивы — это компонент вращающегося баннера или слайдера, который содержит одно изображение, присутствующее в данный момент. Когда активное изображение меняется, старое изображение выполнит анимацию |
Сервис
| Название | Описание |
|---|---|
| $animateCss |
Сервис |
| $animate |
Документация сервиса ngAnimate |
© 2010–2020 Google, Inc.
Licensed under the Creative Commons Attribution License 3.0.
https://code.angularjs.org/1.8.2/docs/api/ngAnimate