feat(animations): support :increment and :decrement transition aliases
This commit is contained in:
parent
65c9e13105
commit
6f45519d6f
|
@ -24,8 +24,14 @@ export function parseTransitionExpr(
|
|||
function parseInnerTransitionStr(
|
||||
eventStr: string, expressions: TransitionMatcherFn[], errors: string[]) {
|
||||
if (eventStr[0] == ':') {
|
||||
eventStr = parseAnimationAlias(eventStr, errors);
|
||||
const result = parseAnimationAlias(eventStr, errors);
|
||||
if (typeof result == 'function') {
|
||||
expressions.push(result);
|
||||
return;
|
||||
}
|
||||
eventStr = result as string;
|
||||
}
|
||||
|
||||
const match = eventStr.match(/^(\*|[-\w]+)\s*(<?[=-]>)\s*(\*|[-\w]+)$/);
|
||||
if (match == null || match.length < 4) {
|
||||
errors.push(`The provided transition expression "${eventStr}" is not supported`);
|
||||
|
@ -43,12 +49,16 @@ function parseInnerTransitionStr(
|
|||
}
|
||||
}
|
||||
|
||||
function parseAnimationAlias(alias: string, errors: string[]): string {
|
||||
function parseAnimationAlias(alias: string, errors: string[]): string|TransitionMatcherFn {
|
||||
switch (alias) {
|
||||
case ':enter':
|
||||
return 'void => *';
|
||||
case ':leave':
|
||||
return '* => void';
|
||||
case ':increment':
|
||||
return (fromState: any, toState: any): boolean => parseFloat(toState) > parseFloat(fromState);
|
||||
case ':decrement':
|
||||
return (fromState: any, toState: any): boolean => parseFloat(toState) < parseFloat(fromState);
|
||||
default:
|
||||
errors.push(`The transition alias value "${alias}" is not supported`);
|
||||
return '* => *';
|
||||
|
|
|
@ -707,7 +707,7 @@ export function keyframes(steps: AnimationStyleMetadata[]): AnimationKeyframesSe
|
|||
* ])
|
||||
* ```
|
||||
*
|
||||
* ### Transition Aliases (`:enter` and `:leave`)
|
||||
* ### Using :enter and :leave
|
||||
*
|
||||
* Given that enter (insertion) and leave (removal) animations are so common, the `transition`
|
||||
* function accepts both `:enter` and `:leave` values which are aliases for the `void => *` and `*
|
||||
|
@ -717,12 +717,88 @@ export function keyframes(steps: AnimationStyleMetadata[]): AnimationKeyframesSe
|
|||
* transition(":enter", [
|
||||
* style({ opacity: 0 }),
|
||||
* animate(500, style({ opacity: 1 }))
|
||||
* ])
|
||||
* ]),
|
||||
* transition(":leave", [
|
||||
* animate(500, style({ opacity: 0 }))
|
||||
* ])
|
||||
* ```
|
||||
*
|
||||
* ### Using :increment and :decrement
|
||||
* In addition to the :enter and :leave transition aliases, the :increment and :decrement aliases
|
||||
* can be used to kick off a transition when a numeric value has increased or decreased in value.
|
||||
*
|
||||
* ```
|
||||
* import {group, animate, query, transition, style, trigger} from '@angular/animations';
|
||||
* import {Component} from '@angular/core';
|
||||
*
|
||||
* @Component({
|
||||
* selector: 'banner-carousel-component',
|
||||
* styles: [`
|
||||
* .banner-container {
|
||||
* position:relative;
|
||||
* height:500px;
|
||||
* overflow:hidden;
|
||||
* }
|
||||
* .banner-container > .banner {
|
||||
* position:absolute;
|
||||
* left:0;
|
||||
* top:0;
|
||||
* font-size:200px;
|
||||
* line-height:500px;
|
||||
* font-weight:bold;
|
||||
* text-align:center;
|
||||
* width:100%;
|
||||
* }
|
||||
* `],
|
||||
* template: `
|
||||
* <button (click)="previous()">Previous</button>
|
||||
* <button (click)="next()">Next</button>
|
||||
* <hr>
|
||||
* <div [@bannerAnimation]="selectedIndex" class="banner-container">
|
||||
* <div class="banner"> {{ banner }} </div>
|
||||
* </div>
|
||||
* `
|
||||
* animations: [
|
||||
* trigger('bannerAnimation', [
|
||||
* transition(":increment", group([
|
||||
* query(':enter', [
|
||||
* style({ left: '100%' }),
|
||||
* animate('0.5s ease-out', style('*'))
|
||||
* ]),
|
||||
* query(':leave', [
|
||||
* animate('0.5s ease-out', style({ left: '-100%' }))
|
||||
* ])
|
||||
* ])),
|
||||
* transition(":decrement", group([
|
||||
* query(':enter', [
|
||||
* style({ left: '-100%' }),
|
||||
* animate('0.5s ease-out', style('*'))
|
||||
* ]),
|
||||
* query(':leave', [
|
||||
* animate('0.5s ease-out', style({ left: '100%' }))
|
||||
* ])
|
||||
* ])),
|
||||
* ])
|
||||
* ]
|
||||
* })
|
||||
* class BannerCarouselComponent {
|
||||
* allBanners: string[] = ['1', '2', '3', '4'];
|
||||
* selectedIndex: number = 0;
|
||||
*
|
||||
* get banners() {
|
||||
* return [this.allBanners[this.selectedIndex]];
|
||||
* }
|
||||
*
|
||||
* previous() {
|
||||
* this.selectedIndex = Math.max(this.selectedIndex - 1, 0);
|
||||
* }
|
||||
*
|
||||
* next() {
|
||||
* this.selectedIndex = Math.min(this.selectedIndex + 1, this.allBanners.length - 1);
|
||||
* }
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* {@example core/animation/ts/dsl/animation_example.ts region='Component'}
|
||||
*
|
||||
* @experimental Animation support is experimental.
|
||||
|
|
|
@ -1541,6 +1541,98 @@ export function main() {
|
|||
const players = getLog();
|
||||
expect(players.length).toEqual(2);
|
||||
});
|
||||
|
||||
describe('transition aliases', () => {
|
||||
describe(':increment', () => {
|
||||
it('should detect when a value has incremented', () => {
|
||||
@Component({
|
||||
selector: 'if-cmp',
|
||||
template: `
|
||||
<div [@myAnimation]="exp"></div>
|
||||
`,
|
||||
animations: [
|
||||
trigger(
|
||||
'myAnimation',
|
||||
[
|
||||
transition(
|
||||
':increment',
|
||||
[
|
||||
animate(1234, style({background: 'red'})),
|
||||
]),
|
||||
]),
|
||||
]
|
||||
})
|
||||
class Cmp {
|
||||
exp: number = 0;
|
||||
}
|
||||
|
||||
TestBed.configureTestingModule({declarations: [Cmp]});
|
||||
const fixture = TestBed.createComponent(Cmp);
|
||||
const cmp = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
let players = getLog();
|
||||
expect(players.length).toEqual(0);
|
||||
|
||||
cmp.exp++;
|
||||
fixture.detectChanges();
|
||||
players = getLog();
|
||||
expect(players.length).toEqual(1);
|
||||
expect(players[0].duration).toEqual(1234);
|
||||
resetLog();
|
||||
|
||||
cmp.exp = 5;
|
||||
fixture.detectChanges();
|
||||
players = getLog();
|
||||
expect(players.length).toEqual(1);
|
||||
expect(players[0].duration).toEqual(1234);
|
||||
});
|
||||
});
|
||||
|
||||
describe(':decrement', () => {
|
||||
it('should detect when a value has decremented', () => {
|
||||
@Component({
|
||||
selector: 'if-cmp',
|
||||
template: `
|
||||
<div [@myAnimation]="exp"></div>
|
||||
`,
|
||||
animations: [
|
||||
trigger(
|
||||
'myAnimation',
|
||||
[
|
||||
transition(
|
||||
':decrement',
|
||||
[
|
||||
animate(1234, style({background: 'red'})),
|
||||
]),
|
||||
]),
|
||||
]
|
||||
})
|
||||
class Cmp {
|
||||
exp: number = 5;
|
||||
}
|
||||
|
||||
TestBed.configureTestingModule({declarations: [Cmp]});
|
||||
const fixture = TestBed.createComponent(Cmp);
|
||||
const cmp = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
let players = getLog();
|
||||
expect(players.length).toEqual(0);
|
||||
|
||||
cmp.exp--;
|
||||
fixture.detectChanges();
|
||||
players = getLog();
|
||||
expect(players.length).toEqual(1);
|
||||
expect(players[0].duration).toEqual(1234);
|
||||
resetLog();
|
||||
|
||||
cmp.exp = 0;
|
||||
fixture.detectChanges();
|
||||
players = getLog();
|
||||
expect(players.length).toEqual(1);
|
||||
expect(players[0].duration).toEqual(1234);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('animation listeners', () => {
|
||||
|
|
Loading…
Reference in New Issue