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();
|
||||
|
||||
11
js/angular/service/clickBlock.js
vendored
11
js/angular/service/clickBlock.js
vendored
@@ -7,6 +7,11 @@ function($document, $ionicBody, $timeout) {
|
||||
var CSS_HIDE = 'click-block-hide';
|
||||
var cbEle, fallbackTimer, pendingShow;
|
||||
|
||||
function preventClick(ev) {
|
||||
ev.preventDefault();
|
||||
ev.stopPropagation();
|
||||
}
|
||||
|
||||
function addClickBlock() {
|
||||
if (pendingShow) {
|
||||
if (cbEle) {
|
||||
@@ -15,6 +20,8 @@ function($document, $ionicBody, $timeout) {
|
||||
cbEle = $document[0].createElement('div');
|
||||
cbEle.className = 'click-block';
|
||||
$ionicBody.append(cbEle);
|
||||
cbEle.addEventListener('touchstart', preventClick);
|
||||
cbEle.addEventListener('mousedown', preventClick);
|
||||
}
|
||||
pendingShow = false;
|
||||
}
|
||||
@@ -29,12 +36,12 @@ function($document, $ionicBody, $timeout) {
|
||||
pendingShow = true;
|
||||
$timeout.cancel(fallbackTimer);
|
||||
fallbackTimer = $timeout(this.hide, autoExpire || 310);
|
||||
ionic.requestAnimationFrame(addClickBlock);
|
||||
addClickBlock();
|
||||
},
|
||||
hide: function() {
|
||||
pendingShow = false;
|
||||
$timeout.cancel(fallbackTimer);
|
||||
ionic.requestAnimationFrame(removeClickBlock);
|
||||
removeClickBlock();
|
||||
}
|
||||
};
|
||||
}]);
|
||||
|
||||
17
js/angular/service/history.js
vendored
17
js/angular/service/history.js
vendored
@@ -410,7 +410,7 @@ function($rootScope, $state, $location, $window, $timeout, $ionicViewSwitcher, $
|
||||
action: action,
|
||||
direction: direction,
|
||||
historyId: historyId,
|
||||
enableBack: !!(viewHistory.backView && viewHistory.backView.historyId === viewHistory.currentView.historyId),
|
||||
enableBack: this.enabledBack(viewHistory.currentView),
|
||||
isHistoryRoot: (viewHistory.currentView.index === 0),
|
||||
ele: ele
|
||||
};
|
||||
@@ -498,10 +498,9 @@ function($rootScope, $state, $location, $window, $timeout, $ionicViewSwitcher, $
|
||||
* @description Gets the back view's title.
|
||||
* @returns {string} Returns the back view's title.
|
||||
*/
|
||||
backTitle: function() {
|
||||
if (viewHistory.backView) {
|
||||
return viewHistory.backView.title;
|
||||
}
|
||||
backTitle: function(view) {
|
||||
var backView = (view && getViewById(view.backViewId)) || viewHistory.backView;
|
||||
return backView && backView.title;
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -560,6 +559,12 @@ function($rootScope, $state, $location, $window, $timeout, $ionicViewSwitcher, $
|
||||
viewHistory.backView && viewHistory.backView.go();
|
||||
},
|
||||
|
||||
|
||||
enabledBack: function(view) {
|
||||
var backView = getBackView(view);
|
||||
return !!(backView && backView.historyId === view.historyId);
|
||||
},
|
||||
|
||||
/**
|
||||
* @ngdoc method
|
||||
* @name $ionicHistory#clearHistory
|
||||
@@ -643,7 +648,7 @@ function($rootScope, $state, $location, $window, $timeout, $ionicViewSwitcher, $
|
||||
nextViewOptions = nextViewOptions || {};
|
||||
extend(nextViewOptions, opts);
|
||||
if (nextViewOptions.expire) {
|
||||
nextViewExpireTimer = $timeout(function(){
|
||||
nextViewExpireTimer = $timeout(function() {
|
||||
nextViewOptions = null;
|
||||
}, nextViewOptions.expire);
|
||||
}
|
||||
|
||||
64
js/angular/service/ionicConfig.js
vendored
64
js/angular/service/ionicConfig.js
vendored
@@ -222,7 +222,9 @@ IonicModule
|
||||
views: {
|
||||
maxCache: PLATFORM,
|
||||
forwardCache: PLATFORM,
|
||||
transition: PLATFORM
|
||||
transition: PLATFORM,
|
||||
swipeBackEnabled: PLATFORM,
|
||||
swipeBackHitWidth: PLATFORM
|
||||
},
|
||||
navBar: {
|
||||
alignTitle: PLATFORM,
|
||||
@@ -262,7 +264,9 @@ IonicModule
|
||||
views: {
|
||||
maxCache: 10,
|
||||
forwardCache: false,
|
||||
transition: 'ios'
|
||||
transition: 'ios',
|
||||
swipeBackEnabled: true,
|
||||
swipeBackHitWidth: 45
|
||||
},
|
||||
|
||||
navBar: {
|
||||
@@ -311,7 +315,8 @@ IonicModule
|
||||
setPlatformConfig('android', {
|
||||
|
||||
views: {
|
||||
transition: 'android'
|
||||
transition: 'android',
|
||||
swipeBackEnabled: false
|
||||
},
|
||||
|
||||
navBar: {
|
||||
@@ -348,42 +353,45 @@ IonicModule
|
||||
// iOS Transitions
|
||||
// -----------------------
|
||||
provider.transitions.views.ios = function(enteringEle, leavingEle, direction, shouldAnimate) {
|
||||
shouldAnimate = shouldAnimate && (direction == 'forward' || direction == 'back');
|
||||
|
||||
function setStyles(ele, opacity, x) {
|
||||
function setStyles(ele, opacity, x, boxShadowOpacity) {
|
||||
var css = {};
|
||||
css[ionic.CSS.TRANSITION_DURATION] = shouldAnimate ? '' : 0;
|
||||
css[ionic.CSS.TRANSITION_DURATION] = d.shouldAnimate ? '' : 0;
|
||||
css.opacity = opacity;
|
||||
if (boxShadowOpacity > -1) {
|
||||
css.boxShadow = '0 0 10px rgba(0,0,0,' + (d.shouldAnimate ? boxShadowOpacity * 0.45 : 0.3) + ')';
|
||||
}
|
||||
css[ionic.CSS.TRANSFORM] = 'translate3d(' + x + '%,0,0)';
|
||||
ionic.DomUtil.cachedStyles(ele, css);
|
||||
}
|
||||
|
||||
return {
|
||||
var d = {
|
||||
run: function(step) {
|
||||
if (direction == 'forward') {
|
||||
setStyles(enteringEle, 1, (1 - step) * 99); // starting at 98% prevents a flicker
|
||||
setStyles(leavingEle, (1 - 0.1 * step), step * -33);
|
||||
setStyles(enteringEle, 1, (1 - step) * 99, 1 - step); // starting at 98% prevents a flicker
|
||||
setStyles(leavingEle, (1 - 0.1 * step), step * -33, -1);
|
||||
|
||||
} else if (direction == 'back') {
|
||||
setStyles(enteringEle, (1 - 0.1 * (1 - step)), (1 - step) * -33);
|
||||
setStyles(leavingEle, 1, step * 100);
|
||||
setStyles(enteringEle, (1 - 0.1 * (1 - step)), (1 - step) * -33, -1);
|
||||
setStyles(leavingEle, 1, step * 100, 1 - step);
|
||||
|
||||
} else {
|
||||
// swap, enter, exit
|
||||
setStyles(enteringEle, 1, 0);
|
||||
setStyles(leavingEle, 0, 0);
|
||||
setStyles(enteringEle, 1, 0, -1);
|
||||
setStyles(leavingEle, 0, 0, -1);
|
||||
}
|
||||
},
|
||||
shouldAnimate: shouldAnimate
|
||||
shouldAnimate: shouldAnimate && (direction == 'forward' || direction == 'back')
|
||||
};
|
||||
|
||||
return d;
|
||||
};
|
||||
|
||||
provider.transitions.navBar.ios = function(enteringHeaderBar, leavingHeaderBar, direction, shouldAnimate) {
|
||||
shouldAnimate = shouldAnimate && (direction == 'forward' || direction == 'back');
|
||||
|
||||
function setStyles(ctrl, opacity, titleX, backTextX) {
|
||||
var css = {};
|
||||
css[ionic.CSS.TRANSITION_DURATION] = shouldAnimate ? '' : 0;
|
||||
css[ionic.CSS.TRANSITION_DURATION] = d.shouldAnimate ? '' : 0;
|
||||
css.opacity = opacity === 1 ? '' : opacity;
|
||||
|
||||
ctrl.setCss('buttons-left', css);
|
||||
@@ -410,11 +418,11 @@ IonicModule
|
||||
setStyles(ctrlA, 1 - step, titleX, 0);
|
||||
}
|
||||
|
||||
return {
|
||||
var d = {
|
||||
run: function(step) {
|
||||
var enteringHeaderCtrl = enteringHeaderBar.controller();
|
||||
var leavingHeaderCtrl = leavingHeaderBar && leavingHeaderBar.controller();
|
||||
if (direction == 'back') {
|
||||
if (d.direction == 'back') {
|
||||
leave(enteringHeaderCtrl, leavingHeaderCtrl, 1 - step);
|
||||
enter(leavingHeaderCtrl, enteringHeaderCtrl, 1 - step);
|
||||
} else {
|
||||
@@ -422,8 +430,11 @@ IonicModule
|
||||
leave(leavingHeaderCtrl, enteringHeaderCtrl, step);
|
||||
}
|
||||
},
|
||||
shouldAnimate: shouldAnimate
|
||||
direction: direction,
|
||||
shouldAnimate: shouldAnimate && (direction == 'forward' || direction == 'back')
|
||||
};
|
||||
|
||||
return d;
|
||||
};
|
||||
|
||||
|
||||
@@ -435,12 +446,12 @@ IonicModule
|
||||
|
||||
function setStyles(ele, x) {
|
||||
var css = {};
|
||||
css[ionic.CSS.TRANSITION_DURATION] = shouldAnimate ? '' : 0;
|
||||
css[ionic.CSS.TRANSITION_DURATION] = d.shouldAnimate ? '' : 0;
|
||||
css[ionic.CSS.TRANSFORM] = 'translate3d(' + x + '%,0,0)';
|
||||
ionic.DomUtil.cachedStyles(ele, css);
|
||||
}
|
||||
|
||||
return {
|
||||
var d = {
|
||||
run: function(step) {
|
||||
if (direction == 'forward') {
|
||||
setStyles(enteringEle, (1 - step) * 99); // starting at 98% prevents a flicker
|
||||
@@ -458,10 +469,11 @@ IonicModule
|
||||
},
|
||||
shouldAnimate: shouldAnimate
|
||||
};
|
||||
|
||||
return d;
|
||||
};
|
||||
|
||||
provider.transitions.navBar.android = function(enteringHeaderBar, leavingHeaderBar, direction, shouldAnimate) {
|
||||
shouldAnimate = shouldAnimate && (direction == 'forward' || direction == 'back');
|
||||
|
||||
function setStyles(ctrl, opacity) {
|
||||
if (!ctrl) return;
|
||||
@@ -480,7 +492,7 @@ IonicModule
|
||||
setStyles(enteringHeaderBar.controller(), step);
|
||||
setStyles(leavingHeaderBar && leavingHeaderBar.controller(), 1 - step);
|
||||
},
|
||||
shouldAnimate: true
|
||||
shouldAnimate: shouldAnimate && (direction == 'forward' || direction == 'back')
|
||||
};
|
||||
};
|
||||
|
||||
@@ -492,7 +504,8 @@ IonicModule
|
||||
return {
|
||||
run: function(step) {
|
||||
provider.transitions.views.android(enteringEle, leavingEle, false, false).run(step);
|
||||
}
|
||||
},
|
||||
shouldAnimate: false
|
||||
};
|
||||
};
|
||||
|
||||
@@ -501,7 +514,8 @@ IonicModule
|
||||
run: function(step) {
|
||||
provider.transitions.navBar.ios(enteringHeaderBar, leavingHeaderBar, false, false).run(step);
|
||||
provider.transitions.navBar.android(enteringHeaderBar, leavingHeaderBar, false, false).run(step);
|
||||
}
|
||||
},
|
||||
shouldAnimate: false
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
9
js/angular/service/scrollDelegate.js
vendored
9
js/angular/service/scrollDelegate.js
vendored
@@ -130,12 +130,19 @@ IonicModule
|
||||
* @param {boolean=} shouldAnimate Whether the scroll should animate.
|
||||
*/
|
||||
'anchorScroll',
|
||||
/**
|
||||
* @ngdoc method
|
||||
* @name $ionicScrollDelegate#freezeScroll
|
||||
* @description Does not allow the scrollView to scroll in either x or y.
|
||||
* @returns {object} If the scroll view is being prevented from scrolling or not.
|
||||
*/
|
||||
'freezeScroll',
|
||||
/**
|
||||
* @ngdoc method
|
||||
* @name $ionicScrollDelegate#getScrollView
|
||||
* @returns {object} The scrollView associated with this delegate.
|
||||
*/
|
||||
'getScrollView',
|
||||
'getScrollView'
|
||||
/**
|
||||
* @ngdoc method
|
||||
* @name $ionicScrollDelegate#$getByHandle
|
||||
|
||||
116
js/angular/service/viewSwitcher.js
vendored
116
js/angular/service/viewSwitcher.js
vendored
@@ -33,10 +33,11 @@ function($timeout, $document, $q, $ionicClickBlock, $ionicConfig, $ionicNavBarDe
|
||||
var isActiveTimer;
|
||||
var cachedAttr = ionic.DomUtil.cachedAttr;
|
||||
var transitionPromises = [];
|
||||
var defaultTimeout = 1100;
|
||||
|
||||
var ionicViewSwitcher = {
|
||||
|
||||
create: function(navViewCtrl, viewLocals, enteringView, leavingView) {
|
||||
create: function(navViewCtrl, viewLocals, enteringView, leavingView, renderStart, renderEnd) {
|
||||
// get a reference to an entering/leaving element if they exist
|
||||
// loop through to see if the view is already in the navViewElement
|
||||
var enteringEle, leavingEle;
|
||||
@@ -56,11 +57,12 @@ function($timeout, $document, $q, $ionicClickBlock, $ionicConfig, $ionicNavBarDe
|
||||
},
|
||||
|
||||
loadViewElements: function(registerData) {
|
||||
var viewEle, viewElements = navViewCtrl.getViewElements();
|
||||
var x, l, viewEle;
|
||||
var viewElements = navViewCtrl.getViewElements();
|
||||
var enteringEleIdentifier = getViewElementIdentifier(viewLocals, enteringView);
|
||||
var navViewActiveEleId = navViewCtrl.activeEleId();
|
||||
|
||||
for (var x = 0, l = viewElements.length; x < l; x++) {
|
||||
for (x = 0, l = viewElements.length; x < l; x++) {
|
||||
viewEle = viewElements.eq(x);
|
||||
|
||||
if (viewEle.data(DATA_ELE_IDENTIFIER) === enteringEleIdentifier) {
|
||||
@@ -92,7 +94,9 @@ function($timeout, $document, $q, $ionicClickBlock, $ionicConfig, $ionicNavBarDe
|
||||
enteringEle.data(DATA_ELE_IDENTIFIER, enteringEleIdentifier);
|
||||
}
|
||||
|
||||
navViewCtrl.activeEleId(enteringEleIdentifier);
|
||||
if (renderEnd) {
|
||||
navViewCtrl.activeEleId(enteringEleIdentifier);
|
||||
}
|
||||
|
||||
registerData.ele = null;
|
||||
},
|
||||
@@ -143,15 +147,15 @@ function($timeout, $document, $q, $ionicClickBlock, $ionicConfig, $ionicNavBarDe
|
||||
callback && callback();
|
||||
},
|
||||
|
||||
transition: function(direction, enableBack) {
|
||||
var deferred = $q.defer();
|
||||
transitionPromises.push(deferred.promise);
|
||||
|
||||
transition: function(direction, enableBack, allowAnimate) {
|
||||
var deferred;
|
||||
var enteringData = getTransitionData(viewLocals, enteringEle, direction, enteringView);
|
||||
var leavingData = extend(extend({}, enteringData), getViewData(leavingView));
|
||||
enteringData.transitionId = leavingData.transitionId = transitionId;
|
||||
enteringData.fromCache = !!alreadyInDom;
|
||||
enteringData.enableBack = !!enableBack;
|
||||
enteringData.renderStart = renderStart;
|
||||
enteringData.renderEnd = renderEnd;
|
||||
|
||||
cachedAttr(enteringEle.parent(), 'nav-view-transition', enteringData.transition);
|
||||
cachedAttr(enteringEle.parent(), 'nav-view-direction', enteringData.direction);
|
||||
@@ -159,42 +163,87 @@ function($timeout, $document, $q, $ionicClickBlock, $ionicConfig, $ionicNavBarDe
|
||||
// cancel any previous transition complete fallbacks
|
||||
$timeout.cancel(enteringEle.data(DATA_FALLBACK_TIMER));
|
||||
|
||||
switcher.emit('before', enteringData, leavingData);
|
||||
|
||||
// 1) get the transition ready and see if it'll animate
|
||||
// get the transition ready and see if it'll animate
|
||||
var transitionFn = $ionicConfig.transitions.views[enteringData.transition] || $ionicConfig.transitions.views.none;
|
||||
var viewTransition = transitionFn(enteringEle, leavingEle, enteringData.direction, enteringData.shouldAnimate);
|
||||
var viewTransition = transitionFn(enteringEle, leavingEle, enteringData.direction,
|
||||
enteringData.shouldAnimate && allowAnimate && renderEnd);
|
||||
|
||||
if (viewTransition.shouldAnimate) {
|
||||
// 2) attach transitionend events (and fallback timer)
|
||||
// attach transitionend events (and fallback timer)
|
||||
enteringEle.on(TRANSITIONEND_EVENT, transitionComplete);
|
||||
enteringEle.data(DATA_FALLBACK_TIMER, $timeout(transitionComplete, 1000));
|
||||
$ionicClickBlock.show();
|
||||
enteringEle.data(DATA_FALLBACK_TIMER, $timeout(transitionComplete, defaultTimeout));
|
||||
$ionicClickBlock.show(defaultTimeout);
|
||||
}
|
||||
|
||||
// 3) stage entering element, opacity 0, no transition duration
|
||||
navViewAttr(enteringEle, VIEW_STATUS_STAGED);
|
||||
if (renderStart) {
|
||||
// notify the views "before" the transition starts
|
||||
switcher.emit('before', enteringData, leavingData);
|
||||
|
||||
// 4) place the elements in the correct step to begin
|
||||
viewTransition.run(0);
|
||||
// stage entering element, opacity 0, no transition duration
|
||||
navViewAttr(enteringEle, VIEW_STATUS_STAGED);
|
||||
|
||||
// render the elements in the correct location for their starting point
|
||||
viewTransition.run(0);
|
||||
}
|
||||
|
||||
if (renderEnd) {
|
||||
// create a promise so we can keep track of when all transitions finish
|
||||
// only required if this transition should complete
|
||||
deferred = $q.defer();
|
||||
transitionPromises.push(deferred.promise);
|
||||
}
|
||||
|
||||
if (renderStart && renderEnd) {
|
||||
// CSS "auto" transitioned, not manually transitioned
|
||||
// wait a frame so the styles apply before auto transitioning
|
||||
$timeout(onReflow, 16);
|
||||
|
||||
} else if (!renderEnd) {
|
||||
// just the start of a manual transition
|
||||
// but it will not render the end of the transition
|
||||
navViewAttr(enteringEle, 'entering');
|
||||
navViewAttr(leavingEle, 'leaving');
|
||||
|
||||
// return the transition run method so each step can be ran manually
|
||||
return {
|
||||
run: viewTransition.run,
|
||||
cancel: function(shouldAnimate) {
|
||||
if (shouldAnimate) {
|
||||
enteringEle.on(TRANSITIONEND_EVENT, cancelTransition);
|
||||
enteringEle.data(DATA_FALLBACK_TIMER, $timeout(cancelTransition, defaultTimeout));
|
||||
$ionicClickBlock.show(defaultTimeout);
|
||||
} else {
|
||||
cancelTransition();
|
||||
}
|
||||
viewTransition.shouldAnimate = shouldAnimate;
|
||||
viewTransition.run(0);
|
||||
viewTransition = null;
|
||||
}
|
||||
};
|
||||
|
||||
} else if (renderEnd) {
|
||||
// just the end of a manual transition
|
||||
// happens after the manual transition has completed
|
||||
// and a full history change has happened
|
||||
onReflow();
|
||||
}
|
||||
|
||||
// 5) wait a frame so the styles apply
|
||||
$timeout(onReflow, 16);
|
||||
|
||||
function onReflow() {
|
||||
// 6) remove that we're staging the entering element so it can transition
|
||||
// remove that we're staging the entering element so it can auto transition
|
||||
navViewAttr(enteringEle, viewTransition.shouldAnimate ? 'entering' : VIEW_STATUS_ACTIVE);
|
||||
navViewAttr(leavingEle, viewTransition.shouldAnimate ? 'leaving' : VIEW_STATUS_CACHED);
|
||||
|
||||
// 7) start the transition
|
||||
// start the auto transition and let the CSS take over
|
||||
viewTransition.run(1);
|
||||
|
||||
// trigger auto transitions on the associated nav bars
|
||||
$ionicNavBarDelegate._instances.forEach(function(instance) {
|
||||
instance.triggerTransitionStart(transitionId);
|
||||
});
|
||||
|
||||
if (!viewTransition.shouldAnimate) {
|
||||
// no animated transition
|
||||
// no animated auto transition
|
||||
transitionComplete();
|
||||
}
|
||||
}
|
||||
@@ -207,20 +256,21 @@ function($timeout, $document, $q, $ionicClickBlock, $ionicConfig, $ionicNavBarDe
|
||||
$timeout.cancel(enteringEle.data(DATA_FALLBACK_TIMER));
|
||||
leavingEle && $timeout.cancel(leavingEle.data(DATA_FALLBACK_TIMER));
|
||||
|
||||
// 8) emit that the views have finished transitioning
|
||||
// emit that the views have finished transitioning
|
||||
// each parent nav-view will update which views are active and cached
|
||||
switcher.emit('after', enteringData, leavingData);
|
||||
|
||||
// 9) resolve that this one transition (there could be many w/ nested views)
|
||||
deferred.resolve(navViewCtrl);
|
||||
// resolve that this one transition (there could be many w/ nested views)
|
||||
deferred && deferred.resolve(navViewCtrl);
|
||||
|
||||
// 10) the most recent transition added has completed and all the active
|
||||
// the most recent transition added has completed and all the active
|
||||
// transition promises should be added to the services array of promises
|
||||
if (transitionId === transitionCounter) {
|
||||
$q.all(transitionPromises).then(ionicViewSwitcher.transitionEnd);
|
||||
switcher.cleanup(enteringData);
|
||||
}
|
||||
|
||||
// tell the nav bars that the transition has ended
|
||||
$ionicNavBarDelegate._instances.forEach(function(instance) {
|
||||
instance.triggerTransitionEnd();
|
||||
});
|
||||
@@ -229,6 +279,14 @@ function($timeout, $document, $q, $ionicClickBlock, $ionicConfig, $ionicNavBarDe
|
||||
nextTransition = nextDirection = enteringView = leavingView = enteringEle = leavingEle = null;
|
||||
}
|
||||
|
||||
function cancelTransition() {
|
||||
navViewAttr(enteringEle, VIEW_STATUS_CACHED);
|
||||
navViewAttr(leavingEle, VIEW_STATUS_ACTIVE);
|
||||
enteringEle.off(TRANSITIONEND_EVENT, cancelTransition);
|
||||
$timeout.cancel(enteringEle.data(DATA_FALLBACK_TIMER));
|
||||
ionicViewSwitcher.transitionEnd([navViewCtrl]);
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
emit: function(step, enteringData, leavingData) {
|
||||
@@ -295,7 +353,7 @@ function($timeout, $document, $q, $ionicClickBlock, $ionicConfig, $ionicNavBarDe
|
||||
},
|
||||
|
||||
transitionEnd: function(navViewCtrls) {
|
||||
forEach(navViewCtrls, function(navViewCtrl){
|
||||
forEach(navViewCtrls, function(navViewCtrl) {
|
||||
navViewCtrl.transitionEnd();
|
||||
});
|
||||
|
||||
|
||||
@@ -11,6 +11,11 @@
|
||||
start: function(e) {
|
||||
var self = this;
|
||||
|
||||
var hitX = ionic.tap.pointerCoord(e).x;
|
||||
if (hitX > 0 && hitX < 45) {
|
||||
return;
|
||||
}
|
||||
|
||||
// when an element is touched/clicked, it climbs up a few
|
||||
// parents to see if it is an .item or .button element
|
||||
ionic.requestAnimationFrame(function() {
|
||||
|
||||
@@ -377,6 +377,8 @@ ionic.views.Scroll = ionic.views.View.inherit({
|
||||
// The ms interval for triggering scroll events
|
||||
scrollEventInterval: 10,
|
||||
|
||||
freeze: false,
|
||||
|
||||
getContentWidth: function() {
|
||||
return Math.max(self.__content.scrollWidth, self.__content.offsetWidth);
|
||||
},
|
||||
@@ -742,7 +744,7 @@ ionic.views.Scroll = ionic.views.View.inherit({
|
||||
};
|
||||
|
||||
self.touchMove = function(e) {
|
||||
if (!self.__isDown ||
|
||||
if (self.options.freeze || !self.__isDown ||
|
||||
(!self.__isDown && e.defaultPrevented) ||
|
||||
(e.target.tagName === 'TEXTAREA' && e.target.parentElement.querySelector(':focus')) ) {
|
||||
return;
|
||||
@@ -843,7 +845,7 @@ ionic.views.Scroll = ionic.views.View.inherit({
|
||||
};
|
||||
|
||||
self.mouseMove = function(e) {
|
||||
if (!mousedown || (!mousedown && e.defaultPrevented)) {
|
||||
if (self.options.freeze || !mousedown || (!mousedown && e.defaultPrevented)) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -935,13 +937,13 @@ ionic.views.Scroll = ionic.views.View.inherit({
|
||||
delete self.__indicatorY;
|
||||
delete self.options.el;
|
||||
|
||||
self.__callback = self.scrollChildIntoView = self.resetScrollView = angular.noop;
|
||||
self.__callback = self.scrollChildIntoView = self.resetScrollView = NOOP;
|
||||
|
||||
self.mouseMove = self.mouseDown = self.mouseUp = self.mouseWheel =
|
||||
self.touchStart = self.touchMove = self.touchEnd = self.touchCancel = angular.noop;
|
||||
self.touchStart = self.touchMove = self.touchEnd = self.touchCancel = NOOP;
|
||||
|
||||
self.resize = self.scrollTo = self.zoomTo =
|
||||
self.__scrollingComplete = angular.noop;
|
||||
self.__scrollingComplete = NOOP;
|
||||
container = null;
|
||||
},
|
||||
|
||||
|
||||
@@ -13,8 +13,8 @@ $ios-transition-container-bg-color: #000 !default;
|
||||
[nav-view="leaving"] {
|
||||
@include transition-duration( $ios-transition-duration );
|
||||
@include transition-timing-function( $ios-transition-timing-function );
|
||||
-webkit-transition-property: opacity, -webkit-transform;
|
||||
transition-property: opacity, transform;
|
||||
-webkit-transition-property: opacity, -webkit-transform, box-shadow;
|
||||
transition-property: opacity, transform, box-shadow;
|
||||
}
|
||||
|
||||
&[nav-view-direction="forward"],
|
||||
@@ -138,6 +138,31 @@ $android-transition-timing-function: cubic-bezier(0.4, 0.6, 0.2, 1) !defaul
|
||||
|
||||
|
||||
|
||||
// Nav Swipe
|
||||
// -------------------------------
|
||||
|
||||
[nav-swipe="fast"] {
|
||||
[nav-view],
|
||||
.title,
|
||||
.buttons,
|
||||
.back-text {
|
||||
@include transition-duration(50ms);
|
||||
@include transition-timing-function(linear);
|
||||
}
|
||||
}
|
||||
|
||||
[nav-swipe="slow"] {
|
||||
[nav-view],
|
||||
.title,
|
||||
.buttons,
|
||||
.back-text {
|
||||
@include transition-duration(160ms);
|
||||
@include transition-timing-function(linear);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Transition Settings
|
||||
// -------------------------------
|
||||
|
||||
|
||||
@@ -65,12 +65,13 @@
|
||||
.click-block {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
z-index: $z-index-click-block;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
opacity: 0;
|
||||
z-index: $z-index-click-block;
|
||||
@include translate3d(0, 0, 0);
|
||||
overflow: hidden;
|
||||
}
|
||||
.click-block-hide {
|
||||
@include translate3d(-9999px, 0, 0);
|
||||
|
||||
Reference in New Issue
Block a user