swiper
Version:
Most modern mobile touch slider and framework with hardware accelerated transitions
360 lines (357 loc) • 12.5 kB
JavaScript
import { c as classesToSelector } from '../shared/classes-to-selector.mjs';
import { s as makeElementsArray, c as classesToTokens, a as createElement, t as nextTick, h as elementOffset } from '../shared/utils.mjs';
import { c as createElementIfNotDefined } from '../shared/create-element-if-not-defined.mjs';
const Scrollbar = ({ swiper, extendParams, on, emit }) => {
let isTouched = false;
let timeout = null;
let dragTimeout = null;
let dragStartPos = 0;
let dragSize = 0;
let trackSize = 0;
let divider = 0;
extendParams({
scrollbar: {
el: null,
dragSize: 'auto',
hide: false,
draggable: false,
snapOnRelease: true,
lockClass: 'swiper-scrollbar-lock',
dragClass: 'swiper-scrollbar-drag',
scrollbarDisabledClass: 'swiper-scrollbar-disabled',
horizontalClass: `swiper-scrollbar-horizontal`,
verticalClass: `swiper-scrollbar-vertical`,
},
});
// Initialized as a partial; remaining methods (updateSize, setTranslate,
// init, destroy, enable, disable) attach after their definitions below.
swiper.scrollbar = {
el: null,
dragEl: null,
};
function getParams() {
return swiper.params.scrollbar;
}
function setTranslate() {
const params = getParams();
if (!params.el || !swiper.scrollbar.el)
return;
const { scrollbar, rtlTranslate: rtl } = swiper;
const { dragEl, el } = scrollbar;
const progress = swiper.params.loop ? (swiper.progressLoop ?? 0) : swiper.progress;
let newSize = dragSize;
let newPos = (trackSize - dragSize) * progress;
if (rtl) {
newPos = -newPos;
if (newPos > 0) {
newSize = dragSize - newPos;
newPos = 0;
}
else if (-newPos + dragSize > trackSize) {
newSize = trackSize + newPos;
}
}
else if (newPos < 0) {
newSize = dragSize + newPos;
newPos = 0;
}
else if (newPos + dragSize > trackSize) {
newSize = trackSize - newPos;
}
if (swiper.isHorizontal()) {
dragEl.style.transform = `translate3d(${newPos}px, 0, 0)`;
dragEl.style.width = `${newSize}px`;
}
else {
dragEl.style.transform = `translate3d(0px, ${newPos}px, 0)`;
dragEl.style.height = `${newSize}px`;
}
if (params.hide) {
if (timeout)
clearTimeout(timeout);
el.style.opacity = '1';
timeout = setTimeout(() => {
el.style.opacity = '0';
el.style.transitionDuration = '400ms';
}, 1000);
}
}
function setTransition(duration) {
if (!getParams().el || !swiper.scrollbar.el)
return;
swiper.scrollbar.dragEl.style.transitionDuration = `${duration}ms`;
}
function updateSize() {
const params = getParams();
if (!params.el || !swiper.scrollbar.el)
return;
const { scrollbar } = swiper;
const { dragEl, el } = scrollbar;
dragEl.style.width = '';
dragEl.style.height = '';
trackSize = swiper.isHorizontal() ? el.offsetWidth : el.offsetHeight;
divider =
swiper.size /
(swiper.virtualSize +
(swiper.params.slidesOffsetBefore ?? 0) -
(swiper.params.centeredSlides ? swiper.snapGrid[0] : 0));
if (params.dragSize === 'auto') {
dragSize = trackSize * divider;
}
else {
dragSize = parseInt(String(params.dragSize), 10);
}
if (swiper.isHorizontal()) {
dragEl.style.width = `${dragSize}px`;
}
else {
dragEl.style.height = `${dragSize}px`;
}
if (divider >= 1) {
el.style.display = 'none';
}
else {
el.style.display = '';
}
if (params.hide) {
el.style.opacity = '0';
}
if (swiper.params.watchOverflow && swiper.enabled) {
scrollbar.el.classList[swiper.isLocked ? 'add' : 'remove'](params.lockClass);
}
}
function getPointerPosition(e) {
if (swiper.isHorizontal()) {
return e.clientX ?? e.touches?.[0]?.clientX ?? 0;
}
return e.clientY ?? e.touches?.[0]?.clientY ?? 0;
}
function setDragPosition(e) {
const { scrollbar, rtlTranslate: rtl } = swiper;
const { el } = scrollbar;
let positionRatio;
positionRatio =
(getPointerPosition(e) -
elementOffset(el)[swiper.isHorizontal() ? 'left' : 'top'] -
(dragStartPos !== null ? dragStartPos : dragSize / 2)) /
(trackSize - dragSize);
positionRatio = Math.max(Math.min(positionRatio, 1), 0);
if (rtl) {
positionRatio = 1 - positionRatio;
}
const position = swiper.minTranslate() + (swiper.maxTranslate() - swiper.minTranslate()) * positionRatio;
swiper.updateProgress(position);
swiper.setTranslate(position);
swiper.updateActiveIndex();
swiper.updateSlidesClasses();
}
function onDragStart(e) {
const params = getParams();
const { scrollbar, wrapperEl } = swiper;
const { el, dragEl } = scrollbar;
isTouched = true;
dragStartPos =
e.target === dragEl
? getPointerPosition(e) -
e.target.getBoundingClientRect()[swiper.isHorizontal() ? 'left' : 'top']
: null;
e.preventDefault();
e.stopPropagation();
wrapperEl.style.transitionDuration = '100ms';
dragEl.style.transitionDuration = '100ms';
setDragPosition(e);
if (dragTimeout)
clearTimeout(dragTimeout);
el.style.transitionDuration = '0ms';
if (params.hide) {
el.style.opacity = '1';
}
if (swiper.params.cssMode) {
swiper.wrapperEl.style.scrollSnapType = 'none';
}
emit('scrollbarDragStart', e);
}
function onDragMove(e) {
const { scrollbar, wrapperEl } = swiper;
const { el, dragEl } = scrollbar;
if (!isTouched)
return;
if (e.cancelable)
e.preventDefault();
setDragPosition(e);
wrapperEl.style.transitionDuration = '0ms';
el.style.transitionDuration = '0ms';
dragEl.style.transitionDuration = '0ms';
emit('scrollbarDragMove', e);
}
function onDragEnd(e) {
const params = getParams();
const { scrollbar, wrapperEl } = swiper;
const { el } = scrollbar;
if (!isTouched)
return;
isTouched = false;
if (swiper.params.cssMode) {
swiper.wrapperEl.style.scrollSnapType = '';
wrapperEl.style.transitionDuration = '';
}
if (params.hide) {
if (dragTimeout)
clearTimeout(dragTimeout);
dragTimeout = nextTick(() => {
el.style.opacity = '0';
el.style.transitionDuration = '400ms';
}, 1000);
}
emit('scrollbarDragEnd', e);
if (params.snapOnRelease) {
swiper.slideToClosest();
}
}
function events(method) {
const { scrollbar, params } = swiper;
const el = scrollbar.el;
if (!el)
return;
const activeListener = params.passiveListeners ? { passive: false, capture: false } : false;
const passiveListener = params.passiveListeners ? { passive: true, capture: false } : false;
const eventMethod = method === 'on' ? 'addEventListener' : 'removeEventListener';
el[eventMethod]('pointerdown', onDragStart, activeListener);
document[eventMethod]('pointermove', onDragMove, activeListener);
document[eventMethod]('pointerup', onDragEnd, passiveListener);
}
function enableDraggable() {
if (!getParams().el || !swiper.scrollbar.el)
return;
events('on');
}
function disableDraggable() {
if (!getParams().el || !swiper.scrollbar.el)
return;
events('off');
}
function init() {
const { scrollbar, el: swiperEl } = swiper;
swiper.params.scrollbar = createElementIfNotDefined(swiper, swiper.originalParams.scrollbar, swiper.params.scrollbar, { el: 'swiper-scrollbar' });
const params = getParams();
if (!params.el)
return;
let el;
if (typeof params.el === 'string' && swiper.isElement) {
el = swiper.el.querySelector(params.el);
}
else {
el = params.el;
}
if (!el && typeof params.el === 'string') {
el = document.querySelectorAll(params.el);
if (!el.length)
return;
}
else if (!el) {
el = params.el;
}
if (swiper.params.uniqueNavElements &&
typeof params.el === 'string' &&
el.length > 1 &&
swiperEl.querySelectorAll(params.el).length === 1) {
el = swiperEl.querySelector(params.el);
}
if (el.length > 0) {
el = el[0];
}
const elTyped = el;
elTyped.classList.add(swiper.isHorizontal() ? params.horizontalClass : params.verticalClass);
let dragEl = null;
if (elTyped) {
dragEl = elTyped.querySelector(classesToSelector(params.dragClass));
if (!dragEl) {
dragEl = createElement('div', params.dragClass);
elTyped.append(dragEl);
}
}
Object.assign(scrollbar, {
el: elTyped,
dragEl,
});
if (params.draggable) {
enableDraggable();
}
if (elTyped) {
elTyped.classList[swiper.enabled ? 'remove' : 'add'](...classesToTokens(params.lockClass));
}
}
function destroy() {
const params = getParams();
const el = swiper.scrollbar.el;
if (el) {
el.classList.remove(...classesToTokens(swiper.isHorizontal() ? params.horizontalClass : params.verticalClass));
}
disableDraggable();
}
on('changeDirection', () => {
if (!swiper.scrollbar || !swiper.scrollbar.el)
return;
const params = getParams();
const els = makeElementsArray(swiper.scrollbar.el);
els.forEach((subEl) => {
subEl.classList.remove(params.horizontalClass, params.verticalClass);
subEl.classList.add(swiper.isHorizontal() ? params.horizontalClass : params.verticalClass);
});
});
on('init', () => {
if (getParams().enabled === false) {
disable();
}
else {
init();
updateSize();
setTranslate();
}
});
on('update resize observerUpdate lock unlock changeDirection', () => {
updateSize();
});
on('setTranslate', () => {
setTranslate();
});
on('setTransition', (_s, duration) => {
setTransition(duration);
});
on('enable disable', () => {
const { el } = swiper.scrollbar;
if (el) {
el.classList[swiper.enabled ? 'remove' : 'add'](...classesToTokens(getParams().lockClass));
}
});
on('destroy', () => {
destroy();
});
const enable = () => {
const params = getParams();
swiper.el.classList.remove(...classesToTokens(params.scrollbarDisabledClass));
if (swiper.scrollbar.el) {
swiper.scrollbar.el.classList.remove(...classesToTokens(params.scrollbarDisabledClass));
}
init();
updateSize();
setTranslate();
};
const disable = () => {
const params = getParams();
swiper.el.classList.add(...classesToTokens(params.scrollbarDisabledClass));
if (swiper.scrollbar.el) {
swiper.scrollbar.el.classList.add(...classesToTokens(params.scrollbarDisabledClass));
}
destroy();
};
Object.assign(swiper.scrollbar, {
enable,
disable,
updateSize,
setTranslate,
init,
destroy,
});
};
export { Scrollbar as default };