Detailed explanation of angular routing animation and high-order animation functions

Time:2021-7-30
catalogue

1、 Routing animation

Routing animation requires a trigger to be specified in the host metadata. Be careful not to have too much animation, otherwise it will backfire.

Content first, guide users to notice a certain content. Animation is only an aid.

Define an approach animation and an exit animation in router.animation.ts.

Because the entry animation and departure animation are used very frequently, there are some people named: enter and: leave.


import { trigger, state, transition, style, animate} from '@angular/animations';

export const slideToRight = trigger('routeAnim',[
    state('void',style({'position':'fixed','width':'100%','height':'100%'})),
    state('*',style({'position':'fixed','width':'100%','height':'80%'})),
    transition('void => *',[
        style({transform:'translateX(-100%)'}),
        animate('.5s ease-in-out', style({transform:'translateX(0)'}))
    ]),
    transition('* => void',[
        style({transform:'translateX(0)'}),
        animate('.5s ease-in-out', style({transform:'translateX(100%)'}))
    ]),
]);

Use routing animation in project list.

import { Component, OnInit , HostBinding } from "@angular/core";
import { MatDialog } from "@angular/material";
import { NewProjectComponent } from "../new-project/new-project.component";
import { InviteComponent } from '../invite/invite.component';
import { ConfirmDialogComponent } from '../../shared/confirm-dialog/confirm-dialog.component';
import {slideToRight} from '../../animate/router.animate'

@Component({
  selector: "app-project-list",
  templateUrl: "./project-list.component.html",
  styleUrls: ["./project-list.component.scss"],
  animations:[
    slideToRight
  ]
})
export class ProjectListComponent implements OnInit {
  @HostBinding('@routeAnim') state;

  projects = [
    {
      Name: "enterprise collaboration platform",
      Desc: "this is an enterprise internal project",
      coverImg: "assets/images/covers/0.jpg"
    },
    {
      Name: "automated test project",
      Desc: "this is an enterprise internal project",
      coverImg: "assets/images/covers/2.jpg"
    }
  ];
  constructor(private dialog: MatDialog) { }

  ngOnInit() { }

  openNewProjectDialog() {
    // this.dialog.open(NewProjectComponent,{data:'this is a dialog'});
    const dialogRef = this.dialog.open(NewProjectComponent, {
      Data: {Title: 'new project'}
    });
    dialogRef.afterClosed().subscribe((result) => {
      console.log(result);
    });
  }

  lauchInviteDialog() {
    const dialogRef = this.dialog.open(InviteComponent);
  }

  lauchUpdateDialog() {
    const dialogRef = this.dialog.open(NewProjectComponent, {
      Data: {Title: 'edit item'}
    });
  }

  lauchConfimDialog() {
    const dialogRef = this.dialog.open(ConfirmDialogComponent, {
      Data: {Title: 'edit item', content: 'are you sure you want to delete this item?'}
    });
  }
}

Use routing animation in task home.

import { Component, OnInit , HostBinding } from "@angular/core";
import { NewTaskComponent } from "../new-task/new-task.component";
import { MatDialog } from "@angular/material";
import { CopyTaskComponent } from "../copy-task/copy-task.component";
import { ConfirmDialogComponent } from "../../shared/confirm-dialog/confirm-dialog.component";
import { NewTaskListComponent } from "../new-task-list/new-task-list.component";
import {slideToRight} from '../../animate/router.animate';

@Component({
  selector: "app-task-home",
  templateUrl: "./task-home.component.html",
  styleUrls: ["./task-home.component.scss"],
  animations:[
    slideToRight
  ]
})
export class TaskHomeComponent implements OnInit {
  constructor(private dialog: MatDialog) {}

  @HostBinding('@routeAnim') state;
  ngOnInit() {}

  launchNewTaskDialog() {
    // this.dialog.open(NewTaskComponent);
    const dialogRef = this.dialog.open(NewTaskComponent, {
      Data: {Title: "new task"}
    });
  }
  lauchCopyTaskDialog() {
    const dialogRef = this.dialog.open(CopyTaskComponent, {
      data: { lists: this.lists }
    });
  }

  launchUpdateTaskDialog(task) {
    const dialogRef = this.dialog.open(NewTaskComponent, {
      Data: {Title: "modify task", task: task}
    });
  }

