Skip to content

Commit

Permalink
fix(animations): fix stagger timing not handling params (#47208)
Browse files Browse the repository at this point in the history
prior to this change the stagger timing was being built during the
ast building instead of dynamically when visiting the stagger animation,
thus it could not handle params correctly, this change makes it so that
during ast building a timing ast is built instead and that ast is used
dynammically to build animations which can handle params correctly

resolves #19786

PR Close #47208
  • Loading branch information
dario-piotrowicz authored and alxhub committed Aug 22, 2022
1 parent 45a5aa8 commit b96e571
Show file tree
Hide file tree
Showing 4 changed files with 109 additions and 4 deletions.
Expand Up @@ -473,7 +473,7 @@ export class AnimationAstBuilderVisitor implements AnimationDslVisitor {
}
const timings = metadata.timings === 'full' ?
{duration: 0, delay: 0, easing: 'full'} :
resolveTiming(metadata.timings, context.errors, true);
constructTimingAst(metadata.timings, context.errors);

return {
type: AnimationMetadataType.Stagger,
Expand Down
Expand Up @@ -278,12 +278,14 @@ export class AnimationTimelineBuilderVisitor implements AstVisitor {
context.previousNode = ast;
}

private _visitTiming(ast: TimingAst, context: AnimationTimelineContext): AnimateTimings {
private _visitTiming(
ast: TimingAst, context: AnimationTimelineContext,
allowNegativeValues = false): AnimateTimings {
if ((ast as DynamicTimingAst).dynamic) {
const strValue = (ast as DynamicTimingAst).strValue;
const timingValue =
context.params ? interpolateParams(strValue, context.params, context.errors) : strValue;
return resolveTiming(timingValue, context.errors);
return resolveTiming(timingValue, context.errors, allowNegativeValues);
} else {
return {duration: ast.duration, delay: ast.delay, easing: ast.easing};
}
Expand Down Expand Up @@ -413,7 +415,8 @@ export class AnimationTimelineBuilderVisitor implements AstVisitor {
visitStagger(ast: StaggerAst, context: AnimationTimelineContext) {
const parentContext = context.parentContext!;
const tl = context.currentTimeline;
const timings = ast.timings;
const timings = this._visitTiming(ast.timings, context, true);

const duration = Math.abs(timings.duration);
const maxTime = duration * (context.currentQueryTotal - 1);
let delay = duration * context.currentQueryIndex;
Expand Down
99 changes: 99 additions & 0 deletions packages/core/test/animation/animation_query_integration_spec.ts
Expand Up @@ -742,6 +742,105 @@ describe('animation query tests', function() {
expect(p5.delay).toEqual(6000);
});

it(`should handle params used in the stagger's timing argument`, () => {
@Component({
selector: 'ani-cmp',
template: `
<div [@myAnimation]="exp">
<div *ngFor="let item of items" class="item">
{{ item }}
</div>
</div>
`,
animations: [
trigger('myAnimation', [
transition('* => go', [
query('.item', [
stagger('{{staggerDelay}}ms',[
style({opacity: 0}), animate(1000, style({opacity: .5})),
animate(500, style({opacity: 1}))
])
])
], {params: { staggerDelay: '1111' }})
])
]
})
class Cmp {
public exp: any;
public items: any[] = [0, 1, 2, 3, 4];
}

TestBed.configureTestingModule({declarations: [Cmp]});

const engine = TestBed.inject(ɵAnimationEngine);
const fixture = TestBed.createComponent(Cmp);
const cmp = fixture.componentInstance;

cmp.exp = 'go';
fixture.detectChanges();
engine.flush();

const players = getLog();
expect(players.length).toEqual(5);

const [p1, p2, p3, p4, p5] = players;
expect(p1.delay).toEqual(0);
expect(p2.delay).toEqual(1111);
expect(p3.delay).toEqual(2222);
expect(p4.delay).toEqual(3333);
expect(p5.delay).toEqual(4444);
});

it(`should handle params used in the stagger's timing argument producing a negative value`,
() => {
@Component({
selector: 'ani-cmp',
template: `
<div [@myAnimation]="exp">
<div *ngFor="let item of items" class="item">
{{ item }}
</div>
</div>
`,
animations: [
trigger('myAnimation', [
transition('* => go', [
query('.item', [
stagger('{{staggerDelay}}ms',[
style({opacity: 0}), animate(1000, style({opacity: .5})),
animate(500, style({opacity: 1}))
])
])
], {params: { staggerDelay: -1111 }})
])
]
})
class Cmp {
public exp: any;
public items: any[] = [0, 1, 2, 3, 4];
}

TestBed.configureTestingModule({declarations: [Cmp]});

const engine = TestBed.inject(ɵAnimationEngine);
const fixture = TestBed.createComponent(Cmp);
const cmp = fixture.componentInstance;

cmp.exp = 'go';
fixture.detectChanges();
engine.flush();

const players = getLog();
expect(players.length).toEqual(5);

const [p1, p2, p3, p4, p5] = players;
expect(p5.delay).toEqual(0);
expect(p4.delay).toEqual(1111);
expect(p3.delay).toEqual(2222);
expect(p2.delay).toEqual(3333);
expect(p1.delay).toEqual(4444);
});

it('should persist inner sub trigger styles once their animation is complete', () => {
@Component({
selector: 'ani-cmp',
Expand Down
Expand Up @@ -647,6 +647,9 @@
{
"name": "connectableObservableDescriptor"
},
{
"name": "constructTimingAst"
},
{
"name": "containsElement"
},
Expand Down

0 comments on commit b96e571

Please sign in to comment.