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(
|
function parseInnerTransitionStr(
|
||||||
eventStr: string, expressions: TransitionMatcherFn[], errors: string[]) {
|
eventStr: string, expressions: TransitionMatcherFn[], errors: string[]) {
|
||||||
if (eventStr[0] == ':') {
|
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]+)$/);
|
const match = eventStr.match(/^(\*|[-\w]+)\s*(<?[=-]>)\s*(\*|[-\w]+)$/);
|
||||||
if (match == null || match.length < 4) {
|
if (match == null || match.length < 4) {
|
||||||
errors.push(`The provided transition expression "${eventStr}" is not supported`);
|
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) {
|
switch (alias) {
|
||||||
case ':enter':
|
case ':enter':
|
||||||
return 'void => *';
|
return 'void => *';
|
||||||
case ':leave':
|
case ':leave':
|
||||||
return '* => void';
|
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:
|
default:
|
||||||
errors.push(`The transition alias value "${alias}" is not supported`);
|
errors.push(`The transition alias value "${alias}" is not supported`);
|
||||||
return '* => *';
|
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`
|
* 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 `*
|
* 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", [
|
* transition(":enter", [
|
||||||
* style({ opacity: 0 }),
|
* style({ opacity: 0 }),
|
||||||
* animate(500, style({ opacity: 1 }))
|
* animate(500, style({ opacity: 1 }))
|
||||||
* ])
|
* ]),
|
||||||
* transition(":leave", [
|
* transition(":leave", [
|
||||||
* animate(500, style({ opacity: 0 }))
|
* 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'}
|
* {@example core/animation/ts/dsl/animation_example.ts region='Component'}
|
||||||
*
|
*
|
||||||
* @experimental Animation support is experimental.
|
* @experimental Animation support is experimental.
|
||||||
|
|
|
@ -1541,6 +1541,98 @@ export function main() {
|
||||||
const players = getLog();
|
const players = getLog();
|
||||||
expect(players.length).toEqual(2);
|
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', () => {
|
describe('animation listeners', () => {
|
||||||
|
|
Loading…
Reference in New Issue