Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(animations): make sure style calculations are not computed too early #15540

Merged
merged 1 commit into from Mar 28, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions packages/animations/browser/src/private_export.ts
Expand Up @@ -13,3 +13,4 @@ export {NoopAnimationDriver as ɵNoopAnimationDriver} from './render/animation_d
export {DomAnimationEngine as ɵDomAnimationEngine} from './render/dom_animation_engine';
export {NoopAnimationEngine as ɵNoopAnimationEngine} from './render/noop_animation_engine';
export {WebAnimationsDriver as ɵWebAnimationsDriver, supportsWebAnimations as ɵsupportsWebAnimations} from './render/web_animations/web_animations_driver';
export {WebAnimationsPlayer as ɵWebAnimationsPlayer} from './render/web_animations/web_animations_player';
Expand Up @@ -259,8 +259,6 @@ export class DomAnimationEngine {
const player = this._buildPlayer(element, instruction, previousPlayers, i);
player.onDestroy(
() => { deleteFromArrayMap(this._activeElementAnimations, element, player); });
player.init();

this._markPlayerAsActive(element, player);
return player;
});
Expand Down Expand Up @@ -354,6 +352,7 @@ export class DomAnimationEngine {
// in the event that an animation throws an error then we do
// not want to re-run animations on any previous animations
// if they have already been kicked off beforehand
player.init();
if (!player.hasStarted()) {
player.play();
}
Expand Down
Expand Up @@ -87,6 +87,22 @@ export function main() {
expect(engine.queuedPlayers.pop() instanceof NoopAnimationPlayer).toBe(true);
});

it('should not initialize the animation until the engine has been flushed', () => {
const engine = makeEngine();
engine.registerTrigger(trigger(
'trig', [transition('* => something', [animate(1000, style({color: 'gold'}))])]));

engine.setProperty(element, 'trig', 'something');
const player = engine.queuedPlayers.pop() as MockAnimationPlayer;

let initialized = false;
player.onInit(() => initialized = true);

expect(initialized).toBe(false);
engine.flush();
expect(initialized).toBe(true);
});

it('should not queue an animation if the property value has not changed at all', () => {
const engine = makeEngine();

Expand Down
11 changes: 11 additions & 0 deletions packages/animations/browser/testing/src/mock_animation_driver.ts
Expand Up @@ -31,6 +31,7 @@ export class MockAnimationDriver implements AnimationDriver {
export class MockAnimationPlayer extends NoopAnimationPlayer {
private __finished = false;
public previousStyles: {[key: string]: string | number} = {};
private _onInitFns: (() => any)[] = [];

constructor(
public element: any, public keyframes: {[key: string]: string | number}[],
Expand All @@ -45,6 +46,16 @@ export class MockAnimationPlayer extends NoopAnimationPlayer {
});
}

/* @internal */
onInit(fn: () => any) { this._onInitFns.push(fn); }

/* @internal */
init() {
super.init();
this._onInitFns.forEach(fn => fn());
this._onInitFns = [];
}

finish(): void {
super.finish();
this.__finished = true;
Expand Down
@@ -0,0 +1,94 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {animate, style, transition, trigger} from '@angular/animations';
import {AnimationDriver, ɵAnimationEngine} from '@angular/animations/browser';
import {ɵDomAnimationEngine, ɵWebAnimationsDriver, ɵWebAnimationsPlayer, ɵsupportsWebAnimations} from '@angular/animations/browser'
import {Component, ViewChild} from '@angular/core';
import {BrowserAnimationsModule} from '@angular/platform-browser/animations';

import {TestBed} from '../../testing';

export function main() {
// these tests are only mean't to be run within the DOM (for now)
if (typeof Element == 'undefined' || !ɵsupportsWebAnimations()) return;

describe('animation integration tests using web animations', function() {

beforeEach(() => {
TestBed.configureTestingModule({
providers: [{provide: AnimationDriver, useClass: ɵWebAnimationsDriver}],
imports: [BrowserAnimationsModule]
});
});

it('should animate a component that captures height during an animation', () => {
@Component({
selector: 'if-cmp',
template: `
<div *ngIf="exp" #element [@myAnimation]="exp">
hello {{ text }}
</div>
`,
animations: [trigger(
'myAnimation',
[
transition('* => *', [style({height: '0px'}), animate(1000, style({height: '*'}))]),
])]
})
class Cmp {
exp: any = false;
text: string;

@ViewChild('element') public element: any;
}

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

const engine = TestBed.get(ɵAnimationEngine);
const fixture = TestBed.createComponent(Cmp);
const cmp = fixture.componentInstance;
cmp.exp = 1;
cmp.text = '';
fixture.detectChanges();
engine.flush();

const element = cmp.element.nativeElement;
element.style.lineHeight = '20px';
element.style.width = '50px';

cmp.exp = 2;
cmp.text = '12345';
fixture.detectChanges();
engine.flush();

let player = engine.activePlayers.pop() as ɵWebAnimationsPlayer;
player.setPosition(1);

assertStyleBetween(element, 'height', 15, 25);

cmp.exp = 3;
cmp.text = '12345-12345-12345-12345';
fixture.detectChanges();
engine.flush();

player = engine.activePlayers.pop() as ɵWebAnimationsPlayer;
player.setPosition(1);
assertStyleBetween(element, 'height', 35, 45);
});
});
}

function assertStyleBetween(
element: any, prop: string, start: string | number, end: string | number) {
const style = (window.getComputedStyle(element) as any)[prop] as string;
if (typeof start == 'number' && typeof end == 'number') {
const value = parseFloat(style);
expect(value).toBeGreaterThan(start);
expect(value).toBeLessThan(end);
}
}