mirror of
https://github.com/ionic-team/ionic-framework.git
synced 2026-03-13 10:22:08 +08:00
refactor: $animate - add $animateClassToggler to stop race conditions
Addresses #1100. Fixes race conditions where $animate.{removeClass,hideClass} are called simultaneously, in addition fixes any blinking that coulld be caused by showing an element just attached to the dom.
This commit is contained in:
55
js/ext/angular/src/service/animateClassToggler.js
vendored
Normal file
55
js/ext/angular/src/service/animateClassToggler.js
vendored
Normal file
@@ -0,0 +1,55 @@
|
||||
/**
|
||||
* Stop race conditions with toggling an ngAnimate on an element rapidly,
|
||||
* due to the asynchronous nature of addClass and removeClass
|
||||
*
|
||||
* Additionally, only toggle classes on requestAnimationFrame, to stop flickers on
|
||||
* iOS and older android for elements that were just attached to the DOM.
|
||||
*/
|
||||
angular.module('ionic')
|
||||
.factory('$animateClassToggler', [
|
||||
'$animate',
|
||||
'$rootScope',
|
||||
function($animate, $rootScope) {
|
||||
|
||||
return function(element, className) {
|
||||
|
||||
var self = {
|
||||
_nextOperation: null,
|
||||
_animating: false,
|
||||
_toggle: toggle,
|
||||
_animate: animate,
|
||||
_animationDone: animationDone,
|
||||
addClass: function() { self._toggle(true); },
|
||||
removeClass: function() { self._toggle(false); }
|
||||
};
|
||||
|
||||
return self;
|
||||
|
||||
function toggle(hasClass) {
|
||||
var operation = hasClass ? 'addClass' : 'removeClass';
|
||||
if (self._animating) {
|
||||
self._nextOperation = operation;
|
||||
} else {
|
||||
self._animate(operation);
|
||||
}
|
||||
}
|
||||
|
||||
function animate(operation) {
|
||||
self._animating = true;
|
||||
ionic.requestAnimationFrame(function() {
|
||||
$rootScope.$evalAsync(function() {
|
||||
$animate[operation](element, className, self._animationDone);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function animationDone() {
|
||||
self._animating = false;
|
||||
if (self._nextOperation) {
|
||||
self._animate(self._nextOperation);
|
||||
self._nextOperation = null;
|
||||
}
|
||||
}
|
||||
};
|
||||
}]);
|
||||
|
||||
22
js/ext/angular/src/service/ionicBackdrop.js
vendored
22
js/ext/angular/src/service/ionicBackdrop.js
vendored
@@ -6,32 +6,30 @@ angular.module('ionic')
|
||||
.factory('$ionicBackdrop', [
|
||||
'$animate',
|
||||
'$document',
|
||||
function($animate, $document) {
|
||||
'$animateClassToggler',
|
||||
function($animate, $document, $animateClassToggler) {
|
||||
|
||||
var el;
|
||||
var el = angular.element('<div class="backdrop ng-hide">');
|
||||
var backdropHolds = 0;
|
||||
var toggler = $animateClassToggler(el, 'ng-hide');
|
||||
|
||||
$document[0].body.appendChild(el[0]);
|
||||
|
||||
return {
|
||||
retain: retain,
|
||||
release: release,
|
||||
_getElement: getElement
|
||||
// exposed for testing
|
||||
_element: el
|
||||
};
|
||||
|
||||
function getElement() {
|
||||
if (!el) {
|
||||
el = angular.element('<div class="backdrop ng-hide">');
|
||||
$document[0].body.appendChild(el[0]);
|
||||
}
|
||||
return el;
|
||||
}
|
||||
function retain() {
|
||||
if ( (++backdropHolds) === 1 ) {
|
||||
$animate.removeClass(getElement(), 'ng-hide');
|
||||
toggler.removeClass();
|
||||
}
|
||||
}
|
||||
function release() {
|
||||
if ( (--backdropHolds) === 0 ) {
|
||||
$animate.addClass(getElement(), 'ng-hide');
|
||||
toggler.addClass();
|
||||
}
|
||||
}
|
||||
}]);
|
||||
|
||||
23
js/ext/angular/src/service/ionicLoading.js
vendored
23
js/ext/angular/src/service/ionicLoading.js
vendored
@@ -41,7 +41,8 @@ angular.module('ionic.service.loading', [])
|
||||
'$q',
|
||||
'$log',
|
||||
'$compile',
|
||||
function($animate, $document, $ionicTemplateLoader, $ionicBackdrop, $timeout, $q, $log, $compile) {
|
||||
'$animateClassToggler',
|
||||
function($animate, $document, $ionicTemplateLoader, $ionicBackdrop, $timeout, $q, $log, $compile, $animateClassToggler) {
|
||||
|
||||
var loaderInstance;
|
||||
//default value
|
||||
@@ -81,6 +82,9 @@ function($animate, $document, $ionicTemplateLoader, $ionicBackdrop, $timeout, $q
|
||||
appendTo: $document[0].body
|
||||
})
|
||||
.then(function(loader) {
|
||||
|
||||
var toggler = $animateClassToggler(loader.element, 'ng-hide');
|
||||
|
||||
loader.show = function(options) {
|
||||
var self = this;
|
||||
var templatePromise = options.templateUrl ?
|
||||
@@ -88,6 +92,7 @@ function($animate, $document, $ionicTemplateLoader, $ionicBackdrop, $timeout, $q
|
||||
//options.content: deprecated
|
||||
$q.when(options.template || options.content || '');
|
||||
|
||||
|
||||
if (!this.isShown) {
|
||||
//options.showBackdrop: deprecated
|
||||
this.hasBackdrop = !options.noBackdrop && options.showBackdrop !== false;
|
||||
@@ -111,26 +116,16 @@ function($animate, $document, $ionicTemplateLoader, $ionicBackdrop, $timeout, $q
|
||||
}
|
||||
});
|
||||
|
||||
toggler.removeClass();
|
||||
ionic.DomUtil.centerElementByMarginTwice(this.element[0]);
|
||||
this.isShown = true;
|
||||
ionic.requestAnimationFrame(function() {
|
||||
if (self.isShown) {
|
||||
$animate.removeClass(self.element, 'ng-hide');
|
||||
//Fix for ios: if we center the element twice, it always gets
|
||||
//position right. Otherwise, it doesn't
|
||||
ionic.DomUtil.centerElementByMargin(self.element[0]);
|
||||
//One frame after it's visible, position it
|
||||
ionic.requestAnimationFrame(function() {
|
||||
ionic.DomUtil.centerElementByMargin(self.element[0]);
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
loader.hide = function() {
|
||||
if (this.isShown) {
|
||||
if (this.hasBackdrop) {
|
||||
$ionicBackdrop.release();
|
||||
}
|
||||
$animate.addClass(this.element, 'ng-hide');
|
||||
toggler.addClass();
|
||||
}
|
||||
$timeout.cancel(this.durationTimeout);
|
||||
this.isShown = false;
|
||||
|
||||
8
js/ext/angular/src/service/ionicPopup.js
vendored
8
js/ext/angular/src/service/ionicPopup.js
vendored
@@ -327,14 +327,8 @@ function($animate, $ionicTemplateLoader, $ionicBackdrop, $log, $q, $timeout, $ro
|
||||
|
||||
self.element.removeClass('popup-hidden');
|
||||
self.element.addClass('popup-showing active');
|
||||
ionic.DomUtil.centerElementByMarginTwice(self.element[0]);
|
||||
focusLastButton(self.element);
|
||||
//Fix for ios: if we center the element twice, it always gets
|
||||
//position right. Otherwise, it doesn't
|
||||
ionic.DomUtil.centerElementByMargin(self.element[0]);
|
||||
//One frame after it's visible, position it
|
||||
ionic.requestAnimationFrame(function() {
|
||||
ionic.DomUtil.centerElementByMargin(self.element[0]);
|
||||
});
|
||||
});
|
||||
};
|
||||
self.hide = function(callback) {
|
||||
|
||||
Reference in New Issue
Block a user