mirror of
https://github.com/ionic-team/ionic-framework.git
synced 2026-03-13 10:22:08 +08:00
344 lines
10 KiB
JavaScript
344 lines
10 KiB
JavaScript
angular.module('ionic.ui.scroll')
|
|
|
|
/**
|
|
* @ngdoc service
|
|
* @name $ionicScrollDelegate
|
|
* @module ionic
|
|
* @description
|
|
* Delegate for controlling scrollViews (created by
|
|
* {@link ionic.directive:ionContent} and
|
|
* {@link ionic.directive:ionScroll} directives).
|
|
*
|
|
* Each method on $ionicScrollDelegate can be called on the service itself to control all scrollViews. Alternatively, one can control one specific scrollView using `withHandle` and `delegate-handle`. See the example below.
|
|
*
|
|
* @usage
|
|
*
|
|
* Basic Usage:
|
|
*
|
|
* ```html
|
|
* <body ng-controller="MainCtrl">
|
|
* <ion-content>
|
|
* <button ng-click="scrollTop()">Scroll to Top!</button>
|
|
* </ion-content>
|
|
* </body>
|
|
* ```
|
|
* ```js
|
|
* function MainCtrl($scope, $ionicScrollDelegate) {
|
|
* $scope.scrollTop = function() {
|
|
* $ionicScrollDelegate.scrollTop();
|
|
* };
|
|
* }
|
|
* ```
|
|
*
|
|
* Example of advanced usage, with two scroll areas using `delegate-handle`
|
|
* for fine control.
|
|
*
|
|
* ```html
|
|
* <body ng-controller="MainCtrl">
|
|
* <ion-content delegate-handle="mainScroll">
|
|
* <button ng-click="scrollMainToTop()">
|
|
* Scroll content to top!
|
|
* </button>
|
|
* <ion-scroll delegate-handle="small" style="height: 100px;">
|
|
* <button ng-click="scrollSmallToTop()">
|
|
* Scroll small area to top!
|
|
* </button>
|
|
* </ion-scroll>
|
|
* </ion-content>
|
|
* </body>
|
|
* ```
|
|
* ```js
|
|
* function MainCtrl($scope, $ionicScrollDelegate) {
|
|
* $scope.scrollMainToTop = function() {
|
|
* $ionicScrollDelegate.withHandle('mainScroll').scrollTop();
|
|
* };
|
|
* $scope.scrollSmallToTop = function() {
|
|
* $ionicScrollDelegate.withHandle('small').scrollTop();
|
|
* };
|
|
* }
|
|
* ```
|
|
*/
|
|
|
|
/**
|
|
* @ngdoc method
|
|
* @name $ionicScrollDelegate#withHandle
|
|
* @param {string} handle
|
|
* @returns `delegateInstance` A delegate instance that controls only the
|
|
* scrollView with delegate-handle matching the given handle.
|
|
*/
|
|
.service('$ionicScrollDelegate', delegateService([
|
|
/**
|
|
* @ngdoc method
|
|
* @name $ionicScrollDelegate#resize
|
|
* @description Tell the scrollView to recalculate the size of its container.
|
|
*/
|
|
'resize',
|
|
/**
|
|
* @ngdoc method
|
|
* @name $ionicScrollDelegate#scrollTop
|
|
* @param {boolean=} shouldAnimate Whether the scroll should animate.
|
|
*/
|
|
'scrollTop',
|
|
/**
|
|
* @ngdoc method
|
|
* @name $ionicScrollDelegate#scrollBottom
|
|
* @param {boolean=} shouldAnimate Whether the scroll should animate.
|
|
*/
|
|
'scrollBottom',
|
|
/**
|
|
* @ngdoc method
|
|
* @name $ionicScrollDelegate#scroll
|
|
* @param {number} left The x-value to scroll to.
|
|
* @param {number} top The y-value to scroll to.
|
|
* @param {boolean=} shouldAnimate Whether the scroll should animate.
|
|
*/
|
|
'scrollTo',
|
|
/**
|
|
* @ngdoc method
|
|
* @name $ionicScrollDelegate#anchorScroll
|
|
* @description Tell the scrollView to scroll to the element with an id
|
|
* matching window.location.hash.
|
|
*
|
|
* If no matching element is found, it will scroll to top.
|
|
*
|
|
* @param {boolean=} shouldAnimate Whether the scroll should animate.
|
|
*/
|
|
'anchorScroll',
|
|
/**
|
|
* @ngdoc method
|
|
* @name $ionicScrollDelegate#rememberScrollPosition
|
|
* @description
|
|
* Will make it so, when this scrollView is destroyed (user leaves the page),
|
|
* the last scroll position the page was on will be saved, indexed by the
|
|
* given id.
|
|
*
|
|
* Note: for pages associated with a view under an ion-nav-view,
|
|
* rememberScrollPosition automatically saves their scroll.
|
|
*
|
|
* Related methods: scrollToRememberedPosition, forgetScrollPosition (below).
|
|
*
|
|
* In the following example, the scroll position of the ion-scroll element
|
|
* will persist, even when the user changes the toggle switch.
|
|
*
|
|
* ```html
|
|
* <ion-toggle ng-model="shouldShowScrollView"></ion-toggle>
|
|
* <ion-scroll delegate-handle="myScroll" ng-if="shouldShowScrollView">
|
|
* <div ng-controller="ScrollCtrl">
|
|
* <ion-list>
|
|
* <ion-item ng-repeat="i in items">{{i}}</ion-item>
|
|
* </ion-list>
|
|
* </div>
|
|
* </ion-scroll>
|
|
* ```
|
|
* ```js
|
|
* function ScrollCtrl($scope, $ionicScrollDelegate) {
|
|
* var delegate = $ionicScrollDelegate.withHandle('myScroll');
|
|
*
|
|
* // Put any unique ID here. The point of this is: every time the controller is recreated
|
|
* // we want to load the correct remembered scroll values.
|
|
* delegate.rememberScrollPosition('my-scroll-id');
|
|
* delegate.scrollToRememberedPosition();
|
|
* $scope.items = [];
|
|
* for (var i=0; i<100; i++) {
|
|
* $scope.items.push(i);
|
|
* }
|
|
* }
|
|
* ```
|
|
*
|
|
* @param {string} id The id to remember the scroll position of this
|
|
* scrollView by.
|
|
*/
|
|
'rememberScrollPosition',
|
|
/**
|
|
* @ngdoc method
|
|
* @name $ionicScrollDelegate#forgetScrollPosition
|
|
* @description
|
|
* Stop remembering the scroll position for this scrollView.
|
|
*/
|
|
'forgetScrollPosition',
|
|
/**
|
|
* @ngdoc method
|
|
* @name $ionicScrollDelegate#scrollToRememberedPosition
|
|
* @description
|
|
* If this scrollView has an id associated with its scroll position,
|
|
* (through calling rememberScrollPosition), and that position is remembered,
|
|
* load the position and scroll to it.
|
|
* @param {boolean=} shouldAnimate Whether to animate the scroll.
|
|
*/
|
|
'scrollToRememberedPosition'
|
|
]))
|
|
|
|
/**
|
|
* @private
|
|
*/
|
|
.factory('$$scrollValueCache', function() {
|
|
return {};
|
|
})
|
|
|
|
.controller('$ionicScroll', [
|
|
'$scope',
|
|
'scrollViewOptions',
|
|
'$timeout',
|
|
'$window',
|
|
'$$scrollValueCache',
|
|
'$location',
|
|
'$rootScope',
|
|
'$document',
|
|
'$ionicScrollDelegate',
|
|
'$parse', //DEPRECATED
|
|
function($scope, scrollViewOptions, $timeout, $window, $$scrollValueCache, $location, $rootScope, $document, $ionicScrollDelegate, $parse) {
|
|
|
|
var self = this;
|
|
|
|
this._scrollViewOptions = scrollViewOptions; //for testing
|
|
|
|
$parse('$ionicScrollController').assign($scope.$parent || $scope, this);
|
|
|
|
var element = this.element = scrollViewOptions.el;
|
|
var $element = this.$element = angular.element(element);
|
|
var scrollView = this.scrollView = new ionic.views.Scroll(scrollViewOptions);
|
|
|
|
//Attach self to element as a controller so other directives can require this controller
|
|
//through `require: '$ionicScroll'
|
|
//Also attach to parent so that sibling elements can require this
|
|
($element.parent().length ? $element.parent() : $element)
|
|
.data('$$ionicScrollController', this);
|
|
|
|
var deregisterInstance = $ionicScrollDelegate._registerInstance(
|
|
this, scrollViewOptions.delegateHandle
|
|
);
|
|
|
|
if (!angular.isDefined(scrollViewOptions.bouncing)) {
|
|
ionic.Platform.ready(function() {
|
|
scrollView.options.bouncing = !ionic.Platform.isAndroid();
|
|
});
|
|
}
|
|
|
|
var resize = angular.bind(scrollView, scrollView.resize);
|
|
ionic.on('resize', resize, $window);
|
|
|
|
// set by rootScope listener if needed
|
|
var backListenDone = angular.noop;
|
|
|
|
$scope.$on('$destroy', function() {
|
|
deregisterInstance();
|
|
ionic.off('resize', resize, $window);
|
|
$window.removeEventListener('resize', resize);
|
|
backListenDone();
|
|
if (self._rememberScrollId) {
|
|
$$scrollValueCache[self._rememberScrollId] = scrollView.getValues();
|
|
}
|
|
});
|
|
|
|
$element.on('scroll', function(e) {
|
|
var detail = (e.originalEvent || e).detail || {};
|
|
$scope.$onScroll && $scope.$onScroll({
|
|
event: e,
|
|
scrollTop: detail.scrollTop || 0,
|
|
scrollLeft: detail.scrollLeft || 0
|
|
});
|
|
});
|
|
|
|
$scope.$on('$viewContentLoaded', function(e, historyData) {
|
|
//only the top-most scroll area under a view should remember that view's
|
|
//scroll position
|
|
if (e.defaultPrevented) { return; }
|
|
e.preventDefault();
|
|
|
|
var viewId = historyData && historyData.viewId;
|
|
if (viewId) {
|
|
self.rememberScrollPosition(viewId);
|
|
self.scrollToRememberedPosition();
|
|
|
|
backListenDone = $rootScope.$on('$viewHistory.viewBack', function(e, fromViewId, toViewId) {
|
|
//When going back from this view, forget its saved scroll position
|
|
if (viewId === fromViewId) {
|
|
self.forgetScrollPosition();
|
|
}
|
|
});
|
|
}
|
|
});
|
|
|
|
$timeout(function() {
|
|
scrollView.run();
|
|
});
|
|
|
|
this._rememberScrollId = null;
|
|
|
|
this.resize = function() {
|
|
return $timeout(resize);
|
|
};
|
|
|
|
this.scrollTop = function(shouldAnimate) {
|
|
this.resize().then(function() {
|
|
scrollView.scrollTo(0, 0, !!shouldAnimate);
|
|
});
|
|
};
|
|
|
|
this.scrollBottom = function(shouldAnimate) {
|
|
this.resize().then(function() {
|
|
var max = scrollView.getScrollMax();
|
|
scrollView.scrollTo(max.left, max.top, !!shouldAnimate);
|
|
});
|
|
};
|
|
|
|
this.scrollTo = function(left, top, shouldAnimate) {
|
|
this.resize().then(function() {
|
|
scrollView.scrollTo(left, top, !!shouldAnimate);
|
|
});
|
|
};
|
|
|
|
this.anchorScroll = function(shouldAnimate) {
|
|
this.resize().then(function() {
|
|
var hash = $location.hash();
|
|
var elm = hash && $document[0].getElementById(hash);
|
|
if (hash && elm) {
|
|
var scroll = ionic.DomUtil.getPositionInParent(elm, self.$element);
|
|
scrollView.scrollTo(scroll.left, scroll.top, !!shouldAnimate);
|
|
} else {
|
|
scrollView.scrollTo(0,0, !!shouldAnimate);
|
|
}
|
|
});
|
|
};
|
|
|
|
this.rememberScrollPosition = function(id) {
|
|
if (!id) {
|
|
throw new Error("Must supply an id to remember the scroll by!");
|
|
}
|
|
this._rememberScrollId = id;
|
|
};
|
|
this.forgetScrollPosition = function() {
|
|
delete $$scrollValueCache[this._rememberScrollId];
|
|
this._rememberScrollId = null;
|
|
};
|
|
this.scrollToRememberedPosition = function(shouldAnimate) {
|
|
var values = $$scrollValueCache[this._rememberScrollId];
|
|
if (values) {
|
|
this.resize().then(function() {
|
|
scrollView.scrollTo(+values.left, +values.top, shouldAnimate);
|
|
});
|
|
}
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
* @private
|
|
*/
|
|
this._setRefresher = function(refresherScope, refresherElement) {
|
|
var refresher = this.refresher = refresherElement;
|
|
var refresherHeight = self.refresher.clientHeight || 0;
|
|
scrollView.activatePullToRefresh(refresherHeight, function() {
|
|
refresher.classList.add('active');
|
|
refresherScope.$onPulling();
|
|
}, function() {
|
|
refresher.classList.remove('refreshing');
|
|
refresher.classList.remove('active');
|
|
}, function() {
|
|
refresher.classList.add('refreshing');
|
|
refresherScope.$onRefresh();
|
|
});
|
|
};
|
|
}]);
|
|
|