autoplay.js 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279
  1. /* eslint no-underscore-dangle: "off" */
  2. /* eslint no-use-before-define: "off" */
  3. import { getDocument } from 'ssr-window';
  4. export default function Autoplay({
  5. swiper,
  6. extendParams,
  7. on,
  8. emit,
  9. params
  10. }) {
  11. swiper.autoplay = {
  12. running: false,
  13. paused: false,
  14. timeLeft: 0
  15. };
  16. extendParams({
  17. autoplay: {
  18. enabled: false,
  19. delay: 3000,
  20. waitForTransition: true,
  21. disableOnInteraction: true,
  22. stopOnLastSlide: false,
  23. reverseDirection: false,
  24. pauseOnMouseEnter: false
  25. }
  26. });
  27. let timeout;
  28. let raf;
  29. let autoplayDelayTotal = params && params.autoplay ? params.autoplay.delay : 3000;
  30. let autoplayDelayCurrent = params && params.autoplay ? params.autoplay.delay : 3000;
  31. let autoplayTimeLeft;
  32. let autoplayStartTime = new Date().getTime;
  33. let wasPaused;
  34. let isTouched;
  35. let pausedByTouch;
  36. let touchStartTimeout;
  37. let slideChanged;
  38. let pausedByInteraction;
  39. function onTransitionEnd(e) {
  40. if (!swiper || swiper.destroyed || !swiper.wrapperEl) return;
  41. if (e.target !== swiper.wrapperEl) return;
  42. swiper.wrapperEl.removeEventListener('transitionend', onTransitionEnd);
  43. resume();
  44. }
  45. const calcTimeLeft = () => {
  46. if (swiper.destroyed || !swiper.autoplay.running) return;
  47. if (swiper.autoplay.paused) {
  48. wasPaused = true;
  49. } else if (wasPaused) {
  50. autoplayDelayCurrent = autoplayTimeLeft;
  51. wasPaused = false;
  52. }
  53. const timeLeft = swiper.autoplay.paused ? autoplayTimeLeft : autoplayStartTime + autoplayDelayCurrent - new Date().getTime();
  54. swiper.autoplay.timeLeft = timeLeft;
  55. emit('autoplayTimeLeft', timeLeft, timeLeft / autoplayDelayTotal);
  56. raf = requestAnimationFrame(() => {
  57. calcTimeLeft();
  58. });
  59. };
  60. const getSlideDelay = () => {
  61. let activeSlideEl;
  62. if (swiper.virtual && swiper.params.virtual.enabled) {
  63. activeSlideEl = swiper.slides.filter(slideEl => slideEl.classList.contains('swiper-slide-active'))[0];
  64. } else {
  65. activeSlideEl = swiper.slides[swiper.activeIndex];
  66. }
  67. if (!activeSlideEl) return undefined;
  68. const currentSlideDelay = parseInt(activeSlideEl.getAttribute('data-swiper-autoplay'), 10);
  69. return currentSlideDelay;
  70. };
  71. const run = delayForce => {
  72. if (swiper.destroyed || !swiper.autoplay.running) return;
  73. cancelAnimationFrame(raf);
  74. calcTimeLeft();
  75. let delay = typeof delayForce === 'undefined' ? swiper.params.autoplay.delay : delayForce;
  76. autoplayDelayTotal = swiper.params.autoplay.delay;
  77. autoplayDelayCurrent = swiper.params.autoplay.delay;
  78. const currentSlideDelay = getSlideDelay();
  79. if (!Number.isNaN(currentSlideDelay) && currentSlideDelay > 0 && typeof delayForce === 'undefined') {
  80. delay = currentSlideDelay;
  81. autoplayDelayTotal = currentSlideDelay;
  82. autoplayDelayCurrent = currentSlideDelay;
  83. }
  84. autoplayTimeLeft = delay;
  85. const speed = swiper.params.speed;
  86. const proceed = () => {
  87. if (!swiper || swiper.destroyed) return;
  88. if (swiper.params.autoplay.reverseDirection) {
  89. if (!swiper.isBeginning || swiper.params.loop || swiper.params.rewind) {
  90. swiper.slidePrev(speed, true, true);
  91. emit('autoplay');
  92. } else if (!swiper.params.autoplay.stopOnLastSlide) {
  93. swiper.slideTo(swiper.slides.length - 1, speed, true, true);
  94. emit('autoplay');
  95. }
  96. } else {
  97. if (!swiper.isEnd || swiper.params.loop || swiper.params.rewind) {
  98. swiper.slideNext(speed, true, true);
  99. emit('autoplay');
  100. } else if (!swiper.params.autoplay.stopOnLastSlide) {
  101. swiper.slideTo(0, speed, true, true);
  102. emit('autoplay');
  103. }
  104. }
  105. if (swiper.params.cssMode) {
  106. autoplayStartTime = new Date().getTime();
  107. requestAnimationFrame(() => {
  108. run();
  109. });
  110. }
  111. };
  112. if (delay > 0) {
  113. clearTimeout(timeout);
  114. timeout = setTimeout(() => {
  115. proceed();
  116. }, delay);
  117. } else {
  118. requestAnimationFrame(() => {
  119. proceed();
  120. });
  121. }
  122. // eslint-disable-next-line
  123. return delay;
  124. };
  125. const start = () => {
  126. swiper.autoplay.running = true;
  127. run();
  128. emit('autoplayStart');
  129. };
  130. const stop = () => {
  131. swiper.autoplay.running = false;
  132. clearTimeout(timeout);
  133. cancelAnimationFrame(raf);
  134. emit('autoplayStop');
  135. };
  136. const pause = (internal, reset) => {
  137. if (swiper.destroyed || !swiper.autoplay.running) return;
  138. clearTimeout(timeout);
  139. if (!internal) {
  140. pausedByInteraction = true;
  141. }
  142. const proceed = () => {
  143. emit('autoplayPause');
  144. if (swiper.params.autoplay.waitForTransition) {
  145. swiper.wrapperEl.addEventListener('transitionend', onTransitionEnd);
  146. } else {
  147. resume();
  148. }
  149. };
  150. swiper.autoplay.paused = true;
  151. if (reset) {
  152. if (slideChanged) {
  153. autoplayTimeLeft = swiper.params.autoplay.delay;
  154. }
  155. slideChanged = false;
  156. proceed();
  157. return;
  158. }
  159. const delay = autoplayTimeLeft || swiper.params.autoplay.delay;
  160. autoplayTimeLeft = delay - (new Date().getTime() - autoplayStartTime);
  161. if (swiper.isEnd && autoplayTimeLeft < 0 && !swiper.params.loop) return;
  162. if (autoplayTimeLeft < 0) autoplayTimeLeft = 0;
  163. proceed();
  164. };
  165. const resume = () => {
  166. if (swiper.isEnd && autoplayTimeLeft < 0 && !swiper.params.loop || swiper.destroyed || !swiper.autoplay.running) return;
  167. autoplayStartTime = new Date().getTime();
  168. if (pausedByInteraction) {
  169. pausedByInteraction = false;
  170. run(autoplayTimeLeft);
  171. } else {
  172. run();
  173. }
  174. swiper.autoplay.paused = false;
  175. emit('autoplayResume');
  176. };
  177. const onVisibilityChange = () => {
  178. if (swiper.destroyed || !swiper.autoplay.running) return;
  179. const document = getDocument();
  180. if (document.visibilityState === 'hidden') {
  181. pausedByInteraction = true;
  182. pause(true);
  183. }
  184. if (document.visibilityState === 'visible') {
  185. resume();
  186. }
  187. };
  188. const onPointerEnter = e => {
  189. if (e.pointerType !== 'mouse') return;
  190. pausedByInteraction = true;
  191. pause(true);
  192. };
  193. const onPointerLeave = e => {
  194. if (e.pointerType !== 'mouse') return;
  195. if (swiper.autoplay.paused) {
  196. resume();
  197. }
  198. };
  199. const attachMouseEvents = () => {
  200. if (swiper.params.autoplay.pauseOnMouseEnter) {
  201. swiper.el.addEventListener('pointerenter', onPointerEnter);
  202. swiper.el.addEventListener('pointerleave', onPointerLeave);
  203. }
  204. };
  205. const detachMouseEvents = () => {
  206. swiper.el.removeEventListener('pointerenter', onPointerEnter);
  207. swiper.el.removeEventListener('pointerleave', onPointerLeave);
  208. };
  209. const attachDocumentEvents = () => {
  210. const document = getDocument();
  211. document.addEventListener('visibilitychange', onVisibilityChange);
  212. };
  213. const detachDocumentEvents = () => {
  214. const document = getDocument();
  215. document.removeEventListener('visibilitychange', onVisibilityChange);
  216. };
  217. on('init', () => {
  218. if (swiper.params.autoplay.enabled) {
  219. attachMouseEvents();
  220. attachDocumentEvents();
  221. autoplayStartTime = new Date().getTime();
  222. start();
  223. }
  224. });
  225. on('destroy', () => {
  226. detachMouseEvents();
  227. detachDocumentEvents();
  228. if (swiper.autoplay.running) {
  229. stop();
  230. }
  231. });
  232. on('beforeTransitionStart', (_s, speed, internal) => {
  233. if (swiper.destroyed || !swiper.autoplay.running) return;
  234. if (internal || !swiper.params.autoplay.disableOnInteraction) {
  235. pause(true, true);
  236. } else {
  237. stop();
  238. }
  239. });
  240. on('sliderFirstMove', () => {
  241. if (swiper.destroyed || !swiper.autoplay.running) return;
  242. if (swiper.params.autoplay.disableOnInteraction) {
  243. stop();
  244. return;
  245. }
  246. isTouched = true;
  247. pausedByTouch = false;
  248. pausedByInteraction = false;
  249. touchStartTimeout = setTimeout(() => {
  250. pausedByInteraction = true;
  251. pausedByTouch = true;
  252. pause(true);
  253. }, 200);
  254. });
  255. on('touchEnd', () => {
  256. if (swiper.destroyed || !swiper.autoplay.running || !isTouched) return;
  257. clearTimeout(touchStartTimeout);
  258. clearTimeout(timeout);
  259. if (swiper.params.autoplay.disableOnInteraction) {
  260. pausedByTouch = false;
  261. isTouched = false;
  262. return;
  263. }
  264. if (pausedByTouch && swiper.params.cssMode) resume();
  265. pausedByTouch = false;
  266. isTouched = false;
  267. });
  268. on('slideChange', () => {
  269. if (swiper.destroyed || !swiper.autoplay.running) return;
  270. slideChanged = true;
  271. });
  272. Object.assign(swiper.autoplay, {
  273. start,
  274. stop,
  275. pause,
  276. resume
  277. });
  278. }