mirror of
https://github.com/ionic-team/ionic-framework.git
synced 2026-03-13 10:22:08 +08:00
feat(refresher): Improve refresher animation. Allow pulling icon rotation to be disabled.
This commit is contained in:
4
js/angular/controller/scrollController.js
vendored
4
js/angular/controller/scrollController.js
vendored
@@ -224,6 +224,7 @@ function($scope, scrollViewOptions, $timeout, $window, $$scrollValueCache, $loca
|
||||
$timeout(function(){
|
||||
refresher.classList.remove('active');
|
||||
refresher.classList.remove('refreshing');
|
||||
refresher.classList.remove('refreshing-tail');
|
||||
refresher.classList.add('invisible');
|
||||
},300);
|
||||
}, function() {
|
||||
@@ -236,6 +237,9 @@ function($scope, scrollViewOptions, $timeout, $window, $$scrollValueCache, $loca
|
||||
},function(){
|
||||
// hideCallback
|
||||
refresher.classList.add('invisible');
|
||||
},function(){
|
||||
// tailCallback
|
||||
refresher.classList.add('refreshing-tail');
|
||||
});
|
||||
};
|
||||
}]);
|
||||
|
||||
7
js/angular/directive/refresher.js
vendored
7
js/angular/directive/refresher.js
vendored
@@ -55,6 +55,8 @@
|
||||
* refresher.
|
||||
* @param {string=} refreshing-text The text to display after the user lets go of
|
||||
* the refresher.
|
||||
* @param {boolean=} disable-pulling-rotation Disables the rotation animation of the pulling
|
||||
* icon when it reaches its activated threshold. To be used with a custom `pulling-icon`.
|
||||
*
|
||||
*/
|
||||
IonicModule
|
||||
@@ -67,7 +69,7 @@ IonicModule
|
||||
'<div class="scroll-refresher" collection-repeat-ignore>' +
|
||||
'<div class="ionic-refresher-content" ' +
|
||||
'ng-class="{\'ionic-refresher-with-text\': pullingText || refreshingText}">' +
|
||||
'<div class="icon-pulling">' +
|
||||
'<div class="icon-pulling" ng-class="{\'pulling-rotation-disabled\':disablePullingRotation}">' +
|
||||
'<i class="icon {{pullingIcon}}"></i>' +
|
||||
'</div>' +
|
||||
'<div class="text-pulling" ng-bind-html="pullingText"></div>' +
|
||||
@@ -77,7 +79,7 @@ IonicModule
|
||||
'</div>',
|
||||
compile: function($element, $attrs) {
|
||||
if (angular.isUndefined($attrs.pullingIcon)) {
|
||||
$attrs.$set('pullingIcon', 'ion-arrow-down-c');
|
||||
$attrs.$set('pullingIcon', 'ion-ios7-arrow-down');
|
||||
}
|
||||
if (angular.isUndefined($attrs.refreshingIcon)) {
|
||||
$attrs.$set('refreshingIcon', 'ion-loading-d');
|
||||
@@ -88,6 +90,7 @@ IonicModule
|
||||
pullingText: '@',
|
||||
refreshingIcon: '@',
|
||||
refreshingText: '@',
|
||||
disablePullingRotation: '@',
|
||||
$onRefresh: '&onRefresh',
|
||||
$onPulling: '&onPulling'
|
||||
});
|
||||
|
||||
@@ -687,13 +687,11 @@ ionic.views.Scroll = ionic.views.View.inherit({
|
||||
|
||||
self.resetScrollView = function(e) {
|
||||
//return scrollview to original height once keyboard has hidden
|
||||
if(self.isScrolledIntoView) {
|
||||
self.isScrolledIntoView = false;
|
||||
container.style.height = "";
|
||||
container.style.overflow = "";
|
||||
self.resize();
|
||||
ionic.scroll.isScrolling = false;
|
||||
}
|
||||
self.isScrolledIntoView = false;
|
||||
container.style.height = "";
|
||||
container.style.overflow = "";
|
||||
self.resize();
|
||||
ionic.scroll.isScrolling = false;
|
||||
};
|
||||
|
||||
//Broadcasted when keyboard is shown on some platforms.
|
||||
@@ -1116,8 +1114,6 @@ ionic.views.Scroll = ionic.views.View.inherit({
|
||||
},
|
||||
|
||||
resize: function() {
|
||||
if(!this.__container || !this.options) return;
|
||||
|
||||
// Update Scroller dimensions for changed content
|
||||
// Add padding to bottom of content
|
||||
this.setDimensions(
|
||||
@@ -1286,17 +1282,21 @@ ionic.views.Scroll = ionic.views.View.inherit({
|
||||
* @param startCallback {Function} Callback to execute to start the real async refresh action. Call {@link #finishPullToRefresh} after finish of refresh.
|
||||
* @param showCallback {Function} Callback to execute when the refresher should be shown. This is for showing the refresher during a negative scrollTop.
|
||||
* @param hideCallback {Function} Callback to execute when the refresher should be hidden. This is for hiding the refresher when it's behind the nav bar.
|
||||
* @param tailCallback {Function} Callback to execute just before the refresher returns to it's original state. This is for zooming out the refresher.
|
||||
*/
|
||||
activatePullToRefresh: function(height, activateCallback, deactivateCallback, startCallback, showCallback, hideCallback) {
|
||||
activatePullToRefresh: function(height, activateCallback, deactivateCallback, startCallback, showCallback, hideCallback, tailCallback) {
|
||||
|
||||
var self = this;
|
||||
|
||||
self.__refreshHeight = height;
|
||||
self.__refreshActivate = activateCallback;
|
||||
self.__refreshDeactivate = deactivateCallback;
|
||||
self.__refreshStart = startCallback;
|
||||
self.__refreshShow = showCallback;
|
||||
self.__refreshHide = hideCallback;
|
||||
self.__refreshActivate = function(){ionic.requestAnimationFrame(activateCallback);};
|
||||
self.__refreshDeactivate = function(){ionic.requestAnimationFrame(deactivateCallback);};
|
||||
self.__refreshStart = function(){ionic.requestAnimationFrame(startCallback);};
|
||||
self.__refreshShow = function(){ionic.requestAnimationFrame(showCallback);};
|
||||
self.__refreshHide = function(){ionic.requestAnimationFrame(hideCallback);};
|
||||
self.__refreshTail = function(){ionic.requestAnimationFrame(tailCallback);};
|
||||
self.__refreshTailTime = 100;
|
||||
self.__minSpinTime = 600;
|
||||
},
|
||||
|
||||
|
||||
@@ -1308,6 +1308,9 @@ ionic.views.Scroll = ionic.views.View.inherit({
|
||||
// We don't need to normalize scrollLeft, zoomLevel, etc. here because we only y-scrolling when pull-to-refresh is enabled
|
||||
this.__publish(this.__scrollLeft, -this.__refreshHeight, this.__zoomLevel, true);
|
||||
|
||||
var d = new Date();
|
||||
self.refreshStartTime = d.getTime();
|
||||
|
||||
if (this.__refreshStart) {
|
||||
this.__refreshStart();
|
||||
}
|
||||
@@ -1320,14 +1323,25 @@ ionic.views.Scroll = ionic.views.View.inherit({
|
||||
finishPullToRefresh: function() {
|
||||
|
||||
var self = this;
|
||||
|
||||
self.__refreshActive = false;
|
||||
if (self.__refreshDeactivate) {
|
||||
self.__refreshDeactivate();
|
||||
// delay to make sure the spinner has a chance to spin for a split second before it's dismissed
|
||||
var d = new Date();
|
||||
var delay = 0;
|
||||
if(self.refreshStartTime + self.__minSpinTime > d.getTime()){
|
||||
delay = self.refreshStartTime + self.__minSpinTime - d.getTime();
|
||||
}
|
||||
setTimeout(function(){
|
||||
if(self.__refreshTail){
|
||||
self.__refreshTail();
|
||||
}
|
||||
setTimeout(function(){
|
||||
self.__refreshActive = false;
|
||||
if (self.__refreshDeactivate) {
|
||||
self.__refreshDeactivate();
|
||||
}
|
||||
|
||||
self.scrollTo(self.__scrollLeft, self.__scrollTop, true);
|
||||
|
||||
self.scrollTo(self.__scrollLeft, self.__scrollTop, true);
|
||||
},self.__refreshTailTime);
|
||||
},delay);
|
||||
},
|
||||
|
||||
|
||||
@@ -1939,10 +1953,14 @@ ionic.views.Scroll = ionic.views.View.inherit({
|
||||
// We don't need to normalize scrollLeft, zoomLevel, etc. here because we only y-scrolling when pull-to-refresh is enabled
|
||||
self.__publish(self.__scrollLeft, -self.__refreshHeight, self.__zoomLevel, true);
|
||||
|
||||
var d = new Date();
|
||||
self.refreshStartTime = d.getTime();
|
||||
|
||||
if (self.__refreshStart) {
|
||||
self.__refreshStart();
|
||||
}
|
||||
|
||||
// for iOS-ey style scrolling
|
||||
if(!ionic.Platform.isAndroid())self.__startDeceleration();
|
||||
} else {
|
||||
|
||||
if (self.__interruptedAnimation || self.__isDragging) {
|
||||
@@ -2139,7 +2157,7 @@ ionic.views.Scroll = ionic.views.View.inherit({
|
||||
self.__minDecelerationScrollTop = 0;
|
||||
self.__maxDecelerationScrollLeft = self.__maxScrollLeft;
|
||||
self.__maxDecelerationScrollTop = self.__maxScrollTop;
|
||||
|
||||
if(self.__refreshActive) self.__minDecelerationScrollTop = self.__refreshHeight *-1;
|
||||
}
|
||||
|
||||
// Wrap class method
|
||||
@@ -2160,11 +2178,11 @@ ionic.views.Scroll = ionic.views.View.inherit({
|
||||
|
||||
//Make sure the scroll values are within the boundaries after a bounce,
|
||||
//not below 0 or above maximum
|
||||
if (self.options.bouncing) {
|
||||
if (self.options.bouncing && !self.__refreshActive) {
|
||||
self.scrollTo(
|
||||
Math.min( Math.max(self.__scrollLeft, 0), self.__maxScrollLeft ),
|
||||
Math.min( Math.max(self.__scrollTop, 0), self.__maxScrollTop ),
|
||||
false
|
||||
self.__refreshActive
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -157,21 +157,21 @@ body.grade-c {
|
||||
|
||||
@keyframes refresh-spin {
|
||||
0% { transform: translate3d(0,0,0) rotate(0); }
|
||||
100% { transform: translate3d(0,0,0) rotate(-180deg); }
|
||||
100% { transform: translate3d(0,0,0) rotate(180deg); }
|
||||
}
|
||||
|
||||
@-webkit-keyframes refresh-spin {
|
||||
0% {-webkit-transform: translate3d(0,0,0) rotate(0); }
|
||||
100% {-webkit-transform: translate3d(0,0,0) rotate(-180deg); }
|
||||
100% {-webkit-transform: translate3d(0,0,0) rotate(180deg); }
|
||||
}
|
||||
|
||||
@keyframes refresh-spin-back {
|
||||
0% { transform: translate3d(0,0,0) rotate(-180deg); }
|
||||
0% { transform: translate3d(0,0,0) rotate(180deg); }
|
||||
100% { transform: translate3d(0,0,0) rotate(0); }
|
||||
}
|
||||
|
||||
@-webkit-keyframes refresh-spin-back {
|
||||
0% {-webkit-transform: translate3d(0,0,0) rotate(-180deg); }
|
||||
0% {-webkit-transform: translate3d(0,0,0) rotate(180deg); }
|
||||
100% {-webkit-transform: translate3d(0,0,0) rotate(0); }
|
||||
}
|
||||
|
||||
@@ -230,12 +230,16 @@ body.grade-c {
|
||||
}
|
||||
|
||||
&.active {
|
||||
.icon-pulling {
|
||||
.icon-pulling:not(.pulling-rotation-disabled) {
|
||||
@include animation-name(refresh-spin);
|
||||
-webkit-transform: translate3d(0,0,0) rotate(-180deg);
|
||||
transform: translate3d(0,0,0) rotate(-180deg);
|
||||
}
|
||||
&.refreshing {
|
||||
@include transition(transform .2s);
|
||||
@include transition(-webkit-transform .2s);
|
||||
-webkit-transform: scale(1,1);
|
||||
transform: scale(1,1);
|
||||
.icon-pulling,
|
||||
.text-pulling {
|
||||
display: none;
|
||||
@@ -244,6 +248,10 @@ body.grade-c {
|
||||
.text-refreshing {
|
||||
display: block;
|
||||
}
|
||||
&.refreshing-tail{
|
||||
-webkit-transform: scale(0,0);
|
||||
transform: scale(0,0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
41
test/html/pull-to-refresh.html
Normal file
41
test/html/pull-to-refresh.html
Normal file
@@ -0,0 +1,41 @@
|
||||
<html ng-app="ionicApp">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="initial-scale=1, maximum-scale=1, user-scalable=no, width=device-width">
|
||||
|
||||
<title>Ionic Pull to Refresh</title>
|
||||
|
||||
<link rel="stylesheet" href="../../dist/css/ionic.css">
|
||||
</head>
|
||||
<body ng-controller="MyCtrl">
|
||||
|
||||
<ion-header-bar class="bar-positive">
|
||||
<h1 class="title">Pull To Refresh</h1>
|
||||
</ion-header-bar>
|
||||
|
||||
<ion-content>
|
||||
<ion-refresher on-refresh="doRefresh()">
|
||||
|
||||
</ion-refresher>
|
||||
<ion-list>
|
||||
<ion-item ng-repeat="item in items">{{item}}</ion-item>
|
||||
</ion-list>
|
||||
</ion-content>
|
||||
<script src="../../dist/js/ionic.bundle.js"></script>
|
||||
<script>
|
||||
angular.module('ionicApp', ['ionic'])
|
||||
|
||||
.controller('MyCtrl', function($scope, $timeout) {
|
||||
$scope.items = ['Item 1', 'Item 2', 'Item 3'];
|
||||
$scope.doRefresh = function() {
|
||||
$timeout( function() {
|
||||
//simulate async response
|
||||
$scope.items.push('New Item ' + Math.floor(Math.random() * 1000) + 4);
|
||||
//Stop the ion-refresher from spinning
|
||||
$scope.$broadcast('scroll.refreshComplete');
|
||||
}, 30);
|
||||
};
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -70,7 +70,7 @@ describe('ionRefresher directive', function() {
|
||||
|
||||
it('should have default pullingIcon', function() {
|
||||
var el = setup();
|
||||
expect(el[0].querySelector('.icon-pulling .ion-arrow-down-c')).toBeTruthy();
|
||||
expect(el[0].querySelector('.icon-pulling .ion-ios7-arrow-down')).toBeTruthy();
|
||||
});
|
||||
it('should allow custom pullingIcon', function() {
|
||||
var el = setup('pulling-icon="super-icon"');
|
||||
@@ -97,4 +97,8 @@ describe('ionRefresher directive', function() {
|
||||
expect(el[0].querySelector('.text-refreshing').innerHTML).toBe('5 <b>text</b>');
|
||||
});
|
||||
|
||||
it('should allow pulling rotation animation to be disabled', function() {
|
||||
var el = setup('disable-pulling-rotation="true"');
|
||||
expect(el[0].querySelector('.pulling-rotation-disabled').innerHTML).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user