Skip to content
This repository was archived by the owner on Apr 12, 2024. It is now read-only.

Commit 310bff5

Browse files
committed
feat($animateCss): add support for temporary styles via cleanupStyles
Some animations make use of the `from` and `to` styling only for the lifetime of the animation. This patch allows for those styles to be removed once the animation is closed automatically within `$animateCss`.
1 parent b3a3c6a commit 310bff5

File tree

4 files changed

+134
-2
lines changed

4 files changed

+134
-2
lines changed

src/ng/animateCss.js

+7
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,13 @@ var $CoreAnimateCssProvider = function() {
4343
};
4444

4545
return function(element, options) {
46+
// there is no point in applying the styles since
47+
// there is no animation that goes on at all in
48+
// this version of $animateCss.
49+
if (options.cleanupStyles) {
50+
options.from = options.to = null;
51+
}
52+
4653
if (options.from) {
4754
element.css(options.from);
4855
options.from = null;

src/ngAnimate/animateCss.js

+30-2
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,7 @@ var ANIMATE_TIMER_KEY = '$$animateCss';
204204
* * `staggerIndex` - The numeric index representing the stagger item (e.g. a value of 5 is equal to the sixth item in the stagger; therefore when a
205205
* * `stagger` option value of `0.1` is used then there will be a stagger delay of `600ms`)
206206
* * `applyClassesEarly` - Whether or not the classes being added or removed will be used when detecting the animation. This is set by `$animate` when enter/leave/move animations are fired to ensure that the CSS classes are resolved in time. (Note that this will prevent any transitions from occuring on the classes being added and removed.)
207+
* * `cleanupStyles` - Whether or not the provided `from` and `to` styles will be removed once the animation is closed. This is useful for when the styles are used purely for the sake of the animation and do not have a lasting visual effect on the element (e.g. a colapse and open animation). By default this value is set to `false`.
207208
*
208209
* @return {object} an object with start and end methods and details about the animation.
209210
*
@@ -324,6 +325,15 @@ function createLocalCacheLookup() {
324325
};
325326
}
326327

328+
function registerRestorableStyles(backup, node, properties) {
329+
forEach(properties, function(prop) {
330+
// we do not reassign an already present style value since
331+
// if we detect the style property value again we may be
332+
// detecting styles that were added via the `from` styles.
333+
backup[prop] = backup[prop] || node.style.getPropertyValue(prop);
334+
});
335+
}
336+
327337
var $AnimateCssProvider = ['$animateProvider', function($animateProvider) {
328338
var gcsLookup = createLocalCacheLookup();
329339
var gcsStaggerLookup = createLocalCacheLookup();
@@ -424,6 +434,7 @@ var $AnimateCssProvider = ['$animateProvider', function($animateProvider) {
424434
}
425435

426436
return function init(element, options) {
437+
var restoreStyles = {};
427438
var node = getDomNode(element);
428439
if (!node
429440
|| !node.parentNode
@@ -625,7 +636,12 @@ var $AnimateCssProvider = ['$animateProvider', function($animateProvider) {
625636
stagger.animationDuration === 0;
626637
}
627638

628-
applyAnimationFromStyles(element, options);
639+
if (options.from) {
640+
if (options.cleanupStyles) {
641+
registerRestorableStyles(restoreStyles, node, Object.keys(options.from));
642+
}
643+
applyAnimationFromStyles(element, options);
644+
}
629645

630646
if (flags.blockTransition || flags.blockKeyframeAnimation) {
631647
applyBlocking(maxDuration);
@@ -692,6 +708,13 @@ var $AnimateCssProvider = ['$animateProvider', function($animateProvider) {
692708
applyAnimationClasses(element, options);
693709
applyAnimationStyles(element, options);
694710

711+
if (Object.keys(restoreStyles).length) {
712+
forEach(restoreStyles, function(value, prop) {
713+
value ? node.style.setProperty(prop, value)
714+
: node.style.removeProperty(prop);
715+
});
716+
}
717+
695718
// the reason why we have this option is to allow a synchronous closing callback
696719
// that is fired as SOON as the animation ends (when the CSS is removed) or if
697720
// the animation never takes off at all. A good example is a leave animation since
@@ -886,7 +909,12 @@ var $AnimateCssProvider = ['$animateProvider', function($animateProvider) {
886909
}
887910

888911
element.on(events.join(' '), onAnimationProgress);
889-
applyAnimationToStyles(element, options);
912+
if (options.to) {
913+
if (options.cleanupStyles) {
914+
registerRestorableStyles(restoreStyles, node, Object.keys(options.to));
915+
}
916+
applyAnimationToStyles(element, options);
917+
}
890918
}
891919

892920
function onAnimationExpired() {

test/ng/animateCssSpec.js

+32
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,38 @@ describe("$animateCss", function() {
115115
expect(cancelSpy).toHaveBeenCalled();
116116
expect(doneSpy).not.toHaveBeenCalled();
117117
}));
118+
119+
it("should not bother applying the provided [from] and [to] styles to the element if [cleanupStyles] is present",
120+
inject(function($animateCss, $rootScope) {
121+
122+
var animator = $animateCss(element, {
123+
cleanupStyles: true,
124+
from: { width: '100px' },
125+
to: { width: '900px', height: '1000px' },
126+
});
127+
128+
assertStyleIsEmpty(element, 'width');
129+
assertStyleIsEmpty(element, 'height');
130+
131+
var runner = animator.start();
132+
133+
assertStyleIsEmpty(element, 'width');
134+
assertStyleIsEmpty(element, 'height');
135+
136+
triggerRAF();
137+
138+
assertStyleIsEmpty(element, 'width');
139+
assertStyleIsEmpty(element, 'height');
140+
141+
runner.end();
142+
143+
assertStyleIsEmpty(element, 'width');
144+
assertStyleIsEmpty(element, 'height');
145+
146+
function assertStyleIsEmpty(element, prop) {
147+
expect(element[0].style.getPropertyValue(prop)).toBeFalsy();
148+
}
149+
}));
118150
});
119151

120152
});

test/ngAnimate/animateCssSpec.js

+65
Original file line numberDiff line numberDiff line change
@@ -2786,6 +2786,71 @@ describe("ngAnimate $animateCss", function() {
27862786
}));
27872787
});
27882788

2789+
describe("[cleanupStyles]", function() {
2790+
it("should cleanup [from] and [to] styles that have been applied for the animation when true",
2791+
inject(function($animateCss) {
2792+
2793+
var runner = $animateCss(element, {
2794+
duration: 1,
2795+
from: { background: 'gold' },
2796+
to: { color: 'brown' },
2797+
cleanupStyles: true
2798+
}).start();
2799+
2800+
assertStyleIsPresent(element, 'background', true);
2801+
assertStyleIsPresent(element, 'color', false);
2802+
2803+
triggerAnimationStartFrame();
2804+
2805+
assertStyleIsPresent(element, 'background', true);
2806+
assertStyleIsPresent(element, 'color', true);
2807+
2808+
runner.end();
2809+
2810+
assertStyleIsPresent(element, 'background', false);
2811+
assertStyleIsPresent(element, 'color', false);
2812+
2813+
function assertStyleIsPresent(element, style, bool) {
2814+
expect(element[0].style[style])[bool ? 'toBeTruthy' : 'toBeFalsy']();
2815+
}
2816+
}));
2817+
2818+
it("should restore existing overidden styles already on present on the element when true",
2819+
inject(function($animateCss) {
2820+
2821+
element.css("height", "100px");
2822+
element.css("width", "111px");
2823+
2824+
var runner = $animateCss(element, {
2825+
duration: 1,
2826+
from: { height: '200px', 'font-size':'66px' },
2827+
to: { height: '300px', width: '222px' },
2828+
cleanupStyles: true
2829+
}).start();
2830+
2831+
assertStyle(element, 'height', "200px");
2832+
assertStyle(element, 'font-size', "66px");
2833+
assertStyle(element, 'width', "111px");
2834+
2835+
triggerAnimationStartFrame();
2836+
2837+
assertStyle(element, 'height', "300px");
2838+
assertStyle(element, 'width', "222px");
2839+
assertStyle(element, 'font-size', "66px");
2840+
2841+
runner.end();
2842+
2843+
assertStyle(element, 'width', "111px");
2844+
assertStyle(element, 'height', "100px");
2845+
2846+
expect(element[0].style.getPropertyValue("font-size")).not.toBe("66px");
2847+
2848+
function assertStyle(element, prop, value) {
2849+
expect(element[0].style.getPropertyValue(prop)).toBe(value);
2850+
}
2851+
}));
2852+
});
2853+
27892854
it('should round up long elapsedTime values to close off a CSS3 animation',
27902855
inject(function($animateCss) {
27912856

0 commit comments

Comments
 (0)