import { nextTick } from '../../shared/utils.js'; export default function Scrollbar({ swiper, extendParams, on, emit }) { let isTouched = false; let timeout = null; let dragTimeout = null; let dragStartPos; let dragSize; let trackSize; let divider; extendParams({ scrollbar: { el: null, dragSize: 'auto', hide: false, draggable: false, snapOnRelease: true, lockClass: 'swiper-scrollbar-lock', dragClass: 'swiper-scrollbar-drag', }, }); swiper.scrollbar = { el: null, dragEl: null, $el: null, $dragEl: null, }; function setTranslate() { if (!swiper.params.scrollbar.el || !swiper.scrollbar.el) return; const { scrollbar, rtlTranslate: rtl, progress } = swiper; const params = swiper.params.scrollbar; 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()) { swiper.$wrapperEl.scrollbarItemTransform(`translate3d(${newPos}px, 0, 0)`); swiper.$wrapperEl.scrollbarItemCss({ width: `${newSize}px` }) } else { swiper.$wrapperEl.scrollbarItemTransform(`translate3d(0px, ${newPos}px, 0)`); swiper.$wrapperEl.scrollbarItemCss({ height: `${newSize}px` }) } if (params.hide) { clearTimeout(timeout); swiper.$wrapperEl.scrollbarCss({ opacity: 1 }) timeout = setTimeout(() => { swiper.$wrapperEl.scrollbarCss({ opacity: 0 }) swiper.$wrapperEl.scrollbarTransition(400) }, 1000); } } function setTransition(duration) { if (!swiper.params.scrollbar.el || !swiper.scrollbar.el) return; swiper.$wrapperEl.scrollbarItemTransition(duration); } async function updateSize() { if (!swiper.params.scrollbar.el || !swiper.scrollbar.el) return; const { scrollbar } = swiper; const { $el, methods } = scrollbar; swiper.$wrapperEl.scrollbarItemCss({ width: '', height: '' }) let rectInfo = await swiper.native.getRectScrollbar(); methods.offset = function() { return rectInfo; } trackSize = swiper.isHorizontal() ? rectInfo.width : rectInfo.height; divider = swiper.size / (swiper.virtualSize + swiper.params.slidesOffsetBefore - (swiper.params.centeredSlides ? swiper.snapGrid[0] : 0)); if (swiper.params.scrollbar.dragSize === 'auto') { dragSize = trackSize * divider; } else { dragSize = parseInt(swiper.params.scrollbar.dragSize, 10); } if (swiper.isHorizontal()) { swiper.$wrapperEl.scrollbarItemCss({ width: `${dragSize}px` }) } else { swiper.$wrapperEl.scrollbarItemCss({ height: `${dragSize}px` }) } if (divider >= 1) { swiper.$wrapperEl.scrollbarCss({ display: 'none' }) } else { swiper.$wrapperEl.scrollbarCss({ display: '' }) } if (swiper.params.scrollbar.hide) { swiper.$wrapperEl.scrollbarCss({ opacity: 0 }) } if (swiper.params.watchOverflow && swiper.enabled) { swiper.$wrapperEl[swiper.isLocked ? 'addScrollbarClass' : 'removeScrollbarClass']( swiper.params.scrollbar.lockClass, ); } } function getPointerPosition(e) { if (swiper.isHorizontal()) { return e.type === 'touchstart' || e.type === 'touchmove' || 'touchStart' || e.type === 'touchMove' ? e.touches[0].clientX : e.clientX; } return e.type === 'touchstart' || e.type === 'touchmove' ? e.touches[0].clientY : e.clientY; } function setDragPosition(e) { const { scrollbar, rtlTranslate: rtl } = swiper; const { $el, methods } = scrollbar; let positionRatio; positionRatio = (getPointerPosition(e) - methods.offset()[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(_s, e) { const params = swiper.params.scrollbar; const { scrollbar, $wrapperEl } = swiper; isTouched = true; dragStartPos = // e.target === // $dragEl[0] || e.target === $dragEl ? // getPointerPosition(e) - // e.target.getBoundingClientRect()[swiper.isHorizontal() ? 'left' : 'top'] : null; // e.preventDefault(); // e.stopPropagation(); $wrapperEl.transition(100); swiper.$wrapperEl.scrollbarItemTransition(100) // $dragEl.transition(100); setDragPosition(e); clearTimeout(dragTimeout); swiper.$wrapperEl.scrollbarTransition(0) if (params.hide) { swiper.$wrapperEl.scrollbarCss({ opacity: 1 }) } if (swiper.params.cssMode) { swiper.$wrapperEl.css({ 'scroll-snap-type': 'none' }); } emit('scrollbarDragStart', e); } function onDragMove(_s, e) { const { scrollbar, $wrapperEl } = swiper; if (!isTouched) return; setDragPosition(e); $wrapperEl.transition(0); swiper.$wrapperEl.scrollbarTransition(0) swiper.$wrapperEl.scrollbarItemTransition(0) emit('scrollbarDragMove', e); } function onDragEnd(_s, e) { const params = swiper.params.scrollbar; const { scrollbar, $wrapperEl } = swiper; const { $el } = scrollbar; if (!isTouched) return; isTouched = false; if (swiper.params.cssMode) { swiper.$wrapperEl.css({ 'scroll-snap-type': '' }); $wrapperEl.transition(''); } if (params.hide) { clearTimeout(dragTimeout); dragTimeout = nextTick(() => { swiper.$wrapperEl.scrollbarCss({ opacity: 0 }) swiper.$wrapperEl.scrollbarTransition(400) }, 1000); } emit('scrollbarDragEnd', e); if (params.snapOnRelease) { swiper.slideToClosest(); } } function events(method) { const { scrollbar, touchEventsTouch, touchEventsDesktop, params, support } = swiper; const $el = scrollbar.$el; const target = $el; const activeListener = support.passiveListener && params.passiveListeners ? { passive: false, capture: false } : false; const passiveListener = support.passiveListener && params.passiveListeners ? { passive: true, capture: false } : false; if (!target) return; const eventMethod = method === 'on' ? 'on' : 'off'; if (!support.touch) { swiper[eventMethod]('touchStartScrollbar', onDragStart, activeListener); swiper[eventMethod]('touchMoveScrollbar', onDragMove, activeListener); swiper[eventMethod]('touchEndScrollbar', onDragEnd, passiveListener); } else { swiper[eventMethod]('touchStartScrollbar', onDragStart, activeListener); swiper[eventMethod]('touchMoveScrollbar', onDragMove, activeListener); swiper[eventMethod]('touchEndScrollbar', onDragEnd, passiveListener); } } function enableDraggable() { if (!swiper.params.scrollbar.el) return; events('on'); } function disableDraggable() { if (!swiper.params.scrollbar.el) return; events('off'); } function init() { const { scrollbar, } = swiper; const params = swiper.params.scrollbar; if (!params.el) return; // swiper.native.updateData({ // scrollbarShow: true // }) let $el = params.el; Object.assign(scrollbar, { $el, el: $el, methods: {} }); if (params.draggable) { enableDraggable(); } swiper.$wrapperEl[swiper.enabled ? 'removeScrollbarClass' : 'addScrollbarClass'](swiper.params.scrollbar .lockClass); return true; } function destroy() { disableDraggable(); } on('init', async () => { await init(); updateSize(); setTranslate(); }); on('update resize observerUpdate lock unlock', () => { updateSize(); }); on('setTranslate', () => { setTranslate(); }); on('setTransition', (_s, duration) => { setTransition(duration); }); on('enable disable', () => { const { $el } = swiper.scrollbar; if ($el) { $el[swiper.enabled ? 'removeClass' : 'addClass'](swiper.params.scrollbar.lockClass); } }); on('destroy', () => { destroy(); }); Object.assign(swiper.scrollbar, { updateSize, setTranslate, init, destroy, }); }