@pi0/framework7
Version:
Full featured mobile HTML framework for building iOS & Android apps
291 lines (272 loc) • 9.01 kB
JavaScript
import $ from 'dom7';
import Utils from '../../utils/utils';
function swipePanel(panel) {
const app = panel.app;
Utils.extend(panel, {
swipeable: true,
swipeInitialized: true,
});
const params = app.params.panel;
const { $el, $backdropEl, side, effect, $viewEl } = panel;
let otherPanel;
let isTouched;
let isMoved;
let isScrolling;
const touchesStart = {};
let touchStartTime;
let touchesDiff;
let translate;
let backdropOpacity;
let panelWidth;
let direction;
function handleTouchStart(e) {
if (!panel.swipeable) return;
if (!app.panel.allowOpen || (!params.swipe && !params.swipeOnlyClose) || isTouched) return;
if ($('.modal-in, .photo-browser-in').length > 0) return;
otherPanel = app.panel[side === 'left' ? 'right' : 'left'] || {};
if (!panel.opened && otherPanel.opened) return;
if (!(params.swipeCloseOpposite || params.swipeOnlyClose)) {
if (otherPanel.opened) return;
}
if (e.target && e.target.nodeName.toLowerCase() === 'input' && e.target.type === 'range') return;
if ($(e.target).closest('.range-slider, .tabs-swipeable-wrap, .calendar-months').length > 0) return;
touchesStart.x = e.type === 'touchstart' ? e.targetTouches[0].pageX : e.pageX;
touchesStart.y = e.type === 'touchstart' ? e.targetTouches[0].pageY : e.pageY;
if (params.swipeOnlyClose && !panel.opened) {
return;
}
if (params.swipe !== 'both' && params.swipeCloseOpposite && params.swipe !== side && !panel.opened) {
return;
}
if (params.swipeActiveArea && !panel.opened) {
if (side === 'left') {
if (touchesStart.x > params.swipeActiveArea) return;
}
if (side === 'right') {
if (touchesStart.x < app.width - params.swipeActiveArea) return;
}
}
isMoved = false;
isTouched = true;
isScrolling = undefined;
touchStartTime = Utils.now();
direction = undefined;
}
function handleTouchMove(e) {
if (!isTouched) return;
if (e.f7PreventPanelSwipe) {
isTouched = false;
return;
}
const pageX = e.type === 'touchmove' ? e.targetTouches[0].pageX : e.pageX;
const pageY = e.type === 'touchmove' ? e.targetTouches[0].pageY : e.pageY;
if (typeof isScrolling === 'undefined') {
isScrolling = !!(isScrolling || Math.abs(pageY - touchesStart.y) > Math.abs(pageX - touchesStart.x));
}
if (isScrolling) {
isTouched = false;
return;
}
if (!direction) {
if (pageX > touchesStart.x) {
direction = 'to-right';
} else {
direction = 'to-left';
}
if (params.swipe === 'both') {
if (params.swipeActiveArea > 0) {
if (side === 'left' && touchesStart.x > params.swipeActiveArea) {
isTouched = false;
return;
}
if (side === 'right' && touchesStart.x < app.width - params.swipeActiveArea) {
isTouched = false;
return;
}
}
}
if ($el.hasClass('panel-visible-by-breakpoint')) {
isTouched = false;
return;
}
if (
(side === 'left' &&
(
direction === 'to-left' && !$el.hasClass('panel-active')
)
)
||
(side === 'right' &&
(
direction === 'to-right' && !$el.hasClass('panel-active')
)
)
) {
isTouched = false;
return;
}
}
if (params.swipeNoFollow) {
const timeDiff = (new Date()).getTime() - touchStartTime;
if (timeDiff < 300) {
if (direction === 'to-left') {
if (side === 'right') app.openPanel(side);
if (side === 'left' && $el.hasClass('panel-active')) app.closePanel();
}
if (direction === 'to-right') {
if (side === 'left') app.openPanel(side);
if (side === 'right' && $el.hasClass('panel-active')) app.closePanel();
}
}
isTouched = false;
isMoved = false;
return;
}
if (!isMoved) {
if (!panel.opened) {
$el.show();
$backdropEl.show();
$el.trigger('panel:swipeopen', panel);
panel.emit('local::swipeOpen panelSwipeOpen', panel);
}
panelWidth = $el[0].offsetWidth;
$el.transition(0);
}
isMoved = true;
e.preventDefault();
let threshold = panel.opened ? 0 : -params.swipeThreshold;
if (side === 'right') threshold = -threshold;
touchesDiff = (pageX - touchesStart.x) + threshold;
if (side === 'right') {
if (effect === 'cover') {
translate = touchesDiff + (panel.opened ? 0 : panelWidth);
if (translate < 0) translate = 0;
if (translate > panelWidth) {
translate = panelWidth;
}
} else {
translate = touchesDiff - (panel.opened ? panelWidth : 0);
if (translate > 0) translate = 0;
if (translate < -panelWidth) {
translate = -panelWidth;
}
}
} else {
translate = touchesDiff + (panel.opened ? panelWidth : 0);
if (translate < 0) translate = 0;
if (translate > panelWidth) {
translate = panelWidth;
}
}
if (effect === 'reveal') {
$viewEl.transform(`translate3d(${translate}px,0,0)`).transition(0);
$backdropEl.transform(`translate3d(${translate}px,0,0)`).transition(0);
$el.trigger('panel:swipe', panel, Math.abs(translate / panelWidth));
panel.emit('local::swipe panelSwipe', panel, Math.abs(translate / panelWidth));
} else {
if (side === 'left') translate -= panelWidth;
$el.transform(`translate3d(${translate}px,0,0)`).transition(0);
$backdropEl.transition(0);
backdropOpacity = 1 - Math.abs(translate / panelWidth);
$backdropEl.css({ opacity: backdropOpacity });
$el.trigger('panel:swipe', panel, Math.abs(translate / panelWidth));
panel.emit('local::swipe panelSwipe', panel, Math.abs(translate / panelWidth));
}
}
function handleTouchEnd() {
if (!isTouched || !isMoved) {
isTouched = false;
isMoved = false;
return;
}
isTouched = false;
isMoved = false;
const timeDiff = (new Date()).getTime() - touchStartTime;
let action;
const edge = (translate === 0 || Math.abs(translate) === panelWidth);
if (!panel.opened) {
if (effect === 'cover') {
if (translate === 0) {
action = 'swap'; // open
} else if (timeDiff < 300 && Math.abs(translate) > 0) {
action = 'swap'; // open
} else if (timeDiff >= 300 && Math.abs(translate) < panelWidth / 2) {
action = 'swap'; // open
} else {
action = 'reset'; // close
}
} else if (translate === 0) {
action = 'reset';
} else if (
(timeDiff < 300 && Math.abs(translate) > 0)
||
(timeDiff >= 300 && (Math.abs(translate) >= panelWidth / 2))
) {
action = 'swap';
} else {
action = 'reset';
}
} else if (effect === 'cover') {
if (translate === 0) {
action = 'reset'; // open
} else if (timeDiff < 300 && Math.abs(translate) > 0) {
action = 'swap'; // open
} else if (timeDiff >= 300 && Math.abs(translate) < panelWidth / 2) {
action = 'reset'; // open
} else {
action = 'swap'; // close
}
} else if (translate === -panelWidth) {
action = 'reset';
} else if (
(timeDiff < 300 && Math.abs(translate) >= 0)
||
(timeDiff >= 300 && (Math.abs(translate) <= panelWidth / 2))
) {
if (side === 'left' && translate === panelWidth) action = 'reset';
else action = 'swap';
} else {
action = 'reset';
}
if (action === 'swap') {
if (panel.opened) {
panel.close(!edge);
} else {
panel.open(!edge);
}
}
if (action === 'reset') {
if (!panel.opened) {
if (edge) {
$el.css({ display: '' });
} else {
const target = effect === 'reveal' ? $viewEl : $el;
$('html').addClass('with-panel-transitioning');
target.transitionEnd(() => {
if ($el.hasClass('panel-active')) return;
$el.css({ display: '' });
$('html').removeClass('with-panel-transitioning');
});
}
}
}
if (effect === 'reveal') {
Utils.nextFrame(() => {
$viewEl.transition('');
$viewEl.transform('');
});
}
$el.transition('').transform('');
$backdropEl.css({ display: '' }).transform('').transition('').css('opacity', '');
}
// Add Events
app.on('touchstart:passive', handleTouchStart);
app.on('touchmove:active', handleTouchMove);
app.on('touchend:passive', handleTouchEnd);
panel.on('panelDestroy', () => {
app.off('touchstart:passive', handleTouchStart);
app.off('touchmove:active', handleTouchMove);
app.off('touchend:passive', handleTouchEnd);
});
}
export default swipePanel;