vue-carousel.js 21 KB

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