SlideHorizontal.vue 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141
  1. <script setup lang="ts">
  2. import { onMounted, onUnmounted, reactive, ref, watch } from 'vue'
  3. import {
  4. getSlideOffset,
  5. slideInit,
  6. slideReset,
  7. slideTouchEnd,
  8. slideTouchMove,
  9. slideTouchStart,
  10. } from '@/utils/slide'
  11. import { SlideType } from '@/utils/const_var'
  12. import { _css } from '@/utils/dom'
  13. const props = defineProps({
  14. index: { type: Number, default: 0 },
  15. name: { type: String, default: '' },
  16. autoplay: { type: Boolean, default: false },
  17. indicator: { type: Boolean, default: false },
  18. changeActiveIndexUseAnim: { type: Boolean, default: true },
  19. })
  20. const emit = defineEmits<{ 'update:index': [number] }>()
  21. let ob: MutationObserver | null = null
  22. const slideListEl = ref<HTMLElement | null>(null)
  23. const state = reactive({
  24. judgeValue: 20,
  25. type: SlideType.HORIZONTAL,
  26. name: props.name,
  27. localIndex: props.index,
  28. needCheck: true,
  29. next: false,
  30. isDown: false,
  31. start: { x: 0, y: 0, time: 0 },
  32. move: { x: 0, y: 0 },
  33. wrapper: { width: 0, height: 0, childrenLength: 0 },
  34. })
  35. watch(
  36. () => props.index,
  37. (newVal) => {
  38. if (state.localIndex !== newVal) {
  39. state.localIndex = newVal
  40. if (!slideListEl.value) return
  41. if (props.changeActiveIndexUseAnim) {
  42. _css(slideListEl.value, 'transition-duration', '300ms')
  43. }
  44. _css(
  45. slideListEl.value,
  46. 'transform',
  47. `translate3d(${getSlideOffset(state, slideListEl.value)}px, 0, 0)`,
  48. )
  49. }
  50. },
  51. )
  52. onMounted(() => {
  53. if (!slideListEl.value) return
  54. slideInit(slideListEl.value, state)
  55. if (props.autoplay) {
  56. setInterval(() => {
  57. if (state.localIndex === state.wrapper.childrenLength - 1) {
  58. emit('update:index', 0)
  59. } else {
  60. emit('update:index', state.localIndex + 1)
  61. }
  62. }, 3000)
  63. }
  64. ob = new MutationObserver(() => {
  65. if (slideListEl.value) state.wrapper.childrenLength = slideListEl.value.children.length
  66. })
  67. ob.observe(slideListEl.value, { childList: true })
  68. })
  69. onUnmounted(() => {
  70. ob?.disconnect()
  71. })
  72. function touchStart(e: PointerEvent) {
  73. if (slideListEl.value) slideTouchStart(e, slideListEl.value, state)
  74. }
  75. function touchMove(e: PointerEvent) {
  76. if (slideListEl.value) slideTouchMove(e, slideListEl.value, state)
  77. }
  78. function touchEnd(e: PointerEvent) {
  79. slideTouchEnd(e, state)
  80. if (slideListEl.value) slideReset(e, slideListEl.value, state, emit)
  81. }
  82. </script>
  83. <template>
  84. <div class="slide horizontal">
  85. <div
  86. v-if="indicator && state.wrapper.childrenLength"
  87. class="indicator-bullets"
  88. >
  89. <div
  90. v-for="item in state.wrapper.childrenLength"
  91. :key="item"
  92. class="bullet"
  93. :class="{ active: state.localIndex === item - 1 }"
  94. />
  95. </div>
  96. <div
  97. ref="slideListEl"
  98. class="slide-list"
  99. @pointerdown.prevent="touchStart"
  100. @pointermove.prevent="touchMove"
  101. @pointerup.prevent="touchEnd"
  102. >
  103. <slot />
  104. </div>
  105. </div>
  106. </template>
  107. <style scoped lang="less">
  108. .indicator-bullets {
  109. position: absolute;
  110. bottom: 10rem;
  111. z-index: 2;
  112. left: 50%;
  113. transform: translateX(-50%);
  114. display: flex;
  115. justify-content: center;
  116. gap: 7rem;
  117. .bullet {
  118. width: 5rem;
  119. height: 5rem;
  120. border-radius: 50%;
  121. background: var(--second-btn-color);
  122. &.active {
  123. background: white;
  124. }
  125. }
  126. }
  127. </style>