vue-carousel.esm.js 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480
  1. /*! vue-carousel v2.0.0 | (c) 2018-present Chen Fengyuan | MIT */
  2. import { defineComponent, nextTick, h } from 'vue';
  3. const IS_BROWSER = typeof window !== 'undefined' && typeof window.document !== 'undefined';
  4. const IS_TOUCH_DEVICE = IS_BROWSER && window.document.documentElement ? 'ontouchstart' in window.document.documentElement : false;
  5. const HAS_POINTER_EVENT = IS_BROWSER ? 'PointerEvent' in window : false;
  6. const ON_TOUCH_START = IS_TOUCH_DEVICE ? 'ontouchstart' : 'onmousedown';
  7. const ON_TOUCH_MOVE = IS_TOUCH_DEVICE ? 'ontouchmove' : 'onmousemove';
  8. const ON_TOUCH_END = IS_TOUCH_DEVICE ? 'ontouchend' : 'onmouseup';
  9. const ON_POINTER_DOWN = HAS_POINTER_EVENT ? 'onpointerdown' : ON_TOUCH_START;
  10. const ON_POINTER_MOVE = HAS_POINTER_EVENT ? 'onpointermove' : ON_TOUCH_MOVE;
  11. const ON_POINTER_UP = HAS_POINTER_EVENT ? 'onpointerup' : ON_TOUCH_END;
  12. const ON_POINTER_ENTER = HAS_POINTER_EVENT ? 'onpointerenter' : 'onmouseenter';
  13. const ON_POINTER_LEAVE = HAS_POINTER_EVENT ? 'onpointerleave' : 'onmouseleave';
  14. const createVueComponent = (content) => {
  15. if ((typeof content === 'object' && content !== null) || typeof content === 'function') {
  16. return content;
  17. }
  18. return {
  19. template: String(content),
  20. };
  21. };
  22. var script = defineComponent({
  23. name: 'VueCarousel',
  24. props: {
  25. autoplay: {
  26. type: Boolean,
  27. default: true,
  28. },
  29. controls: {
  30. type: [Boolean, String],
  31. default: 'hover',
  32. },
  33. data: {
  34. type: Array,
  35. default: undefined,
  36. },
  37. direction: {
  38. type: String,
  39. default: 'left',
  40. },
  41. duration: {
  42. type: Number,
  43. default: 600,
  44. },
  45. indicators: {
  46. type: [Boolean, String],
  47. default: true,
  48. },
  49. indicatorTrigger: {
  50. type: String,
  51. default: 'click',
  52. },
  53. indicatorType: {
  54. type: String,
  55. default: 'line',
  56. },
  57. interval: {
  58. type: Number,
  59. default: 5000,
  60. },
  61. pauseOnEnter: {
  62. type: Boolean,
  63. default: true,
  64. },
  65. slideOnSwipe: {
  66. type: Boolean,
  67. default: true,
  68. },
  69. tag: {
  70. type: String,
  71. default: 'div',
  72. },
  73. },
  74. emits: ['slide', 'slid'],
  75. data() {
  76. return {
  77. endX: 0,
  78. endY: 0,
  79. index: 0,
  80. list: [],
  81. onVisibilityChange: () => { },
  82. playing: false,
  83. sliding: false,
  84. startX: 0,
  85. startY: 0,
  86. timeout: 0,
  87. };
  88. },
  89. watch: {
  90. data() {
  91. this.init();
  92. },
  93. },
  94. created() {
  95. this.init();
  96. },
  97. mounted() {
  98. document.addEventListener('visibilitychange', this.onVisibilityChange = () => {
  99. if (this.playing) {
  100. if (document.visibilityState === 'visible') {
  101. this.cycle();
  102. }
  103. else {
  104. this.pause();
  105. }
  106. }
  107. });
  108. if (this.autoplay) {
  109. this.play();
  110. }
  111. },
  112. beforeUnmount() {
  113. document.removeEventListener('visibilitychange', this.onVisibilityChange);
  114. },
  115. methods: {
  116. init() {
  117. const { data } = this;
  118. const list = [];
  119. if (data && data.length > 0) {
  120. const maxIndex = data.length - 1;
  121. // In case the number of the data list is reduced (#23).
  122. if (this.index > maxIndex) {
  123. this.index = maxIndex;
  124. }
  125. data.forEach((rawItem, index) => {
  126. const active = index === this.index;
  127. const item = {
  128. ...(rawItem && rawItem.content !== undefined ? rawItem : {
  129. content: rawItem,
  130. }),
  131. active,
  132. bottom: false,
  133. left: false,
  134. raw: rawItem,
  135. right: false,
  136. sliding: active,
  137. toBottom: false,
  138. toLeft: false,
  139. toRight: false,
  140. toTop: false,
  141. top: false,
  142. };
  143. list.push(item);
  144. });
  145. }
  146. this.list = list;
  147. },
  148. play() {
  149. if (!this.playing) {
  150. this.playing = true;
  151. this.cycle();
  152. }
  153. },
  154. cycle() {
  155. if (this.playing) {
  156. this.pause();
  157. this.timeout = window.setTimeout(() => {
  158. this.next(() => {
  159. this.cycle();
  160. });
  161. }, this.interval);
  162. }
  163. },
  164. pause() {
  165. window.clearTimeout(this.timeout);
  166. this.timeout = 0;
  167. },
  168. stop() {
  169. if (this.playing) {
  170. this.pause();
  171. this.playing = false;
  172. }
  173. },
  174. prev(done) {
  175. this.slideTo(this.index - 1, done);
  176. },
  177. next(done) {
  178. this.slideTo(this.index + 1, done);
  179. },
  180. slide(index, reverse = false, done = () => { }) {
  181. if (document.hidden || this.sliding) {
  182. done();
  183. return;
  184. }
  185. const { list } = this;
  186. const minIndex = 0;
  187. const maxIndex = list.length - 1;
  188. if (index > maxIndex) {
  189. index = minIndex;
  190. }
  191. else if (index < minIndex) {
  192. index = maxIndex;
  193. }
  194. if (index === this.index) {
  195. done();
  196. return;
  197. }
  198. this.sliding = true;
  199. this.$emit('slide', index, this.index);
  200. const active = list[this.index];
  201. const next = list[index];
  202. switch (this.direction) {
  203. case 'up':
  204. next.bottom = !reverse;
  205. next.top = reverse;
  206. break;
  207. case 'right':
  208. next.left = !reverse;
  209. next.right = reverse;
  210. break;
  211. case 'down':
  212. next.top = !reverse;
  213. next.bottom = reverse;
  214. break;
  215. // case 'left':
  216. default:
  217. next.right = !reverse;
  218. next.left = reverse;
  219. }
  220. // Waiting for the class change applied
  221. nextTick(() => {
  222. // Force reflow to enable CSS3 transition
  223. // eslint-disable-next-line
  224. this.$el.offsetWidth;
  225. switch (this.direction) {
  226. case 'up':
  227. active.toTop = !reverse;
  228. active.toBottom = reverse;
  229. next.toTop = !reverse;
  230. next.toBottom = reverse;
  231. break;
  232. case 'right':
  233. active.toRight = !reverse;
  234. active.toLeft = reverse;
  235. next.toRight = !reverse;
  236. next.toLeft = reverse;
  237. break;
  238. case 'down':
  239. active.toBottom = !reverse;
  240. active.toTop = reverse;
  241. next.toBottom = !reverse;
  242. next.toTop = reverse;
  243. break;
  244. // case 'left':
  245. default:
  246. active.toLeft = !reverse;
  247. active.toRight = reverse;
  248. next.toLeft = !reverse;
  249. next.toRight = reverse;
  250. }
  251. active.sliding = false;
  252. next.sliding = true;
  253. setTimeout(() => {
  254. active.active = false;
  255. active.top = false;
  256. active.right = false;
  257. active.bottom = false;
  258. active.left = false;
  259. active.toTop = false;
  260. active.toRight = false;
  261. active.toBottom = false;
  262. active.toLeft = false;
  263. next.active = true;
  264. next.top = false;
  265. next.right = false;
  266. next.bottom = false;
  267. next.left = false;
  268. next.toTop = false;
  269. next.toRight = false;
  270. next.toBottom = false;
  271. next.toLeft = false;
  272. this.$emit('slid', index, this.index);
  273. this.index = index;
  274. this.sliding = false;
  275. done();
  276. }, this.duration);
  277. });
  278. },
  279. slideTo(index, done) {
  280. if (index === this.index) {
  281. return;
  282. }
  283. this.slide(index, index < this.index, done);
  284. },
  285. slideStart(event) {
  286. const touch = event.touches ? event.touches[0] : null;
  287. if (this.playing && this.pauseOnEnter) {
  288. this.stop();
  289. }
  290. this.startX = touch ? touch.pageX : event.pageX;
  291. this.startY = touch ? touch.pageY : event.pageY;
  292. },
  293. slideMove(event) {
  294. const touch = event.touches ? event.touches[0] : null;
  295. event.preventDefault();
  296. this.endX = touch ? touch.pageX : event.pageX;
  297. this.endY = touch ? touch.pageY : event.pageY;
  298. },
  299. slideEnd() {
  300. const moveX = this.endX - this.startX;
  301. const moveY = this.endY - this.startY;
  302. this.endX = this.startX;
  303. this.endY = this.startY;
  304. // Ignore click events
  305. if (moveX === 0 && moveY === 0) {
  306. return;
  307. }
  308. const thresholdX = this.$el.offsetWidth / 5;
  309. const thresholdY = this.$el.offsetHeight / 5;
  310. const top = moveY < -thresholdY;
  311. const right = moveX > thresholdX;
  312. const bottom = moveY > thresholdY;
  313. const left = moveX < -thresholdX;
  314. const done = () => {
  315. if (!this.playing && this.pauseOnEnter) {
  316. this.play();
  317. }
  318. };
  319. let prev = false;
  320. let next = false;
  321. switch (this.direction) {
  322. case 'up':
  323. prev = bottom;
  324. next = top;
  325. break;
  326. case 'right':
  327. prev = left;
  328. next = right;
  329. break;
  330. case 'down':
  331. prev = top;
  332. next = bottom;
  333. break;
  334. // case 'left':
  335. default:
  336. prev = right;
  337. next = left;
  338. }
  339. if (prev) {
  340. this.prev(done);
  341. }
  342. else if (next) {
  343. this.next(done);
  344. }
  345. else {
  346. done();
  347. }
  348. },
  349. },
  350. render() {
  351. return h(this.tag, {
  352. class: {
  353. 'vue-carousel': true,
  354. [`vue-carousel--${this.direction}`]: this.direction,
  355. 'vue-carousel--slidable': this.slideOnSwipe,
  356. 'vue-carousel--controls': this.controls === 'hover',
  357. 'vue-carousel--indicators': this.indicators === 'hover',
  358. },
  359. ...(this.pauseOnEnter ? {
  360. [ON_POINTER_ENTER]: this.pause,
  361. [ON_POINTER_LEAVE]: this.cycle,
  362. } : {}),
  363. ...(this.slideOnSwipe ? {
  364. [ON_POINTER_DOWN]: this.slideStart,
  365. [ON_POINTER_MOVE]: this.slideMove,
  366. [ON_POINTER_UP]: this.slideEnd,
  367. } : {}),
  368. }, this.list.length === 0 ? [] : [
  369. h('ul', {
  370. class: 'vue-carousel__list',
  371. }, this.list.map((item, index) => h('li', {
  372. 'data-index': index,
  373. class: {
  374. 'vue-carousel__item': true,
  375. 'vue-carousel__item--active': item.active,
  376. 'vue-carousel__item--top': item.top,
  377. 'vue-carousel__item--right': item.right,
  378. 'vue-carousel__item--bottom': item.bottom,
  379. 'vue-carousel__item--left': item.left,
  380. 'vue-carousel__item--to-top': item.toTop,
  381. 'vue-carousel__item--to-right': item.toRight,
  382. 'vue-carousel__item--to-bottom': item.toBottom,
  383. 'vue-carousel__item--to-left': item.toLeft,
  384. },
  385. style: {
  386. 'transition-duration': `${this.duration / 1000}s`,
  387. },
  388. }, [
  389. h(createVueComponent(item.content)),
  390. ]))),
  391. this.indicators ? h('ol', {
  392. class: {
  393. 'vue-carousel__indicators': true,
  394. [`vue-carousel__indicators--${this.indicatorType}`]: this.indicatorType,
  395. },
  396. }, this.list.map((item, index) => h('li', {
  397. 'data-slide-to': index,
  398. class: {
  399. 'vue-carousel__indicator': true,
  400. 'vue-carousel__indicator--active': item.sliding,
  401. },
  402. ...(() => {
  403. const listeners = {};
  404. const slide = () => {
  405. this.slideTo(index);
  406. };
  407. if (this.indicatorTrigger === 'hover') {
  408. listeners[ON_POINTER_ENTER] = slide;
  409. if (IS_TOUCH_DEVICE && !HAS_POINTER_EVENT) {
  410. listeners[ON_TOUCH_START] = slide;
  411. }
  412. }
  413. else {
  414. listeners.onclick = slide;
  415. }
  416. return listeners;
  417. })(),
  418. }))) : '',
  419. this.controls ? h('button', {
  420. type: 'button',
  421. 'data-slide': 'prev',
  422. class: 'vue-carousel__control vue-carousel__control--prev',
  423. onclick: () => {
  424. if (['right', 'down'].indexOf(this.direction) > -1) {
  425. this.next();
  426. }
  427. else {
  428. this.prev();
  429. }
  430. },
  431. }) : '',
  432. this.controls ? h('button', {
  433. type: 'button',
  434. 'data-slide': 'next',
  435. class: 'vue-carousel__control vue-carousel__control--next',
  436. onclick: () => {
  437. if (['right', 'down'].indexOf(this.direction) > -1) {
  438. this.prev();
  439. }
  440. else {
  441. this.next();
  442. }
  443. },
  444. }) : '',
  445. ]);
  446. },
  447. });
  448. function styleInject(css, ref) {
  449. if ( ref === void 0 ) ref = {};
  450. var insertAt = ref.insertAt;
  451. if (!css || typeof document === 'undefined') { return; }
  452. var head = document.head || document.getElementsByTagName('head')[0];
  453. var style = document.createElement('style');
  454. style.type = 'text/css';
  455. if (insertAt === 'top') {
  456. if (head.firstChild) {
  457. head.insertBefore(style, head.firstChild);
  458. } else {
  459. head.appendChild(style);
  460. }
  461. } else {
  462. head.appendChild(style);
  463. }
  464. if (style.styleSheet) {
  465. style.styleSheet.cssText = css;
  466. } else {
  467. style.appendChild(document.createTextNode(css));
  468. }
  469. }
  470. var css_248z = ".vue-carousel{position:relative;user-select:none}.vue-carousel--slidable{touch-action:none}.vue-carousel--down>.vue-carousel__indicators,.vue-carousel--up>.vue-carousel__indicators{bottom:auto;flex-direction:column;left:auto;right:0;top:50%;transform:translateY(-50%)}.vue-carousel--down>.vue-carousel__indicators>.vue-carousel__indicator:before,.vue-carousel--up>.vue-carousel__indicators>.vue-carousel__indicator:before{height:100%;width:.125rem}.vue-carousel--down>.vue-carousel__indicators--disc>.vue-carousel__indicator,.vue-carousel--up>.vue-carousel__indicators--disc>.vue-carousel__indicator{height:.75rem;width:1.5rem}.vue-carousel--down>.vue-carousel__indicators--disc>.vue-carousel__indicator:before,.vue-carousel--up>.vue-carousel__indicators--disc>.vue-carousel__indicator:before{height:.5rem;width:.5rem}.vue-carousel--right>.vue-carousel__indicators{flex-direction:row-reverse}.vue-carousel--down>.vue-carousel__indicators{flex-direction:column-reverse}.vue-carousel--controls:hover>.vue-carousel__control{opacity:.5;transform:translateX(0);z-index:1}.vue-carousel--controls:hover>.vue-carousel__control:focus,.vue-carousel--controls:hover>.vue-carousel__control:hover{opacity:1}.vue-carousel--controls>.vue-carousel__control{opacity:0;z-index:-1}.vue-carousel--controls>.vue-carousel__control--prev{transform:translateX(-50%)}.vue-carousel--controls>.vue-carousel__control--next{transform:translateX(50%)}.vue-carousel--indicators:hover>.vue-carousel__indicators{opacity:1;z-index:1}.vue-carousel--indicators>.vue-carousel__indicators{opacity:0;transition:opacity .15s;z-index:-1}.vue-carousel__list{margin:0;overflow:hidden;padding:0;position:relative;width:100%}.vue-carousel__item{display:none;margin:0}.vue-carousel__item--active,.vue-carousel__item--bottom,.vue-carousel__item--left,.vue-carousel__item--right,.vue-carousel__item--top{display:block;transition:transform .6s ease-in-out;width:100%}.vue-carousel__item--bottom,.vue-carousel__item--left,.vue-carousel__item--right,.vue-carousel__item--top{left:0;position:absolute;top:0}.vue-carousel__item--top{transform:translateY(-100%)}.vue-carousel__item--top.vue-carousel__item--to-bottom{transform:translateY(0)}.vue-carousel__item--right{transform:translateX(100%)}.vue-carousel__item--right.vue-carousel__item--to-left{transform:translateX(0)}.vue-carousel__item--bottom{transform:translateY(100%)}.vue-carousel__item--bottom.vue-carousel__item--to-top{transform:translateY(0)}.vue-carousel__item--left{transform:translateX(-100%)}.vue-carousel__item--left.vue-carousel__item--to-right{transform:translateX(0)}.vue-carousel__item--active{transform:translate(0);z-index:1}.vue-carousel__item--active.vue-carousel__item--to-top{transform:translateY(-100%)}.vue-carousel__item--active.vue-carousel__item--to-right{transform:translateX(100%)}.vue-carousel__item--active.vue-carousel__item--to-bottom{transform:translateY(100%)}.vue-carousel__item--active.vue-carousel__item--to-left{transform:translateX(-100%)}.vue-carousel__indicators{bottom:0;display:flex;justify-content:center;left:50%;list-style:none;margin:0;padding:0;position:absolute;transform:translateX(-50%);z-index:1}.vue-carousel__indicators--disc>.vue-carousel__indicator{width:.75rem}.vue-carousel__indicators--disc>.vue-carousel__indicator:before{border-radius:50%;height:.5rem;width:.5rem}.vue-carousel__indicator{cursor:pointer;height:1.5rem;margin:.125rem;opacity:.5;position:relative;transition:opacity .15s;width:1.5rem}.vue-carousel__indicator:before{background-color:#fff;content:\"\";display:block;height:.125rem;left:50%;position:absolute;top:50%;transform:translate(-50%,-50%);width:100%}.vue-carousel__indicator--active{opacity:1}.vue-carousel__control{background-color:rgba(0,0,0,.5);border:0;border-radius:50%;color:#fff;cursor:pointer;height:2rem;margin-top:-1rem;opacity:.5;padding:.5rem;position:absolute;top:50%;transition:all .15s;width:2rem}.vue-carousel__control:focus,.vue-carousel__control:hover{opacity:1}.vue-carousel__control:focus{outline:none}.vue-carousel__control:before{border:.0625rem solid transparent;border-radius:.125rem;content:\"\";display:block;height:.5rem;left:50%;position:absolute;top:50%;transform:translate(-50%,-50%) rotate(45deg);width:.5rem}.vue-carousel__control--prev{left:1rem}.vue-carousel__control--prev:before{border-bottom-color:#fff;border-left-color:#fff;margin-left:.125rem}.vue-carousel__control--next{right:1rem}.vue-carousel__control--next:before{border-right-color:#fff;border-top-color:#fff;margin-left:-.125rem}";
  471. styleInject(css_248z);
  472. export { script as default };