2016-03-27 21:32:36 +03:00
|
|
|
@ngdoc tutorial
|
|
|
|
|
@name 14 - Animations
|
|
|
|
|
@step 14
|
|
|
|
|
@description
|
|
|
|
|
|
|
|
|
|
<ul doc-tutorial-nav="14"></ul>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
In this step, we will enhance our web application by adding CSS and JavaScript animations on top of
|
|
|
|
|
the template code we created earlier.
|
|
|
|
|
|
|
|
|
|
* We now use the {@link ngAnimate ngAnimate} module to enable animations throughout the application.
|
|
|
|
|
* We also rely on built-in directives to automatically trigger hooks for animations to tap into.
|
|
|
|
|
* When an animation is found, it will run along with the actual DOM operation that is being issued
|
|
|
|
|
on the element at the given time (e.g. inserting/removing nodes on {@link ngRepeat ngRepeat} or
|
|
|
|
|
adding/removing classes on {@link ngClass ngClass}).
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<div doc-tutorial-reset="14"></div>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
## Dependencies
|
|
|
|
|
|
2017-01-24 17:23:54 +00:00
|
|
|
The animation functionality is provided by AngularJS in the `ngAnimate` module, which is distributed
|
2018-11-02 16:41:12 +02:00
|
|
|
separately from the core AngularJS framework. In addition we will use [jQuery][jquery] in this
|
|
|
|
|
project to do extra JavaScript animations.
|
2016-03-27 21:32:36 +03:00
|
|
|
|
2018-11-02 16:41:12 +02:00
|
|
|
Since we are using [npm][npm] to install client-side dependencies, this step updates the
|
|
|
|
|
`package.json` configuration file to include the new dependencies:
|
2016-03-27 21:32:36 +03:00
|
|
|
|
|
|
|
|
<br />
|
2018-11-02 16:41:12 +02:00
|
|
|
**`package.json`:**
|
2016-03-27 21:32:36 +03:00
|
|
|
|
2018-11-02 16:41:12 +02:00
|
|
|
```json
|
2016-03-27 21:32:36 +03:00
|
|
|
{
|
|
|
|
|
"name": "angular-phonecat",
|
2018-11-02 16:41:12 +02:00
|
|
|
...
|
2016-03-27 21:32:36 +03:00
|
|
|
"dependencies": {
|
2020-05-20 08:18:22 +01:00
|
|
|
"angular": "1.8.x",
|
|
|
|
|
"angular-animate": "1.8.x",
|
|
|
|
|
"angular-resource": "1.8.x",
|
|
|
|
|
"angular-route": "1.8.x",
|
2016-03-27 21:32:36 +03:00
|
|
|
"bootstrap": "3.3.x",
|
2020-05-20 08:18:22 +01:00
|
|
|
"jquery": "^3.5.1"
|
2018-11-02 16:41:12 +02:00
|
|
|
},
|
|
|
|
|
...
|
2016-03-27 21:32:36 +03:00
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
2020-05-20 08:18:22 +01:00
|
|
|
* `"angular-animate": "1.8.x"` tells npm to install a version of the angular-animate module that
|
|
|
|
|
is compatible with version 1.8.x of AngularJS.
|
|
|
|
|
* `"jquery": "^3.5.1"` tells npm to install a version of jQuery that is compatible with 3.5.x and at least 3.5.1.
|
2018-11-02 16:41:12 +02:00
|
|
|
Note that this is not an AngularJS library; it is the standard jQuery library. We can use npm to
|
2016-03-27 21:32:36 +03:00
|
|
|
install a wide range of 3rd party libraries.
|
|
|
|
|
|
2018-11-02 16:41:12 +02:00
|
|
|
Now, we must tell npm to download and install these dependencies.
|
2016-03-27 21:32:36 +03:00
|
|
|
|
|
|
|
|
```
|
|
|
|
|
npm install
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
## How Animations work with `ngAnimate`
|
|
|
|
|
|
|
|
|
|
To get an idea of how animations work with AngularJS, you might want to read the
|
|
|
|
|
[Animations](guide/animations) section of the Developer Guide first.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
## Template
|
|
|
|
|
|
|
|
|
|
In order to enable animations, we need to update `index.html`, loading the necessary dependencies
|
|
|
|
|
(**angular-animate.js** and **jquery.js**) and the files that contain the CSS and JavaScript code
|
|
|
|
|
used in CSS/JavaScript animations. The animation module, {@link ngAnimate ngAnimate}, contains the
|
|
|
|
|
code necessary to make your application "animation aware".
|
|
|
|
|
|
|
|
|
|
<br />
|
|
|
|
|
**`app/index.html`:**
|
|
|
|
|
|
|
|
|
|
```html
|
|
|
|
|
...
|
|
|
|
|
|
|
|
|
|
<!-- Defines CSS necessary for animations -->
|
|
|
|
|
<link rel="stylesheet" href="app.animations.css" />
|
|
|
|
|
|
|
|
|
|
...
|
|
|
|
|
|
|
|
|
|
<!-- Used for JavaScript animations (include this before angular.js) -->
|
2018-11-02 16:41:12 +02:00
|
|
|
<script src="lib/jquery/dist/jquery.js"></script>
|
2016-03-27 21:32:36 +03:00
|
|
|
|
|
|
|
|
...
|
|
|
|
|
|
|
|
|
|
<!-- Adds animation support in AngularJS -->
|
2018-11-02 16:41:12 +02:00
|
|
|
<script src="lib/angular-animate/angular-animate.js"></script>
|
2016-03-27 21:32:36 +03:00
|
|
|
|
|
|
|
|
<!-- Defines JavaScript animations -->
|
|
|
|
|
<script src="app.animations.js"></script>
|
|
|
|
|
|
|
|
|
|
...
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
<div class="alert alert-error">
|
2018-11-02 16:41:12 +02:00
|
|
|
**Important:** Be sure to use jQuery version 2.1 or newer, when using AngularJS 1.5 or newer;
|
|
|
|
|
jQuery 1.x is not officially supported.
|
2017-01-24 17:23:54 +00:00
|
|
|
In order for AngularJS to detect jQuery and take advantage of it, make sure to include `jquery.js`
|
2016-03-27 21:32:36 +03:00
|
|
|
before `angular.js`.
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
Animations can now be created within the CSS code (`app.animations.css`) as well as the JavaScript
|
|
|
|
|
code (`app.animations.js`).
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
## Dependencies
|
|
|
|
|
|
|
|
|
|
We need to add a dependency on `ngAnimate` to our main module first:
|
|
|
|
|
|
|
|
|
|
<br />
|
|
|
|
|
**`app/app.module.js`:**
|
|
|
|
|
|
|
|
|
|
```js
|
|
|
|
|
angular.
|
|
|
|
|
module('phonecatApp', [
|
|
|
|
|
'ngAnimate',
|
|
|
|
|
...
|
|
|
|
|
]);
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Now that our application is "animation aware", let's create some fancy animations!
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
## CSS Transition Animations: Animating `ngRepeat`
|
|
|
|
|
|
|
|
|
|
We will start off by adding CSS transition animations to our `ngRepeat` directive present on the
|
|
|
|
|
`phoneList` component's template. We need to add an extra CSS class to our repeated element, in
|
|
|
|
|
order to be able to hook into it with our CSS animation code.
|
|
|
|
|
|
|
|
|
|
<br />
|
|
|
|
|
**`app/phone-list/phone-list.template.html`:**
|
|
|
|
|
|
|
|
|
|
```html
|
|
|
|
|
...
|
|
|
|
|
<ul class="phones">
|
|
|
|
|
<li ng-repeat="phone in $ctrl.phones | filter:$ctrl.query | orderBy:$ctrl.orderProp"
|
|
|
|
|
class="thumbnail phone-list-item">
|
|
|
|
|
<a href="#!/phones/{{phone.id}}" class="thumb">
|
|
|
|
|
<img ng-src="{{phone.imageUrl}}" alt="{{phone.name}}" />
|
|
|
|
|
</a>
|
|
|
|
|
<a href="#!/phones/{{phone.id}}">{{phone.name}}</a>
|
|
|
|
|
<p>{{phone.snippet}}</p>
|
|
|
|
|
</li>
|
|
|
|
|
</ul>
|
|
|
|
|
...
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Did you notice the added `phone-list-item` CSS class? This is all we need in our HTML code to get
|
|
|
|
|
animations working.
|
|
|
|
|
|
|
|
|
|
Now for the actual CSS transition animation code:
|
|
|
|
|
|
|
|
|
|
<br />
|
|
|
|
|
**`app/app.animations.css`:**
|
|
|
|
|
|
|
|
|
|
```css
|
|
|
|
|
.phone-list-item.ng-enter,
|
|
|
|
|
.phone-list-item.ng-leave,
|
|
|
|
|
.phone-list-item.ng-move {
|
|
|
|
|
transition: 0.5s linear all;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.phone-list-item.ng-enter,
|
|
|
|
|
.phone-list-item.ng-move {
|
|
|
|
|
height: 0;
|
|
|
|
|
opacity: 0;
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.phone-list-item.ng-enter.ng-enter-active,
|
|
|
|
|
.phone-list-item.ng-move.ng-move-active {
|
|
|
|
|
height: 120px;
|
|
|
|
|
opacity: 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.phone-list-item.ng-leave {
|
|
|
|
|
opacity: 1;
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.phone-list-item.ng-leave.ng-leave-active {
|
|
|
|
|
height: 0;
|
|
|
|
|
opacity: 0;
|
|
|
|
|
padding-bottom: 0;
|
|
|
|
|
padding-top: 0;
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
As you can see, our `phone-list-item` CSS class is combined together with the animation hooks that
|
|
|
|
|
occur when items are inserted into and removed from the list:
|
|
|
|
|
|
|
|
|
|
* The `ng-enter` class is applied to the element when a new phone is added to the list and rendered
|
|
|
|
|
on the page.
|
|
|
|
|
* The `ng-move` class is applied to the element when a phone's relative position in the list
|
|
|
|
|
changes.
|
|
|
|
|
* The `ng-leave` class is applied to the element when a phone is removed from the list.
|
|
|
|
|
|
|
|
|
|
The phone list items are added and removed based on the data passed to the `ngRepeat` directive.
|
|
|
|
|
For example, if the filter data changes, the items will be animated in and out of the repeat list.
|
|
|
|
|
|
|
|
|
|
Something important to note is that, when an animation occurs, two sets of CSS classes are added to
|
|
|
|
|
the element:
|
|
|
|
|
|
|
|
|
|
1. A "starting" class that represents the style at the beginning of the animation.
|
|
|
|
|
2. An "active" class that represents the style at the end of the animation.
|
|
|
|
|
|
|
|
|
|
The name of the starting class is the name of the event that is fired (like `enter`, `move` or
|
|
|
|
|
`leave`) prefixed with `ng-`. So an `enter` event will result in adding the `ng-enter` class.
|
|
|
|
|
|
|
|
|
|
The active class name is derived from the starting class by appending an `-active` suffix.
|
|
|
|
|
This two-class CSS naming convention allows the developer to craft an animation, beginning to end.
|
|
|
|
|
|
|
|
|
|
In the example above, animated elements are expanded from a height of **0px** to **120px** when they
|
|
|
|
|
are added to the list and are collapsed back down to **0px** before being removed from the list.
|
|
|
|
|
There is also a catchy fade-in/fade-out effect that occurs at the same time. All this is handled by
|
|
|
|
|
the CSS transition declaration at the top of the CSS file.
|
|
|
|
|
|
|
|
|
|
<div class="alert alert-warning">
|
|
|
|
|
Although all modern browsers have good support for [CSS transitions][caniuse-css-transitions] and
|
|
|
|
|
[CSS animations][caniuse-css-animation], IE9 and earlier IE versions do not.
|
|
|
|
|
If you want animations that are backwards-compatible with older browsers, consider using
|
|
|
|
|
JavaScript-based animations, which are demonstrated below.
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
## CSS Keyframe Animations: Animating `ngView`
|
|
|
|
|
|
|
|
|
|
Next, let's add an animation for transitions between route changes in
|
|
|
|
|
{@link ngRoute.directive:ngView ngView}.
|
|
|
|
|
|
|
|
|
|
Again, we need to prepare our HTML template by adding a new CSS class, this time to the `ng-view`
|
|
|
|
|
element. In order to gain more "expressive power" for our animations, we will also wrap the
|
|
|
|
|
`[ng-view]` element in a container element.
|
|
|
|
|
|
|
|
|
|
<br />
|
|
|
|
|
**`app/index.html`:**
|
|
|
|
|
|
|
|
|
|
```html
|
|
|
|
|
<div class="view-container">
|
|
|
|
|
<div ng-view class="view-frame"></div>
|
|
|
|
|
</div>
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
We have applied a `position: relative` CSS style to the `.view-container` wrapper, so that it is
|
|
|
|
|
easier for us to manage the `.view-frame` element's positioning during the animation.
|
|
|
|
|
|
|
|
|
|
With our preparation code in place, let's move on to the actual CSS styles for this transition
|
|
|
|
|
animation.
|
|
|
|
|
|
|
|
|
|
<br />
|
|
|
|
|
**`app/app.animations.css`:**
|
|
|
|
|
|
|
|
|
|
```css
|
|
|
|
|
...
|
|
|
|
|
|
|
|
|
|
.view-container {
|
|
|
|
|
position: relative;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.view-frame.ng-enter,
|
|
|
|
|
.view-frame.ng-leave {
|
|
|
|
|
background: white;
|
|
|
|
|
left: 0;
|
|
|
|
|
position: absolute;
|
|
|
|
|
right: 0;
|
|
|
|
|
top: 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.view-frame.ng-enter {
|
|
|
|
|
animation: 1s fade-in;
|
|
|
|
|
z-index: 100;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.view-frame.ng-leave {
|
|
|
|
|
animation: 1s fade-out;
|
|
|
|
|
z-index: 99;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@keyframes fade-in {
|
|
|
|
|
from { opacity: 0; }
|
|
|
|
|
to { opacity: 1; }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@keyframes fade-out {
|
|
|
|
|
from { opacity: 1; }
|
|
|
|
|
to { opacity: 0; }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Older browsers might need vendor-prefixes for keyframes and animation! */
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Nothing fancy here! Just a simple fade-in/fade-out effect between pages. The only thing out of the
|
|
|
|
|
ordinary here is that we are using absolute positioning to position the entering page (identified
|
|
|
|
|
by the `ng-enter` class) on top of the leaving page (identified by the `ng-leave` class). At the
|
|
|
|
|
same time a cross-fade animation is performed. So, as the previous page is just about to be removed,
|
|
|
|
|
it fades out, while the new page fades in right on top of it.
|
|
|
|
|
|
|
|
|
|
Once the `leave` animation is over, the element is removed from the DOM. Likewise, once the `enter`
|
|
|
|
|
animation is complete, the `ng-enter` and `ng-enter-active` CSS classes are removed from the
|
|
|
|
|
element, causing it to rerender and reposition itself with its default CSS styles (so no more
|
|
|
|
|
absolute positioning once the animation is over). This works fluidly and the pages flow naturally
|
|
|
|
|
between route changes, without anything jumping around.
|
|
|
|
|
|
|
|
|
|
The applied CSS classes are much the same as with `ngRepeat`. Each time a new page is loaded the
|
|
|
|
|
`ngView` directive will create a copy of itself, download the template and append the contents. This
|
|
|
|
|
ensures that all views are contained within a single HTML element, which allows for easy animation
|
|
|
|
|
control.
|
|
|
|
|
|
2017-10-22 13:05:44 +05:30
|
|
|
For more on CSS animations, see the [MDN web docs][mdn-animations].
|
2016-03-27 21:32:36 +03:00
|
|
|
|
|
|
|
|
|
|
|
|
|
## Animating `ngClass` with JavaScript
|
|
|
|
|
|
|
|
|
|
Let's add another animation to our application. On our `phone-detail.template.html` view, we have a
|
|
|
|
|
nice thumbnail swapper. By clicking on the thumbnails listed on the page, the profile phone image
|
|
|
|
|
changes. But how can we incorporate animations?
|
|
|
|
|
|
|
|
|
|
Let's give it some thought first. Basically, when a user clicks on a thumbnail image, they are
|
|
|
|
|
changing the state of the profile image to reflect the newly selected thumbnail image. The best way
|
|
|
|
|
to specify state changes within HTML is to use classes. Much like before — when we used a CSS
|
|
|
|
|
class to drive the animation — this time the animation will occur when the CSS class itself
|
|
|
|
|
changes.
|
|
|
|
|
|
|
|
|
|
Every time a phone thumbnail is selected, the state changes and the `.selected` CSS class is added
|
|
|
|
|
to the matching profile image. This will trigger the animation.
|
|
|
|
|
|
|
|
|
|
We will start by tweaking our HTML code in `phone-detail.template.html`. Notice that we have changed
|
|
|
|
|
the way we display our large image:
|
|
|
|
|
|
|
|
|
|
<br />
|
|
|
|
|
**`app/phone-detail/phone-detail.template.html`:**
|
|
|
|
|
|
|
|
|
|
```html
|
|
|
|
|
<div class="phone-images">
|
|
|
|
|
<img ng-src="{{img}}" class="phone"
|
|
|
|
|
ng-class="{selected: img === $ctrl.mainImageUrl}"
|
|
|
|
|
ng-repeat="img in $ctrl.phone.images" />
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
...
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Just like with the thumbnails, we are using a repeater to display **all** the profile images as a
|
|
|
|
|
list, however we're not animating any repeat-related transitions. Instead, we will be keeping our
|
|
|
|
|
eye on each element's classes and especially the `selected` class, since its presence or absence
|
|
|
|
|
will determine if the element is visible or hidden. The addition/removal of the `selected` class is
|
|
|
|
|
managed by the {@link ngClass ngClass} directive, based on the specified condition
|
|
|
|
|
(`img === $ctrl.mainImageUrl`).
|
|
|
|
|
In our case, there is always exactly one element that has the `selected` class, and therefore there
|
|
|
|
|
will be exactly one phone profile image visible on the screen at all times.
|
|
|
|
|
|
|
|
|
|
When the `selected` class is added to an element, the `selected-add` and `selected-add-active`
|
|
|
|
|
classes are added just before to signal AngularJS to fire off an animation. When the `selected`
|
|
|
|
|
class is removed from an element, the `selected-remove` and `selected-remove-active` classes are
|
|
|
|
|
applied to the element, triggering another animation.
|
|
|
|
|
|
|
|
|
|
Finally, in order to ensure that the phone images are displayed correctly when the page is first
|
|
|
|
|
loaded, we also tweak the detail page CSS styles:
|
|
|
|
|
|
|
|
|
|
<br />
|
|
|
|
|
**`app/app.css`:**
|
|
|
|
|
|
|
|
|
|
```css
|
|
|
|
|
...
|
|
|
|
|
|
|
|
|
|
.phone {
|
|
|
|
|
background-color: white;
|
|
|
|
|
display: none;
|
|
|
|
|
float: left;
|
|
|
|
|
height: 400px;
|
|
|
|
|
margin-bottom: 2em;
|
|
|
|
|
margin-right: 3em;
|
|
|
|
|
padding: 2em;
|
|
|
|
|
width: 400px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.phone:first-child {
|
|
|
|
|
display: block;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.phone-images {
|
|
|
|
|
background-color: white;
|
|
|
|
|
float: left;
|
|
|
|
|
height: 450px;
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
position: relative;
|
|
|
|
|
width: 450px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
...
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
You may be thinking that we are just going to create another CSS-based animation. Although we could
|
|
|
|
|
do that, let's take the opportunity to learn how to create JavaScript-based animations with the
|
|
|
|
|
{@link ng.angular.Module#animation .animation()} module method.
|
|
|
|
|
|
|
|
|
|
<br />
|
|
|
|
|
**`app/app.animations.js`:**
|
|
|
|
|
|
|
|
|
|
```js
|
|
|
|
|
angular.
|
|
|
|
|
module('phonecatApp').
|
|
|
|
|
animation('.phone', function phoneAnimationFactory() {
|
|
|
|
|
return {
|
|
|
|
|
addClass: animateIn,
|
|
|
|
|
removeClass: animateOut
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
function animateIn(element, className, done) {
|
|
|
|
|
if (className !== 'selected') return;
|
|
|
|
|
|
|
|
|
|
element.
|
|
|
|
|
css({
|
|
|
|
|
display: 'block',
|
|
|
|
|
position: 'absolute',
|
|
|
|
|
top: 500,
|
|
|
|
|
left: 0
|
|
|
|
|
}).
|
|
|
|
|
animate({
|
|
|
|
|
top: 0
|
|
|
|
|
}, done);
|
|
|
|
|
|
|
|
|
|
return function animateInEnd(wasCanceled) {
|
|
|
|
|
if (wasCanceled) element.stop();
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function animateOut(element, className, done) {
|
|
|
|
|
if (className !== 'selected') return;
|
|
|
|
|
|
|
|
|
|
element.
|
|
|
|
|
css({
|
|
|
|
|
position: 'absolute',
|
|
|
|
|
top: 0,
|
|
|
|
|
left: 0
|
|
|
|
|
}).
|
|
|
|
|
animate({
|
|
|
|
|
top: -500
|
|
|
|
|
}, done);
|
|
|
|
|
|
|
|
|
|
return function animateOutEnd(wasCanceled) {
|
|
|
|
|
if (wasCanceled) element.stop();
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
We are creating a custom animation by specifying the target elements via a CSS class selector (here
|
|
|
|
|
`.phone`) and an animation _factory_ function (here `phoneAnimationFactory()`). The factory function
|
|
|
|
|
returns an object associating specific _events_ (object keys) to animation _callbacks_ (object
|
|
|
|
|
values). The _events_ correspond to DOM actions that `ngAnimate` recognizes and can hook into, such
|
|
|
|
|
as `addClass`/`removeClass`/`setClass`, `enter`/`move`/`leave` and `animate`. The associated
|
|
|
|
|
callbacks are called by `ngAnimate` at appropriate times.
|
|
|
|
|
|
|
|
|
|
For more info on animation factories, check out the
|
|
|
|
|
{@link ng.$animateProvider#register API Reference}.
|
|
|
|
|
|
|
|
|
|
In this case, we are interested in a class getting added to/removed from a `.phone` element, thus we
|
|
|
|
|
specify callbacks for the `addClass` and `removeClass` events. When the `selected` class is added to
|
|
|
|
|
an element (via the `ngClass` directive), the `addClass` JavaScript callback will be executed with
|
|
|
|
|
`element` passed in as a parameter. The last parameter passed in is the `done` callback function. We
|
2017-01-24 17:23:54 +00:00
|
|
|
call `done()` to let AngularJS know that our custom JavaScript animation has ended. The `removeClass`
|
2016-03-27 21:32:36 +03:00
|
|
|
callback works the same way, but instead gets executed when a class is removed.
|
|
|
|
|
|
|
|
|
|
Note that we are using [jQuery][jquery]'s `animate()` helper to implement the animation. jQuery
|
|
|
|
|
isn't required to do JavaScript animations with AngularJS, but we use it here anyway in order to
|
|
|
|
|
keep the example simple. More info on `jQuery.animate()` can be found in the
|
|
|
|
|
[jQuery documentation][jquery-animate].
|
|
|
|
|
|
|
|
|
|
Within the event callbacks, we create the animation by manipulating the DOM. In the code above,
|
|
|
|
|
this is achieved using `element.css()` and `element.animate()`. As a result the new element is
|
|
|
|
|
positioned with an offset of **500px** and then both elements — the previous and the new
|
|
|
|
|
— are animated together by shifting each one up by **500px**. The outcome is a conveyor-belt
|
|
|
|
|
like animation. After the `animate()` function has completed the animation, it calls `done` to
|
2017-01-24 17:23:54 +00:00
|
|
|
notify AngularJS.
|
2016-03-27 21:32:36 +03:00
|
|
|
|
|
|
|
|
You may have noticed that each animation callback returns a function. This is an **optional**
|
|
|
|
|
function, which (if provided) will be called when the animation ends, either because it ran to
|
|
|
|
|
completion or because it was canceled (for example another animation took place on the same
|
|
|
|
|
element). A boolean parameter (`wasCanceled`) is passed to the function, letting the developer know
|
|
|
|
|
if the animation was canceled or not. Use this function to do any necessary clean-up.
|
|
|
|
|
|
|
|
|
|
|
2018-02-27 17:33:42 +01:00
|
|
|
## Experiments
|
2016-03-27 21:32:36 +03:00
|
|
|
|
|
|
|
|
<div></div>
|
|
|
|
|
|
|
|
|
|
* Reverse the animation, so that the elements animate downwards.
|
|
|
|
|
|
|
|
|
|
* Make the animation run faster or slower, by passing a `duration` argument to `.animate()`:
|
|
|
|
|
|
|
|
|
|
```js
|
|
|
|
|
element.css({...}).animate({...}, 1000 /* 1 second */, done);
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
* Make the animations "asymmetrical". For example, have the previous element fade out, while the new
|
|
|
|
|
element zooms in:
|
|
|
|
|
|
|
|
|
|
```js
|
|
|
|
|
// animateIn()
|
|
|
|
|
element.css({
|
|
|
|
|
display: 'block',
|
|
|
|
|
opacity: 1,
|
|
|
|
|
position: 'absolute',
|
|
|
|
|
width: 0,
|
|
|
|
|
height: 0,
|
|
|
|
|
top: 200,
|
|
|
|
|
left: 200
|
|
|
|
|
}).animate({
|
|
|
|
|
width: 400,
|
|
|
|
|
height: 400,
|
|
|
|
|
top: 0,
|
|
|
|
|
left: 0
|
|
|
|
|
}, done);
|
|
|
|
|
|
|
|
|
|
// animateOut()
|
|
|
|
|
element.animate({
|
|
|
|
|
opacity: 0
|
|
|
|
|
}, done);
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
* Go crazy and come up with your own funky animations!
|
|
|
|
|
|
|
|
|
|
|
2018-02-27 17:33:42 +01:00
|
|
|
## Summary
|
2016-03-27 21:32:36 +03:00
|
|
|
|
|
|
|
|
Our application is now much more pleasant to use, thanks to the smooth transitions between pages
|
|
|
|
|
and UI states.
|
|
|
|
|
|
|
|
|
|
There you have it! We have created a web application in a relatively short amount of time. In the
|
|
|
|
|
{@link the_end closing notes} we will cover where to go from here.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<ul doc-tutorial-nav="14"></ul>
|
|
|
|
|
|
|
|
|
|
|
2018-11-02 16:38:46 +02:00
|
|
|
[caniuse-css-animation]: https://caniuse.com/#feat=css-animation
|
|
|
|
|
[caniuse-css-transitions]: https://caniuse.com/#feat=css-transitions
|
2016-03-27 21:32:36 +03:00
|
|
|
[jquery]: https://jquery.com/
|
2018-11-02 16:38:46 +02:00
|
|
|
[jquery-animate]: https://api.jquery.com/animate
|
2017-10-22 13:05:44 +05:30
|
|
|
[mdn-animations]: https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Animations/Using_CSS_animations
|
2018-11-02 16:41:12 +02:00
|
|
|
[npm]: https://www.npmjs.com/
|