mirror of
https://github.com/Hopiu/angular.js.git
synced 2026-05-26 06:43:43 +00:00
fix($animate): only execute a timeout when transitions or keyframe animations are used
ngAnimate causes a 1ms flicker on the screen when no CSS animations are present on the element. The solution is to change $animate to only use $timeouts when a duration is found on the element before the transition/keyframe animation takes over. Closes #3613
This commit is contained in:
parent
fb3a7db080
commit
ee2f3d21da
3 changed files with 371 additions and 338 deletions
|
|
@ -118,14 +118,12 @@ describe('Docs Annotations', function() {
|
||||||
expect(foldout.html()).toContain('loading');
|
expect(foldout.html()).toContain('loading');
|
||||||
}));
|
}));
|
||||||
|
|
||||||
it('should download a foldout HTML page and animate the contents', inject(function($httpBackend, $timeout) {
|
it('should download a foldout HTML page and animate the contents', inject(function($httpBackend, $timeout, $sniffer) {
|
||||||
$httpBackend.expect('GET', url).respond('hello');
|
$httpBackend.expect('GET', url).respond('hello');
|
||||||
|
|
||||||
element.triggerHandler('click');
|
element.triggerHandler('click');
|
||||||
$httpBackend.flush();
|
$httpBackend.flush();
|
||||||
|
|
||||||
$timeout.flushNext(0);
|
|
||||||
$timeout.flushNext(1);
|
|
||||||
$timeout.flushNext(0);
|
$timeout.flushNext(0);
|
||||||
$timeout.flushNext(1000);
|
$timeout.flushNext(1000);
|
||||||
|
|
||||||
|
|
@ -134,27 +132,22 @@ describe('Docs Annotations', function() {
|
||||||
expect(foldout.text()).toContain('hello');
|
expect(foldout.text()).toContain('hello');
|
||||||
}));
|
}));
|
||||||
|
|
||||||
it('should hide then show when clicked again', inject(function($httpBackend, $timeout) {
|
it('should hide then show when clicked again', inject(function($httpBackend, $timeout, $sniffer) {
|
||||||
$httpBackend.expect('GET', url).respond('hello');
|
$httpBackend.expect('GET', url).respond('hello');
|
||||||
|
|
||||||
//enter
|
//enter
|
||||||
element.triggerHandler('click');
|
element.triggerHandler('click');
|
||||||
$httpBackend.flush();
|
$httpBackend.flush();
|
||||||
$timeout.flushNext(0);
|
$timeout.flushNext(0);
|
||||||
$timeout.flushNext(1);
|
|
||||||
$timeout.flushNext(0);
|
|
||||||
$timeout.flushNext(1000);
|
$timeout.flushNext(1000);
|
||||||
|
|
||||||
//hide
|
//hide
|
||||||
element.triggerHandler('click');
|
element.triggerHandler('click');
|
||||||
$timeout.flushNext(1);
|
|
||||||
$timeout.flushNext(0);
|
$timeout.flushNext(0);
|
||||||
$timeout.flushNext(200);
|
$timeout.flushNext(200);
|
||||||
$timeout.flushNext(0);
|
|
||||||
|
|
||||||
//show
|
//show
|
||||||
element.triggerHandler('click');
|
element.triggerHandler('click');
|
||||||
$timeout.flushNext(1);
|
|
||||||
$timeout.flushNext(0);
|
$timeout.flushNext(0);
|
||||||
$timeout.flushNext(500);
|
$timeout.flushNext(500);
|
||||||
$timeout.flushNext(0);
|
$timeout.flushNext(0);
|
||||||
|
|
|
||||||
|
|
@ -282,7 +282,9 @@ angular.module('ngAnimate', ['ng'])
|
||||||
*/
|
*/
|
||||||
enter : function(element, parent, after, done) {
|
enter : function(element, parent, after, done) {
|
||||||
$delegate.enter(element, parent, after);
|
$delegate.enter(element, parent, after);
|
||||||
performAnimation('enter', 'ng-enter', element, parent, after, done);
|
performAnimation('enter', 'ng-enter', element, parent, after, function() {
|
||||||
|
$timeout(done || noop, 0, false);
|
||||||
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -350,7 +352,9 @@ angular.module('ngAnimate', ['ng'])
|
||||||
*/
|
*/
|
||||||
move : function(element, parent, after, done) {
|
move : function(element, parent, after, done) {
|
||||||
$delegate.move(element, parent, after);
|
$delegate.move(element, parent, after);
|
||||||
performAnimation('move', 'ng-move', element, null, null, done);
|
performAnimation('move', 'ng-move', element, null, null, function() {
|
||||||
|
$timeout(done || noop, 0, false);
|
||||||
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -361,7 +365,8 @@ angular.module('ngAnimate', ['ng'])
|
||||||
* @description
|
* @description
|
||||||
* Triggers a custom animation event based off the className variable and then attaches the className value to the element as a CSS class.
|
* Triggers a custom animation event based off the className variable and then attaches the className value to the element as a CSS class.
|
||||||
* Unlike the other animation methods, the animate service will suffix the className value with {@type -add} in order to provide
|
* Unlike the other animation methods, the animate service will suffix the className value with {@type -add} in order to provide
|
||||||
* the animate service the setup and active CSS classes in order to trigger the animation.
|
* the animate service the setup and active CSS classes in order to trigger the animation (this will be skipped if no CSS transitions
|
||||||
|
* or keyframes are defined on the -add CSS class).
|
||||||
*
|
*
|
||||||
* Below is a breakdown of each step that occurs during addClass animation:
|
* Below is a breakdown of each step that occurs during addClass animation:
|
||||||
*
|
*
|
||||||
|
|
@ -395,7 +400,8 @@ angular.module('ngAnimate', ['ng'])
|
||||||
* @description
|
* @description
|
||||||
* Triggers a custom animation event based off the className variable and then removes the CSS class provided by the className value
|
* Triggers a custom animation event based off the className variable and then removes the CSS class provided by the className value
|
||||||
* from the element. Unlike the other animation methods, the animate service will suffix the className value with {@type -remove} in
|
* from the element. Unlike the other animation methods, the animate service will suffix the className value with {@type -remove} in
|
||||||
* order to provide the animate service the setup and active CSS classes in order to trigger the animation.
|
* order to provide the animate service the setup and active CSS classes in order to trigger the animation (this will be skipped if
|
||||||
|
* no CSS transitions or keyframes are defined on the -remove CSS class).
|
||||||
*
|
*
|
||||||
* Below is a breakdown of each step that occurs during removeClass animation:
|
* Below is a breakdown of each step that occurs during removeClass animation:
|
||||||
*
|
*
|
||||||
|
|
@ -546,33 +552,9 @@ angular.module('ngAnimate', ['ng'])
|
||||||
function animate(element, className, done) {
|
function animate(element, className, done) {
|
||||||
if (!($sniffer.transitions || $sniffer.animations)) {
|
if (!($sniffer.transitions || $sniffer.animations)) {
|
||||||
done();
|
done();
|
||||||
} else {
|
return;
|
||||||
var activeClassName = '';
|
|
||||||
$timeout(startAnimation, 1, false);
|
|
||||||
|
|
||||||
//this acts as the cancellation function in case
|
|
||||||
//a new animation is triggered while another animation
|
|
||||||
//is still going on (otherwise the active className
|
|
||||||
//would still hang around until the timer is complete).
|
|
||||||
return onEnd;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseMaxTime(str) {
|
|
||||||
var total = 0, values = angular.isString(str) ? str.split(/\s*,\s*/) : [];
|
|
||||||
forEach(values, function(value) {
|
|
||||||
total = Math.max(parseFloat(value) || 0, total);
|
|
||||||
});
|
|
||||||
return total;
|
|
||||||
}
|
|
||||||
|
|
||||||
function startAnimation() {
|
|
||||||
var duration = 0;
|
|
||||||
forEach(className.split(' '), function(klass, i) {
|
|
||||||
activeClassName += (i > 0 ? ' ' : '') + klass + '-active';
|
|
||||||
});
|
|
||||||
|
|
||||||
element.addClass(activeClassName);
|
|
||||||
|
|
||||||
//one day all browsers will have these properties
|
//one day all browsers will have these properties
|
||||||
var w3cAnimationProp = 'animation';
|
var w3cAnimationProp = 'animation';
|
||||||
var w3cTransitionProp = 'transition';
|
var w3cTransitionProp = 'transition';
|
||||||
|
|
@ -586,7 +568,7 @@ angular.module('ngAnimate', ['ng'])
|
||||||
animationIterationCountKey = 'IterationCount';
|
animationIterationCountKey = 'IterationCount';
|
||||||
|
|
||||||
//we want all the styles defined before and after
|
//we want all the styles defined before and after
|
||||||
var ELEMENT_NODE = 1;
|
var duration = 0, ELEMENT_NODE = 1;
|
||||||
forEach(element, function(element) {
|
forEach(element, function(element) {
|
||||||
if (element.nodeType == ELEMENT_NODE) {
|
if (element.nodeType == ELEMENT_NODE) {
|
||||||
var elementStyles = $window.getComputedStyle(element) || {};
|
var elementStyles = $window.getComputedStyle(element) || {};
|
||||||
|
|
@ -615,13 +597,24 @@ angular.module('ngAnimate', ['ng'])
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/* there is no point in performing a reflow if the animation
|
||||||
|
timeout is empty (this would cause a flicker bug normally
|
||||||
|
in the page */
|
||||||
|
if(duration > 0) {
|
||||||
|
var activeClassName = '';
|
||||||
|
forEach(className.split(' '), function(klass, i) {
|
||||||
|
activeClassName += (i > 0 ? ' ' : '') + klass + '-active';
|
||||||
|
});
|
||||||
|
|
||||||
|
$timeout(function() {
|
||||||
|
element.addClass(activeClassName);
|
||||||
$timeout(done, duration * 1000, false);
|
$timeout(done, duration * 1000, false);
|
||||||
}
|
},0,false);
|
||||||
|
|
||||||
//this will automatically be called by $animate so
|
//this will automatically be called by $animate so
|
||||||
//there is no need to attach this internally to the
|
//there is no need to attach this internally to the
|
||||||
//timeout done method
|
//timeout done method
|
||||||
function onEnd(cancelled) {
|
return function onEnd(cancelled) {
|
||||||
element.removeClass(activeClassName);
|
element.removeClass(activeClassName);
|
||||||
|
|
||||||
//only when the animation is cancelled is the done()
|
//only when the animation is cancelled is the done()
|
||||||
|
|
@ -632,6 +625,18 @@ angular.module('ngAnimate', ['ng'])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
|
done();
|
||||||
|
}
|
||||||
|
|
||||||
|
function parseMaxTime(str) {
|
||||||
|
var total = 0, values = angular.isString(str) ? str.split(/\s*,\s*/) : [];
|
||||||
|
forEach(values, function(value) {
|
||||||
|
total = Math.max(parseFloat(value) || 0, total);
|
||||||
|
});
|
||||||
|
return total;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
enter : function(element, done) {
|
enter : function(element, done) {
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load diff
Loading…
Reference in a new issue