SwiperBanner3.vue 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272
  1. <template>
  2. <section class="swiper--banner--wrapper3" :data-type="type" :data-fit="fit">
  3. <div class="top--text--wrap">
  4. <h2 v-html="mtitle"></h2>
  5. <div class="mt--40" v-html="stitle"></div>
  6. </div>
  7. <div class="swiper--banner--container">
  8. <!-- 70% 영역: 단일 배너 -->
  9. <div class="swiper--banner--section">
  10. <div class="swiper--container" ref="swiperContainer">
  11. <div class="swiper-wrapper">
  12. <div v-for="(slide, index) in slides" :key="index" class="swiper-slide">
  13. <div class="slide--image" v-if="slide.image">
  14. <img
  15. :src="slide.image"
  16. :alt="slide.alt || 'Banner Image'"
  17. loading="lazy"
  18. />
  19. <div class="desc--wrapper">
  20. <div class="desc--wrap">
  21. <div class="btn--actions">
  22. <div class="swiper-button-prev" ref="prevRef">
  23. <svg
  24. xmlns="http://www.w3.org/2000/svg"
  25. width="24"
  26. height="24"
  27. viewBox="0 0 24 24"
  28. fill="none"
  29. >
  30. <path
  31. d="M15 18L9 12L15 6"
  32. stroke="black"
  33. stroke-width="2"
  34. stroke-linecap="round"
  35. stroke-linejoin="round"
  36. />
  37. </svg>
  38. </div>
  39. <div class="swiper-button-next" ref="nextRef">
  40. <svg
  41. xmlns="http://www.w3.org/2000/svg"
  42. width="24"
  43. height="24"
  44. viewBox="0 0 24 24"
  45. fill="none"
  46. >
  47. <path
  48. d="M9 18L15 12L9 6"
  49. stroke="black"
  50. stroke-width="2"
  51. stroke-linecap="round"
  52. stroke-linejoin="round"
  53. />
  54. </svg>
  55. </div>
  56. </div>
  57. <h2 v-if="getSlideTitle(index)" class="main--title">
  58. {{ getSlideTitle(index) }}
  59. </h2>
  60. <h4
  61. v-if="getSlideSmalldesc(index)"
  62. class="desc--title"
  63. v-html="getSlideSmalldesc(index)"
  64. ></h4>
  65. </div>
  66. </div>
  67. </div>
  68. </div>
  69. </div>
  70. </div>
  71. </div>
  72. </div>
  73. </section>
  74. </template>
  75. <script setup>
  76. import { Swiper } from "swiper";
  77. import { Navigation, Pagination, Autoplay, EffectFade } from "swiper/modules";
  78. import "swiper/css";
  79. import "swiper/css/navigation";
  80. import "swiper/css/pagination";
  81. import "swiper/css/effect-fade";
  82. const { getMediaUrl } = useImage();
  83. // Props 정의
  84. const props = defineProps({
  85. slides: {
  86. type: Array,
  87. default: () => [],
  88. },
  89. height: {
  90. type: String,
  91. default: "60%",
  92. },
  93. mtitle: {
  94. type: String,
  95. default: "",
  96. },
  97. stitle: {
  98. type: String,
  99. default: "",
  100. },
  101. title: {
  102. type: String,
  103. default: "",
  104. },
  105. subtitle: {
  106. type: String,
  107. default: "",
  108. },
  109. smalldesc: {
  110. type: String,
  111. default: "",
  112. },
  113. cautiondesc: {
  114. type: String,
  115. default: "",
  116. },
  117. morelink: {
  118. type: String,
  119. default: "",
  120. },
  121. morelinktitle: {
  122. type: String,
  123. default: "자세히 보기",
  124. },
  125. morelinktarget: {
  126. type: String,
  127. default: "_self",
  128. },
  129. notice: {
  130. type: String,
  131. default: "",
  132. },
  133. autoplay: {
  134. type: [Boolean, Object],
  135. default: () => ({ delay: 5000 }),
  136. },
  137. loop: {
  138. type: Boolean,
  139. default: true,
  140. },
  141. type: {
  142. type: String,
  143. default: "horizental", // 'horz' , 'vert'
  144. validator: (value) => ["horizental", "vertical"].includes(value),
  145. },
  146. fit: {
  147. type: String,
  148. default: "cover",
  149. validator: (value) => ["cover", "contain"].includes(value),
  150. },
  151. });
  152. // Refs
  153. const swiperContainer = ref(null);
  154. const paginationRef = ref(null);
  155. const prevRef = ref(null);
  156. const nextRef = ref(null);
  157. const textSlider = ref(null);
  158. let swiperInstance = null;
  159. // 현재 슬라이드 인덱스
  160. const currentSlide = ref(0);
  161. // 슬라이드별 텍스트 반환 함수들
  162. const getSlideTitle = (index) => {
  163. return props.slides[index]?.title || props.title;
  164. };
  165. const getSlideSubtitle = (index) => {
  166. return props.slides[index]?.subtitle || props.subtitle;
  167. };
  168. const getSlideSmalldesc = (index) => {
  169. return props.slides[index]?.smalldesc || props.smalldesc;
  170. };
  171. const getSlideMorelink = (index) => {
  172. return props.slides[index]?.morelink || props.morelink;
  173. };
  174. const getSlideMorelinktitle = (index) => {
  175. return props.slides[index]?.morelinktitle || props.morelinktitle;
  176. };
  177. const getSlideTarget = (index) => {
  178. return props.slides[index]?.morelinktarget || props.morelinktarget;
  179. };
  180. const getSlideCautiondesc = (index) => {
  181. return props.slides[index]?.cautiondesc || props.cautiondesc;
  182. };
  183. onMounted(() => {
  184. // Swiper 인스턴스 초기화
  185. swiperInstance = new Swiper(swiperContainer.value, {
  186. modules: [Navigation, Pagination, Autoplay],
  187. slidesPerView: 1,
  188. spaceBetween: 0,
  189. loop: props.loop,
  190. autoplay: props.autoplay
  191. ? {
  192. delay:
  193. typeof props.autoplay === "object" ? props.autoplay.delay || 5000 : 5000,
  194. disableOnInteraction: false,
  195. pauseOnMouseEnter: true,
  196. }
  197. : false,
  198. navigation: {
  199. nextEl: nextRef.value,
  200. prevEl: prevRef.value,
  201. },
  202. pagination: {
  203. el: paginationRef.value,
  204. clickable: true,
  205. type: "bullets",
  206. // renderBullet: function (index, className) {
  207. // return '<span class="' + className + '">' + (index + 1) + "</span>";
  208. // },
  209. },
  210. effect: "fade",
  211. fadeEffect: {
  212. crossFade: true,
  213. },
  214. speed: 800,
  215. // 슬라이드 변경 이벤트
  216. on: {
  217. slideChange: function () {
  218. if (this && this.realIndex !== undefined) {
  219. currentSlide.value = this.realIndex;
  220. }
  221. // 모든 비디오 일시정지
  222. const allVideos = this.el.querySelectorAll("video");
  223. allVideos.forEach((video) => {
  224. video.pause();
  225. video.currentTime = 0;
  226. });
  227. // 현재 슬라이드의 비디오 재생
  228. const currentSlideEl = this.slides[this.activeIndex];
  229. if (currentSlideEl) {
  230. const video = currentSlideEl.querySelector("video");
  231. if (video) {
  232. video.play().catch((err) => console.log("Video play failed:", err));
  233. }
  234. }
  235. },
  236. init: function () {
  237. if (this && this.realIndex !== undefined) {
  238. currentSlide.value = this.realIndex;
  239. }
  240. // 초기 슬라이드의 비디오 재생
  241. const currentSlideEl = this.slides[this.activeIndex];
  242. if (currentSlideEl) {
  243. const video = currentSlideEl.querySelector("video");
  244. if (video) {
  245. video.play().catch((err) => console.log("Video play failed:", err));
  246. }
  247. }
  248. },
  249. },
  250. });
  251. });
  252. onUnmounted(() => {
  253. if (swiperInstance) {
  254. swiperInstance.destroy(true, true);
  255. }
  256. });
  257. </script>