  launchConfirmDialog() {
    const dialogRef = this.dialog.open(ConfirmDialogComponent, {
      Data: {Title: "delete task list", content: "are you sure you want to delete this task list?"}
    });
  }

  launchEditListDialog() {
    const dialogRef = this.dialog.open(NewTaskListComponent, {
      Data: {Title: "change list name"}
    });
    dialogRef.afterClosed().subscribe(result => console.log(result));
  }
  launchNewListDialog() {
    const dialogRef = this.dialog.open(NewTaskListComponent, {
      Data: {Title: "new list name"}
    });
    dialogRef.afterClosed().subscribe(result => console.log(result));
  }
  lists = [
    {
      id: 1,
      Name: "to do",
      tasks: [
        {
          id: 1,
          Desc: "task 1: go to Starbucks to buy coffee",
          completed: true,
          priority: 3,
          owner: {
            id: 1,
            Name: "Zhang San",
            avatar: "avatars:svg-11"
          },
          dueDate: new Date(),
          reminder: new Date()
        },
        {
          id: 2,
          Desc: "task 1: complete the PPT assigned by the boss",
          completed: false,
          priority: 2,
          owner: {
            id: 2,
            Name: "Li Si",
            avatar: "avatars:svg-12"
          },
          dueDate: new Date()
        }
      ]
    },
    {
      id: 2,
      Name: "in progress",
      tasks: [
        {
          id: 1,
          Desc: "task 3: project code review",
          completed: false,
          priority: 1,
          owner: {
            id: 1,
            Name: "Wang Wu",
            avatar: "avatars:svg-13"
          },
          dueDate: new Date()
        },
        {
          id: 2,
          Desc: "task 1: make project plan",
          completed: false,
          priority: 2,
          owner: {
            id: 2,
            Name: "Li Si",
            avatar: "avatars:svg-12"
          },
          dueDate: new Date()
        }
      ]
    }
  ];
}

Define route

<mat-list-item [routerLink]="['/project']"> 
    <mat-icon mat-list-icon svgIcon="projects"></mat-icon>
    < H4 mat line > project home page < / H4 >
    < p mat line mat subheader > view all your items</p>
  </mat-list-item>
  <mat-list-item [routerLink]="['/task']"> 
    <mat-icon mat-list-icon svgIcon="projects"></mat-icon>
    < H4 mat line > task home page < / H4 >
    < p mat line mat subheader > view all your items</p>
  </mat-list-item>

Note: be sure to use the form of hostbinding.

2、 Group

Lets you perform a set of animation transformations at the same time

Group ([animate (…), animate (…)…]) receives an array in which multiple animations are written.


import { trigger, state, transition, style, animate, group} from '@angular/animations';

export const slideToRight = trigger('routeAnim',[
    state('void',style({'position':'fixed','width':'100%','height':'80%'})),
    state('*',style({'position':'fixed','width':'100%','height':'80%'})),
    transition(':enter',[
        style({transform:'translateX(-100%)',opacity:'0'}),
        group([
            animate('.5s ease-in-out', style({transform:'translateX(0)'})),
            animate('.3s ease-in', style({opacity:1}))
        ])
    ]),
    transition(':leave',[
        style({transform:'translateX(0)',opacity:'1'}),
        group([
            animate('.5s ease-in-out', style({transform:'translateX(100%)'})),
            animate('.3s ease-in', style({opacity:0}))
        ])
    ]),
]);

3、 Query & stagger

Query is used for parent nodes to find child nodes and apply animation to selected elements. Very powerful.

The stagger specifies that there are multiple elements that satisfy the query, and there is an interval between the animations of each element.

Take an example: when creating a new project, create two new projects at the same time, and the animation of the two new projects will be generated in turn. The second project will not start until the first project is completed.

Create list.animate.ts

For the approach animation, hide it first, and make a 1s animation through the stagger interval of 1000s.

import { trigger, state, transition, style, animate, query, animation,stagger} from '@angular/animations';

