From d056ad1fefaf2132e88ed2ec522d69b05fc28744 Mon Sep 17 00:00:00 2001 From: Andy Joslin Date: Mon, 17 Feb 2014 17:25:31 -0500 Subject: [PATCH] perf(animations): throttle calls to requestAnimationFrame Closes #582 --- js/ext/angular/src/directive/ionicSideMenu.js | 12 +- .../angular/src/directive/ionicViewState.js | 2 +- .../delegates/sideMenuDelegate.unit.js | 28 ---- js/ionic.js | 2 +- js/utils/dom.js | 52 ++++++- js/utils/keyboard.js | 2 +- js/utils/poly.js | 31 ++-- js/views/headerBarView.js | 112 +++++++------- js/views/listView.js | 138 +++++++++--------- js/views/popupView.js | 2 +- js/views/scrollView.js | 3 +- js/views/sideMenuView.js | 4 +- 12 files changed, 193 insertions(+), 195 deletions(-) diff --git a/js/ext/angular/src/directive/ionicSideMenu.js b/js/ext/angular/src/directive/ionicSideMenu.js index 5c6b84f469..17b79f6d22 100644 --- a/js/ext/angular/src/directive/ionicSideMenu.js +++ b/js/ext/angular/src/directive/ionicSideMenu.js @@ -110,14 +110,12 @@ angular.module('ionic.ui.sideMenu', ['ionic.service.gesture', 'ionic.service.vie getTranslateX: function() { return $scope.sideMenuContentTranslateX || 0; }, - setTranslateX: function(amount) { - window.rAF(function() { - $element[0].style.webkitTransform = 'translate3d(' + amount + 'px, 0, 0)'; - $timeout(function() { - $scope.sideMenuContentTranslateX = amount; - }); + setTranslateX: ionic.animationFrameThrottle(function(amount) { + $element[0].style.webkitTransform = 'translate3d(' + amount + 'px, 0, 0)'; + $timeout(function() { + $scope.sideMenuContentTranslateX = amount; }); - }, + }), enableAnimation: function() { //this.el.classList.add(this.animateClass); $scope.animationEnabled = true; diff --git a/js/ext/angular/src/directive/ionicViewState.js b/js/ext/angular/src/directive/ionicViewState.js index e167778b05..839c638263 100644 --- a/js/ext/angular/src/directive/ionicViewState.js +++ b/js/ext/angular/src/directive/ionicViewState.js @@ -158,7 +158,7 @@ angular.module('ionic.ui.viewState', ['ionic.service.view', 'ionic.service.gestu */ .directive('asyncVisible', function() { return function($scope, $element) { - window.rAF(function() { + ionic.requestAnimationFrame(function() { $element[0].classList.remove('invisible'); }); }; diff --git a/js/ext/angular/test/service/delegates/sideMenuDelegate.unit.js b/js/ext/angular/test/service/delegates/sideMenuDelegate.unit.js index 6e83642759..f5e2a48f5e 100644 --- a/js/ext/angular/test/service/delegates/sideMenuDelegate.unit.js +++ b/js/ext/angular/test/service/delegates/sideMenuDelegate.unit.js @@ -19,32 +19,4 @@ describe('Ionic SideMenuDelegate Service', function() { expect(sc).not.toBe(undefined); }); - /* - it('Should open and close', function() { - var scope = rootScope.$new(); - var el = compile('
')(scope); - var sc = del.getSideMenuController(scope); - - window.rAF = function( callback ){ - window.setTimeout(callback, 1); - }; - - del.openLeft(scope); - timeout.flush(); - expect(sc.isOpen()).toBe(true); - expect(sc.getOpenAmount()).toBe(100); - - del.close(scope); - expect(sc.isOpen()).toBe(false); - expect(sc.getOpenAmount()).toBe(0); - - del.openRight(scope); - expect(sc.isOpen()).toBe(true); - expect(sc.getOpenAmount()).toBe(-100); - - del.close(scope); - expect(sc.isOpen()).toBe(false); - expect(sc.getOpenAmount()).toBe(0); - }); - */ }); diff --git a/js/ionic.js b/js/ionic.js index 936ac31f7c..77c713c184 100644 --- a/js/ionic.js +++ b/js/ionic.js @@ -4,4 +4,4 @@ window.ionic = { controllers: {}, views: {}, version: '{{ VERSION }}' -}; \ No newline at end of file +}; diff --git a/js/utils/dom.js b/js/utils/dom.js index 3a477117f7..f0c020b621 100644 --- a/js/utils/dom.js +++ b/js/utils/dom.js @@ -3,14 +3,58 @@ var readyCallbacks = [], domReady = function() { for(var x=0; x 10) { - titleEl.style.left = margin + 'px'; - titleEl.style.right = margin + 'px'; - } - if(titleEl.offsetWidth < titleEl.scrollWidth) { - if(rightWidth > 0) { - titleEl.style.right = (rightWidth + 5) + 'px'; - } - } - } else if(this.alignTitle == 'left') { - titleEl.classList.add('titleEl-left'); - if(leftWidth > 0) { - titleEl.style.left = (leftWidth + 15) + 'px'; - } - } else if(this.alignTitle == 'right') { - titleEl.classList.add('titleEl-right'); + // Size and align the header titleEl based on the sizes of the left and + // right children, and the desired alignment mode + if(this.alignTitle == 'center') { + if(margin > 10) { + titleEl.style.left = margin + 'px'; + titleEl.style.right = margin + 'px'; + } + if(titleEl.offsetWidth < titleEl.scrollWidth) { if(rightWidth > 0) { - titleEl.style.right = (rightWidth + 15) + 'px'; + titleEl.style.right = (rightWidth + 5) + 'px'; } } - }, this)); - } + } else if(this.alignTitle == 'left') { + titleEl.classList.add('titleEl-left'); + if(leftWidth > 0) { + titleEl.style.left = (leftWidth + 15) + 'px'; + } + } else if(this.alignTitle == 'right') { + titleEl.classList.add('titleEl-right'); + if(rightWidth > 0) { + titleEl.style.right = (rightWidth + 15) + 'px'; + } + } + }) }); })(ionic); diff --git a/js/views/listView.js b/js/views/listView.js index 761bf849f3..58cc8b72b3 100644 --- a/js/views/listView.js +++ b/js/views/listView.js @@ -62,41 +62,39 @@ }; }; - SlideDrag.prototype.drag = function(e) { - var _this = this, buttonsWidth; + SlideDrag.prototype.drag = ionic.animationFrameThrottle(function(e) { + var buttonsWidth; - window.rAF(function() { - // We really aren't dragging - if(!_this._currentDrag) { - return; + // We really aren't dragging + if(!this._currentDrag) { + return; + } + + // Check if we should start dragging. Check if we've dragged past the threshold, + // or we are starting from the open state. + if(!this._isDragging && + ((Math.abs(e.gesture.deltaX) > this.dragThresholdX) || + (Math.abs(this._currentDrag.startOffsetX) > 0))) + { + this._isDragging = true; + } + + if(this._isDragging) { + buttonsWidth = this._currentDrag.buttonsWidth; + + // Grab the new X point, capping it at zero + var newX = Math.min(0, this._currentDrag.startOffsetX + e.gesture.deltaX); + + // If the new X position is past the buttons, we need to slow down the drag (rubber band style) + if(newX < -buttonsWidth) { + // Calculate the new X position, capped at the top of the buttons + newX = Math.min(-buttonsWidth, -buttonsWidth + (((e.gesture.deltaX + buttonsWidth) * 0.4))); } - // Check if we should start dragging. Check if we've dragged past the threshold, - // or we are starting from the open state. - if(!_this._isDragging && - ((Math.abs(e.gesture.deltaX) > _this.dragThresholdX) || - (Math.abs(_this._currentDrag.startOffsetX) > 0))) - { - _this._isDragging = true; - } - - if(_this._isDragging) { - buttonsWidth = _this._currentDrag.buttonsWidth; - - // Grab the new X point, capping it at zero - var newX = Math.min(0, _this._currentDrag.startOffsetX + e.gesture.deltaX); - - // If the new X position is past the buttons, we need to slow down the drag (rubber band style) - if(newX < -buttonsWidth) { - // Calculate the new X position, capped at the top of the buttons - newX = Math.min(-buttonsWidth, -buttonsWidth + (((e.gesture.deltaX + buttonsWidth) * 0.4))); - } - - _this._currentDrag.content.style.webkitTransform = 'translate3d(' + newX + 'px, 0, 0)'; - _this._currentDrag.content.style.webkitTransition = 'none'; - } - }); - }; + this._currentDrag.content.style.webkitTransform = 'translate3d(' + newX + 'px, 0, 0)'; + this._currentDrag.content.style.webkitTransition = 'none'; + } + }); SlideDrag.prototype.end = function(e, doneCallback) { var _this = this; @@ -133,7 +131,7 @@ // e.target.removeEventListener('webkitTransitionEnd', onRestingAnimationEnd); // }; - window.rAF(function() { + ionic.requestAnimationFrame(function() { // var currentX = parseFloat(_this._currentDrag.content.style.webkitTransform.replace('translate3d(', '').split(',')[0]) || 0; // if(currentX !== restingPoint) { // _this._currentDrag.content.classList.add(ITEM_SLIDING_CLASS); @@ -206,53 +204,49 @@ this._moveElement(e); }; - ReorderDrag.prototype.drag = function(e) { - var _this = this; + ReorderDrag.prototype.drag = ionic.animationFrameThrottle(function(e) { + // We really aren't dragging + if(!this._currentDrag) { + return; + } - window.rAF(function() { - // We really aren't dragging - if(!_this._currentDrag) { - return; + var scrollY = 0; + var pageY = e.gesture.center.pageY; + + //If we have a scrollView, check scroll boundaries for dragged element and scroll if necessary + if (this.scrollView) { + var container = this.scrollEl; + + scrollY = this.scrollView.getValues().top; + + var containerTop = container.offsetTop; + var pixelsPastTop = containerTop - pageY + this._currentDrag.elementHeight/2; + var pixelsPastBottom = pageY + this._currentDrag.elementHeight/2 - containerTop - container.offsetHeight; + + if (e.gesture.deltaY < 0 && pixelsPastTop > 0 && scrollY > 0) { + this.scrollView.scrollBy(null, -pixelsPastTop); } - - var scrollY = 0; - var pageY = e.gesture.center.pageY; - - //If we have a scrollView, check scroll boundaries for dragged element and scroll if necessary - if (_this.scrollView) { - var container = _this.scrollEl; - - scrollY = _this.scrollView.getValues().top; - - var containerTop = container.offsetTop; - var pixelsPastTop = containerTop - pageY + _this._currentDrag.elementHeight/2; - var pixelsPastBottom = pageY + _this._currentDrag.elementHeight/2 - containerTop - container.offsetHeight; - - if (e.gesture.deltaY < 0 && pixelsPastTop > 0 && scrollY > 0) { - _this.scrollView.scrollBy(null, -pixelsPastTop); - } - if (e.gesture.deltaY > 0 && pixelsPastBottom > 0) { - if (scrollY < _this.scrollView.getScrollMax().top) { - _this.scrollView.scrollBy(null, pixelsPastBottom); - } + if (e.gesture.deltaY > 0 && pixelsPastBottom > 0) { + if (scrollY < this.scrollView.getScrollMax().top) { + this.scrollView.scrollBy(null, pixelsPastBottom); } } + } - // Check if we should start dragging. Check if we've dragged past the threshold, - // or we are starting from the open state. - if(!_this._isDragging && Math.abs(e.gesture.deltaY) > _this.dragThresholdY) { - _this._isDragging = true; - } + // Check if we should start dragging. Check if we've dragged past the threshold, + // or we are starting from the open state. + if(!this._isDragging && Math.abs(e.gesture.deltaY) > this.dragThresholdY) { + this._isDragging = true; + } - if(_this._isDragging) { - _this._moveElement(e); + if(this._isDragging) { + this._moveElement(e); - _this._currentDrag.currentY = scrollY + pageY - _this._currentDrag.placeholder.parentNode.offsetTop; + this._currentDrag.currentY = scrollY + pageY - this._currentDrag.placeholder.parentNode.offsetTop; - _this._reorderItems(); - } - }); - }; + this._reorderItems(); + } + }); // When an item is dragged, we need to reorder any items for sorting purposes ReorderDrag.prototype._reorderItems = function() { diff --git a/js/views/popupView.js b/js/views/popupView.js index b320d2d934..713113d45e 100644 --- a/js/views/popupView.js +++ b/js/views/popupView.js @@ -22,7 +22,7 @@ alert: function(message) { var _this = this; - window.rAF(function() { + ionic.requestAnimationFrame(function() { _this.setTitle(message); _this.el.classList.add('active'); }); diff --git a/js/views/scrollView.js b/js/views/scrollView.js index b28087eec8..e6e177cf34 100644 --- a/js/views/scrollView.js +++ b/js/views/scrollView.js @@ -624,11 +624,12 @@ ionic.views.Scroll = ionic.views.View.inherit({ if ('ontouchstart' in window) { container.addEventListener("touchstart", function(e) { + console.log('touchstart scrollview'); if (e.defaultPrevented || shouldIgnorePress(e)) { return; } self.doTouchStart(e.touches, e.timeStamp); - e.preventDefault(); + // e.preventDefault(); }, false); document.addEventListener("touchmove", function(e) { diff --git a/js/views/sideMenuView.js b/js/views/sideMenuView.js index 5927acf9f5..ea1c81702a 100644 --- a/js/views/sideMenuView.js +++ b/js/views/sideMenuView.js @@ -59,9 +59,9 @@ getTranslateX: function() { return parseFloat(this.el.style.webkitTransform.replace('translate3d(', '').split(',')[0]); }, - setTranslateX: function(x) { + setTranslateX: ionic.animationFrameThrottle(function(x) { this.el.style.webkitTransform = 'translate3d(' + x + 'px, 0, 0)'; - } + }) }); })(ionic);