From bc3e223e339efb7e26ad9222bd166d1aa7b9b878 Mon Sep 17 00:00:00 2001 From: Andy Joslin Date: Fri, 11 Apr 2014 07:32:34 -0600 Subject: [PATCH] refactor(): ggnore, ngAnimate Addresses #1100 --- .../src/service/animateClassToggler.js | 55 ---------------- js/ext/angular/src/service/ionicBackdrop.js | 17 +++-- js/ext/angular/src/service/ionicLoading.js | 25 ++++--- .../test/directive/ionicBackdrop.unit.js | 35 +++++----- .../test/service/animateClassToggler.unit.js | 66 ------------------- .../angular/test/service/ionicLoading.unit.js | 12 ++-- scss/_backdrop.scss | 12 +++- scss/_loading.scss | 12 +++- scss/_mixins.scss | 12 ---- 9 files changed, 69 insertions(+), 177 deletions(-) delete mode 100644 js/ext/angular/src/service/animateClassToggler.js delete mode 100644 js/ext/angular/test/service/animateClassToggler.unit.js diff --git a/js/ext/angular/src/service/animateClassToggler.js b/js/ext/angular/src/service/animateClassToggler.js deleted file mode 100644 index 2db2cfde80..0000000000 --- a/js/ext/angular/src/service/animateClassToggler.js +++ /dev/null @@ -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; - } - } - }; -}]); - diff --git a/js/ext/angular/src/service/ionicBackdrop.js b/js/ext/angular/src/service/ionicBackdrop.js index bbb375bf33..920865acc0 100644 --- a/js/ext/angular/src/service/ionicBackdrop.js +++ b/js/ext/angular/src/service/ionicBackdrop.js @@ -4,14 +4,11 @@ angular.module('ionic') * @private */ .factory('$ionicBackdrop', [ - '$animate', '$document', - '$animateClassToggler', -function($animate, $document, $animateClassToggler) { +function($document) { - var el = angular.element('
'); + var el = angular.element('
'); 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); } } }]); diff --git a/js/ext/angular/src/service/ionicLoading.js b/js/ext/angular/src/service/ionicLoading.js index 1b5d695c78..34bd4a3768 100644 --- a/js/ext/angular/src/service/ionicLoading.js +++ b/js/ext/angular/src/service/ionicLoading.js @@ -1,6 +1,6 @@ var TPL_LOADING = - '
' + + '
' + '
'; 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; diff --git a/js/ext/angular/test/directive/ionicBackdrop.unit.js b/js/ext/angular/test/directive/ionicBackdrop.unit.js index 47c7c9f604..d37dc75869 100644 --- a/js/ext/angular/test/directive/ionicBackdrop.unit.js +++ b/js/ext/angular/test/directive/ionicBackdrop.unit.js @@ -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); })); }); diff --git a/js/ext/angular/test/service/animateClassToggler.unit.js b/js/ext/angular/test/service/animateClassToggler.unit.js deleted file mode 100644 index 3226432fa0..0000000000 --- a/js/ext/angular/test/service/animateClassToggler.unit.js +++ /dev/null @@ -1,66 +0,0 @@ -describe('$animateClassToggler service', function() { - beforeEach(module('ionic')); - - var toggler, el; - beforeEach(inject(function($animateClassToggler) { - el = angular.element('
'); - 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); - }); - -}); diff --git a/js/ext/angular/test/service/ionicLoading.unit.js b/js/ext/angular/test/service/ionicLoading.unit.js index d786130c59..c20881db8e 100644 --- a/js/ext/angular/test/service/ionicLoading.unit.js +++ b/js/ext/angular/test/service/ionicLoading.unit.js @@ -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(); }; })); diff --git a/scss/_backdrop.scss b/scss/_backdrop.scss index 2527125fc0..2dc36bd5d0 100644 --- a/scss/_backdrop.scss +++ b/scss/_backdrop.scss @@ -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); } diff --git a/scss/_loading.scss b/scss/_loading.scss index ec7d08b53a..f713c1564a 100644 --- a/scss/_loading.scss +++ b/scss/_loading.scss @@ -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%; diff --git a/scss/_mixins.scss b/scss/_mixins.scss index 167ebb8e85..8b0d4bf40a 100644 --- a/scss/_mixins.scss +++ b/scss/_mixins.scss @@ -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;