|
| 1 | +import { ɵDomAnimationEngine as DomAnimationEngine } from "@angular/animations/browser"; |
| 2 | +import { AnimationEvent, AnimationPlayer } from "@angular/animations"; |
| 3 | + |
| 4 | +import { NgView } from "../element-registry"; |
| 5 | +import { |
| 6 | + copyArray, |
| 7 | + cssClasses, |
| 8 | + deleteFromArrayMap, |
| 9 | + eraseStylesOverride, |
| 10 | + getOrSetAsInMap, |
| 11 | + makeAnimationEvent, |
| 12 | + optimizeGroupPlayer, |
| 13 | + setStyles, |
| 14 | +} from "./dom-utils"; |
| 15 | + |
| 16 | +const MARKED_FOR_ANIMATION = "ng-animate"; |
| 17 | + |
| 18 | +interface QueuedAnimationTransitionTuple { |
| 19 | + element: NgView; |
| 20 | + player: AnimationPlayer; |
| 21 | + triggerName: string; |
| 22 | + event: AnimationEvent; |
| 23 | +} |
| 24 | + |
| 25 | +// we are extending Angular's animation engine and |
| 26 | +// overriding a few methods that work on the DOM |
| 27 | +export class NativeScriptAnimationEngine extends DomAnimationEngine { |
| 28 | + // this method is almost completely copied from |
| 29 | + // the original animation engine, just replaced |
| 30 | + // a few method invocations with overriden ones |
| 31 | + animateTransition(element: NgView, instruction: any): AnimationPlayer { |
| 32 | + const triggerName = instruction.triggerName; |
| 33 | + |
| 34 | + let previousPlayers: AnimationPlayer[]; |
| 35 | + if (instruction.isRemovalTransition) { |
| 36 | + previousPlayers = this._onRemovalTransitionOverride(element); |
| 37 | + } else { |
| 38 | + previousPlayers = []; |
| 39 | + const existingTransitions = this._getTransitionAnimation(element); |
| 40 | + const existingPlayer = existingTransitions ? existingTransitions[triggerName] : null; |
| 41 | + if (existingPlayer) { |
| 42 | + previousPlayers.push(existingPlayer); |
| 43 | + } |
| 44 | + } |
| 45 | + |
| 46 | + // it's important to do this step before destroying the players |
| 47 | + // so that the onDone callback below won"t fire before this |
| 48 | + eraseStylesOverride(element, instruction.fromStyles); |
| 49 | + |
| 50 | + // we first run this so that the previous animation player |
| 51 | + // data can be passed into the successive animation players |
| 52 | + let totalTime = 0; |
| 53 | + const players = instruction.timelines.map(timelineInstruction => { |
| 54 | + totalTime = Math.max(totalTime, timelineInstruction.totalTime); |
| 55 | + return (<any>this)._buildPlayer(element, timelineInstruction, previousPlayers); |
| 56 | + }); |
| 57 | + |
| 58 | + previousPlayers.forEach(previousPlayer => previousPlayer.destroy()); |
| 59 | + const player = optimizeGroupPlayer(players); |
| 60 | + player.onDone(() => { |
| 61 | + player.destroy(); |
| 62 | + const elmTransitionMap = this._getTransitionAnimation(element); |
| 63 | + if (elmTransitionMap) { |
| 64 | + delete elmTransitionMap[triggerName]; |
| 65 | + if (Object.keys(elmTransitionMap).length === 0) { |
| 66 | + (<any>this)._activeTransitionAnimations.delete(element); |
| 67 | + } |
| 68 | + } |
| 69 | + deleteFromArrayMap((<any>this)._activeElementAnimations, element, player); |
| 70 | + setStyles(element, instruction.toStyles); |
| 71 | + }); |
| 72 | + |
| 73 | + const elmTransitionMap = getOrSetAsInMap((<any>this)._activeTransitionAnimations, element, {}); |
| 74 | + elmTransitionMap[triggerName] = player; |
| 75 | + |
| 76 | + this._queuePlayerOverride( |
| 77 | + element, triggerName, player, |
| 78 | + makeAnimationEvent( |
| 79 | + element, triggerName, instruction.fromState, instruction.toState, |
| 80 | + null, // this will be filled in during event creation |
| 81 | + totalTime)); |
| 82 | + |
| 83 | + return player; |
| 84 | + } |
| 85 | + |
| 86 | + // overriden to use eachChild method of View |
| 87 | + // instead of DOM querySelectorAll |
| 88 | + private _onRemovalTransitionOverride(element: NgView): AnimationPlayer[] { |
| 89 | + // when a parent animation is set to trigger a removal we want to |
| 90 | + // find all of the children that are currently animating and clear |
| 91 | + // them out by destroying each of them. |
| 92 | + let elms = []; |
| 93 | + element._eachLayoutView(child => { |
| 94 | + if (cssClasses(<NgView>child).get(MARKED_FOR_ANIMATION)) { |
| 95 | + elms.push(child); |
| 96 | + } |
| 97 | + |
| 98 | + return true; |
| 99 | + }); |
| 100 | + |
| 101 | + for (let i = 0; i < elms.length; i++) { |
| 102 | + const elm = elms[i]; |
| 103 | + const activePlayers = this._getElementAnimation(elm); |
| 104 | + if (activePlayers) { |
| 105 | + activePlayers.forEach(player => player.destroy()); |
| 106 | + } |
| 107 | + |
| 108 | + const activeTransitions = this._getTransitionAnimation(elm); |
| 109 | + if (activeTransitions) { |
| 110 | + Object.keys(activeTransitions).forEach(triggerName => { |
| 111 | + const player = activeTransitions[triggerName]; |
| 112 | + if (player) { |
| 113 | + player.destroy(); |
| 114 | + } |
| 115 | + }); |
| 116 | + } |
| 117 | + } |
| 118 | + |
| 119 | + // we make a copy of the array because the actual source array is modified |
| 120 | + // each time a player is finished/destroyed (the forEach loop would fail otherwise) |
| 121 | + return copyArray(this._getElementAnimation(element)); |
| 122 | + } |
| 123 | + |
| 124 | + // overriden to use cssClasses method to access native element's styles |
| 125 | + // instead of DOM element's classList |
| 126 | + private _queuePlayerOverride( |
| 127 | + element: NgView, triggerName: string, player: AnimationPlayer, event: AnimationEvent) { |
| 128 | + const tuple = <QueuedAnimationTransitionTuple>{ element, player, triggerName, event }; |
| 129 | + (<any>this)._queuedTransitionAnimations.push(tuple); |
| 130 | + player.init(); |
| 131 | + |
| 132 | + cssClasses(element).set(MARKED_FOR_ANIMATION, true); |
| 133 | + player.onDone(() => cssClasses(element).set(MARKED_FOR_ANIMATION, false)); |
| 134 | + } |
| 135 | + |
| 136 | + private _getElementAnimation(element: NgView) { |
| 137 | + return (<any>this)._activeElementAnimations.get(element); |
| 138 | + } |
| 139 | + |
| 140 | + private _getTransitionAnimation(element: NgView) { |
| 141 | + return (<any>this)._activeTransitionAnimations.get(element); |
| 142 | + } |
| 143 | +} |
0 commit comments