scrollbar.js 8.3 KB

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