export const listAnimation = trigger('listAnim', [
    transition('* => *', [
      Query (': Enter', style ({opacity: 0}), {optional: true}), // adding optional is true, and the following state animation is optional
      query(':enter', stagger(1000, [
        animate('1s', style({opacity: 1}))
      ]), { optional: true }),
      query(':leave', style({opacity: 1}), { optional: true }),
      query(':leave', stagger(1000, [
        animate('1s', style({opacity: 0}))
      ]), { optional: true })
    ])
  ]);

In project_ List

The application of query animation is generally with * ngfor, and a layer of div needs to be set outside.


<div [@listAnim]="projects.length">
  <app-project-item *ngFor="let project of projects" [item]="project"
 
  (onInvite)="lauchInviteDialog()"
  (onEdit)="lauchUpdateDialog()"
  (onDelete)="lauchConfimDialog(project)">
  </app-project-item>
</div>
<button mat-fab type="button" (click)="openNewProjectDialog()">
  <mat-icon>add</mat-icon>
</button>

Modify the corresponding CSS

// :host{
//     display: flex;
//     flex-direction: row;
//     flex-wrap: wrap;
// }

//Change host to Div
.container{
    display: flex;
    flex-direction: row;
    flex-wrap: wrap;
}

Modify the component

import { Component, OnInit , HostBinding } from "@angular/core";
import { MatDialog } from "@angular/material";
import { NewProjectComponent } from "../new-project/new-project.component";
import { InviteComponent } from '../invite/invite.component';
import { ConfirmDialogComponent } from '../../shared/confirm-dialog/confirm-dialog.component';
import {slideToRight} from '../../animate/router.animate'
import { listAnimation } from '../../animate/list.animate';
import { projection } from '@angular/core/src/render3';

@Component({
  selector: "app-project-list",
  templateUrl: "./project-list.component.html",
  styleUrls: ["./project-list.component.scss"],
  animations:[
    Slidetoright, listanimation // step 1: import listanimation
  ]
})
export class ProjectListComponent implements OnInit {
  @HostBinding('@routeAnim') state;

  //The second step is to transform the array and add ID
  projects = [
    {
      id:1,
      Name: "enterprise collaboration platform",
      Desc: "this is an enterprise internal project",
      coverImg: "assets/images/covers/0.jpg"
    },
    {
      id:2,
      Name: "automated test project",
      Desc: "this is an enterprise internal project",
      coverImg: "assets/images/covers/2.jpg"
    }
  ];
  constructor(private dialog: MatDialog) { }

  ngOnInit() { }

  //Step 3: add hard code when adding new elements
  openNewProjectDialog() {
    // this.dialog.open(NewProjectComponent,{data:'this is a dialog'});
    const dialogRef = this.dialog.open(NewProjectComponent, {
      Data: {Title: 'new project'}
    });
    dialogRef.afterClosed().subscribe((result) => {
      console.log(result);
      this.projects = [...this.projects, 
        {ID: 3, name: 'a new project', desc: 'this is a new project', coverimg: "assets / images / covers / 3. JPG"},
        {ID: 4, name: 'another new project', desc: 'this is another new project', coverimg: "assets / images / covers / 4. JPG"}]
    });
  }

  lauchInviteDialog() {
    const dialogRef = this.dialog.open(InviteComponent);
  }

  lauchUpdateDialog() {
    const dialogRef = this.dialog.open(NewProjectComponent, {
      Data: {Title: 'edit item'}
    });
  }

  //The fourth step is to transform and delete the project
  lauchConfimDialog(project) {
    const dialogRef = this.dialog.open(ConfirmDialogComponent, {
      Data: {Title: 'delete item', content: 'are you sure you want to delete this item?'}
    });
    dialogRef.afterClosed().subscribe(result=>{
      console.log(result);
      this.projects=this.projects.filter(p=>p.id!=project.id);
    });
  }
}

Stagger makes the animation interlace rather than together when multiple elements.

The above is the detailed explanation of angular routing animation and high-order animation functions. For more information about angular routing animation and high-order animation functions, please pay attention to other relevant articles of developeppaer!

Recommended Today

Swift advanced 08: closure & capture principle

closure closurecanCapture and storageOf any constants and variables defined in their contextquote, this is the so-calledClose and wrap those constants and variablesTherefore, it is called“closure”Swift can handle everything for youCaptured memory managementOperation of. Three forms of closure [global function is a special closure]: a global function is a closure that has a name but does […]