X Tutup
Skip to content

Commit bffa2cb

Browse files
committed
feat(animate): cross-browser compatibility
Closes #4243
1 parent 4f56a01 commit bffa2cb

File tree

6 files changed

+105
-19
lines changed

6 files changed

+105
-19
lines changed

modules/angular2/src/animate/animation.ts

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@ import {
22
DateWrapper,
33
StringWrapper,
44
RegExpWrapper,
5-
NumberWrapper
5+
NumberWrapper,
6+
isPresent
67
} from 'angular2/src/core/facade/lang';
78
import {Math} from 'angular2/src/core/facade/math';
89
import {camelCaseToDashCase} from 'angular2/src/core/render/dom/util';
@@ -31,6 +32,8 @@ export class Animation {
3132
/** flag used to track whether or not the animation has finished */
3233
completed: boolean = false;
3334

35+
private _stringPrefix: string = '';
36+
3437
/** total amount of time that the animation should take including delay */
3538
get totalTime(): number {
3639
let delay = this.computedDelay != null ? this.computedDelay : 0;
@@ -47,6 +50,7 @@ export class Animation {
4750
constructor(public element: HTMLElement, public data: CssAnimationOptions,
4851
public browserDetails: BrowserDetails) {
4952
this.startTime = DateWrapper.toMillis(DateWrapper.now());
53+
this._stringPrefix = DOM.getAnimationPrefix();
5054
this.setup();
5155
this.wait(timestamp => this.start());
5256
}
@@ -77,11 +81,14 @@ export class Animation {
7781
if (this.data.toStyles != null) this.applyStyles(this.data.toStyles);
7882
var computedStyles = DOM.getComputedStyle(this.element);
7983
this.computedDelay =
80-
Math.max(this.parseDurationString(computedStyles.getPropertyValue('transition-delay')),
81-
this.parseDurationString(this.element.style.getPropertyValue('transition-delay')));
82-
this.computedDuration = Math.max(
83-
this.parseDurationString(computedStyles.getPropertyValue('transition-duration')),
84-
this.parseDurationString(this.element.style.getPropertyValue('transition-duration')));
84+
Math.max(this.parseDurationString(
85+
computedStyles.getPropertyValue(this._stringPrefix + 'transition-delay')),
86+
this.parseDurationString(
87+
this.element.style.getPropertyValue(this._stringPrefix + 'transition-delay')));
88+
this.computedDuration = Math.max(this.parseDurationString(computedStyles.getPropertyValue(
89+
this._stringPrefix + 'transition-duration')),
90+
this.parseDurationString(this.element.style.getPropertyValue(
91+
this._stringPrefix + 'transition-duration')));
8592
this.addEvents();
8693
}
8794

@@ -91,7 +98,12 @@ export class Animation {
9198
*/
9299
applyStyles(styles: StringMap<string, any>): void {
93100
StringMapWrapper.forEach(styles, (value, key) => {
94-
DOM.setStyle(this.element, camelCaseToDashCase(key), value.toString());
101+
var dashCaseKey = camelCaseToDashCase(key);
102+
if (isPresent(DOM.getStyle(this.element, dashCaseKey))) {
103+
DOM.setStyle(this.element, dashCaseKey, value.toString());
104+
} else {
105+
DOM.setStyle(this.element, this._stringPrefix + dashCaseKey, value.toString());
106+
}
95107
});
96108
}
97109

@@ -117,7 +129,7 @@ export class Animation {
117129
addEvents(): void {
118130
if (this.totalTime > 0) {
119131
this.eventClearFunctions.push(DOM.onAndCancel(
120-
this.element, 'transitionend', (event: any) => this.handleAnimationEvent(event)));
132+
this.element, DOM.getTransitionEnd(), (event: any) => this.handleAnimationEvent(event)));
121133
} else {
122134
this.handleAnimationCompleted();
123135
}

modules/angular2/src/core/dom/dom_adapter.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,4 +139,7 @@ export class DomAdapter {
139139
requestAnimationFrame(callback): number { throw _abstract(); }
140140
cancelAnimationFrame(id) { throw _abstract(); }
141141
performanceNow(): number { throw _abstract(); }
142+
getAnimationPrefix(): string { throw _abstract(); }
143+
getTransitionEnd(): string { throw _abstract(); }
144+
supportsAnimation(): boolean { throw _abstract(); }
142145
}

modules/angular2/src/core/dom/generic_browser_adapter.ts

Lines changed: 42 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,44 @@
1-
import {ListWrapper} from 'angular2/src/core/facade/collection';
2-
import {isPresent, isFunction} from 'angular2/src/core/facade/lang';
1+
import {ListWrapper, StringMapWrapper} from 'angular2/src/core/facade/collection';
2+
import {isPresent, isFunction, StringWrapper} from 'angular2/src/core/facade/lang';
33
import {DomAdapter} from './dom_adapter';
44

55
/**
66
* Provides DOM operations in any browser environment.
77
*/
88
export class GenericBrowserDomAdapter extends DomAdapter {
9+
private _animationPrefix: string = null;
10+
private _transitionEnd: string = null;
11+
constructor() {
12+
super();
13+
try {
14+
var element = this.createElement('div', this.defaultDoc());
15+
if (isPresent(this.getStyle(element, 'animationName'))) {
16+
this._animationPrefix = '';
17+
} else {
18+
var domPrefixes = ['Webkit', 'Moz', 'O', 'ms'];
19+
for (var i = 0; i < domPrefixes.length; i++) {
20+
if (isPresent(this.getStyle(element, domPrefixes[i] + 'AnimationName'))) {
21+
this._animationPrefix = '-' + StringWrapper.toLowerCase(domPrefixes[i]) + '-';
22+
break;
23+
}
24+
}
25+
}
26+
var transEndEventNames = {
27+
WebkitTransition: 'webkitTransitionEnd',
28+
MozTransition: 'transitionend',
29+
OTransition: 'oTransitionEnd otransitionend',
30+
transition: 'transitionend'
31+
};
32+
StringMapWrapper.forEach(transEndEventNames, (value, key) => {
33+
if (isPresent(this.getStyle(element, key))) {
34+
this._transitionEnd = value;
35+
}
36+
});
37+
} catch (e) {
38+
this._animationPrefix = null;
39+
this._transitionEnd = null;
40+
}
41+
}
942
getDistributedNodes(el: HTMLElement): Node[] { return (<any>el).getDistributedNodes(); }
1043
resolveAndSetHref(el: HTMLAnchorElement, baseUrl: string, href: string) {
1144
el.href = href == null ? baseUrl : baseUrl + '/../' + href;
@@ -37,4 +70,11 @@ export class GenericBrowserDomAdapter extends DomAdapter {
3770
supportsNativeShadowDOM(): boolean {
3871
return isFunction((<any>this.defaultDoc().body).createShadowRoot);
3972
}
73+
getAnimationPrefix(): string {
74+
return isPresent(this._animationPrefix) ? this._animationPrefix : "";
75+
}
76+
getTransitionEnd(): string { return isPresent(this._transitionEnd) ? this._transitionEnd : ""; }
77+
supportsAnimation(): boolean {
78+
return isPresent(this._animationPrefix) && isPresent(this._transitionEnd);
79+
}
4080
}

modules/angular2/src/core/dom/html_adapter.dart

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -435,4 +435,16 @@ class Html5LibDomAdapter implements DomAdapter {
435435
performanceNow() {
436436
throw 'not implemented';
437437
}
438+
439+
getAnimationPrefix() {
440+
throw 'not implemented';
441+
}
442+
443+
getTransitionEnd() {
444+
throw 'not implemented';
445+
}
446+
447+
supportsAnimation() {
448+
throw 'not implemented';
449+
}
438450
}

modules/angular2/src/core/dom/parse5_adapter.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -552,6 +552,9 @@ export class Parse5DomAdapter extends DomAdapter {
552552
requestAnimationFrame(callback): number { return setTimeout(callback, 0); }
553553
cancelAnimationFrame(id: number) { clearTimeout(id); }
554554
performanceNow(): number { return DateWrapper.toMillis(DateWrapper.now()); }
555+
getAnimationPrefix(): string { return ''; }
556+
getTransitionEnd(): string { return 'transitionend'; }
557+
supportsAnimation(): boolean { return true; }
555558
}
556559

557560
// TODO: build a proper list, this one is all the keys of a HTMLInputElement

modules/angular2/test/animate/animation_builder_spec.ts

Lines changed: 25 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
1-
import {el, describe, it, expect, inject, SpyObject} from 'angular2/test_lib';
1+
import {el, describe, it, iit, expect, inject, SpyObject} from 'angular2/test_lib';
22
import {AnimationBuilder} from 'angular2/src/animate/animation_builder';
3+
import {DOM} from 'angular2/src/core/dom/dom_adapter';
34

45
export function main() {
56
describe("AnimationBuilder", () => {
@@ -54,8 +55,13 @@ export function main() {
5455
var runner = animateCss.start(element);
5556
runner.flush();
5657

57-
expect(runner.computedDelay).toBe(100);
58-
expect(runner.computedDuration).toBe(200);
58+
if (DOM.supportsAnimation()) {
59+
expect(runner.computedDelay).toBe(100);
60+
expect(runner.computedDuration).toBe(200);
61+
} else {
62+
expect(runner.computedDelay).toBe(0);
63+
expect(runner.computedDuration).toBe(0);
64+
}
5965
}));
6066

6167
it('should support from styles', inject([AnimationBuilder], animate => {
@@ -71,12 +77,18 @@ export function main() {
7177

7278
it('should support duration and delay defined in CSS', inject([AnimationBuilder], (animate) => {
7379
var animateCss = animate.css();
74-
var element = el('<div style="transition: 0.5s ease 250ms;"></div>');
80+
var element =
81+
el(`<div style="${DOM.getAnimationPrefix()}transition: 0.5s ease 250ms;"></div>`);
7582
var runner = animateCss.start(element);
7683
runner.flush();
7784

78-
expect(runner.computedDuration).toEqual(500);
79-
expect(runner.computedDelay).toEqual(250);
85+
if (DOM.supportsAnimation()) {
86+
expect(runner.computedDelay).toBe(250);
87+
expect(runner.computedDuration).toBe(500);
88+
} else {
89+
expect(runner.computedDelay).toEqual(0);
90+
expect(runner.computedDuration).toEqual(0);
91+
}
8092
}));
8193

8294
it('should add classes', inject([AnimationBuilder], (animate) => {
@@ -108,11 +120,15 @@ export function main() {
108120

109121
runner.flush();
110122

111-
expect(callback).not.toHaveBeenCalled();
123+
if (DOM.supportsAnimation()) {
124+
expect(callback).not.toHaveBeenCalled();
112125

113-
runner.handleAnimationCompleted();
126+
runner.handleAnimationCompleted();
114127

115-
expect(callback).toHaveBeenCalled();
128+
expect(callback).toHaveBeenCalled();
129+
} else {
130+
expect(callback).toHaveBeenCalled();
131+
}
116132
}));
117133

118134
});

0 commit comments

Comments
 (0)
X Tutup