swiper
Version:
Most modern mobile touch slider and framework with hardware accelerated transitions
294 lines (272 loc) • 10.3 kB
JavaScript
import Utils from '../../../utils/utils';
export default function (event) {
const swiper = this;
const data = swiper.touchEventsData;
const {
params, touches, rtlTranslate: rtl, $wrapperEl, slidesGrid, snapGrid,
} = swiper;
let e = event;
if (e.originalEvent) e = e.originalEvent;
if (data.allowTouchCallbacks) {
swiper.emit('touchEnd', e);
}
data.allowTouchCallbacks = false;
if (!data.isTouched) {
if (data.isMoved && params.grabCursor) {
swiper.setGrabCursor(false);
}
data.isMoved = false;
data.startMoving = false;
return;
}
// Return Grab Cursor
if (params.grabCursor && data.isMoved && data.isTouched && (swiper.allowSlideNext === true || swiper.allowSlidePrev === true)) {
swiper.setGrabCursor(false);
}
// Time diff
const touchEndTime = Utils.now();
const timeDiff = touchEndTime - data.touchStartTime;
// Tap, doubleTap, Click
if (swiper.allowClick) {
swiper.updateClickedSlide(e);
swiper.emit('tap click', e);
if (timeDiff < 300 && (touchEndTime - data.lastClickTime) < 300) {
swiper.emit('doubleTap doubleClick', e);
}
}
data.lastClickTime = Utils.now();
Utils.nextTick(() => {
if (!swiper.destroyed) swiper.allowClick = true;
});
if (!data.isTouched || !data.isMoved || !swiper.swipeDirection || touches.diff === 0 || data.currentTranslate === data.startTranslate) {
data.isTouched = false;
data.isMoved = false;
data.startMoving = false;
return;
}
data.isTouched = false;
data.isMoved = false;
data.startMoving = false;
let currentPos;
if (params.followFinger) {
currentPos = rtl ? swiper.translate : -swiper.translate;
} else {
currentPos = -data.currentTranslate;
}
if (params.cssMode) {
return;
}
if (params.freeMode) {
if (currentPos < -swiper.minTranslate()) {
swiper.slideTo(swiper.activeIndex);
return;
}
if (currentPos > -swiper.maxTranslate()) {
if (swiper.slides.length < snapGrid.length) {
swiper.slideTo(snapGrid.length - 1);
} else {
swiper.slideTo(swiper.slides.length - 1);
}
return;
}
if (params.freeModeMomentum) {
if (data.velocities.length > 1) {
const lastMoveEvent = data.velocities.pop();
const velocityEvent = data.velocities.pop();
const distance = lastMoveEvent.position - velocityEvent.position;
const time = lastMoveEvent.time - velocityEvent.time;
swiper.velocity = distance / time;
swiper.velocity /= 2;
if (Math.abs(swiper.velocity) < params.freeModeMinimumVelocity) {
swiper.velocity = 0;
}
// this implies that the user stopped moving a finger then released.
// There would be no events with distance zero, so the last event is stale.
if (time > 150 || (Utils.now() - lastMoveEvent.time) > 300) {
swiper.velocity = 0;
}
} else {
swiper.velocity = 0;
}
swiper.velocity *= params.freeModeMomentumVelocityRatio;
data.velocities.length = 0;
let momentumDuration = 1000 * params.freeModeMomentumRatio;
const momentumDistance = swiper.velocity * momentumDuration;
let newPosition = swiper.translate + momentumDistance;
if (rtl) newPosition = -newPosition;
let doBounce = false;
let afterBouncePosition;
const bounceAmount = Math.abs(swiper.velocity) * 20 * params.freeModeMomentumBounceRatio;
let needsLoopFix;
if (newPosition < swiper.maxTranslate()) {
if (params.freeModeMomentumBounce) {
if (newPosition + swiper.maxTranslate() < -bounceAmount) {
newPosition = swiper.maxTranslate() - bounceAmount;
}
afterBouncePosition = swiper.maxTranslate();
doBounce = true;
data.allowMomentumBounce = true;
} else {
newPosition = swiper.maxTranslate();
}
if (params.loop && params.centeredSlides) needsLoopFix = true;
} else if (newPosition > swiper.minTranslate()) {
if (params.freeModeMomentumBounce) {
if (newPosition - swiper.minTranslate() > bounceAmount) {
newPosition = swiper.minTranslate() + bounceAmount;
}
afterBouncePosition = swiper.minTranslate();
doBounce = true;
data.allowMomentumBounce = true;
} else {
newPosition = swiper.minTranslate();
}
if (params.loop && params.centeredSlides) needsLoopFix = true;
} else if (params.freeModeSticky) {
let nextSlide;
for (let j = 0; j < snapGrid.length; j += 1) {
if (snapGrid[j] > -newPosition) {
nextSlide = j;
break;
}
}
if (Math.abs(snapGrid[nextSlide] - newPosition) < Math.abs(snapGrid[nextSlide - 1] - newPosition) || swiper.swipeDirection === 'next') {
newPosition = snapGrid[nextSlide];
} else {
newPosition = snapGrid[nextSlide - 1];
}
newPosition = -newPosition;
}
if (needsLoopFix) {
swiper.once('transitionEnd', () => {
swiper.loopFix();
});
}
// Fix duration
if (swiper.velocity !== 0) {
if (rtl) {
momentumDuration = Math.abs((-newPosition - swiper.translate) / swiper.velocity);
} else {
momentumDuration = Math.abs((newPosition - swiper.translate) / swiper.velocity);
}
if (params.freeModeSticky) {
// If freeModeSticky is active and the user ends a swipe with a slow-velocity
// event, then durations can be 20+ seconds to slide one (or zero!) slides.
// It's easy to see this when simulating touch with mouse events. To fix this,
// limit single-slide swipes to the default slide duration. This also has the
// nice side effect of matching slide speed if the user stopped moving before
// lifting finger or mouse vs. moving slowly before lifting the finger/mouse.
// For faster swipes, also apply limits (albeit higher ones).
const moveDistance = Math.abs((rtl ? -newPosition : newPosition) - swiper.translate);
const currentSlideSize = swiper.slidesSizesGrid[swiper.activeIndex];
if (moveDistance < currentSlideSize) {
momentumDuration = params.speed;
} else if (moveDistance < 2 * currentSlideSize) {
momentumDuration = params.speed * 1.5;
} else {
momentumDuration = params.speed * 2.5;
}
}
} else if (params.freeModeSticky) {
swiper.slideToClosest();
return;
}
if (params.freeModeMomentumBounce && doBounce) {
swiper.updateProgress(afterBouncePosition);
swiper.setTransition(momentumDuration);
swiper.setTranslate(newPosition);
swiper.transitionStart(true, swiper.swipeDirection);
swiper.animating = true;
$wrapperEl.transitionEnd(() => {
if (!swiper || swiper.destroyed || !data.allowMomentumBounce) return;
swiper.emit('momentumBounce');
swiper.setTransition(params.speed);
setTimeout(() => {
swiper.setTranslate(afterBouncePosition);
$wrapperEl.transitionEnd(() => {
if (!swiper || swiper.destroyed) return;
swiper.transitionEnd();
});
}, 0);
});
} else if (swiper.velocity) {
swiper.updateProgress(newPosition);
swiper.setTransition(momentumDuration);
swiper.setTranslate(newPosition);
swiper.transitionStart(true, swiper.swipeDirection);
if (!swiper.animating) {
swiper.animating = true;
$wrapperEl.transitionEnd(() => {
if (!swiper || swiper.destroyed) return;
swiper.transitionEnd();
});
}
} else {
swiper.updateProgress(newPosition);
}
swiper.updateActiveIndex();
swiper.updateSlidesClasses();
} else if (params.freeModeSticky) {
swiper.slideToClosest();
return;
}
if (!params.freeModeMomentum || timeDiff >= params.longSwipesMs) {
swiper.updateProgress();
swiper.updateActiveIndex();
swiper.updateSlidesClasses();
}
return;
}
// Find current slide
let stopIndex = 0;
let groupSize = swiper.slidesSizesGrid[0];
for (let i = 0; i < slidesGrid.length; i += (i < params.slidesPerGroupSkip ? 1 : params.slidesPerGroup)) {
const increment = (i < params.slidesPerGroupSkip - 1 ? 1 : params.slidesPerGroup);
if (typeof slidesGrid[i + increment] !== 'undefined') {
if (currentPos >= slidesGrid[i] && currentPos < slidesGrid[i + increment]) {
stopIndex = i;
groupSize = slidesGrid[i + increment] - slidesGrid[i];
}
} else if (currentPos >= slidesGrid[i]) {
stopIndex = i;
groupSize = slidesGrid[slidesGrid.length - 1] - slidesGrid[slidesGrid.length - 2];
}
}
// Find current slide size
const ratio = (currentPos - slidesGrid[stopIndex]) / groupSize;
const increment = (stopIndex < params.slidesPerGroupSkip - 1 ? 1 : params.slidesPerGroup);
if (timeDiff > params.longSwipesMs) {
// Long touches
if (!params.longSwipes) {
swiper.slideTo(swiper.activeIndex);
return;
}
if (swiper.swipeDirection === 'next') {
if (ratio >= params.longSwipesRatio) swiper.slideTo(stopIndex + increment);
else swiper.slideTo(stopIndex);
}
if (swiper.swipeDirection === 'prev') {
if (ratio > (1 - params.longSwipesRatio)) swiper.slideTo(stopIndex + increment);
else swiper.slideTo(stopIndex);
}
} else {
// Short swipes
if (!params.shortSwipes) {
swiper.slideTo(swiper.activeIndex);
return;
}
const isNavButtonTarget = swiper.navigation && (e.target === swiper.navigation.nextEl || e.target === swiper.navigation.prevEl);
if (!isNavButtonTarget) {
if (swiper.swipeDirection === 'next') {
swiper.slideTo(stopIndex + increment);
}
if (swiper.swipeDirection === 'prev') {
swiper.slideTo(stopIndex);
}
} else if (e.target === swiper.navigation.nextEl) {
swiper.slideTo(stopIndex + increment);
} else {
swiper.slideTo(stopIndex);
}
}
}