main.vue 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248
  1. <template>
  2. <div
  3. class="el-carousel"
  4. :class="{ 'el-carousel--card': type === 'card' }"
  5. @mouseenter.stop="handleMouseEnter"
  6. @mouseleave.stop="handleMouseLeave">
  7. <div
  8. class="el-carousel__container"
  9. :style="{ height: height }">
  10. <transition name="carousel-arrow-left">
  11. <button
  12. v-if="arrow !== 'never'"
  13. v-show="arrow === 'always' || hover"
  14. @mouseenter="handleButtonEnter('left')"
  15. @mouseleave="handleButtonLeave"
  16. @click.stop="throttledArrowClick(activeIndex - 1)"
  17. class="el-carousel__arrow el-carousel__arrow--left">
  18. <i class="el-icon-arrow-left"></i>
  19. </button>
  20. </transition>
  21. <transition name="carousel-arrow-right">
  22. <button
  23. v-if="arrow !== 'never'"
  24. v-show="arrow === 'always' || hover"
  25. @mouseenter="handleButtonEnter('right')"
  26. @mouseleave="handleButtonLeave"
  27. @click.stop="throttledArrowClick(activeIndex + 1)"
  28. class="el-carousel__arrow el-carousel__arrow--right">
  29. <i class="el-icon-arrow-right"></i>
  30. </button>
  31. </transition>
  32. <slot></slot>
  33. </div>
  34. <ul
  35. class="el-carousel__indicators"
  36. v-if="indicatorPosition !== 'none'"
  37. :class="{ 'el-carousel__indicators--labels': hasLabel, 'el-carousel__indicators--outside': indicatorPosition === 'outside' || type === 'card' }">
  38. <li
  39. v-for="(item, index) in items"
  40. class="el-carousel__indicator"
  41. :class="{ 'is-active': index === activeIndex }"
  42. @mouseenter="throttledIndicatorHover(index)"
  43. @click.stop="handleIndicatorClick(index)">
  44. <button class="el-carousel__button"><span v-if="hasLabel">{{ item.label }}</span></button>
  45. </li>
  46. </ul>
  47. </div>
  48. </template>
  49. <script>
  50. import throttle from 'throttle-debounce/throttle';
  51. import { addResizeListener, removeResizeListener } from 'element-ui/src/utils/resize-event';
  52. export default {
  53. name: 'ElCarousel',
  54. props: {
  55. initialIndex: {
  56. type: Number,
  57. default: 0
  58. },
  59. height: String,
  60. trigger: {
  61. type: String,
  62. default: 'hover'
  63. },
  64. autoplay: {
  65. type: Boolean,
  66. default: true
  67. },
  68. interval: {
  69. type: Number,
  70. default: 3000
  71. },
  72. indicatorPosition: String,
  73. indicator: {
  74. type: Boolean,
  75. default: true
  76. },
  77. arrow: {
  78. type: String,
  79. default: 'hover'
  80. },
  81. type: String
  82. },
  83. data() {
  84. return {
  85. items: [],
  86. activeIndex: -1,
  87. containerWidth: 0,
  88. timer: null,
  89. hover: false
  90. };
  91. },
  92. computed: {
  93. hasLabel() {
  94. return this.items.some(item => item.label.toString().length > 0);
  95. }
  96. },
  97. watch: {
  98. items(val) {
  99. if (val.length > 0) this.setActiveItem(this.initialIndex);
  100. },
  101. activeIndex(val, oldVal) {
  102. this.resetItemPosition(oldVal);
  103. this.$emit('change', val, oldVal);
  104. },
  105. autoplay(val) {
  106. val ? this.startTimer() : this.pauseTimer();
  107. }
  108. },
  109. methods: {
  110. handleMouseEnter() {
  111. this.hover = true;
  112. this.pauseTimer();
  113. },
  114. handleMouseLeave() {
  115. this.hover = false;
  116. this.startTimer();
  117. },
  118. itemInStage(item, index) {
  119. const length = this.items.length;
  120. if (index === length - 1 && item.inStage && this.items[0].active ||
  121. (item.inStage && this.items[index + 1] && this.items[index + 1].active)) {
  122. return 'left';
  123. } else if (index === 0 && item.inStage && this.items[length - 1].active ||
  124. (item.inStage && this.items[index - 1] && this.items[index - 1].active)) {
  125. return 'right';
  126. }
  127. return false;
  128. },
  129. handleButtonEnter(arrow) {
  130. this.items.forEach((item, index) => {
  131. if (arrow === this.itemInStage(item, index)) {
  132. item.hover = true;
  133. }
  134. });
  135. },
  136. handleButtonLeave() {
  137. this.items.forEach(item => {
  138. item.hover = false;
  139. });
  140. },
  141. updateItems() {
  142. this.items = this.$children.filter(child => child.$options.name === 'ElCarouselItem');
  143. },
  144. resetItemPosition(oldIndex) {
  145. this.items.forEach((item, index) => {
  146. item.translateItem(index, this.activeIndex, oldIndex);
  147. });
  148. },
  149. playSlides() {
  150. if (this.activeIndex < this.items.length - 1) {
  151. this.activeIndex++;
  152. } else {
  153. this.activeIndex = 0;
  154. }
  155. },
  156. pauseTimer() {
  157. clearInterval(this.timer);
  158. },
  159. startTimer() {
  160. if (this.interval <= 0 || !this.autoplay) return;
  161. this.timer = setInterval(this.playSlides, this.interval);
  162. },
  163. setActiveItem(index) {
  164. if (typeof index === 'string') {
  165. const filteredItems = this.items.filter(item => item.name === index);
  166. if (filteredItems.length > 0) {
  167. index = this.items.indexOf(filteredItems[0]);
  168. }
  169. }
  170. index = Number(index);
  171. if (isNaN(index) || index !== Math.floor(index)) {
  172. process.env.NODE_ENV !== 'production' &&
  173. console.warn('[Element Warn][Carousel]index must be an integer.');
  174. return;
  175. }
  176. let length = this.items.length;
  177. if (index < 0) {
  178. this.activeIndex = length - 1;
  179. } else if (index >= length) {
  180. this.activeIndex = 0;
  181. } else {
  182. this.activeIndex = index;
  183. }
  184. },
  185. prev() {
  186. this.setActiveItem(this.activeIndex - 1);
  187. },
  188. next() {
  189. this.setActiveItem(this.activeIndex + 1);
  190. },
  191. handleIndicatorClick(index) {
  192. this.activeIndex = index;
  193. },
  194. handleIndicatorHover(index) {
  195. if (this.trigger === 'hover' && index !== this.activeIndex) {
  196. this.activeIndex = index;
  197. }
  198. }
  199. },
  200. created() {
  201. this.throttledArrowClick = throttle(300, true, index => {
  202. this.setActiveItem(index);
  203. });
  204. this.throttledIndicatorHover = throttle(300, index => {
  205. this.handleIndicatorHover(index);
  206. });
  207. },
  208. mounted() {
  209. this.updateItems();
  210. this.$nextTick(() => {
  211. addResizeListener(this.$el, this.resetItemPosition);
  212. if (this.initialIndex < this.items.length && this.initialIndex >= 0) {
  213. this.activeIndex = this.initialIndex;
  214. }
  215. this.startTimer();
  216. });
  217. },
  218. beforeDestroy() {
  219. if (this.$el) removeResizeListener(this.$el, this.resetItemPosition);
  220. }
  221. };
  222. </script>