fix(animations): ensure all child elements are rendered before running animations
Closes #9402 Closes #9775 Closes #9887
This commit is contained in:
parent
daa9da4047
commit
c3bdd504d0
|
@ -271,7 +271,7 @@ class _AnimationBuilder implements AnimationAstVisitor {
|
||||||
|
|
||||||
statements.push(_ANIMATION_FACTORY_VIEW_VAR
|
statements.push(_ANIMATION_FACTORY_VIEW_VAR
|
||||||
.callMethod(
|
.callMethod(
|
||||||
'registerAndStartAnimation',
|
'queueAnimation',
|
||||||
[
|
[
|
||||||
_ANIMATION_FACTORY_ELEMENT_VAR, o.literal(this.animationName),
|
_ANIMATION_FACTORY_ELEMENT_VAR, o.literal(this.animationName),
|
||||||
_ANIMATION_PLAYER_VAR
|
_ANIMATION_PLAYER_VAR
|
||||||
|
|
|
@ -36,6 +36,8 @@ function createCurrValueExpr(exprIndex: number): o.ReadVarExpr {
|
||||||
return o.variable(`currVal_${exprIndex}`); // fix syntax highlighting: `
|
return o.variable(`currVal_${exprIndex}`); // fix syntax highlighting: `
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const _animationViewCheckedFlagMap = new Map<CompileView, boolean>();
|
||||||
|
|
||||||
function bind(
|
function bind(
|
||||||
view: CompileView, currValExpr: o.ReadVarExpr, fieldExpr: o.ReadPropExpr,
|
view: CompileView, currValExpr: o.ReadVarExpr, fieldExpr: o.ReadPropExpr,
|
||||||
parsedExpression: cdAst.AST, context: o.Expression, actions: o.Statement[],
|
parsedExpression: cdAst.AST, context: o.Expression, actions: o.Statement[],
|
||||||
|
@ -171,6 +173,13 @@ function bindAndWriteToRenderer(
|
||||||
animation.fnVariable.callFn([o.THIS_EXPR, renderNode, oldRenderValue, emptyStateValue])
|
animation.fnVariable.callFn([o.THIS_EXPR, renderNode, oldRenderValue, emptyStateValue])
|
||||||
.toStmt());
|
.toStmt());
|
||||||
|
|
||||||
|
if (!_animationViewCheckedFlagMap.get(view)) {
|
||||||
|
_animationViewCheckedFlagMap.set(view, true);
|
||||||
|
var triggerStmt = o.THIS_EXPR.callMethod('triggerQueuedAnimations', []).toStmt();
|
||||||
|
view.afterViewLifecycleCallbacksMethod.addStmt(triggerStmt);
|
||||||
|
view.detachMethod.addStmt(triggerStmt);
|
||||||
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -14,6 +14,8 @@ import {AnimationPlayer} from './animation_player';
|
||||||
export class AnimationGroupPlayer implements AnimationPlayer {
|
export class AnimationGroupPlayer implements AnimationPlayer {
|
||||||
private _subscriptions: Function[] = [];
|
private _subscriptions: Function[] = [];
|
||||||
private _finished = false;
|
private _finished = false;
|
||||||
|
private _started = false;
|
||||||
|
|
||||||
public parentPlayer: AnimationPlayer = null;
|
public parentPlayer: AnimationPlayer = null;
|
||||||
|
|
||||||
constructor(private _players: AnimationPlayer[]) {
|
constructor(private _players: AnimationPlayer[]) {
|
||||||
|
@ -44,9 +46,19 @@ export class AnimationGroupPlayer implements AnimationPlayer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
init(): void { this._players.forEach(player => player.init()); }
|
||||||
|
|
||||||
onDone(fn: Function): void { this._subscriptions.push(fn); }
|
onDone(fn: Function): void { this._subscriptions.push(fn); }
|
||||||
|
|
||||||
play() { this._players.forEach(player => player.play()); }
|
hasStarted() { return this._started; }
|
||||||
|
|
||||||
|
play() {
|
||||||
|
if (!isPresent(this.parentPlayer)) {
|
||||||
|
this.init();
|
||||||
|
}
|
||||||
|
this._started = true;
|
||||||
|
this._players.forEach(player => player.play());
|
||||||
|
}
|
||||||
|
|
||||||
pause(): void { this._players.forEach(player => player.pause()); }
|
pause(): void { this._players.forEach(player => player.pause()); }
|
||||||
|
|
||||||
|
|
|
@ -15,6 +15,8 @@ import {scheduleMicroTask} from '../facade/lang';
|
||||||
*/
|
*/
|
||||||
export abstract class AnimationPlayer {
|
export abstract class AnimationPlayer {
|
||||||
abstract onDone(fn: Function): void;
|
abstract onDone(fn: Function): void;
|
||||||
|
abstract init(): void;
|
||||||
|
abstract hasStarted(): boolean;
|
||||||
abstract play(): void;
|
abstract play(): void;
|
||||||
abstract pause(): void;
|
abstract pause(): void;
|
||||||
abstract restart(): void;
|
abstract restart(): void;
|
||||||
|
@ -31,6 +33,7 @@ export abstract class AnimationPlayer {
|
||||||
|
|
||||||
export class NoOpAnimationPlayer implements AnimationPlayer {
|
export class NoOpAnimationPlayer implements AnimationPlayer {
|
||||||
private _subscriptions: any[] /** TODO #9100 */ = [];
|
private _subscriptions: any[] /** TODO #9100 */ = [];
|
||||||
|
private _started = false;
|
||||||
public parentPlayer: AnimationPlayer = null;
|
public parentPlayer: AnimationPlayer = null;
|
||||||
constructor() { scheduleMicroTask(() => this._onFinish()); }
|
constructor() { scheduleMicroTask(() => this._onFinish()); }
|
||||||
/** @internal */
|
/** @internal */
|
||||||
|
@ -39,7 +42,9 @@ export class NoOpAnimationPlayer implements AnimationPlayer {
|
||||||
this._subscriptions = [];
|
this._subscriptions = [];
|
||||||
}
|
}
|
||||||
onDone(fn: Function): void { this._subscriptions.push(fn); }
|
onDone(fn: Function): void { this._subscriptions.push(fn); }
|
||||||
play(): void {}
|
hasStarted(): boolean { return this._started; }
|
||||||
|
init(): void {}
|
||||||
|
play(): void { this._started = true; }
|
||||||
pause(): void {}
|
pause(): void {}
|
||||||
restart(): void {}
|
restart(): void {}
|
||||||
finish(): void { this._onFinish(); }
|
finish(): void { this._onFinish(); }
|
||||||
|
|
|
@ -15,6 +15,7 @@ export class AnimationSequencePlayer implements AnimationPlayer {
|
||||||
private _activePlayer: AnimationPlayer;
|
private _activePlayer: AnimationPlayer;
|
||||||
private _subscriptions: Function[] = [];
|
private _subscriptions: Function[] = [];
|
||||||
private _finished = false;
|
private _finished = false;
|
||||||
|
private _started: boolean = false;
|
||||||
|
|
||||||
public parentPlayer: AnimationPlayer = null;
|
public parentPlayer: AnimationPlayer = null;
|
||||||
|
|
||||||
|
@ -54,9 +55,19 @@ export class AnimationSequencePlayer implements AnimationPlayer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
init(): void { this._players.forEach(player => player.init()); }
|
||||||
|
|
||||||
onDone(fn: Function): void { this._subscriptions.push(fn); }
|
onDone(fn: Function): void { this._subscriptions.push(fn); }
|
||||||
|
|
||||||
play(): void { this._activePlayer.play(); }
|
hasStarted() { return this._started; }
|
||||||
|
|
||||||
|
play(): void {
|
||||||
|
if (!isPresent(this.parentPlayer)) {
|
||||||
|
this.init();
|
||||||
|
}
|
||||||
|
this._started = true;
|
||||||
|
this._activePlayer.play();
|
||||||
|
}
|
||||||
|
|
||||||
pause(): void { this._activePlayer.pause(); }
|
pause(): void { this._activePlayer.pause(); }
|
||||||
|
|
||||||
|
|
|
@ -11,7 +11,7 @@ import {isPresent} from '../facade/lang';
|
||||||
|
|
||||||
import {AnimationPlayer} from './animation_player';
|
import {AnimationPlayer} from './animation_player';
|
||||||
|
|
||||||
export class ActiveAnimationPlayersMap {
|
export class ViewAnimationMap {
|
||||||
private _map = new Map<any, {[key: string]: AnimationPlayer}>();
|
private _map = new Map<any, {[key: string]: AnimationPlayer}>();
|
||||||
private _allPlayers: AnimationPlayer[] = [];
|
private _allPlayers: AnimationPlayer[] = [];
|
||||||
|
|
||||||
|
@ -25,9 +25,9 @@ export class ActiveAnimationPlayersMap {
|
||||||
}
|
}
|
||||||
|
|
||||||
findAllPlayersByElement(element: any): AnimationPlayer[] {
|
findAllPlayersByElement(element: any): AnimationPlayer[] {
|
||||||
var players: any[] /** TODO #9100 */ = [];
|
var players: AnimationPlayer[] = [];
|
||||||
StringMapWrapper.forEach(
|
StringMapWrapper.forEach(
|
||||||
this._map.get(element), (player: any /** TODO #9100 */) => players.push(player));
|
this._map.get(element), (player: AnimationPlayer) => players.push(player));
|
||||||
return players;
|
return players;
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,7 +28,7 @@ import {AnimationPlayer} from '../animation/animation_player';
|
||||||
import {AnimationGroupPlayer} from '../animation/animation_group_player';
|
import {AnimationGroupPlayer} from '../animation/animation_group_player';
|
||||||
import {AnimationKeyframe} from '../animation/animation_keyframe';
|
import {AnimationKeyframe} from '../animation/animation_keyframe';
|
||||||
import {AnimationStyles} from '../animation/animation_styles';
|
import {AnimationStyles} from '../animation/animation_styles';
|
||||||
import {ActiveAnimationPlayersMap} from '../animation/active_animation_players_map';
|
import {ViewAnimationMap} from '../animation/view_animation_map';
|
||||||
|
|
||||||
var _scope_check: WtfScopeFn = wtfCreateScope(`AppView#check(ascii id)`);
|
var _scope_check: WtfScopeFn = wtfCreateScope(`AppView#check(ascii id)`);
|
||||||
|
|
||||||
|
@ -54,7 +54,7 @@ export abstract class AppView<T> {
|
||||||
|
|
||||||
private _hasExternalHostElement: boolean;
|
private _hasExternalHostElement: boolean;
|
||||||
|
|
||||||
public activeAnimationPlayers = new ActiveAnimationPlayersMap();
|
public animationPlayers = new ViewAnimationMap();
|
||||||
|
|
||||||
public context: T;
|
public context: T;
|
||||||
|
|
||||||
|
@ -74,21 +74,27 @@ export abstract class AppView<T> {
|
||||||
|
|
||||||
cancelActiveAnimation(element: any, animationName: string, removeAllAnimations: boolean = false) {
|
cancelActiveAnimation(element: any, animationName: string, removeAllAnimations: boolean = false) {
|
||||||
if (removeAllAnimations) {
|
if (removeAllAnimations) {
|
||||||
this.activeAnimationPlayers.findAllPlayersByElement(element).forEach(
|
this.animationPlayers.findAllPlayersByElement(element).forEach(player => player.destroy());
|
||||||
player => player.destroy());
|
|
||||||
} else {
|
} else {
|
||||||
var player = this.activeAnimationPlayers.find(element, animationName);
|
var player = this.animationPlayers.find(element, animationName);
|
||||||
if (isPresent(player)) {
|
if (isPresent(player)) {
|
||||||
player.destroy();
|
player.destroy();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
registerAndStartAnimation(element: any, animationName: string, player: AnimationPlayer): void {
|
queueAnimation(element: any, animationName: string, player: AnimationPlayer): void {
|
||||||
this.activeAnimationPlayers.set(element, animationName, player);
|
this.animationPlayers.set(element, animationName, player);
|
||||||
player.onDone(() => { this.activeAnimationPlayers.remove(element, animationName); });
|
player.onDone(() => { this.animationPlayers.remove(element, animationName); });
|
||||||
|
}
|
||||||
|
|
||||||
|
triggerQueuedAnimations() {
|
||||||
|
this.animationPlayers.getAllPlayers().forEach(player => {
|
||||||
|
if (!player.hasStarted()) {
|
||||||
player.play();
|
player.play();
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
create(context: T, givenProjectableNodes: Array<any|any[]>, rootSelectorOrNode: string|any):
|
create(context: T, givenProjectableNodes: Array<any|any[]>, rootSelectorOrNode: string|any):
|
||||||
AppElement {
|
AppElement {
|
||||||
|
@ -201,10 +207,10 @@ export abstract class AppView<T> {
|
||||||
this.destroyInternal();
|
this.destroyInternal();
|
||||||
this.dirtyParentQueriesInternal();
|
this.dirtyParentQueriesInternal();
|
||||||
|
|
||||||
if (this.activeAnimationPlayers.length == 0) {
|
if (this.animationPlayers.length == 0) {
|
||||||
this.renderer.destroyView(hostElement, this.allNodes);
|
this.renderer.destroyView(hostElement, this.allNodes);
|
||||||
} else {
|
} else {
|
||||||
var player = new AnimationGroupPlayer(this.activeAnimationPlayers.getAllPlayers());
|
var player = new AnimationGroupPlayer(this.animationPlayers.getAllPlayers());
|
||||||
player.onDone(() => { this.renderer.destroyView(hostElement, this.allNodes); });
|
player.onDone(() => { this.renderer.destroyView(hostElement, this.allNodes); });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -221,10 +227,10 @@ export abstract class AppView<T> {
|
||||||
|
|
||||||
detach(): void {
|
detach(): void {
|
||||||
this.detachInternal();
|
this.detachInternal();
|
||||||
if (this.activeAnimationPlayers.length == 0) {
|
if (this.animationPlayers.length == 0) {
|
||||||
this.renderer.detachView(this.flatRootNodes);
|
this.renderer.detachView(this.flatRootNodes);
|
||||||
} else {
|
} else {
|
||||||
var player = new AnimationGroupPlayer(this.activeAnimationPlayers.getAllPlayers());
|
var player = new AnimationGroupPlayer(this.animationPlayers.getAllPlayers());
|
||||||
player.onDone(() => { this.renderer.detachView(this.flatRootNodes); });
|
player.onDone(() => { this.renderer.detachView(this.flatRootNodes); });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,10 +9,10 @@
|
||||||
import {getDOM} from '@angular/platform-browser/src/dom/dom_adapter';
|
import {getDOM} from '@angular/platform-browser/src/dom/dom_adapter';
|
||||||
import {el} from '@angular/platform-browser/testing/browser_util';
|
import {el} from '@angular/platform-browser/testing/browser_util';
|
||||||
|
|
||||||
import {ActiveAnimationPlayersMap} from '../../src/animation/active_animation_players_map';
|
import {MockAnimationPlayer} from '../../../platform-browser/testing/mock_animation_player';
|
||||||
|
import {ViewAnimationMap} from '../../src/animation/view_animation_map';
|
||||||
import {isPresent} from '../../src/facade/lang';
|
import {isPresent} from '../../src/facade/lang';
|
||||||
import {fakeAsync, flushMicrotasks} from '../../testing';
|
import {fakeAsync, flushMicrotasks} from '../../testing';
|
||||||
import {MockAnimationPlayer} from '../../testing/animation/mock_animation_player';
|
|
||||||
import {AsyncTestCompleter, beforeEach, ddescribe, describe, expect, iit, inject, it, xdescribe, xit} from '../../testing/testing_internal';
|
import {AsyncTestCompleter, beforeEach, ddescribe, describe, expect, iit, inject, it, xdescribe, xit} from '../../testing/testing_internal';
|
||||||
|
|
||||||
export function main() {
|
export function main() {
|
||||||
|
@ -22,7 +22,7 @@ export function main() {
|
||||||
var animationName = 'animationName';
|
var animationName = 'animationName';
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
playersMap = new ActiveAnimationPlayersMap();
|
playersMap = new ViewAnimationMap();
|
||||||
elementNode = el('<div></div>');
|
elementNode = el('<div></div>');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -6,10 +6,10 @@
|
||||||
* found in the LICENSE file at https://angular.io/license
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import {MockAnimationPlayer} from '../../../platform-browser/testing/mock_animation_player';
|
||||||
import {AnimationGroupPlayer} from '../../src/animation/animation_group_player';
|
import {AnimationGroupPlayer} from '../../src/animation/animation_group_player';
|
||||||
import {isPresent} from '../../src/facade/lang';
|
import {isPresent} from '../../src/facade/lang';
|
||||||
import {fakeAsync, flushMicrotasks} from '../../testing';
|
import {fakeAsync, flushMicrotasks} from '../../testing';
|
||||||
import {MockAnimationPlayer} from '../../testing/animation/mock_animation_player';
|
|
||||||
import {AsyncTestCompleter, beforeEach, ddescribe, describe, expect, iit, inject, it, xdescribe, xit} from '../../testing/testing_internal';
|
import {AsyncTestCompleter, beforeEach, ddescribe, describe, expect, iit, inject, it, xdescribe, xit} from '../../testing/testing_internal';
|
||||||
|
|
||||||
export function main() {
|
export function main() {
|
||||||
|
|
|
@ -12,9 +12,13 @@ import {TestComponentBuilder} from '@angular/compiler/testing';
|
||||||
import {AnimationDriver} from '@angular/platform-browser/src/dom/animation_driver';
|
import {AnimationDriver} from '@angular/platform-browser/src/dom/animation_driver';
|
||||||
import {getDOM} from '@angular/platform-browser/src/dom/dom_adapter';
|
import {getDOM} from '@angular/platform-browser/src/dom/dom_adapter';
|
||||||
import {MockAnimationDriver} from '@angular/platform-browser/testing/mock_animation_driver';
|
import {MockAnimationDriver} from '@angular/platform-browser/testing/mock_animation_driver';
|
||||||
|
import {MockAnimationPlayer} from '@angular/platform-browser/testing/mock_animation_player';
|
||||||
|
|
||||||
import {Component} from '../../index';
|
import {Component} from '../../index';
|
||||||
import {DEFAULT_STATE} from '../../src/animation/animation_constants';
|
import {DEFAULT_STATE} from '../../src/animation/animation_constants';
|
||||||
|
import {AnimationKeyframe} from '../../src/animation/animation_keyframe';
|
||||||
|
import {AnimationPlayer} from '../../src/animation/animation_player';
|
||||||
|
import {AnimationStyles} from '../../src/animation/animation_styles';
|
||||||
import {AnimationEntryMetadata, animate, group, keyframes, sequence, state, style, transition, trigger} from '../../src/animation/metadata';
|
import {AnimationEntryMetadata, animate, group, keyframes, sequence, state, style, transition, trigger} from '../../src/animation/metadata';
|
||||||
import {AUTO_STYLE} from '../../src/animation/metadata';
|
import {AUTO_STYLE} from '../../src/animation/metadata';
|
||||||
import {IS_DART, isArray, isPresent} from '../../src/facade/lang';
|
import {IS_DART, isArray, isPresent} from '../../src/facade/lang';
|
||||||
|
@ -26,7 +30,6 @@ export function main() {
|
||||||
declareTests({useJit: false});
|
declareTests({useJit: false});
|
||||||
} else {
|
} else {
|
||||||
describe('jit', () => { declareTests({useJit: true}); });
|
describe('jit', () => { declareTests({useJit: true}); });
|
||||||
|
|
||||||
describe('no jit', () => { declareTests({useJit: false}); });
|
describe('no jit', () => { declareTests({useJit: false}); });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -748,6 +751,132 @@ function declareTests({useJit}: {useJit: boolean}) {
|
||||||
})));
|
})));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('DOM order tracking', () => {
|
||||||
|
if (!getDOM().supportsDOMEvents()) return;
|
||||||
|
|
||||||
|
beforeEachProviders(
|
||||||
|
() => [{provide: AnimationDriver, useClass: InnerContentTrackingAnimationDriver}]);
|
||||||
|
|
||||||
|
it('should evaluate all inner children and their bindings before running the animation on a parent',
|
||||||
|
inject(
|
||||||
|
[TestComponentBuilder, AnimationDriver],
|
||||||
|
fakeAsync((tcb: TestComponentBuilder, driver: InnerContentTrackingAnimationDriver) => {
|
||||||
|
makeAnimationCmp(
|
||||||
|
tcb, `<div class="target" [@status]="exp">
|
||||||
|
<div *ngIf="exp2" class="inner">inner child guy</div>
|
||||||
|
</div>`,
|
||||||
|
[trigger(
|
||||||
|
'status',
|
||||||
|
[
|
||||||
|
state('final', style({'height': '*'})),
|
||||||
|
transition('* => *', [animate(1000)])
|
||||||
|
])],
|
||||||
|
(fixture: any /** TODO #9100 */) => {
|
||||||
|
tick();
|
||||||
|
|
||||||
|
var cmp = fixture.debugElement.componentInstance;
|
||||||
|
var node =
|
||||||
|
getDOM().querySelector(fixture.debugElement.nativeElement, '.target');
|
||||||
|
cmp.exp = true;
|
||||||
|
cmp.exp2 = true;
|
||||||
|
fixture.detectChanges();
|
||||||
|
flushMicrotasks();
|
||||||
|
|
||||||
|
var animation = driver.log.pop();
|
||||||
|
var player = <InnerContentTrackingAnimationPlayer>animation['player'];
|
||||||
|
expect(player.capturedInnerText).toEqual('inner child guy');
|
||||||
|
});
|
||||||
|
})));
|
||||||
|
|
||||||
|
it('should run the initialization stage after all children have been evaluated',
|
||||||
|
inject(
|
||||||
|
[TestComponentBuilder, AnimationDriver],
|
||||||
|
fakeAsync((tcb: TestComponentBuilder, driver: InnerContentTrackingAnimationDriver) => {
|
||||||
|
makeAnimationCmp(
|
||||||
|
tcb, `<div class="target" [@status]="exp">
|
||||||
|
<div style="height:20px"></div>
|
||||||
|
<div *ngIf="exp2" style="height:40px;" class="inner">inner child guy</div>
|
||||||
|
</div>`,
|
||||||
|
[trigger('status', [transition('* => *', sequence([
|
||||||
|
animate(1000, style({height: 0})),
|
||||||
|
animate(1000, style({height: '*'}))
|
||||||
|
]))])],
|
||||||
|
(fixture: any /** TODO #9100 */) => {
|
||||||
|
tick();
|
||||||
|
|
||||||
|
var cmp = fixture.debugElement.componentInstance;
|
||||||
|
cmp.exp = true;
|
||||||
|
cmp.exp2 = true;
|
||||||
|
fixture.detectChanges();
|
||||||
|
flushMicrotasks();
|
||||||
|
fixture.detectChanges();
|
||||||
|
|
||||||
|
var animation = driver.log.pop();
|
||||||
|
var player = <InnerContentTrackingAnimationPlayer>animation['player'];
|
||||||
|
|
||||||
|
// this is just to confirm that the player is using the parent element
|
||||||
|
expect(player.element.className).toEqual('target');
|
||||||
|
expect(player.computedHeight).toEqual('60px');
|
||||||
|
});
|
||||||
|
})));
|
||||||
|
|
||||||
|
it('should not trigger animations more than once within a view that contains multiple animation triggers',
|
||||||
|
inject(
|
||||||
|
[TestComponentBuilder, AnimationDriver],
|
||||||
|
fakeAsync((tcb: TestComponentBuilder, driver: InnerContentTrackingAnimationDriver) => {
|
||||||
|
makeAnimationCmp(
|
||||||
|
tcb, `<div *ngIf="exp" @one><div class="inner"></div></div>
|
||||||
|
<div *ngIf="exp2" @two><div class="inner"></div></div>`,
|
||||||
|
[
|
||||||
|
trigger('one', [transition('* => *', [animate(1000)])]),
|
||||||
|
trigger('two', [transition('* => *', [animate(2000)])])
|
||||||
|
],
|
||||||
|
(fixture: any /** TODO #9100 */) => {
|
||||||
|
var cmp = fixture.debugElement.componentInstance;
|
||||||
|
cmp.exp = true;
|
||||||
|
cmp.exp2 = true;
|
||||||
|
fixture.detectChanges();
|
||||||
|
flushMicrotasks();
|
||||||
|
|
||||||
|
expect(driver.log.length).toEqual(2);
|
||||||
|
var animation1 = driver.log.pop();
|
||||||
|
var animation2 = driver.log.pop();
|
||||||
|
var player1 = <InnerContentTrackingAnimationPlayer>animation1['player'];
|
||||||
|
var player2 = <InnerContentTrackingAnimationPlayer>animation2['player'];
|
||||||
|
expect(player1.playAttempts).toEqual(1);
|
||||||
|
expect(player2.playAttempts).toEqual(1);
|
||||||
|
});
|
||||||
|
})));
|
||||||
|
|
||||||
|
it('should trigger animations when animations are detached from the page',
|
||||||
|
inject(
|
||||||
|
[TestComponentBuilder, AnimationDriver],
|
||||||
|
fakeAsync((tcb: TestComponentBuilder, driver: InnerContentTrackingAnimationDriver) => {
|
||||||
|
makeAnimationCmp(
|
||||||
|
tcb, `<div *ngIf="exp" @trigger><div class="inner"></div></div>`,
|
||||||
|
[
|
||||||
|
trigger('trigger', [transition('* => void', [animate(1000)])]),
|
||||||
|
],
|
||||||
|
(fixture: any /** TODO #9100 */) => {
|
||||||
|
var cmp = fixture.debugElement.componentInstance;
|
||||||
|
cmp.exp = true;
|
||||||
|
fixture.detectChanges();
|
||||||
|
flushMicrotasks();
|
||||||
|
|
||||||
|
expect(driver.log.length).toEqual(0);
|
||||||
|
|
||||||
|
cmp.exp = false;
|
||||||
|
fixture.detectChanges();
|
||||||
|
flushMicrotasks();
|
||||||
|
|
||||||
|
expect(driver.log.length).toEqual(1);
|
||||||
|
var animation = driver.log.pop();
|
||||||
|
var player = <InnerContentTrackingAnimationPlayer>animation['player'];
|
||||||
|
expect(player.playAttempts).toEqual(1);
|
||||||
|
});
|
||||||
|
})));
|
||||||
|
});
|
||||||
|
|
||||||
describe('animation states', () => {
|
describe('animation states', () => {
|
||||||
it('should retain the destination animation state styles once the animation is complete',
|
it('should retain the destination animation state styles once the animation is complete',
|
||||||
inject(
|
inject(
|
||||||
|
@ -1049,3 +1178,26 @@ class DummyIfCmp {
|
||||||
exp = false;
|
exp = false;
|
||||||
exp2 = false;
|
exp2 = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class InnerContentTrackingAnimationDriver extends MockAnimationDriver {
|
||||||
|
animate(
|
||||||
|
element: any, startingStyles: AnimationStyles, keyframes: AnimationKeyframe[],
|
||||||
|
duration: number, delay: number, easing: string): AnimationPlayer {
|
||||||
|
super.animate(element, startingStyles, keyframes, duration, delay, easing);
|
||||||
|
var player = new InnerContentTrackingAnimationPlayer(element);
|
||||||
|
this.log[this.log.length - 1]['player'] = player;
|
||||||
|
return player;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class InnerContentTrackingAnimationPlayer extends MockAnimationPlayer {
|
||||||
|
constructor(public element: any) { super(); }
|
||||||
|
public computedHeight: number;
|
||||||
|
public capturedInnerText: string;
|
||||||
|
public playAttempts = 0;
|
||||||
|
init() { this.computedHeight = getDOM().getComputedStyle(this.element)['height']; }
|
||||||
|
play() {
|
||||||
|
this.playAttempts++;
|
||||||
|
this.capturedInnerText = this.element.querySelector('.inner').innerText;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -6,10 +6,10 @@
|
||||||
* found in the LICENSE file at https://angular.io/license
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import {MockAnimationPlayer} from '../../../platform-browser/testing/mock_animation_player';
|
||||||
import {AnimationSequencePlayer} from '../../src/animation/animation_sequence_player';
|
import {AnimationSequencePlayer} from '../../src/animation/animation_sequence_player';
|
||||||
import {isPresent} from '../../src/facade/lang';
|
import {isPresent} from '../../src/facade/lang';
|
||||||
import {fakeAsync, flushMicrotasks} from '../../testing';
|
import {fakeAsync, flushMicrotasks} from '../../testing';
|
||||||
import {MockAnimationPlayer} from '../../testing/animation/mock_animation_player';
|
|
||||||
import {AsyncTestCompleter, beforeEach, ddescribe, describe, expect, iit, inject, it, xdescribe, xit} from '../../testing/testing_internal';
|
import {AsyncTestCompleter, beforeEach, ddescribe, describe, expect, iit, inject, it, xdescribe, xit} from '../../testing/testing_internal';
|
||||||
|
|
||||||
export function main() {
|
export function main() {
|
||||||
|
|
|
@ -13,7 +13,7 @@ import {Math, global, isFunction, isPromise} from '../src/facade/lang';
|
||||||
import {AsyncTestCompleter} from './async_test_completer';
|
import {AsyncTestCompleter} from './async_test_completer';
|
||||||
import {getTestInjector, inject} from './test_injector';
|
import {getTestInjector, inject} from './test_injector';
|
||||||
|
|
||||||
export {MockAnimationPlayer} from './animation/mock_animation_player';
|
export {MockAnimationPlayer} from '@angular/platform-browser/testing/mock_animation_player';
|
||||||
export {AsyncTestCompleter} from './async_test_completer';
|
export {AsyncTestCompleter} from './async_test_completer';
|
||||||
export {inject} from './test_injector';
|
export {inject} from './test_injector';
|
||||||
export {expect} from './testing';
|
export {expect} from './testing';
|
||||||
|
|
|
@ -11,9 +11,7 @@ import {AUTO_STYLE, BaseException} from '@angular/core';
|
||||||
import {AnimationKeyframe, AnimationPlayer, AnimationStyles, NoOpAnimationPlayer} from '../../core_private';
|
import {AnimationKeyframe, AnimationPlayer, AnimationStyles, NoOpAnimationPlayer} from '../../core_private';
|
||||||
import {StringMapWrapper} from '../facade/collection';
|
import {StringMapWrapper} from '../facade/collection';
|
||||||
import {StringWrapper, isNumber, isPresent} from '../facade/lang';
|
import {StringWrapper, isNumber, isPresent} from '../facade/lang';
|
||||||
|
|
||||||
import {AnimationDriver} from './animation_driver';
|
import {AnimationDriver} from './animation_driver';
|
||||||
import {getDOM} from './dom_adapter';
|
|
||||||
import {DomAnimatePlayer} from './dom_animate_player';
|
import {DomAnimatePlayer} from './dom_animate_player';
|
||||||
import {dashCaseToCamelCase} from './util';
|
import {dashCaseToCamelCase} from './util';
|
||||||
import {WebAnimationsPlayer} from './web_animations_player';
|
import {WebAnimationsPlayer} from './web_animations_player';
|
||||||
|
@ -21,19 +19,17 @@ import {WebAnimationsPlayer} from './web_animations_player';
|
||||||
export class WebAnimationsDriver implements AnimationDriver {
|
export class WebAnimationsDriver implements AnimationDriver {
|
||||||
animate(
|
animate(
|
||||||
element: any, startingStyles: AnimationStyles, keyframes: AnimationKeyframe[],
|
element: any, startingStyles: AnimationStyles, keyframes: AnimationKeyframe[],
|
||||||
duration: number, delay: number, easing: string): AnimationPlayer {
|
duration: number, delay: number, easing: string): WebAnimationsPlayer {
|
||||||
var anyElm = <any>element;
|
|
||||||
|
|
||||||
var formattedSteps: {[key: string]: string | number}[] = [];
|
var formattedSteps: {[key: string]: string | number}[] = [];
|
||||||
var startingStyleLookup: {[key: string]: string | number} = {};
|
var startingStyleLookup: {[key: string]: string | number} = {};
|
||||||
if (isPresent(startingStyles) && startingStyles.styles.length > 0) {
|
if (isPresent(startingStyles) && startingStyles.styles.length > 0) {
|
||||||
startingStyleLookup = _populateStyles(anyElm, startingStyles, {});
|
startingStyleLookup = _populateStyles(element, startingStyles, {});
|
||||||
startingStyleLookup['offset'] = 0;
|
startingStyleLookup['offset'] = 0;
|
||||||
formattedSteps.push(startingStyleLookup);
|
formattedSteps.push(startingStyleLookup);
|
||||||
}
|
}
|
||||||
|
|
||||||
keyframes.forEach((keyframe: AnimationKeyframe) => {
|
keyframes.forEach((keyframe: AnimationKeyframe) => {
|
||||||
let data = _populateStyles(anyElm, keyframe.styles, startingStyleLookup);
|
let data = _populateStyles(element, keyframe.styles, startingStyleLookup);
|
||||||
data['offset'] = keyframe.offset;
|
data['offset'] = keyframe.offset;
|
||||||
formattedSteps.push(data);
|
formattedSteps.push(data);
|
||||||
});
|
});
|
||||||
|
@ -60,14 +56,7 @@ export class WebAnimationsDriver implements AnimationDriver {
|
||||||
playerOptions['easing'] = easing;
|
playerOptions['easing'] = easing;
|
||||||
}
|
}
|
||||||
|
|
||||||
var player = this._triggerWebAnimation(anyElm, formattedSteps, playerOptions);
|
return new WebAnimationsPlayer(element, formattedSteps, playerOptions);
|
||||||
|
|
||||||
return new WebAnimationsPlayer(player, duration);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @internal */
|
|
||||||
_triggerWebAnimation(elm: any, keyframes: any[], options: any): DomAnimatePlayer {
|
|
||||||
return elm.animate(keyframes, options);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -78,9 +67,8 @@ function _populateStyles(
|
||||||
styles.styles.forEach((entry) => {
|
styles.styles.forEach((entry) => {
|
||||||
StringMapWrapper.forEach(entry, (val: any, prop: string) => {
|
StringMapWrapper.forEach(entry, (val: any, prop: string) => {
|
||||||
var formattedProp = dashCaseToCamelCase(prop);
|
var formattedProp = dashCaseToCamelCase(prop);
|
||||||
data[formattedProp] = val == AUTO_STYLE ?
|
data[formattedProp] =
|
||||||
_computeStyle(element, formattedProp) :
|
val == AUTO_STYLE ? val : val.toString() + _resolveStyleUnit(val, prop, formattedProp);
|
||||||
val.toString() + _resolveStyleUnit(val, prop, formattedProp);
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
StringMapWrapper.forEach(defaultStyles, (value: string, prop: string) => {
|
StringMapWrapper.forEach(defaultStyles, (value: string, prop: string) => {
|
||||||
|
@ -154,7 +142,3 @@ function _isPixelDimensionStyle(prop: string): boolean {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function _computeStyle(element: any, prop: string): string {
|
|
||||||
return getDOM().getComputedStyle(element)[prop];
|
|
||||||
}
|
|
||||||
|
|
|
@ -6,20 +6,29 @@
|
||||||
* found in the LICENSE file at https://angular.io/license
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import {AUTO_STYLE} from '@angular/core';
|
||||||
|
|
||||||
import {AnimationPlayer} from '../../core_private';
|
import {AnimationPlayer} from '../../core_private';
|
||||||
|
import {StringMapWrapper} from '../facade/collection';
|
||||||
import {isPresent} from '../facade/lang';
|
import {isPresent} from '../facade/lang';
|
||||||
|
|
||||||
|
import {getDOM} from './dom_adapter';
|
||||||
import {DomAnimatePlayer} from './dom_animate_player';
|
import {DomAnimatePlayer} from './dom_animate_player';
|
||||||
|
|
||||||
export class WebAnimationsPlayer implements AnimationPlayer {
|
export class WebAnimationsPlayer implements AnimationPlayer {
|
||||||
private _subscriptions: Function[] = [];
|
private _subscriptions: Function[] = [];
|
||||||
private _finished = false;
|
private _finished = false;
|
||||||
|
private _initialized = false;
|
||||||
|
private _player: DomAnimatePlayer;
|
||||||
|
private _started: boolean = false;
|
||||||
|
private _duration: number;
|
||||||
|
|
||||||
public parentPlayer: AnimationPlayer = null;
|
public parentPlayer: AnimationPlayer = null;
|
||||||
|
|
||||||
constructor(private _player: DomAnimatePlayer, public totalTime: number) {
|
constructor(
|
||||||
// this is required to make the player startable at a later time
|
public element: any, public keyframes: {[key: string]: string | number}[],
|
||||||
this.reset();
|
public options: {[key: string]: string | number}) {
|
||||||
this._player.onfinish = () => this._onFinish();
|
this._duration = <number>options['duration'];
|
||||||
}
|
}
|
||||||
|
|
||||||
private _onFinish() {
|
private _onFinish() {
|
||||||
|
@ -33,13 +42,44 @@ export class WebAnimationsPlayer implements AnimationPlayer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
init(): void {
|
||||||
|
if (this._initialized) return;
|
||||||
|
this._initialized = true;
|
||||||
|
|
||||||
|
var keyframes = this.keyframes.map(styles => {
|
||||||
|
var formattedKeyframe: {[key: string]: string | number} = {};
|
||||||
|
StringMapWrapper.forEach(styles, (value: string | number, prop: string) => {
|
||||||
|
formattedKeyframe[prop] = value == AUTO_STYLE ? _computeStyle(this.element, prop) : value;
|
||||||
|
});
|
||||||
|
return formattedKeyframe;
|
||||||
|
});
|
||||||
|
|
||||||
|
this._player = this._triggerWebAnimation(this.element, keyframes, this.options);
|
||||||
|
|
||||||
|
// this is required so that the player doesn't start to animate right away
|
||||||
|
this.reset();
|
||||||
|
this._player.onfinish = () => this._onFinish();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @internal */
|
||||||
|
_triggerWebAnimation(element: any, keyframes: any[], options: any): DomAnimatePlayer {
|
||||||
|
return <DomAnimatePlayer>element.animate(keyframes, options);
|
||||||
|
}
|
||||||
|
|
||||||
onDone(fn: Function): void { this._subscriptions.push(fn); }
|
onDone(fn: Function): void { this._subscriptions.push(fn); }
|
||||||
|
|
||||||
play(): void { this._player.play(); }
|
play(): void {
|
||||||
|
this.init();
|
||||||
|
this._player.play();
|
||||||
|
}
|
||||||
|
|
||||||
pause(): void { this._player.pause(); }
|
pause(): void {
|
||||||
|
this.init();
|
||||||
|
this._player.pause();
|
||||||
|
}
|
||||||
|
|
||||||
finish(): void {
|
finish(): void {
|
||||||
|
this.init();
|
||||||
this._onFinish();
|
this._onFinish();
|
||||||
this._player.finish();
|
this._player.finish();
|
||||||
}
|
}
|
||||||
|
@ -51,12 +91,20 @@ export class WebAnimationsPlayer implements AnimationPlayer {
|
||||||
this.play();
|
this.play();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
hasStarted(): boolean { return this._started; }
|
||||||
|
|
||||||
destroy(): void {
|
destroy(): void {
|
||||||
this.reset();
|
this.reset();
|
||||||
this._onFinish();
|
this._onFinish();
|
||||||
}
|
}
|
||||||
|
|
||||||
setPosition(p: any /** TODO #9100 */): void { this._player.currentTime = p * this.totalTime; }
|
get totalTime(): number { return this._duration; }
|
||||||
|
|
||||||
|
setPosition(p: number): void { this._player.currentTime = p * this.totalTime; }
|
||||||
|
|
||||||
getPosition(): number { return this._player.currentTime / this.totalTime; }
|
getPosition(): number { return this._player.currentTime / this.totalTime; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function _computeStyle(element: any, prop: string): string {
|
||||||
|
return getDOM().getComputedStyle(element)[prop];
|
||||||
|
}
|
||||||
|
|
|
@ -12,6 +12,7 @@ import {el} from '@angular/platform-browser/testing/browser_util';
|
||||||
import {AnimationKeyframe, AnimationStyles} from '../../core_private';
|
import {AnimationKeyframe, AnimationStyles} from '../../core_private';
|
||||||
import {DomAnimatePlayer} from '../../src/dom/dom_animate_player';
|
import {DomAnimatePlayer} from '../../src/dom/dom_animate_player';
|
||||||
import {WebAnimationsDriver} from '../../src/dom/web_animations_driver';
|
import {WebAnimationsDriver} from '../../src/dom/web_animations_driver';
|
||||||
|
import {WebAnimationsPlayer} from '../../src/dom/web_animations_player';
|
||||||
import {StringMapWrapper} from '../../src/facade/collection';
|
import {StringMapWrapper} from '../../src/facade/collection';
|
||||||
import {MockDomAnimatePlayer} from '../../testing/mock_dom_animate_player';
|
import {MockDomAnimatePlayer} from '../../testing/mock_dom_animate_player';
|
||||||
|
|
||||||
|
@ -51,8 +52,8 @@ export function main() {
|
||||||
_makeKeyframe(1, {'font-size': '555px'})
|
_makeKeyframe(1, {'font-size': '555px'})
|
||||||
];
|
];
|
||||||
|
|
||||||
driver.animate(elm, startingStyles, styles, 0, 0, 'linear');
|
var player = driver.animate(elm, startingStyles, styles, 0, 0, 'linear');
|
||||||
var details = driver.log.pop();
|
var details = _formatOptions(player);
|
||||||
var startKeyframe = details['keyframes'][0];
|
var startKeyframe = details['keyframes'][0];
|
||||||
var firstKeyframe = details['keyframes'][1];
|
var firstKeyframe = details['keyframes'][1];
|
||||||
var lastKeyframe = details['keyframes'][2];
|
var lastKeyframe = details['keyframes'][2];
|
||||||
|
@ -71,8 +72,8 @@ export function main() {
|
||||||
var startingStyles = _makeStyles({'borderTopWidth': 40});
|
var startingStyles = _makeStyles({'borderTopWidth': 40});
|
||||||
var styles = [_makeKeyframe(0, {'font-size': 100}), _makeKeyframe(1, {'height': '555em'})];
|
var styles = [_makeKeyframe(0, {'font-size': 100}), _makeKeyframe(1, {'height': '555em'})];
|
||||||
|
|
||||||
driver.animate(elm, startingStyles, styles, 0, 0, 'linear');
|
var player = driver.animate(elm, startingStyles, styles, 0, 0, 'linear');
|
||||||
var details = driver.log.pop();
|
var details = _formatOptions(player);
|
||||||
var startKeyframe = details['keyframes'][0];
|
var startKeyframe = details['keyframes'][0];
|
||||||
var firstKeyframe = details['keyframes'][1];
|
var firstKeyframe = details['keyframes'][1];
|
||||||
var lastKeyframe = details['keyframes'][2];
|
var lastKeyframe = details['keyframes'][2];
|
||||||
|
@ -88,8 +89,8 @@ export function main() {
|
||||||
var startingStyles = _makeStyles({});
|
var startingStyles = _makeStyles({});
|
||||||
var styles = [_makeKeyframe(0, {'color': 'green'}), _makeKeyframe(1, {'color': 'red'})];
|
var styles = [_makeKeyframe(0, {'color': 'green'}), _makeKeyframe(1, {'color': 'red'})];
|
||||||
|
|
||||||
driver.animate(elm, startingStyles, styles, 1000, 1000, 'linear');
|
var player = driver.animate(elm, startingStyles, styles, 1000, 1000, 'linear');
|
||||||
var details = driver.log.pop();
|
var details = _formatOptions(player);
|
||||||
var options = details['options'];
|
var options = details['options'];
|
||||||
expect(options['fill']).toEqual('both');
|
expect(options['fill']).toEqual('both');
|
||||||
});
|
});
|
||||||
|
@ -98,8 +99,8 @@ export function main() {
|
||||||
var startingStyles = _makeStyles({});
|
var startingStyles = _makeStyles({});
|
||||||
var styles = [_makeKeyframe(0, {'color': 'green'}), _makeKeyframe(1, {'color': 'red'})];
|
var styles = [_makeKeyframe(0, {'color': 'green'}), _makeKeyframe(1, {'color': 'red'})];
|
||||||
|
|
||||||
driver.animate(elm, startingStyles, styles, 1000, 1000, 'ease-out');
|
var player = driver.animate(elm, startingStyles, styles, 1000, 1000, 'ease-out');
|
||||||
var details = driver.log.pop();
|
var details = _formatOptions(player);
|
||||||
var options = details['options'];
|
var options = details['options'];
|
||||||
expect(options['easing']).toEqual('ease-out');
|
expect(options['easing']).toEqual('ease-out');
|
||||||
});
|
});
|
||||||
|
@ -108,11 +109,15 @@ export function main() {
|
||||||
var startingStyles = _makeStyles({});
|
var startingStyles = _makeStyles({});
|
||||||
var styles = [_makeKeyframe(0, {'color': 'green'}), _makeKeyframe(1, {'color': 'red'})];
|
var styles = [_makeKeyframe(0, {'color': 'green'}), _makeKeyframe(1, {'color': 'red'})];
|
||||||
|
|
||||||
driver.animate(elm, startingStyles, styles, 1000, 1000, null);
|
var player = driver.animate(elm, startingStyles, styles, 1000, 1000, null);
|
||||||
var details = driver.log.pop();
|
var details = _formatOptions(player);
|
||||||
var options = details['options'];
|
var options = details['options'];
|
||||||
var keys = StringMapWrapper.keys(options);
|
var keys = StringMapWrapper.keys(options);
|
||||||
expect(keys.indexOf('easing')).toEqual(-1);
|
expect(keys.indexOf('easing')).toEqual(-1);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function _formatOptions(player: WebAnimationsPlayer): {[key: string]: any} {
|
||||||
|
return {'element': player.element, 'keyframes': player.keyframes, 'options': player.options};
|
||||||
|
}
|
||||||
|
|
|
@ -7,16 +7,32 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {AsyncTestCompleter, MockAnimationPlayer, beforeEach, beforeEachProviders, ddescribe, describe, expect, iit, inject, it, xdescribe, xit} from '@angular/core/testing/testing_internal';
|
import {AsyncTestCompleter, MockAnimationPlayer, beforeEach, beforeEachProviders, ddescribe, describe, expect, iit, inject, it, xdescribe, xit} from '@angular/core/testing/testing_internal';
|
||||||
|
import {el} from '@angular/platform-browser/testing/browser_util';
|
||||||
|
|
||||||
|
import {DomAnimatePlayer} from '../../src/dom/dom_animate_player';
|
||||||
import {WebAnimationsPlayer} from '../../src/dom/web_animations_player';
|
import {WebAnimationsPlayer} from '../../src/dom/web_animations_player';
|
||||||
import {MockDomAnimatePlayer} from '../../testing/mock_dom_animate_player';
|
import {MockDomAnimatePlayer} from '../../testing/mock_dom_animate_player';
|
||||||
|
|
||||||
|
class ExtendedWebAnimationsPlayer extends WebAnimationsPlayer {
|
||||||
|
public domPlayer = new MockDomAnimatePlayer();
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
public element: HTMLElement, public keyframes: {[key: string]: string | number}[],
|
||||||
|
public options: {[key: string]: string | number}) {
|
||||||
|
super(element, keyframes, options);
|
||||||
|
}
|
||||||
|
|
||||||
|
_triggerWebAnimation(elm: any, keyframes: any[], options: any): DomAnimatePlayer {
|
||||||
|
return this.domPlayer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export function main() {
|
export function main() {
|
||||||
function makePlayer(): {[key: string]: any} {
|
function makePlayer(): {[key: string]: any} {
|
||||||
var mockPlayer = new MockDomAnimatePlayer();
|
var someElm = el('<div></div>');
|
||||||
var c = mockPlayer.captures;
|
var player = new ExtendedWebAnimationsPlayer(someElm, [], {});
|
||||||
var p = new WebAnimationsPlayer(mockPlayer, 0);
|
player.init();
|
||||||
return {'captures': c, 'player': p};
|
return {'captures': player.domPlayer.captures, 'player': player};
|
||||||
}
|
}
|
||||||
|
|
||||||
describe('WebAnimationsPlayer', () => {
|
describe('WebAnimationsPlayer', () => {
|
||||||
|
|
|
@ -14,7 +14,7 @@ import {AnimationDriver} from '../src/dom/animation_driver';
|
||||||
import {StringMapWrapper} from '../src/facade/collection';
|
import {StringMapWrapper} from '../src/facade/collection';
|
||||||
|
|
||||||
export class MockAnimationDriver extends AnimationDriver {
|
export class MockAnimationDriver extends AnimationDriver {
|
||||||
log: any[] /** TODO #9100 */ = [];
|
public log: {[key: string]: any}[] = [];
|
||||||
animate(
|
animate(
|
||||||
element: any, startingStyles: AnimationStyles, keyframes: AnimationKeyframe[],
|
element: any, startingStyles: AnimationStyles, keyframes: AnimationKeyframe[],
|
||||||
duration: number, delay: number, easing: string): AnimationPlayer {
|
duration: number, delay: number, easing: string): AnimationPlayer {
|
||||||
|
@ -38,11 +38,9 @@ function _serializeKeyframes(keyframes: AnimationKeyframe[]): any[] {
|
||||||
}
|
}
|
||||||
|
|
||||||
function _serializeStyles(styles: AnimationStyles): {[key: string]: any} {
|
function _serializeStyles(styles: AnimationStyles): {[key: string]: any} {
|
||||||
var flatStyles = {};
|
var flatStyles: {[key: string]: any} = {};
|
||||||
styles.styles.forEach(
|
styles.styles.forEach(entry => StringMapWrapper.forEach(entry, (val: any, prop: string) => {
|
||||||
entry => StringMapWrapper.forEach(
|
flatStyles[prop] = val;
|
||||||
entry, (val: any /** TODO #9100 */, prop: any /** TODO #9100 */) => {
|
|
||||||
(flatStyles as any /** TODO #9100 */)[prop] = val;
|
|
||||||
}));
|
}));
|
||||||
return flatStyles;
|
return flatStyles;
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,13 +6,15 @@
|
||||||
* found in the LICENSE file at https://angular.io/license
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {AnimationPlayer} from '../../src/animation/animation_player';
|
import {AnimationPlayer} from '../../core/src/animation/animation_player';
|
||||||
import {isPresent} from '../../src/facade/lang';
|
import {isPresent} from '../../core/src/facade/lang';
|
||||||
|
|
||||||
export class MockAnimationPlayer implements AnimationPlayer {
|
export class MockAnimationPlayer implements AnimationPlayer {
|
||||||
private _subscriptions: any[] /** TODO #9100 */ = [];
|
private _subscriptions: any[] /** TODO #9100 */ = [];
|
||||||
private _finished = false;
|
private _finished = false;
|
||||||
private _destroyed = false;
|
private _destroyed = false;
|
||||||
|
private _started: boolean = false;
|
||||||
|
|
||||||
public parentPlayer: AnimationPlayer = null;
|
public parentPlayer: AnimationPlayer = null;
|
||||||
|
|
||||||
public log: any[] /** TODO #9100 */ = [];
|
public log: any[] /** TODO #9100 */ = [];
|
||||||
|
@ -30,9 +32,16 @@ export class MockAnimationPlayer implements AnimationPlayer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
init(): void { this.log.push('init'); }
|
||||||
|
|
||||||
onDone(fn: Function): void { this._subscriptions.push(fn); }
|
onDone(fn: Function): void { this._subscriptions.push(fn); }
|
||||||
|
|
||||||
play(): void { this.log.push('play'); }
|
hasStarted() { return this._started; }
|
||||||
|
|
||||||
|
play(): void {
|
||||||
|
this._started = true;
|
||||||
|
this.log.push('play');
|
||||||
|
}
|
||||||
|
|
||||||
pause(): void { this.log.push('pause'); }
|
pause(): void { this.log.push('pause'); }
|
||||||
|
|
|
@ -32,6 +32,9 @@ import {
|
||||||
<hr />
|
<hr />
|
||||||
<div *ngFor="let item of items" class="box" [@boxAnimation]="state">
|
<div *ngFor="let item of items" class="box" [@boxAnimation]="state">
|
||||||
{{ item }}
|
{{ item }}
|
||||||
|
<div *ngIf="true">
|
||||||
|
something inside
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
`,
|
`,
|
||||||
|
|
|
@ -67,6 +67,8 @@ export declare abstract class AnimationPlayer {
|
||||||
abstract destroy(): void;
|
abstract destroy(): void;
|
||||||
abstract finish(): void;
|
abstract finish(): void;
|
||||||
abstract getPosition(): number;
|
abstract getPosition(): number;
|
||||||
|
abstract hasStarted(): boolean;
|
||||||
|
abstract init(): void;
|
||||||
abstract onDone(fn: Function): void;
|
abstract onDone(fn: Function): void;
|
||||||
abstract pause(): void;
|
abstract pause(): void;
|
||||||
abstract play(): void;
|
abstract play(): void;
|
||||||
|
|
Loading…
Reference in New Issue