| OLD | NEW |
| (Empty) |
| 1 part of angular.animate; | |
| 2 | |
| 3 /** | |
| 4 * The optimizer tracks elements and running animations. It's used to control | |
| 5 * and optionally skip certain animations that are deemed "expensive" such as | |
| 6 * running animations on child elements while the dom parent is also running an | |
| 7 * animation. | |
| 8 */ | |
| 9 @Injectable() | |
| 10 class AnimationOptimizer { | |
| 11 final Map<dom.Element, Set<Animation>> _elements = new Map<dom.Element, | |
| 12 Set<Animation>>(); | |
| 13 final Map<Animation, dom.Element> _animations = new Map<Animation, | |
| 14 dom.Element>(); | |
| 15 | |
| 16 final Map<dom.Node, bool> _alwaysAnimate = new Map<dom.Node, bool>(); | |
| 17 final Map<dom.Node, bool> _alwaysAnimateChildren = new Map<dom.Node, bool>(); | |
| 18 | |
| 19 Expando _expando; | |
| 20 | |
| 21 AnimationOptimizer(this._expando); | |
| 22 | |
| 23 /** | |
| 24 * Track an animation that is running against a dom element. Usually, this | |
| 25 * should occur when an animation starts. | |
| 26 */ | |
| 27 void track(Animation animation, dom.Element forElement) { | |
| 28 if (forElement != null) { | |
| 29 var animations = _elements.putIfAbsent(forElement, () => | |
| 30 new Set<Animation>()); | |
| 31 animations.add(animation); | |
| 32 _animations[animation] = forElement; | |
| 33 } | |
| 34 } | |
| 35 | |
| 36 /** | |
| 37 * Stop tracking an animation. If it's the last tracked animation on an | |
| 38 * element forget about that element as well. | |
| 39 */ | |
| 40 void forget(Animation animation) { | |
| 41 var element = _animations.remove(animation); | |
| 42 if (element != null) { | |
| 43 var animationsOnElement = _elements[element]; | |
| 44 animationsOnElement.remove(animation); | |
| 45 // It may be more efficient just to keep sets around even after | |
| 46 // animations complete. | |
| 47 if (animationsOnElement.length == 0) { | |
| 48 _elements.remove(element); | |
| 49 } | |
| 50 } | |
| 51 } | |
| 52 | |
| 53 /** | |
| 54 * Since we can't overload forget... | |
| 55 */ | |
| 56 void detachAlwaysAnimateOptions(dom.Element element) { | |
| 57 _alwaysAnimate.remove(element); | |
| 58 _alwaysAnimateChildren.remove(element); | |
| 59 } | |
| 60 | |
| 61 /** | |
| 62 * Control animation for a specific element, ignoring every other option. | |
| 63 * [mode] "always" will always animate this element. | |
| 64 * [mode] "never" will never animate this element. | |
| 65 * [mode] "auto" will detect if a parent animation is running or has child a
nimations set. | |
| 66 */ | |
| 67 void alwaysAnimate(dom.Element element, String mode) { | |
| 68 if (mode == "always") { | |
| 69 _alwaysAnimate[element] = true; | |
| 70 } else if (mode == "never") { | |
| 71 _alwaysAnimate[element] = false; | |
| 72 } else if (mode == "auto") { | |
| 73 _alwaysAnimate.remove(element); | |
| 74 } | |
| 75 } | |
| 76 | |
| 77 /** | |
| 78 * Control animation for child elements, ignoring running animations unless 'a
uto' is provided as an option. | |
| 79 * [mode] "always" will always animate children, unless it is specifically m
arked not to by [alwaysAnimate]. | |
| 80 * [mode] "never" will never animate children. | |
| 81 * [mode] "auto" will detect if a parent animation is running or has child a
nimations set. | |
| 82 */ | |
| 83 void alwaysAnimateChildren(dom.Element element, String mode) { | |
| 84 if (mode == "always") { | |
| 85 _alwaysAnimateChildren[element] = true; | |
| 86 } else if (mode == "never") { | |
| 87 _alwaysAnimateChildren[element] = false; | |
| 88 } else if (mode == "auto") { | |
| 89 _alwaysAnimateChildren.remove(element); | |
| 90 } | |
| 91 } | |
| 92 | |
| 93 /** | |
| 94 * Returns true if there is tracked animation on the given element. | |
| 95 */ | |
| 96 bool _isAnimating(dom.Element element) { | |
| 97 return _elements.containsKey(element); | |
| 98 } | |
| 99 | |
| 100 /** | |
| 101 * Given all the information this optimizer knows about currently executing | |
| 102 * animations, return [true] if this element can be animated in an ideal case | |
| 103 * and [false] if the optimizer thinks that it should not execute. | |
| 104 */ | |
| 105 bool shouldAnimate(dom.Node node) { | |
| 106 bool alwaysAnimate = _alwaysAnimate[node]; | |
| 107 if (alwaysAnimate != null) { | |
| 108 return alwaysAnimate; | |
| 109 } | |
| 110 | |
| 111 // If there are 'always allow' or 'always prevent' animations declared, | |
| 112 // fallback to the automatic detection of running parent animations. By | |
| 113 // default, we assume that we can run. | |
| 114 bool autoDecision = true; | |
| 115 | |
| 116 node = node.parentNode; | |
| 117 while (node != null) { | |
| 118 // Does this node give us animation information about our children? | |
| 119 alwaysAnimate = _alwaysAnimateChildren[node]; | |
| 120 if (alwaysAnimate != null) { | |
| 121 return alwaysAnimate; | |
| 122 } | |
| 123 | |
| 124 // If we hit a running parent animation, we still need to continue up | |
| 125 // the dom tree to see if there is or is not an 'alwaysAnimateChildren' | |
| 126 // decision somewhere. | |
| 127 if (autoDecision | |
| 128 && node.nodeType == dom.Node.ELEMENT_NODE | |
| 129 && _isAnimating(node)) { | |
| 130 // If there is an already running animation, don't animate. | |
| 131 autoDecision = false; | |
| 132 } | |
| 133 | |
| 134 // If we hit a null parent, try to break out of shadow dom. | |
| 135 if (node.parentNode == null) { | |
| 136 var probe = _findElementProbe(node); | |
| 137 if (probe != null && probe.parent != null) { | |
| 138 // Escape shadow dom! | |
| 139 node = probe.parent.element; | |
| 140 } else { | |
| 141 // If we can't go any further, return the auto decision because we | |
| 142 // havent hit any other more important optimizations. | |
| 143 return autoDecision; | |
| 144 } | |
| 145 } else { | |
| 146 node = node.parentNode; | |
| 147 } | |
| 148 } | |
| 149 | |
| 150 return autoDecision; | |
| 151 } | |
| 152 | |
| 153 // Search and find the element probe for a given node. | |
| 154 ElementProbe _findElementProbe(dom.Node node) { | |
| 155 while (node != null) { | |
| 156 if (_expando[node] != null) { | |
| 157 return _expando[node]; | |
| 158 } | |
| 159 node = node.parentNode; | |
| 160 } | |
| 161 return null; | |
| 162 } | |
| 163 } | |
| OLD | NEW |