mirror of
https://github.com/ionic-team/ionic-framework.git
synced 2026-03-13 10:22:08 +08:00
@@ -1,55 +0,0 @@
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
}
|
||||
};
|
||||
}]);
|
||||
|
||||
17
js/ext/angular/src/service/ionicBackdrop.js
vendored
17
js/ext/angular/src/service/ionicBackdrop.js
vendored
@@ -4,14 +4,11 @@ angular.module('ionic')
|
||||
* @private
|
||||
*/
|
||||
.factory('$ionicBackdrop', [
|
||||
'$animate',
|
||||
'$document',
|
||||
'$animateClassToggler',
|
||||
function($animate, $document, $animateClassToggler) {
|
||||
function($document) {
|
||||
|
||||
var el = angular.element('<div class="backdrop ng-hide">');
|
||||
var el = angular.element('<div class="backdrop">');
|
||||
var backdropHolds = 0;
|
||||
var toggler = $animateClassToggler(el, 'ng-hide');
|
||||
|
||||
$document[0].body.appendChild(el[0]);
|
||||
|
||||
@@ -24,12 +21,18 @@ function($animate, $document, $animateClassToggler) {
|
||||
|
||||
function retain() {
|
||||
if ( (++backdropHolds) === 1 ) {
|
||||
toggler.removeClass();
|
||||
el.addClass('visible');
|
||||
ionic.requestAnimationFrame(function() {
|
||||
backdropHolds && el.addClass('active');
|
||||
});
|
||||
}
|
||||
}
|
||||
function release() {
|
||||
if ( (--backdropHolds) === 0 ) {
|
||||
toggler.addClass();
|
||||
el.removeClass('active');
|
||||
setTimeout(function() {
|
||||
!backdropHolds && el.removeClass('visible');
|
||||
}, 100);
|
||||
}
|
||||
}
|
||||
}]);
|
||||
|
||||
25
js/ext/angular/src/service/ionicLoading.js
vendored
25
js/ext/angular/src/service/ionicLoading.js
vendored
@@ -1,6 +1,6 @@
|
||||
|
||||
var TPL_LOADING =
|
||||
'<div class="loading ng-hide">' +
|
||||
'<div class="loading">' +
|
||||
'</div>';
|
||||
|
||||
var HIDE_LOADING_DEPRECATED = '$ionicLoading instance.hide() has been deprecated. Use $ionicLoading.hide().';
|
||||
@@ -33,7 +33,6 @@ angular.module('ionic.service.loading', [])
|
||||
* ```
|
||||
*/
|
||||
.factory('$ionicLoading', [
|
||||
'$animate',
|
||||
'$document',
|
||||
'$ionicTemplateLoader',
|
||||
'$ionicBackdrop',
|
||||
@@ -41,8 +40,7 @@ angular.module('ionic.service.loading', [])
|
||||
'$q',
|
||||
'$log',
|
||||
'$compile',
|
||||
'$animateClassToggler',
|
||||
function($animate, $document, $ionicTemplateLoader, $ionicBackdrop, $timeout, $q, $log, $compile, $animateClassToggler) {
|
||||
function($document, $ionicTemplateLoader, $ionicBackdrop, $timeout, $q, $log, $compile) {
|
||||
|
||||
var loaderInstance;
|
||||
//default value
|
||||
@@ -83,10 +81,9 @@ function($animate, $document, $ionicTemplateLoader, $ionicBackdrop, $timeout, $q
|
||||
})
|
||||
.then(function(loader) {
|
||||
|
||||
var toggler = $animateClassToggler(loader.element, 'ng-hide');
|
||||
var self = loader;
|
||||
|
||||
loader.show = function(options) {
|
||||
var self = this;
|
||||
var templatePromise = options.templateUrl ?
|
||||
$ionicTemplateLoader.load(options.templateUrl) :
|
||||
//options.content: deprecated
|
||||
@@ -114,10 +111,17 @@ function($animate, $document, $ionicTemplateLoader, $ionicBackdrop, $timeout, $q
|
||||
self.element.html(html);
|
||||
$compile(self.element.contents())(self.scope);
|
||||
}
|
||||
|
||||
//Don't show until template changes
|
||||
if (self.isShown) {
|
||||
self.element.addClass('visible');
|
||||
ionic.DomUtil.centerElementByMarginTwice(self.element[0]);
|
||||
ionic.requestAnimationFrame(function() {
|
||||
self.isShown && self.element.addClass('active');
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
toggler.removeClass();
|
||||
ionic.DomUtil.centerElementByMarginTwice(this.element[0]);
|
||||
this.isShown = true;
|
||||
};
|
||||
loader.hide = function() {
|
||||
@@ -125,7 +129,10 @@ function($animate, $document, $ionicTemplateLoader, $ionicBackdrop, $timeout, $q
|
||||
if (this.hasBackdrop) {
|
||||
$ionicBackdrop.release();
|
||||
}
|
||||
toggler.addClass();
|
||||
self.element.removeClass('active');
|
||||
setTimeout(function() {
|
||||
!self.isShown && self.element.removeClass('visible');
|
||||
}, 200);
|
||||
}
|
||||
$timeout.cancel(this.durationTimeout);
|
||||
this.isShown = false;
|
||||
|
||||
@@ -5,41 +5,36 @@ describe('$ionicBackdrop service', function() {
|
||||
ionic.requestAnimationFrame = function(cb) { cb(); };
|
||||
}));
|
||||
|
||||
it('should remove ngHide on retain', inject(function($ionicBackdrop, $timeout) {
|
||||
it('should add active on retain', inject(function($ionicBackdrop) {
|
||||
var el = $ionicBackdrop._element;
|
||||
expect(el.hasClass('ng-hide')).toBe(true);
|
||||
expect(el.hasClass('active')).toBe(false);
|
||||
$ionicBackdrop.retain();
|
||||
$timeout.flush();
|
||||
expect(el.hasClass('ng-hide')).toBe(false);
|
||||
expect(el.hasClass('active')).toBe(true);
|
||||
}));
|
||||
|
||||
it('should add ngHide on retain', inject(function($ionicBackdrop, $timeout) {
|
||||
it('should add and remove active on retain and release', inject(function($ionicBackdrop) {
|
||||
var el = $ionicBackdrop._element;
|
||||
expect(el.hasClass('ng-hide')).toBe(true);
|
||||
expect(el.hasClass('active')).toBe(false);
|
||||
$ionicBackdrop.retain();
|
||||
$timeout.flush();
|
||||
expect(el.hasClass('ng-hide')).toBe(false);
|
||||
expect(el.hasClass('active')).toBe(true);
|
||||
$ionicBackdrop.release();
|
||||
$timeout.flush();
|
||||
expect(el.hasClass('ng-hide')).toBe(true);
|
||||
expect(el.hasClass('active')).toBe(false);
|
||||
}));
|
||||
|
||||
it('should require equal releases and retains', inject(function($ionicBackdrop, $timeout) {
|
||||
it('should require equal releases and retains', inject(function($ionicBackdrop) {
|
||||
var el = $ionicBackdrop._element;
|
||||
expect(el.hasClass('ng-hide')).toBe(true);
|
||||
expect(el.hasClass('active')).toBe(false);
|
||||
$ionicBackdrop.retain();
|
||||
$timeout.flush();
|
||||
expect(el.hasClass('ng-hide')).toBe(false);
|
||||
expect(el.hasClass('active')).toBe(true);
|
||||
$ionicBackdrop.retain();
|
||||
expect(el.hasClass('ng-hide')).toBe(false);
|
||||
expect(el.hasClass('active')).toBe(true);
|
||||
$ionicBackdrop.retain();
|
||||
expect(el.hasClass('ng-hide')).toBe(false);
|
||||
expect(el.hasClass('active')).toBe(true);
|
||||
$ionicBackdrop.release();
|
||||
expect(el.hasClass('ng-hide')).toBe(false);
|
||||
expect(el.hasClass('active')).toBe(true);
|
||||
$ionicBackdrop.release();
|
||||
expect(el.hasClass('ng-hide')).toBe(false);
|
||||
expect(el.hasClass('active')).toBe(true);
|
||||
$ionicBackdrop.release();
|
||||
$timeout.flush();
|
||||
expect(el.hasClass('ng-hide')).toBe(true);
|
||||
expect(el.hasClass('active')).toBe(false);
|
||||
}));
|
||||
});
|
||||
|
||||
@@ -1,66 +0,0 @@
|
||||
describe('$animateClassToggler service', function() {
|
||||
beforeEach(module('ionic'));
|
||||
|
||||
var toggler, el;
|
||||
beforeEach(inject(function($animateClassToggler) {
|
||||
el = angular.element('<div>');
|
||||
toggler = $animateClassToggler(el, 'foo');
|
||||
ionic.requestAnimationFrame = function(cb) { cb(); };
|
||||
}));
|
||||
|
||||
function flush() {
|
||||
inject(function($timeout) {
|
||||
$timeout.flush();
|
||||
});
|
||||
}
|
||||
|
||||
it('addClass should go to toggle', function() {
|
||||
spyOn(toggler, '_toggle');
|
||||
toggler.addClass();
|
||||
expect(toggler._toggle).toHaveBeenCalledWith(true);
|
||||
});
|
||||
it('removeClass should go to toggle', function() {
|
||||
spyOn(toggler, '_toggle');
|
||||
toggler.removeClass();
|
||||
expect(toggler._toggle).toHaveBeenCalledWith(false);
|
||||
});
|
||||
it('toggle should add class and remove class', function() {
|
||||
toggler._toggle(true);
|
||||
flush();
|
||||
expect(el.hasClass('foo')).toBe(true);
|
||||
toggler._toggle(false);
|
||||
flush();
|
||||
expect(el.hasClass('foo')).toBe(false);
|
||||
});
|
||||
|
||||
it('toggle should set nextOperation if animating', function() {
|
||||
toggler._animating = true;
|
||||
toggler._toggle(true);
|
||||
expect(toggler._nextOperation).toBe('addClass');
|
||||
toggler._toggle(false);
|
||||
expect(toggler._nextOperation).toBe('removeClass');
|
||||
});
|
||||
|
||||
it('animate should set animating to true and animationDone when done', inject(function($rootScope, $animate) {
|
||||
spyOn($rootScope, '$evalAsync').andCallThrough();
|
||||
spyOn($animate, 'addClass');
|
||||
toggler._animate('addClass');
|
||||
expect(toggler._animating).toBe(true);
|
||||
expect($rootScope.$evalAsync).toHaveBeenCalled();
|
||||
flush();
|
||||
expect($animate.addClass).toHaveBeenCalledWith(el, 'foo', toggler._animationDone);
|
||||
}));
|
||||
|
||||
it('animationDone should use nextOperation and unset animating if no more', function() {
|
||||
spyOn(toggler, '_animate').andCallThrough();
|
||||
toggler._animating = true;
|
||||
toggler._nextOperation = 'addClass';
|
||||
toggler._animationDone();
|
||||
expect(toggler._nextOperation).toBeFalsy();
|
||||
expect(toggler._animate).toHaveBeenCalled();
|
||||
flush();
|
||||
expect(el.hasClass('foo')).toBe(true);
|
||||
expect(toggler._animating).toBe(false);
|
||||
});
|
||||
|
||||
});
|
||||
@@ -41,13 +41,13 @@ describe('$ionicLoading service', function() {
|
||||
expect(loader.durationTimeout).toBeTruthy();
|
||||
expect(loader.durationTimeout.$$timeoutId).toBeTruthy();
|
||||
}));
|
||||
it('should remove ng-hide', inject(function($ionicLoading, $timeout) {
|
||||
it('should add active', inject(function($ionicLoading, $timeout) {
|
||||
var loader = TestUtil.unwrapPromise($ionicLoading._getLoader());
|
||||
ionic.requestAnimationFrame = function(cb) { cb(); };
|
||||
expect(loader.element.hasClass('ng-hide')).toBe(true);
|
||||
expect(loader.element.hasClass('active')).toBe(false);
|
||||
loader.show({});
|
||||
$timeout.flush();
|
||||
expect(loader.element.hasClass('ng-hide')).toBe(false);
|
||||
expect(loader.element.hasClass('active')).toBe(true);
|
||||
}));
|
||||
it('should isShown = true', inject(function($ionicLoading) {
|
||||
var loader = TestUtil.unwrapPromise($ionicLoading._getLoader());
|
||||
@@ -133,9 +133,9 @@ describe('$ionicLoading service', function() {
|
||||
expect(loader.show).toHaveBeenCalled();
|
||||
expect(loader.hide).toHaveBeenCalled();
|
||||
expect(loader.isShown).toBe(false);
|
||||
expect(loader.element.hasClass('ng-hide')).toBe(true);
|
||||
expect(loader.element.hasClass('active')).toBe(false);
|
||||
}));
|
||||
it('show should only removeClass after raf is still isShown', inject(function($ionicLoading) {
|
||||
it('show should only active after raf is still isShown', inject(function($ionicLoading) {
|
||||
var loader = TestUtil.unwrapPromise($ionicLoading._getLoader());
|
||||
var rafCallback;
|
||||
ionic.requestAnimationFrame = function(cb) {
|
||||
@@ -146,7 +146,7 @@ describe('$ionicLoading service', function() {
|
||||
loader.hide();
|
||||
expect(loader.isShown).toBe(false);
|
||||
rafCallback();
|
||||
expect(loader.element.hasClass('ng-hide')).toBe(true);
|
||||
expect(loader.element.hasClass('active')).toBe(false);
|
||||
ionic.requestAnimationFrame = function(cb) { cb(); };
|
||||
}));
|
||||
|
||||
|
||||
@@ -10,5 +10,15 @@
|
||||
|
||||
background-color: rgba(0,0,0,0.4);
|
||||
|
||||
@include ng-hide-fade();
|
||||
visibility: hidden;
|
||||
opacity: 0;
|
||||
|
||||
&.visible {
|
||||
visibility: visible;
|
||||
}
|
||||
&.active {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
@include transition(0.1s opacity linear);
|
||||
}
|
||||
|
||||
@@ -5,7 +5,17 @@
|
||||
*/
|
||||
|
||||
.loading {
|
||||
@include ng-hide-fade(0.2s);
|
||||
@include transition(0.2s opacity linear);
|
||||
|
||||
visibility: hidden;
|
||||
opacity: 0;
|
||||
|
||||
&.visible {
|
||||
visibility: visible;
|
||||
}
|
||||
&.active {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
position: fixed;
|
||||
top: 50%;
|
||||
|
||||
@@ -2,18 +2,6 @@
|
||||
// Button Mixins
|
||||
// --------------------------------------------------
|
||||
|
||||
@mixin ng-hide-fade($transition-duration:0.15s, $transition-timing-function:ease-out) {
|
||||
@include transition(opacity $transition-duration $transition-timing-function);
|
||||
opacity: 1;
|
||||
&.ng-hide {
|
||||
opacity: 0;
|
||||
}
|
||||
&.ng-hide-remove,
|
||||
&.ng-hide-add {
|
||||
display: block !important;
|
||||
}
|
||||
}
|
||||
|
||||
@mixin button-style($bg-color, $border-color, $active-bg-color, $active-border-color, $color) {
|
||||
border-color: $border-color;
|
||||
background-color: $bg-color;
|
||||
|
||||
Reference in New Issue
Block a user