Skip to content

Commit

Permalink
fix(animations): fix stagger timing not handling params
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
  • Loading branch information
dario-piotrowicz committed Aug 21, 2022
1 parent a360309 commit c2bb768
Show file tree
Hide file tree
Showing 4 changed files with 109 additions and 4 deletions.
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
Expand Up @@ -647,6 +647,9 @@
{
"name": "connectableObservableDescriptor"
},
{
"name": "constructTimingAst"
},
{
"name": "containsElement"
},
Expand Down

0 comments on commit c2bb768

Please sign in to comment.