Skip to content

Commit

Permalink
fix(animations): make sure that the useAnimation function delay is ap…
Browse files Browse the repository at this point in the history
…plied (#47468)

make sure that when an animation is used via the `useAnimation` function
and a delay has been provided then that delay gets correctly applied

(this PR is a follow up for #47285)

PR Close #47468
  • Loading branch information
dario-piotrowicz authored and pkozlowski-opensource committed Sep 20, 2022
1 parent fe6d33c commit bba2dae
Show file tree
Hide file tree
Showing 2 changed files with 114 additions and 25 deletions.
31 changes: 12 additions & 19 deletions packages/animations/browser/src/dsl/animation_timeline_builder.ts
Expand Up @@ -183,32 +183,25 @@ export class AnimationTimelineBuilderVisitor implements AstVisitor {
visitAnimateRef(ast: AnimateRefAst, context: AnimationTimelineContext): any {
const innerContext = context.createSubContext(ast.options);
innerContext.transformIntoNewTimeline();
this._applyAnimateRefDelay(ast.animation, context, innerContext);
this._applyAnimationRefDelays([ast.options, ast.animation.options], context, innerContext);
this.visitReference(ast.animation, innerContext);
context.transformIntoNewTimeline(innerContext.currentTimeline.currentTime);
context.previousNode = ast;
}

private _applyAnimateRefDelay(
animation: ReferenceAst, context: AnimationTimelineContext,
private _applyAnimationRefDelays(
animationsRefsOptions: (AnimationOptions|null)[], context: AnimationTimelineContext,
innerContext: AnimationTimelineContext) {
const animationDelay = animation.options?.delay;

if (!animationDelay) {
return;
}

let animationDelayValue: number;

if (typeof animationDelay === 'string') {
const interpolatedDelay =
interpolateParams(animationDelay, animation.options?.params ?? {}, context.errors);
animationDelayValue = resolveTimingValue(interpolatedDelay);
} else {
animationDelayValue = animationDelay;
for (const animationRefOptions of animationsRefsOptions) {
const animationDelay = animationRefOptions?.delay;
if (animationDelay) {
const animationDelayValue = typeof animationDelay === 'number' ?
animationDelay :
resolveTimingValue(interpolateParams(
animationDelay, animationRefOptions?.params ?? {}, context.errors));
innerContext.delayNextStep(animationDelayValue);
}
}

innerContext.delayNextStep(animationDelayValue);
}

private _visitSubInstructions(
Expand Down
108 changes: 102 additions & 6 deletions packages/core/test/animation/animation_integration_spec.ts
Expand Up @@ -3923,13 +3923,57 @@ describe('animation tests', function() {
]);
});

it('should combine the delay specified in the animation with that of the caller', () => {
it('should apply the delay specified in the useAnimation call', () => {
const animationMetaData = animation([
style({color: 'red'}),
animate(550, style({color: 'green'})),
]);

@Component({
selector: 'cmp',
template: `
<div @anim *ngIf="exp">
</div>
`,
animations: [
trigger('anim', [transition(
':enter',
useAnimation(animationMetaData, {delay: 1500}),
)]),
]
})
class Cmp {
exp: boolean = false;
}

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

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

fixture.detectChanges();
engine.flush();

const players = getLog();
expect(players.length).toEqual(1);
const [player] = players;
expect(player.delay).toEqual(1500);
expect(player.duration).toEqual(550);
expect(player.keyframes).toEqual([
new Map<string, string|number>([['color', 'red'], ['offset', 0]]),
new Map<string, string|number>([['color', 'green'], ['offset', 1]]),
]);
});

it('should apply the delay specified in the useAnimation call using params', () => {
const animationMetaData = animation(
[
style({color: 'red'}),
animate(500, style({color: 'green'})),
animate(700, style({color: 'green'})),
],
{delay: 2000});
);

@Component({
selector: 'cmp',
Expand All @@ -3938,7 +3982,13 @@ describe('animation tests', function() {
</div>
`,
animations: [
trigger('anim', [transition(':enter', useAnimation(animationMetaData), {delay: 750})]),
trigger('anim', [transition(
':enter',
useAnimation(animationMetaData, {
delay: '{{useAnimationDelay}}ms',
params: {useAnimationDelay: 7500}
}),
)]),
]
})
class Cmp {
Expand All @@ -3958,13 +4008,59 @@ describe('animation tests', function() {
const players = getLog();
expect(players.length).toEqual(1);
const [player] = players;
expect(player.delay).toEqual(2750);
expect(player.duration).toEqual(500);
expect(player.delay).toEqual(7500);
expect(player.duration).toEqual(700);
expect(player.keyframes).toEqual([
new Map<string, string|number>([['color', 'red'], ['offset', 0]]),
new Map<string, string|number>([['color', 'green'], ['offset', 1]]),
]);
});

it('should combine the delays specified in the animation and the useAnimation with that of the caller',
() => {
const animationMetaData = animation(
[
style({color: 'red'}),
animate(567, style({color: 'green'})),
],
{delay: 1000});

@Component({
selector: 'cmp',
template: `
<div @anim *ngIf="exp">
</div>
`,
animations: [
trigger('anim', [transition(
':enter', useAnimation(animationMetaData, {delay: 34}),
{delay: 200})]),
]
})
class Cmp {
exp: boolean = false;
}

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

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

fixture.detectChanges();
engine.flush();

const players = getLog();
expect(players.length).toEqual(1);
const [player] = players;
expect(player.delay).toEqual(1234);
expect(player.duration).toEqual(567);
expect(player.keyframes).toEqual([
new Map<string, string|number>([['color', 'red'], ['offset', 0]]),
new Map<string, string|number>([['color', 'green'], ['offset', 1]]),
]);
});
});

it('should combine multiple errors together into one exception when an animation fails to be built',
Expand Down

0 comments on commit bba2dae

Please sign in to comment.