mirror of
https://github.com/ionic-team/ionic-framework.git
synced 2026-03-13 10:22:08 +08:00
feat(swipe): iOS swipe to go back
This commit is contained in:
5
js/angular/controller/headerBarController.js
vendored
5
js/angular/controller/headerBarController.js
vendored
@@ -122,13 +122,14 @@ function($scope, $element, $attrs, $q, $ionicConfig, $ionicHistory) {
|
||||
};
|
||||
|
||||
|
||||
self.resetBackButton = function() {
|
||||
self.resetBackButton = function(viewData) {
|
||||
if ($ionicConfig.backButton.previousTitleText()) {
|
||||
var previousTitleEle = getEle(PREVIOUS_TITLE);
|
||||
if (previousTitleEle) {
|
||||
previousTitleEle.classList.remove(HIDE);
|
||||
|
||||
var newPreviousTitleText = $ionicHistory.backTitle();
|
||||
var view = (viewData && $ionicHistory.getViewById(viewData.viewId));
|
||||
var newPreviousTitleText = $ionicHistory.backTitle(view);
|
||||
|
||||
if (newPreviousTitleText !== previousTitleText) {
|
||||
previousTitleText = previousTitleEle.innerHTML = newPreviousTitleText;
|
||||
|
||||
54
js/angular/controller/navBarController.js
vendored
54
js/angular/controller/navBarController.js
vendored
@@ -239,6 +239,7 @@ function($scope, $element, $attrs, $compile, $timeout, $ionicNavBarDelegate, $io
|
||||
self.transition(enteringHeaderBar, leavingHeaderBar, viewData);
|
||||
|
||||
self.isInitialized = true;
|
||||
navSwipeAttr('');
|
||||
};
|
||||
|
||||
|
||||
@@ -254,17 +255,39 @@ function($scope, $element, $attrs, $compile, $timeout, $ionicNavBarDelegate, $io
|
||||
ionic.DomUtil.cachedAttr($element, 'nav-bar-transition', viewData.navBarTransition);
|
||||
ionic.DomUtil.cachedAttr($element, 'nav-bar-direction', viewData.direction);
|
||||
|
||||
if (navBarTransition.shouldAnimate) {
|
||||
if (navBarTransition.shouldAnimate && viewData.renderEnd) {
|
||||
navBarAttr(enteringHeaderBar, 'stage');
|
||||
} else {
|
||||
navBarAttr(enteringHeaderBar, 'entering');
|
||||
navBarAttr(leavingHeaderBar, 'leaving');
|
||||
}
|
||||
|
||||
enteringHeaderBarCtrl.resetBackButton();
|
||||
enteringHeaderBarCtrl.resetBackButton(viewData);
|
||||
|
||||
navBarTransition.run(0);
|
||||
|
||||
self.activeTransition = {
|
||||
run: function(step) {
|
||||
navBarTransition.shouldAnimate = false;
|
||||
navBarTransition.direction = 'back';
|
||||
navBarTransition.run(step);
|
||||
},
|
||||
cancel: function(shouldAnimate, speed) {
|
||||
navSwipeAttr(speed);
|
||||
navBarAttr(leavingHeaderBar, 'active');
|
||||
navBarAttr(enteringHeaderBar, 'cached');
|
||||
navBarTransition.shouldAnimate = shouldAnimate;
|
||||
navBarTransition.run(0);
|
||||
self.activeTransition = navBarTransition = null;
|
||||
},
|
||||
complete: function(shouldAnimate, speed) {
|
||||
navSwipeAttr(speed);
|
||||
navBarTransition.shouldAnimate = shouldAnimate;
|
||||
navBarTransition.run(1);
|
||||
queuedTransitionEnd = transitionEnd;
|
||||
}
|
||||
};
|
||||
|
||||
$timeout(enteringHeaderBarCtrl.align, 16);
|
||||
|
||||
queuedTransitionStart = function() {
|
||||
@@ -277,23 +300,26 @@ function($scope, $element, $attrs, $compile, $timeout, $ionicNavBarDelegate, $io
|
||||
|
||||
queuedTransitionEnd = function() {
|
||||
if (latestTransitionId == transitionId || !navBarTransition.shouldAnimate) {
|
||||
for (var x = 0; x < headerBars.length; x++) {
|
||||
headerBars[x].isActive = false;
|
||||
}
|
||||
enteringHeaderBar.isActive = true;
|
||||
|
||||
navBarAttr(enteringHeaderBar, 'active');
|
||||
navBarAttr(leavingHeaderBar, 'cached');
|
||||
|
||||
queuedTransitionEnd = null;
|
||||
transitionEnd();
|
||||
}
|
||||
};
|
||||
|
||||
queuedTransitionStart = null;
|
||||
};
|
||||
|
||||
queuedTransitionStart();
|
||||
function transitionEnd() {
|
||||
for (var x = 0; x < headerBars.length; x++) {
|
||||
headerBars[x].isActive = false;
|
||||
}
|
||||
enteringHeaderBar.isActive = true;
|
||||
|
||||
navBarAttr(enteringHeaderBar, 'active');
|
||||
navBarAttr(leavingHeaderBar, 'cached');
|
||||
|
||||
self.activeTransition = navBarTransition = queuedTransitionEnd = null;
|
||||
}
|
||||
|
||||
queuedTransitionStart();
|
||||
};
|
||||
|
||||
|
||||
@@ -437,6 +463,10 @@ function($scope, $element, $attrs, $compile, $timeout, $ionicNavBarDelegate, $io
|
||||
ctrl && ionic.DomUtil.cachedAttr(ctrl.containerEle(), 'nav-bar', val);
|
||||
}
|
||||
|
||||
function navSwipeAttr(val) {
|
||||
ionic.DomUtil.cachedAttr($element, 'nav-swipe', val);
|
||||
}
|
||||
|
||||
|
||||
$scope.$on('$destroy', function() {
|
||||
$scope.$parent.$hasHeader = false;
|
||||
|
||||
169
js/angular/controller/navViewController.js
vendored
169
js/angular/controller/navViewController.js
vendored
@@ -9,7 +9,9 @@ IonicModule
|
||||
'$ionicNavViewDelegate',
|
||||
'$ionicHistory',
|
||||
'$ionicViewSwitcher',
|
||||
function($scope, $element, $attrs, $compile, $controller, $ionicNavBarDelegate, $ionicNavViewDelegate, $ionicHistory, $ionicViewSwitcher) {
|
||||
'$ionicConfig',
|
||||
'$ionicScrollDelegate',
|
||||
function($scope, $element, $attrs, $compile, $controller, $ionicNavBarDelegate, $ionicNavViewDelegate, $ionicHistory, $ionicViewSwitcher, $ionicConfig, $ionicScrollDelegate) {
|
||||
|
||||
var DATA_ELE_IDENTIFIER = '$eleId';
|
||||
var DATA_DESTROY_ELE = '$destroyEle';
|
||||
@@ -23,6 +25,8 @@ function($scope, $element, $attrs, $compile, $controller, $ionicNavBarDelegate,
|
||||
var navBarDelegate;
|
||||
var activeEleId;
|
||||
var navViewAttr = $ionicViewSwitcher.navViewAttr;
|
||||
var disableRenderStartViewId, disableAnimation;
|
||||
var transitionDuration, transitionTiming;
|
||||
|
||||
self.scope = $scope;
|
||||
|
||||
@@ -44,6 +48,12 @@ function($scope, $element, $attrs, $compile, $controller, $ionicNavBarDelegate,
|
||||
|
||||
$scope.$on('$ionicHistory.deselect', self.cacheCleanup);
|
||||
|
||||
ionic.Platform.ready(function() {
|
||||
if (ionic.Platform.isWebView() && $ionicConfig.views.swipeBackEnabled()) {
|
||||
self.initSwipeBack();
|
||||
}
|
||||
});
|
||||
|
||||
return viewData;
|
||||
};
|
||||
|
||||
@@ -58,7 +68,10 @@ function($scope, $element, $attrs, $compile, $controller, $ionicNavBarDelegate,
|
||||
self.update(registerData);
|
||||
|
||||
// begin rendering and transitioning
|
||||
self.render(registerData, viewLocals, leavingView);
|
||||
var enteringView = $ionicHistory.getViewById(registerData.viewId) || {};
|
||||
|
||||
var renderStart = (disableRenderStartViewId !== registerData.viewId);
|
||||
self.render(registerData, viewLocals, enteringView, leavingView, renderStart, true);
|
||||
};
|
||||
|
||||
|
||||
@@ -94,19 +107,22 @@ function($scope, $element, $attrs, $compile, $controller, $ionicNavBarDelegate,
|
||||
};
|
||||
|
||||
|
||||
self.render = function(registerData, viewLocals, leavingView) {
|
||||
var enteringView = $ionicHistory.getViewById(registerData.viewId) || {};
|
||||
|
||||
self.render = function(registerData, viewLocals, enteringView, leavingView, renderStart, renderEnd) {
|
||||
// register the view and figure out where it lives in the various
|
||||
// histories and nav stacks, along with how views should enter/leave
|
||||
var switcher = $ionicViewSwitcher.create(self, viewLocals, enteringView, leavingView);
|
||||
var switcher = $ionicViewSwitcher.create(self, viewLocals, enteringView, leavingView, renderStart, renderEnd);
|
||||
|
||||
// init the rendering of views for this navView directive
|
||||
switcher.init(registerData, function() {
|
||||
// the view is now compiled, in the dom and linked, now lets transition the views.
|
||||
// this uses a callback incase THIS nav-view has a nested nav-view, and after the NESTED
|
||||
// nav-view links, the NESTED nav-view would update which direction THIS nav-view should use
|
||||
switcher.transition(self.direction(), registerData.enableBack);
|
||||
|
||||
// kick off the transition of views
|
||||
switcher.transition(self.direction(), registerData.enableBack, !disableAnimation);
|
||||
|
||||
// reset private vars for next time
|
||||
disableRenderStartViewId = disableAnimation = null;
|
||||
});
|
||||
|
||||
};
|
||||
@@ -118,6 +134,7 @@ function($scope, $element, $attrs, $compile, $controller, $ionicNavBarDelegate,
|
||||
navBarDelegate = transitionData.navBarDelegate;
|
||||
var associatedNavBarCtrl = getAssociatedNavBarCtrl();
|
||||
associatedNavBarCtrl && associatedNavBarCtrl.update(transitionData);
|
||||
navSwipeAttr('');
|
||||
}
|
||||
};
|
||||
|
||||
@@ -152,6 +169,8 @@ function($scope, $element, $attrs, $compile, $controller, $ionicNavBarDelegate,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
navSwipeAttr('');
|
||||
};
|
||||
|
||||
|
||||
@@ -261,6 +280,142 @@ function($scope, $element, $attrs, $compile, $controller, $ionicNavBarDelegate,
|
||||
};
|
||||
|
||||
|
||||
self.initSwipeBack = function() {
|
||||
var swipeBackHitWidth = $ionicConfig.views.swipeBackHitWidth();
|
||||
var viewTransition, associatedNavBarCtrl, backView;
|
||||
var deregDragStart, deregDrag, deregRelease;
|
||||
var windowWidth, startDragX, dragPoints;
|
||||
|
||||
function onDragStart(ev) {
|
||||
if (!isPrimary) return;
|
||||
|
||||
startDragX = getDragX(ev);
|
||||
if (startDragX > swipeBackHitWidth) return;
|
||||
|
||||
backView = $ionicHistory.backView();
|
||||
|
||||
if (!backView || backView.historyId !== $ionicHistory.currentView().historyId) return;
|
||||
|
||||
if (!windowWidth) windowWidth = window.innerWidth;
|
||||
|
||||
freezeScrolls(true);
|
||||
|
||||
var registerData = {
|
||||
direction: 'back'
|
||||
};
|
||||
|
||||
dragPoints = [];
|
||||
|
||||
var switcher = $ionicViewSwitcher.create(self, registerData, backView, $ionicHistory.currentView(), true, false);
|
||||
switcher.loadViewElements(registerData);
|
||||
switcher.render(registerData);
|
||||
|
||||
viewTransition = switcher.transition('back', $ionicHistory.enabledBack(backView), true);
|
||||
|
||||
associatedNavBarCtrl = getAssociatedNavBarCtrl();
|
||||
|
||||
deregDrag = ionic.onGesture('drag', onDrag, $element[0]);
|
||||
deregRelease = ionic.onGesture('release', onRelease, $element[0]);
|
||||
}
|
||||
|
||||
function onDrag(ev) {
|
||||
if (isPrimary && viewTransition) {
|
||||
var dragX = getDragX(ev);
|
||||
|
||||
dragPoints.push({
|
||||
t: Date.now(),
|
||||
x: dragX
|
||||
});
|
||||
|
||||
if (dragX >= windowWidth - 15) {
|
||||
onRelease(ev);
|
||||
|
||||
} else {
|
||||
var step = Math.min(Math.max(getSwipeCompletion(dragX), 0), 1);
|
||||
viewTransition.run(step);
|
||||
associatedNavBarCtrl && associatedNavBarCtrl.activeTransition && associatedNavBarCtrl.activeTransition.run(step);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
function onRelease(ev) {
|
||||
if (isPrimary && viewTransition && dragPoints && dragPoints.length > 1) {
|
||||
|
||||
var now = Date.now();
|
||||
var releaseX = getDragX(ev);
|
||||
var startDrag = dragPoints[dragPoints.length - 1];
|
||||
|
||||
for (var x = dragPoints.length - 2; x >= 0; x--) {
|
||||
if (now - startDrag.t > 200) {
|
||||
break;
|
||||
}
|
||||
startDrag = dragPoints[x];
|
||||
}
|
||||
|
||||
var isSwipingRight = (releaseX >= dragPoints[dragPoints.length - 2].x);
|
||||
var releaseSwipeCompletion = getSwipeCompletion(releaseX);
|
||||
var velocity = Math.abs(startDrag.x - releaseX) / (now - startDrag.t);
|
||||
|
||||
// private variables because ui-router has no way to pass custom data using $state.go
|
||||
disableRenderStartViewId = backView.viewId;
|
||||
disableAnimation = (releaseSwipeCompletion < 0.03 || releaseSwipeCompletion > 0.97);
|
||||
|
||||
if (isSwipingRight && (releaseSwipeCompletion > 0.5 || velocity > 0.1)) {
|
||||
var speed = (velocity > 0.5 || velocity < 0.05 || releaseX > windowWidth - 45) ? 'fast' : 'slow';
|
||||
navSwipeAttr(disableAnimation ? '' : speed);
|
||||
backView.go();
|
||||
associatedNavBarCtrl && associatedNavBarCtrl.activeTransition && associatedNavBarCtrl.activeTransition.complete(!disableAnimation, speed);
|
||||
|
||||
} else {
|
||||
navSwipeAttr(disableAnimation ? '' : 'fast');
|
||||
disableRenderStartViewId = null;
|
||||
viewTransition.cancel(!disableAnimation);
|
||||
associatedNavBarCtrl && associatedNavBarCtrl.activeTransition && associatedNavBarCtrl.activeTransition.cancel(!disableAnimation, 'fast');
|
||||
disableAnimation = null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
ionic.offGesture(deregDrag, 'drag', onDrag);
|
||||
ionic.offGesture(deregRelease, 'release', onRelease);
|
||||
|
||||
windowWidth = viewTransition = dragPoints = null;
|
||||
|
||||
freezeScrolls(false);
|
||||
}
|
||||
|
||||
function getDragX(ev) {
|
||||
return ionic.tap.pointerCoord(ev.gesture.srcEvent).x;
|
||||
}
|
||||
|
||||
function getSwipeCompletion(dragX) {
|
||||
return (dragX - startDragX) / windowWidth;
|
||||
}
|
||||
|
||||
deregDragStart = ionic.onGesture('dragstart', onDragStart, $element[0]);
|
||||
|
||||
$scope.$on('$destroy', function() {
|
||||
ionic.offGesture(deregDragStart, 'dragstart', onDragStart);
|
||||
ionic.offGesture(deregDrag, 'drag', onDrag);
|
||||
ionic.offGesture(deregRelease, 'release', onRelease);
|
||||
viewTransition = associatedNavBarCtrl = null;
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
function freezeScrolls(freeze) {
|
||||
forEach($ionicScrollDelegate._instances, function(instance) {
|
||||
instance.freezeScroll(freeze);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
function navSwipeAttr(val) {
|
||||
ionic.DomUtil.cachedAttr($element, 'nav-swipe', val);
|
||||
}
|
||||
|
||||
|
||||
function getAssociatedNavBarCtrl() {
|
||||
if (navBarDelegate) {
|
||||
for (var x=0; x < $ionicNavBarDelegate._instances.length; x++) {
|
||||
|
||||
26
js/angular/controller/scrollController.js
vendored
26
js/angular/controller/scrollController.js
vendored
@@ -45,7 +45,7 @@ function($scope,
|
||||
}
|
||||
);
|
||||
|
||||
if (!angular.isDefined(scrollViewOptions.bouncing)) {
|
||||
if (!isDefined(scrollViewOptions.bouncing)) {
|
||||
ionic.Platform.ready(function() {
|
||||
if (scrollView.options) {
|
||||
scrollView.options.bouncing = true;
|
||||
@@ -76,22 +76,11 @@ function($scope,
|
||||
|
||||
$scope.$on('$destroy', function() {
|
||||
deregisterInstance();
|
||||
//Windows: make sure the scrollView.__cleanup exists before calling it
|
||||
if (scrollView.__cleanup) {
|
||||
scrollView.__cleanup();
|
||||
}
|
||||
scrollView && scrollView.__cleanup && scrollView.__cleanup();
|
||||
ionic.off('resize', resize, $window);
|
||||
$window.removeEventListener('resize', resize);
|
||||
scrollViewOptions = null;
|
||||
self._scrollViewOptions.el = null;
|
||||
self._scrollViewOptions = null;
|
||||
$element.off('scroll', scrollFunc);
|
||||
$element = null;
|
||||
self.$element = null;
|
||||
element = null;
|
||||
self.element = null;
|
||||
self.scrollView = null;
|
||||
scrollView = null;
|
||||
scrollView = self.scrollView = scrollViewOptions = self._scrollViewOptions = scrollViewOptions.el = self._scrollViewOptions.el = $element = self.$element = element = null;
|
||||
});
|
||||
|
||||
$timeout(function() {
|
||||
@@ -99,11 +88,11 @@ function($scope,
|
||||
});
|
||||
|
||||
self.getScrollView = function() {
|
||||
return self.scrollView;
|
||||
return scrollView;
|
||||
};
|
||||
|
||||
self.getScrollPosition = function() {
|
||||
return self.scrollView.getValues();
|
||||
return scrollView.getValues();
|
||||
};
|
||||
|
||||
self.resize = function() {
|
||||
@@ -169,6 +158,11 @@ function($scope,
|
||||
});
|
||||
};
|
||||
|
||||
self.freezeScroll = function(shouldFreeze) {
|
||||
if (arguments.length) scrollView.options.freeze = shouldFreeze;
|
||||
return scrollView.options.freeze;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @private
|
||||
|
||||
10
js/angular/controller/viewController.js
vendored
10
js/angular/controller/viewController.js
vendored
@@ -51,20 +51,14 @@ function($scope, $element, $attrs, $compile, $rootScope, $ionicViewSwitcher) {
|
||||
navBarItems[n] = generateNavBarItem(navElementHtml[n]);
|
||||
}
|
||||
|
||||
navViewCtrl.beforeEnter({
|
||||
navViewCtrl.beforeEnter(extend(transData, {
|
||||
title: viewTitle,
|
||||
direction: transData.direction,
|
||||
transition: transData.transition,
|
||||
navBarTransition: transData.navBarTransition,
|
||||
transitionId: transData.transitionId,
|
||||
shouldAnimate: transData.shouldAnimate,
|
||||
enableBack: transData.enableBack,
|
||||
showBack: !attrTrue('hideBackButton'),
|
||||
navBarItems: navBarItems,
|
||||
navBarDelegate: navBarDelegateHandle || null,
|
||||
showNavBar: !attrTrue('hideNavBar'),
|
||||
hasHeaderBar: !!hasViewHeaderBar
|
||||
});
|
||||
}));
|
||||
|
||||
// make sure any existing observers are cleaned up
|
||||
deregisterFns();
|
||||
|
||||
Reference in New Issue
Block a user