scrollbar.js 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362
  1. import { getDocument } from 'ssr-window';
  2. import $ from '../../shared/dom.js';
  3. import { nextTick } from '../../shared/utils.js';
  4. import createElementIfNotDefined from '../../shared/create-element-if-not-defined.js';
  5. export default function Scrollbar(_ref) {
  6. let {
  7. swiper,
  8. extendParams,
  9. on,
  10. emit
  11. } = _ref;
  12. const document = getDocument();
  13. let isTouched = false;
  14. let timeout = null;
  15. let dragTimeout = null;
  16. let dragStartPos;
  17. let dragSize;
  18. let trackSize;
  19. let divider;
  20. extendParams({
  21. scrollbar: {
  22. el: null,
  23. dragSize: 'auto',
  24. hide: false,
  25. draggable: false,
  26. snapOnRelease: true,
  27. lockClass: 'swiper-scrollbar-lock',
  28. dragClass: 'swiper-scrollbar-drag'
  29. }
  30. });
  31. swiper.scrollbar = {
  32. el: null,
  33. dragEl: null,
  34. $el: null,
  35. $dragEl: null
  36. };
  37. function setTranslate() {
  38. if (!swiper.params.scrollbar.el || !swiper.scrollbar.el) return;
  39. const {
  40. scrollbar,
  41. rtlTranslate: rtl,
  42. progress
  43. } = swiper;
  44. const {
  45. $dragEl,
  46. $el
  47. } = scrollbar;
  48. const params = swiper.params.scrollbar;
  49. let newSize = dragSize;
  50. let newPos = (trackSize - dragSize) * progress;
  51. if (rtl) {
  52. newPos = -newPos;
  53. if (newPos > 0) {
  54. newSize = dragSize - newPos;
  55. newPos = 0;
  56. } else if (-newPos + dragSize > trackSize) {
  57. newSize = trackSize + newPos;
  58. }
  59. } else if (newPos < 0) {
  60. newSize = dragSize + newPos;
  61. newPos = 0;
  62. } else if (newPos + dragSize > trackSize) {
  63. newSize = trackSize - newPos;
  64. }
  65. if (swiper.isHorizontal()) {
  66. $dragEl.transform(`translate3d(${newPos}px, 0, 0)`);
  67. $dragEl[0].style.width = `${newSize}px`;
  68. } else {
  69. $dragEl.transform(`translate3d(0px, ${newPos}px, 0)`);
  70. $dragEl[0].style.height = `${newSize}px`;
  71. }
  72. if (params.hide) {
  73. clearTimeout(timeout);
  74. $el[0].style.opacity = 1;
  75. timeout = setTimeout(() => {
  76. $el[0].style.opacity = 0;
  77. $el.transition(400);
  78. }, 1000);
  79. }
  80. }
  81. function setTransition(duration) {
  82. if (!swiper.params.scrollbar.el || !swiper.scrollbar.el) return;
  83. swiper.scrollbar.$dragEl.transition(duration);
  84. }
  85. function updateSize() {
  86. if (!swiper.params.scrollbar.el || !swiper.scrollbar.el) return;
  87. const {
  88. scrollbar
  89. } = swiper;
  90. const {
  91. $dragEl,
  92. $el
  93. } = scrollbar;
  94. $dragEl[0].style.width = '';
  95. $dragEl[0].style.height = '';
  96. trackSize = swiper.isHorizontal() ? $el[0].offsetWidth : $el[0].offsetHeight;
  97. divider = swiper.size / (swiper.virtualSize + swiper.params.slidesOffsetBefore - (swiper.params.centeredSlides ? swiper.snapGrid[0] : 0));
  98. if (swiper.params.scrollbar.dragSize === 'auto') {
  99. dragSize = trackSize * divider;
  100. } else {
  101. dragSize = parseInt(swiper.params.scrollbar.dragSize, 10);
  102. }
  103. if (swiper.isHorizontal()) {
  104. $dragEl[0].style.width = `${dragSize}px`;
  105. } else {
  106. $dragEl[0].style.height = `${dragSize}px`;
  107. }
  108. if (divider >= 1) {
  109. $el[0].style.display = 'none';
  110. } else {
  111. $el[0].style.display = '';
  112. }
  113. if (swiper.params.scrollbar.hide) {
  114. $el[0].style.opacity = 0;
  115. }
  116. if (swiper.params.watchOverflow && swiper.enabled) {
  117. scrollbar.$el[swiper.isLocked ? 'addClass' : 'removeClass'](swiper.params.scrollbar.lockClass);
  118. }
  119. }
  120. function getPointerPosition(e) {
  121. if (swiper.isHorizontal()) {
  122. return e.type === 'touchstart' || e.type === 'touchmove' ? e.targetTouches[0].clientX : e.clientX;
  123. }
  124. return e.type === 'touchstart' || e.type === 'touchmove' ? e.targetTouches[0].clientY : e.clientY;
  125. }
  126. function setDragPosition(e) {
  127. const {
  128. scrollbar,
  129. rtlTranslate: rtl
  130. } = swiper;
  131. const {
  132. $el
  133. } = scrollbar;
  134. let positionRatio;
  135. positionRatio = (getPointerPosition(e) - $el.offset()[swiper.isHorizontal() ? 'left' : 'top'] - (dragStartPos !== null ? dragStartPos : dragSize / 2)) / (trackSize - dragSize);
  136. positionRatio = Math.max(Math.min(positionRatio, 1), 0);
  137. if (rtl) {
  138. positionRatio = 1 - positionRatio;
  139. }
  140. const position = swiper.minTranslate() + (swiper.maxTranslate() - swiper.minTranslate()) * positionRatio;
  141. swiper.updateProgress(position);
  142. swiper.setTranslate(position);
  143. swiper.updateActiveIndex();
  144. swiper.updateSlidesClasses();
  145. }
  146. function onDragStart(e) {
  147. const params = swiper.params.scrollbar;
  148. const {
  149. scrollbar,
  150. $wrapperEl
  151. } = swiper;
  152. const {
  153. $el,
  154. $dragEl
  155. } = scrollbar;
  156. isTouched = true;
  157. dragStartPos = e.target === $dragEl[0] || e.target === $dragEl ? getPointerPosition(e) - e.target.getBoundingClientRect()[swiper.isHorizontal() ? 'left' : 'top'] : null;
  158. e.preventDefault();
  159. e.stopPropagation();
  160. $wrapperEl.transition(100);
  161. $dragEl.transition(100);
  162. setDragPosition(e);
  163. clearTimeout(dragTimeout);
  164. $el.transition(0);
  165. if (params.hide) {
  166. $el.css('opacity', 1);
  167. }
  168. if (swiper.params.cssMode) {
  169. swiper.$wrapperEl.css('scroll-snap-type', 'none');
  170. }
  171. emit('scrollbarDragStart', e);
  172. }
  173. function onDragMove(e) {
  174. const {
  175. scrollbar,
  176. $wrapperEl
  177. } = swiper;
  178. const {
  179. $el,
  180. $dragEl
  181. } = scrollbar;
  182. if (!isTouched) return;
  183. if (e.preventDefault) e.preventDefault();else e.returnValue = false;
  184. setDragPosition(e);
  185. $wrapperEl.transition(0);
  186. $el.transition(0);
  187. $dragEl.transition(0);
  188. emit('scrollbarDragMove', e);
  189. }
  190. function onDragEnd(e) {
  191. const params = swiper.params.scrollbar;
  192. const {
  193. scrollbar,
  194. $wrapperEl
  195. } = swiper;
  196. const {
  197. $el
  198. } = scrollbar;
  199. if (!isTouched) return;
  200. isTouched = false;
  201. if (swiper.params.cssMode) {
  202. swiper.$wrapperEl.css('scroll-snap-type', '');
  203. $wrapperEl.transition('');
  204. }
  205. if (params.hide) {
  206. clearTimeout(dragTimeout);
  207. dragTimeout = nextTick(() => {
  208. $el.css('opacity', 0);
  209. $el.transition(400);
  210. }, 1000);
  211. }
  212. emit('scrollbarDragEnd', e);
  213. if (params.snapOnRelease) {
  214. swiper.slideToClosest();
  215. }
  216. }
  217. function events(method) {
  218. const {
  219. scrollbar,
  220. touchEventsTouch,
  221. touchEventsDesktop,
  222. params,
  223. support
  224. } = swiper;
  225. const $el = scrollbar.$el;
  226. const target = $el[0];
  227. const activeListener = support.passiveListener && params.passiveListeners ? {
  228. passive: false,
  229. capture: false
  230. } : false;
  231. const passiveListener = support.passiveListener && params.passiveListeners ? {
  232. passive: true,
  233. capture: false
  234. } : false;
  235. if (!target) return;
  236. const eventMethod = method === 'on' ? 'addEventListener' : 'removeEventListener';
  237. if (!support.touch) {
  238. target[eventMethod](touchEventsDesktop.start, onDragStart, activeListener);
  239. document[eventMethod](touchEventsDesktop.move, onDragMove, activeListener);
  240. document[eventMethod](touchEventsDesktop.end, onDragEnd, passiveListener);
  241. } else {
  242. target[eventMethod](touchEventsTouch.start, onDragStart, activeListener);
  243. target[eventMethod](touchEventsTouch.move, onDragMove, activeListener);
  244. target[eventMethod](touchEventsTouch.end, onDragEnd, passiveListener);
  245. }
  246. }
  247. function enableDraggable() {
  248. if (!swiper.params.scrollbar.el) return;
  249. events('on');
  250. }
  251. function disableDraggable() {
  252. if (!swiper.params.scrollbar.el) return;
  253. events('off');
  254. }
  255. function init() {
  256. const {
  257. scrollbar,
  258. $el: $swiperEl
  259. } = swiper;
  260. swiper.params.scrollbar = createElementIfNotDefined(swiper, swiper.originalParams.scrollbar, swiper.params.scrollbar, {
  261. el: 'swiper-scrollbar'
  262. });
  263. const params = swiper.params.scrollbar;
  264. if (!params.el) return;
  265. let $el = $(params.el);
  266. if (swiper.params.uniqueNavElements && typeof params.el === 'string' && $el.length > 1 && $swiperEl.find(params.el).length === 1) {
  267. $el = $swiperEl.find(params.el);
  268. }
  269. let $dragEl = $el.find(`.${swiper.params.scrollbar.dragClass}`);
  270. if ($dragEl.length === 0) {
  271. $dragEl = $(`<div class="${swiper.params.scrollbar.dragClass}"></div>`);
  272. $el.append($dragEl);
  273. }
  274. Object.assign(scrollbar, {
  275. $el,
  276. el: $el[0],
  277. $dragEl,
  278. dragEl: $dragEl[0]
  279. });
  280. if (params.draggable) {
  281. enableDraggable();
  282. }
  283. if ($el) {
  284. $el[swiper.enabled ? 'removeClass' : 'addClass'](swiper.params.scrollbar.lockClass);
  285. }
  286. }
  287. function destroy() {
  288. disableDraggable();
  289. }
  290. on('init', () => {
  291. init();
  292. updateSize();
  293. setTranslate();
  294. });
  295. on('update resize observerUpdate lock unlock', () => {
  296. updateSize();
  297. });
  298. on('setTranslate', () => {
  299. setTranslate();
  300. });
  301. on('setTransition', (_s, duration) => {
  302. setTransition(duration);
  303. });
  304. on('enable disable', () => {
  305. const {
  306. $el
  307. } = swiper.scrollbar;
  308. if ($el) {
  309. $el[swiper.enabled ? 'removeClass' : 'addClass'](swiper.params.scrollbar.lockClass);
  310. }
  311. });
  312. on('destroy', () => {
  313. destroy();
  314. });
  315. Object.assign(swiper.scrollbar, {
  316. updateSize,
  317. setTranslate,
  318. init,
  319. destroy
  320. });
  321. }