|
392 | 392 | *
|
393 | 393 | * To learn more about what's possible be sure to visit the {@link ngAnimate.$animateCss $animateCss service}.
|
394 | 394 | *
|
| 395 | + * ## Animation Anchoring (via `ng-animate-ref`) |
| 396 | + * |
| 397 | + * ngAnimate in AngularJS 1.4 comes packed with the ability to cross-animate elements between |
| 398 | + * structural areas of an application (like views) by pairing up elements using an attribute |
| 399 | + * called `ng-animate-ref`. |
| 400 | + * |
| 401 | + * Let's say for example we have two views that are managed by `ng-view` and we want to show |
| 402 | + * that there is a relationship between two components situated in different views. By using the |
| 403 | + * `ng-animate-ref` attribute we can identify that the two components are paired together and we |
| 404 | + * can then attach an animation, which is triggered when the view changes. |
| 405 | + * |
| 406 | + * ```html |
| 407 | + * <!-- index.html --> |
| 408 | + * <div ng-view class="view-animation"> |
| 409 | + * </div> |
| 410 | + * |
| 411 | + * <!-- home.html --> |
| 412 | + * <a href="#/banner-page"> |
| 413 | + * <img src="./banner.jpg" ng-animate-ref="banner"> |
| 414 | + * </a> |
| 415 | + * |
| 416 | + * <!-- banner-page.html --> |
| 417 | + * <img src="./banner.jpg" ng-animate-ref="banner"> |
| 418 | + * ``` |
| 419 | + * |
| 420 | + * Now, when the view changes (once the link is clicked), ngAnimate will examine the |
| 421 | + * HTML contents to see if there is a match reference between any components in the view |
| 422 | + * that is leaving and the view that is entering. It will then attempt to trigger a CSS |
| 423 | + * animation on the `.view-animation-anchor` CSS class (notice how `.view-animation` is |
| 424 | + * a shared CSS class on the ng-view element? This means that view-animation will apply to |
| 425 | + * both the enter and leave animations). |
| 426 | + * |
| 427 | + * The two images match since they share the same ref value. ngAnimate will now apply a |
| 428 | + * suffixed version of each of the shared CSS classes with `-anchor`. Therefore we will |
| 429 | + * have a shared class of `view-animation-anchor` which we can use to setup our transition animation. |
| 430 | + * |
| 431 | + * We can now attach a transition onto the `.view-animation-anchor` CSS class and then |
| 432 | + * ngAnimate will handle the entire transition for us as well as the addition and removal of |
| 433 | + * any changes of CSS classes between the elements: |
| 434 | + * |
| 435 | + * ```css |
| 436 | + * .view-animation-anchor { |
| 437 | + * /* this animation will last for 1 second since there are |
| 438 | + * two phases to the animation (an `in` and an `out` phase) */ |
| 439 | + * transition:0.5s linear all; |
| 440 | + * } |
| 441 | + * ``` |
| 442 | + * |
| 443 | + * There are two stages for an anchor animation: `out` and `in`. The `out` stage happens first and that |
| 444 | + * is when the element is animated away from its origin. Once that animation is over then the `in` stage |
| 445 | + * occurs which animates the element to its destination. The reason why there are two animations is to |
| 446 | + * give enough time for the enter animation on the new element to be ready. |
| 447 | + * |
| 448 | + * The example above sets up a transition for both the in and out phases, but we can also target the out or |
| 449 | + * in phases directly via `ng-anchor-out` and `ng-anchor-in`. |
| 450 | + * |
| 451 | + * ```css |
| 452 | + * .view-animation-anchor.ng-anchor-out { |
| 453 | + * transition: 0.5s linear all; |
| 454 | + * |
| 455 | + * /* the scale will be applied during the out animation, |
| 456 | + * but will be animated away when the in animation runs */ |
| 457 | + * transform: scale(1.2); |
| 458 | + * } |
| 459 | + * |
| 460 | + * .view-animation-anchor.ng-anchor-in { |
| 461 | + * transition: 1s linear all; |
| 462 | + * } |
| 463 | + * ``` |
| 464 | + * |
| 465 | + * |
| 466 | + * |
| 467 | + * |
| 468 | + * ### Anchoring Demo |
| 469 | + * |
| 470 | + <example module="anchoringExample" |
| 471 | + name="anchoringExample" |
| 472 | + id="anchoringExample" |
| 473 | + deps="angular-animate.js;angular-route.js" |
| 474 | + animations="true"> |
| 475 | + <file name="index.html"> |
| 476 | + <a href="#/">Home</a> |
| 477 | + <hr /> |
| 478 | + <div class="view-container"> |
| 479 | + <div ng-view class="view"></div> |
| 480 | + </div> |
| 481 | + </file> |
| 482 | + <file name="script.js"> |
| 483 | + angular.module('anchoringExample', ['ngAnimate', 'ngRoute']) |
| 484 | + .config(['$routeProvider', function($routeProvider) { |
| 485 | + $routeProvider.when('/', { |
| 486 | + templateUrl: 'home.html', |
| 487 | + controller: 'HomeController as home' |
| 488 | + }); |
| 489 | + $routeProvider.when('/profile/:id', { |
| 490 | + templateUrl: 'profile.html', |
| 491 | + controller: 'ProfileController as profile' |
| 492 | + }); |
| 493 | + }]) |
| 494 | + .run(['$rootScope', function($rootScope) { |
| 495 | + $rootScope.records = [ |
| 496 | + { id:1, title: "Miss Beulah Roob" }, |
| 497 | + { id:2, title: "Trent Morissette" }, |
| 498 | + { id:3, title: "Miss Ava Pouros" }, |
| 499 | + { id:4, title: "Rod Pouros" }, |
| 500 | + { id:5, title: "Abdul Rice" }, |
| 501 | + { id:6, title: "Laurie Rutherford Sr." }, |
| 502 | + { id:7, title: "Nakia McLaughlin" }, |
| 503 | + { id:8, title: "Jordon Blanda DVM" }, |
| 504 | + { id:9, title: "Rhoda Hand" }, |
| 505 | + { id:10, title: "Alexandrea Sauer" } |
| 506 | + ]; |
| 507 | + }]) |
| 508 | + .controller('HomeController', [function() { |
| 509 | + //empty |
| 510 | + }]) |
| 511 | + .controller('ProfileController', ['$rootScope', '$routeParams', function($rootScope, $routeParams) { |
| 512 | + var index = parseInt($routeParams.id, 10); |
| 513 | + var record = $rootScope.records[index - 1]; |
| 514 | +
|
| 515 | + this.title = record.title; |
| 516 | + this.id = record.id; |
| 517 | + }]); |
| 518 | + </file> |
| 519 | + <file name="home.html"> |
| 520 | + <h2>Welcome to the home page</h1> |
| 521 | + <p>Please click on an element</p> |
| 522 | + <a class="record" |
| 523 | + ng-href="#/profile/{{ record.id }}" |
| 524 | + ng-animate-ref="{{ record.id }}" |
| 525 | + ng-repeat="record in records"> |
| 526 | + {{ record.title }} |
| 527 | + </a> |
| 528 | + </file> |
| 529 | + <file name="profile.html"> |
| 530 | + <div class="profile record" ng-animate-ref="{{ profile.id }}"> |
| 531 | + {{ profile.title }} |
| 532 | + </div> |
| 533 | + </file> |
| 534 | + <file name="animations.css"> |
| 535 | + .record { |
| 536 | + display:block; |
| 537 | + font-size:20px; |
| 538 | + } |
| 539 | + .profile { |
| 540 | + background:black; |
| 541 | + color:white; |
| 542 | + font-size:100px; |
| 543 | + } |
| 544 | + .view-container { |
| 545 | + position:relative; |
| 546 | + } |
| 547 | + .view-container > .view.ng-animate { |
| 548 | + position:absolute; |
| 549 | + top:0; |
| 550 | + left:0; |
| 551 | + width:100%; |
| 552 | + min-height:500px; |
| 553 | + } |
| 554 | + .view.ng-enter { |
| 555 | + transition:0.5s linear all; |
| 556 | + transform:translateX(100%); |
| 557 | + } |
| 558 | + .view.ng-enter.ng-enter-active { |
| 559 | + transform:translateX(0%); |
| 560 | + } |
| 561 | + .view.ng-leave { |
| 562 | + transition:0.5s linear all; |
| 563 | + } |
| 564 | + .view.ng-leave.ng-leave-active { |
| 565 | + transform:translateX(-100%); |
| 566 | + } |
| 567 | + .view-anchor { |
| 568 | + transition:0.5s linear all; |
| 569 | + } |
| 570 | + </file> |
| 571 | + </example> |
| 572 | + * |
| 573 | + * ### How is the element transported? |
| 574 | + * |
| 575 | + * When an anchor animation occurs, ngAnimate will clone the starting element and position it exactly where the starting |
| 576 | + * element is located on screen via absolute positioning. The cloned element will be placed inside of the root element |
| 577 | + * of the application (where ng-app was defined) and all of the CSS classes of the starting element will be applied. The |
| 578 | + * element will then animate into the `out` and `in` animations and will eventually reach the coordinates and match |
| 579 | + * the dimensions of the destination element. During the entire animation a CSS class of `.ng-animate-shim` will be applied |
| 580 | + * to both the starting and destination elements in order to hide them from being visible (the CSS styling for the class |
| 581 | + * is: `visibility:hidden`). Once the anchor reaches its destination then it will be removed and the destination element |
| 582 | + * will become visible since the shim class will be removed. |
| 583 | + * |
| 584 | + * ### How is the morphing handled? |
| 585 | + * |
| 586 | + * CSS Anchoring relies on transitions and keyframes and the internal code is intelligent enough to figure out |
| 587 | + * what CSS classes differ between the starting element and the destination element. These different CSS classes |
| 588 | + * will be added/removed on the anchor element and a transition will be applied (the transition that is provided |
| 589 | + * in the anchor class). Long story short, ngAnimate will figure out what classes to add and remove which will |
| 590 | + * make the transition of the element as smooth and automatic as possible. Be sure to use simple CSS classes that |
| 591 | + * do not rely on DOM nesting structure so that the anchor element appears the same as the starting element (since |
| 592 | + * the cloned element is placed inside of root element which is likely close to the body element). |
| 593 | + * |
| 594 | + * Note that if the root element is on the `<html>` element then the cloned node will be placed inside of body. |
| 595 | + * |
395 | 596 | *
|
396 | 597 | * ## Using $animate in your directive code
|
397 | 598 | *
|
|
0 commit comments