slides.com/gerardsans | @gerardsans
Text
850
950
Angular In Flip Flops
source: blog
SELECTOR
a.active:hover {
color: #333;
}
PROPERTY
VALUE
DECLARATION
:hover
:link
:active
:target
:not(selector)
:focus
::first-letter
::first-line
::before
::after
::selection
Element style
Element id
class/attribute selectors
element selectors
last CSS rule wins
source: blog
source: blog
<html>
<head>
<script src="https://unpkg.com/web-animations-js@2.2.5"></script>
...
</head>
<body>
<my-app>
loading...
</my-app>
</body>
</html>
npm install web-animations-js --save
// src/polyfills.ts
import 'web-animations-js';
DURATION
START
END
Not all CSS properties are animatable (list)
all background* border* bottom box-shadow clip clip-path color filter font* height left margin* mask* offset* opacity outline* padding* perspective* right text-decoration text-shadow top transform vertical-align visibility width z-index
PROPERTY
transition: color 5s ease-in 1s;
DURATION
TIMING
DELAY
DURATION
0%
100%
KEYFRAMES
NAME
animation: fade 5s 1s infinite linear;
DURATION
DELAY
ITERATIONS
TIMING
@keyframes fade {
0% { opacity: 1; }
100% { opacity: 0; }
}
@keyframes fade {
from { opacity: 1; }
to { opacity: 0; }
}
fadeIn
fadeOut
TRANSITIONS
STATE
STATE
fadeIn => fadeOut
fadeOut => fadeIn
fadeIn <=> fadeOut
void
*
void => * :enter
* => void :leave
void <=> *
STATE
STATE
// npm install --save @angular/animations
// app.module.ts
import {Component, NgModule} from '@angular/core';
import {BrowserAnimationsModule} from '@angular/platform-browser/animations';
@Component({ })
export class App { }
@NgModule({
imports: [ BrowserModule, BrowserAnimationsModule ],
declarations: [ App ],
bootstrap: [ App ]
})
export class AppModule {}
platformBrowserDynamic().bootstrapModule(AppModule)
import {fade} from './animations';
@Component({
selector: 'my-app',
template: `<button [@fade]='fade' (click)="toggleFade()">Fade</button>`,
animations: [ fade ]
})
export class App {
fade = 'fadeIn';
toggleFade(){
this.fade = this.fade === 'fadeIn' ? 'fadeOut' : 'fadeIn';
}
}
import {
trigger, transition, state, style, animate
} from '@angular/animations';
export const fade = trigger('fade', [
state('fadeIn', style({ opacity: 1 })),
state('fadeOut', style({ opacity: 0.1 })),
transition('fadeIn <=> fadeOut', animate('2000ms linear'))
]);
import {
trigger, transition, state, style, animate, keyframes
} from '@angular/animations';
export const grow = trigger('grow' , [
state('small', style({ transform: 'scale(1)' })),
state('large', style({ transform: 'scale(1.4)' })),
transition('small <=> large', animate('200ms linear'))
])
import {
trigger, transition, state, style, animate, keyframes
} from '@angular/animations';
export const grow = trigger('grow' , [
state('small', style({ transform: 'scale(1)' })),
state('large', style({ transform: 'scale(1.4)' })),
transition('small <=> large', animate('200ms linear', keyframes([
style({transform: 'scale(1.6)', offset: 0}),
style({transform: 'scale(1.4)', offset: 1})
])
))
])
import {fade} from './animations';
@Component({
selector: 'my-app',
template: `<img [@fade]='fade'
(@fade.start)="start($event)"
(@fade.done)="done($event) >`,
animations: [ fade ]
})
export class App {
start(e) { }
done(e) { }
}
$event == {
element: any, // div element
triggerName: string, // "fade"
phaseName: string // "start" or "done"
fromState: string, // "in" or "out"
toState: string, // "in" or "out"
totalTime: number, // 1000 (milliseconds)
}
export const revealGroup = trigger('revealGroup', [
transition(':enter', [
query('*', style({ opacity: 0 })),
group([
query('*', animate(2000, style({ opacity: 1 })))
])
])
]);
export const revealGroup = trigger('revealGroup', [
transition(':enter', [
query('*', style({ opacity: 0 })),
sequence([
query('*', animate(2000, style({ opacity: 1 })))
query('*', animate(1000, style({ color: 'red' })))
])
])
]);
export const revealStagger = trigger('revealStagger', [
transition(':enter', [
query('*', style({ opacity: 0 })),
query('*', stagger(1000, [
animate(2000, style({ opacity: 1 }))
])
)
])
]);
export const reveal = trigger('reveal', [
transition(':enter', [
query('@fade', [
animateChild()
]),
query('@*', [
animateChild()
]),
])
]);
import { routerAnimations } from './router.animations';
@Component({
template: `
<div [@routerAnimations]="routeTransition(outlet)">
<router-outlet #outlet="outlet"></router-outlet>
</div>
`,
animations: [ routerAnimations ]
})
export class App {
routeTransition(outlet) {
const routeAnimation = outlet.activatedRouteData['animation'] || {};
return routeAnimation['value'] || null;
}
}
const routes = [
{ path: '', redirectTo: 'home', pathMatch: 'full' },
{ path: 'home', component: Home,
data: {
animation: { value: 'home' }
}
},
{ path: '**', component: NotFound }
];
export const routerAnimations = trigger('routerAnimations', [
transition('about <=> home', [
query(':enter, :leave', style({ position: 'fixed', width:'100%' })),
group([
query(':enter', [
style({ transform: 'translateX(100%)' }),
animate('0.5s ease-in-out', style({ transform: 'translateX(0%)' }))
]),
query(':leave', [
style({ transform: 'translateX(0%)' }),
animate('0.5s ease-in-out', style({ transform: 'translateX(100%)' }
]),
])
])
])
.visible.advert {
will-change: transform;
}
.visible.advert:hover {
transform: scale(1.2);
}