fix(animations): only use the WA-polyfill alongside AnimationBuilder (#22143)
This patch removes the need to include the Web Animations API Polyfill (web-animations-js) as a dependency. Angular will now fallback to using CSS Keyframes in the event that `element.animate` is no longer supported by the browser. In the event that an application does use `AnimationBuilder` then the web-animations-js polyfill is required to enable programmatic, position-based access to an animation. Closes #17496 PR Close #22143
This commit is contained in:

committed by
Victor Berchet

parent
9eecb0b27f
commit
b2f366b3b7
@ -7,12 +7,17 @@
|
||||
*/
|
||||
import {AnimationPlayer, ɵStyleData} from '@angular/animations';
|
||||
|
||||
import {allowPreviousPlayerStylesMerge, balancePreviousStylesIntoKeyframes, copyStyles} from '../../util';
|
||||
import {AnimationDriver} from '../animation_driver';
|
||||
import {CssKeyframesDriver} from '../css_keyframes/css_keyframes_driver';
|
||||
import {containsElement, invokeQuery, matchesElement, validateStyleProperty} from '../shared';
|
||||
|
||||
import {WebAnimationsPlayer} from './web_animations_player';
|
||||
|
||||
export class WebAnimationsDriver implements AnimationDriver {
|
||||
private _isNativeImpl = /\{\s*\[native\s+code\]\s*\}/.test(getElementAnimateFn().toString());
|
||||
private _cssKeyframesDriver = new CssKeyframesDriver();
|
||||
|
||||
validateStyleProperty(prop: string): boolean { return validateStyleProperty(prop); }
|
||||
|
||||
matchesElement(element: any, selector: string): boolean {
|
||||
@ -29,24 +34,46 @@ export class WebAnimationsDriver implements AnimationDriver {
|
||||
return (window.getComputedStyle(element) as any)[prop] as string;
|
||||
}
|
||||
|
||||
overrideWebAnimationsSupport(supported: boolean) { this._isNativeImpl = supported; }
|
||||
|
||||
animate(
|
||||
element: any, keyframes: ɵStyleData[], duration: number, delay: number, easing: string,
|
||||
previousPlayers: AnimationPlayer[] = []): WebAnimationsPlayer {
|
||||
previousPlayers: AnimationPlayer[] = [], scrubberAccessRequested?: boolean): AnimationPlayer {
|
||||
const useKeyframes = !scrubberAccessRequested && !this._isNativeImpl;
|
||||
if (useKeyframes) {
|
||||
return this._cssKeyframesDriver.animate(
|
||||
element, keyframes, duration, delay, easing, previousPlayers);
|
||||
}
|
||||
|
||||
const fill = delay == 0 ? 'both' : 'forwards';
|
||||
const playerOptions: {[key: string]: string | number} = {duration, delay, fill};
|
||||
|
||||
// we check for this to avoid having a null|undefined value be present
|
||||
// for the easing (which results in an error for certain browsers #9752)
|
||||
if (easing) {
|
||||
playerOptions['easing'] = easing;
|
||||
}
|
||||
|
||||
const previousStyles: {[key: string]: any} = {};
|
||||
const previousWebAnimationPlayers = <WebAnimationsPlayer[]>previousPlayers.filter(
|
||||
player => { return player instanceof WebAnimationsPlayer; });
|
||||
return new WebAnimationsPlayer(element, keyframes, playerOptions, previousWebAnimationPlayers);
|
||||
player => player instanceof WebAnimationsPlayer);
|
||||
|
||||
if (allowPreviousPlayerStylesMerge(duration, delay)) {
|
||||
previousWebAnimationPlayers.forEach(player => {
|
||||
let styles = player.currentSnapshot;
|
||||
Object.keys(styles).forEach(prop => previousStyles[prop] = styles[prop]);
|
||||
});
|
||||
}
|
||||
|
||||
keyframes = keyframes.map(styles => copyStyles(styles, false));
|
||||
keyframes = balancePreviousStylesIntoKeyframes(element, keyframes, previousStyles);
|
||||
return new WebAnimationsPlayer(element, keyframes, playerOptions);
|
||||
}
|
||||
}
|
||||
|
||||
export function supportsWebAnimations() {
|
||||
return typeof Element !== 'undefined' && typeof(<any>Element).prototype['animate'] === 'function';
|
||||
return typeof getElementAnimateFn() === 'function';
|
||||
}
|
||||
|
||||
function getElementAnimateFn(): any {
|
||||
return (typeof Element !== 'undefined' && (<any>Element).prototype['animate']) || {};
|
||||
}
|
||||
|
@ -7,7 +7,7 @@
|
||||
*/
|
||||
import {AnimationPlayer} from '@angular/animations';
|
||||
|
||||
import {allowPreviousPlayerStylesMerge, copyStyles} from '../../util';
|
||||
import {allowPreviousPlayerStylesMerge, balancePreviousStylesIntoKeyframes, computeStyle, copyStyles} from '../../util';
|
||||
|
||||
import {DOMAnimation} from './dom_animation';
|
||||
|
||||
@ -27,23 +27,14 @@ export class WebAnimationsPlayer implements AnimationPlayer {
|
||||
public time = 0;
|
||||
|
||||
public parentPlayer: AnimationPlayer|null = null;
|
||||
public previousStyles: {[styleName: string]: string | number} = {};
|
||||
public currentSnapshot: {[styleName: string]: string | number} = {};
|
||||
|
||||
constructor(
|
||||
public element: any, public keyframes: {[key: string]: string | number}[],
|
||||
public options: {[key: string]: string | number},
|
||||
private previousPlayers: WebAnimationsPlayer[] = []) {
|
||||
public options: {[key: string]: string | number}) {
|
||||
this._duration = <number>options['duration'];
|
||||
this._delay = <number>options['delay'] || 0;
|
||||
this.time = this._duration + this._delay;
|
||||
|
||||
if (allowPreviousPlayerStylesMerge(this._duration, this._delay)) {
|
||||
previousPlayers.forEach(player => {
|
||||
let styles = player.currentSnapshot;
|
||||
Object.keys(styles).forEach(prop => this.previousStyles[prop] = styles[prop]);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private _onFinish() {
|
||||
@ -63,30 +54,7 @@ export class WebAnimationsPlayer implements AnimationPlayer {
|
||||
if (this._initialized) return;
|
||||
this._initialized = true;
|
||||
|
||||
const keyframes = this.keyframes.map(styles => copyStyles(styles, false));
|
||||
const previousStyleProps = Object.keys(this.previousStyles);
|
||||
if (previousStyleProps.length && keyframes.length) {
|
||||
let startingKeyframe = keyframes[0];
|
||||
let missingStyleProps: string[] = [];
|
||||
previousStyleProps.forEach(prop => {
|
||||
if (!startingKeyframe.hasOwnProperty(prop)) {
|
||||
missingStyleProps.push(prop);
|
||||
}
|
||||
startingKeyframe[prop] = this.previousStyles[prop];
|
||||
});
|
||||
|
||||
if (missingStyleProps.length) {
|
||||
const self = this;
|
||||
// tslint:disable-next-line
|
||||
for (var i = 1; i < keyframes.length; i++) {
|
||||
let kf = keyframes[i];
|
||||
missingStyleProps.forEach(function(prop) {
|
||||
kf[prop] = _computeStyle(self.element, prop);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const keyframes = this.keyframes;
|
||||
(this as{domPlayer: DOMAnimation}).domPlayer =
|
||||
this._triggerWebAnimation(this.element, keyframes, this.options);
|
||||
this._finalKeyframe = keyframes.length ? keyframes[keyframes.length - 1] : {};
|
||||
@ -178,7 +146,7 @@ export class WebAnimationsPlayer implements AnimationPlayer {
|
||||
Object.keys(this._finalKeyframe).forEach(prop => {
|
||||
if (prop != 'offset') {
|
||||
styles[prop] =
|
||||
this._finished ? this._finalKeyframe[prop] : _computeStyle(this.element, prop);
|
||||
this._finished ? this._finalKeyframe[prop] : computeStyle(this.element, prop);
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -192,7 +160,3 @@ export class WebAnimationsPlayer implements AnimationPlayer {
|
||||
methods.length = 0;
|
||||
}
|
||||
}
|
||||
|
||||
function _computeStyle(element: any, prop: string): string {
|
||||
return (<any>window.getComputedStyle(element))[prop];
|
||||
}
|
||||
|
Reference in New Issue
Block a user