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

Commit cef084a

Browse files
committed
feat(ngAnimate): provide configuration support to match specific className values to trigger animations
Closes #5357 Closes #5283
1 parent 937caab commit cef084a

File tree

3 files changed

+86
-4
lines changed

3 files changed

+86
-4
lines changed

src/ng/animate.js

+22
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,28 @@ var $AnimateProvider = ['$provide', function($provide) {
6161
$provide.factory(key, factory);
6262
};
6363

64+
/**
65+
* @ngdoc function
66+
* @name ng.$animateProvider#classNameFilter
67+
* @methodOf ng.$animateProvider
68+
*
69+
* @description
70+
* Sets and/or returns the CSS class regular expression that is checked when performing
71+
* an animation. Upon bootstrap the classNameFilter value is not set at all and will
72+
* therefore enable $animate to attempt to perform an animation on any element.
73+
* When setting the classNameFilter value, animations will only be performed on elements
74+
* that successfully match the filter expression. This in turn can boost performance
75+
* for low-powered devices as well as applications containing a lot of structural operations.
76+
* @param {RegExp=} expression The className expression which will be checked against all animations
77+
* @return {RegExp} The current CSS className expression value. If null then there is no expression value
78+
*/
79+
this.classNameFilter = function(expression) {
80+
if(arguments.length === 1) {
81+
this.$$classNameFilter = (expression instanceof RegExp) ? expression : null;
82+
}
83+
return this.$$classNameFilter;
84+
};
85+
6486
this.$get = ['$timeout', function($timeout) {
6587

6688
/**

src/ngAnimate/animate.js

+14-4
Original file line numberDiff line numberDiff line change
@@ -288,6 +288,13 @@ angular.module('ngAnimate', ['ng'])
288288
});
289289
});
290290

291+
var classNameFilter = $animateProvider.classNameFilter();
292+
var isAnimatableClassName = !classNameFilter
293+
? function() { return true; }
294+
: function(className) {
295+
return classNameFilter.test(className);
296+
};
297+
291298
function lookup(name) {
292299
if (name) {
293300
var matches = [],
@@ -569,17 +576,20 @@ angular.module('ngAnimate', ['ng'])
569576
and the onComplete callback will be fired once the animation is fully complete.
570577
*/
571578
function performAnimation(animationEvent, className, element, parentElement, afterElement, domOperation, doneCallback) {
572-
var node = extractElementNode(element);
579+
var currentClassName, classes, node = extractElementNode(element);
580+
if(node) {
581+
currentClassName = node.className;
582+
classes = currentClassName + ' ' + className;
583+
}
584+
573585
//transcluded directives may sometimes fire an animation using only comment nodes
574586
//best to catch this early on to prevent any animation operations from occurring
575-
if(!node) {
587+
if(!node || !isAnimatableClassName(classes)) {
576588
fireDOMOperation();
577589
closeAnimation();
578590
return;
579591
}
580592

581-
var currentClassName = node.className;
582-
var classes = currentClassName + ' ' + className;
583593
var animationLookup = (' ' + classes).replace(/\s+/g,'.');
584594
if (!parentElement) {
585595
parentElement = afterElement ? afterElement.parent() : element.parent();

test/ngAnimate/animateSpec.js

+50
Original file line numberDiff line numberDiff line change
@@ -2947,5 +2947,55 @@ describe("ngAnimate", function() {
29472947
expect(capturedAnimation).toBe('leave');
29482948
});
29492949
});
2950+
2951+
it('should animate only the specified CSS className', function() {
2952+
var captures = {};
2953+
module(function($animateProvider) {
2954+
$animateProvider.classNameFilter(/prefixed-animation/);
2955+
$animateProvider.register('.capture', function() {
2956+
return {
2957+
enter : buildFn('enter'),
2958+
leave : buildFn('leave')
2959+
};
2960+
2961+
function buildFn(key) {
2962+
return function(element, className, done) {
2963+
captures[key] = true;
2964+
(done || className)();
2965+
}
2966+
}
2967+
});
2968+
});
2969+
inject(function($rootScope, $compile, $rootElement, $document, $timeout, $templateCache, $sniffer, $animate) {
2970+
if(!$sniffer.transitions) return;
2971+
2972+
var element = $compile('<div class="capture"></div>')($rootScope);
2973+
$rootElement.append(element);
2974+
jqLite($document[0].body).append($rootElement);
2975+
2976+
var enterDone = false;
2977+
$animate.enter(element, $rootElement, null, function() {
2978+
enterDone = true;
2979+
});
2980+
2981+
$rootScope.$digest();
2982+
$timeout.flush();
2983+
2984+
expect(captures['enter']).toBeUndefined();
2985+
expect(enterDone).toBe(true);
2986+
2987+
element.addClass('prefixed-animation');
2988+
2989+
var leaveDone = false;
2990+
$animate.leave(element, function() {
2991+
leaveDone = true;
2992+
});
2993+
$rootScope.$digest();
2994+
$timeout.flush();
2995+
2996+
expect(captures['leave']).toBe(true);
2997+
expect(leaveDone).toBe(true);
2998+
});
2999+
});
29503000
});
29513001
});

0 commit comments

Comments
 (0)