TitleVisual.vue 2.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293
  1. <template>
  2. <section class="title--visual" :data-theme="theme">
  3. <div class="title--visual--wrapper">
  4. <div
  5. class="title--visual--content"
  6. :data-align="textAlign"
  7. :data-animated="animation"
  8. :class="{ visible: isVisible }"
  9. >
  10. <h2 class="title--main">{{ title }}</h2>
  11. <span class="title--description">{{ description }}</span>
  12. <div v-if="$slots.default" class="title--additional">
  13. <slot></slot>
  14. </div>
  15. </div>
  16. </div>
  17. </section>
  18. </template>
  19. <script setup>
  20. // Props 정의
  21. const props = defineProps({
  22. title: {
  23. type: String,
  24. required: true,
  25. default: "특별함을 창조하다",
  26. },
  27. description: {
  28. type: String,
  29. required: true,
  30. default:
  31. "아우디 RS e-tron GT, 아우디 RS 6 Avant, 아우디 RS Q8의 예에서 여러 옵션을 결합하여 Audi exclusive order의 다양성과 개인 맞춤 구성 옵션을 경험해 보세요. 3D 버튼을 클릭하여 3D 보기를 활성화하면 인터랙티브 경험을 할 수 있습니다.",
  32. },
  33. textAlign: {
  34. type: String,
  35. default: "center", // 'left', 'center', 'right'
  36. validator: (value) => ["left", "center", "right"].includes(value),
  37. },
  38. theme: {
  39. type: String,
  40. default: "light", // 'light', 'dark'
  41. validator: (value) => ["light", "dark"].includes(value),
  42. },
  43. animation: {
  44. type: Boolean,
  45. default: true,
  46. },
  47. animationDelay: {
  48. type: Number,
  49. default: 300, // milliseconds
  50. },
  51. });
  52. // 애니메이션 로직
  53. import { onMounted, ref } from "vue";
  54. const isVisible = ref(false);
  55. onMounted(() => {
  56. if (props.animation) {
  57. // IntersectionObserver로 스크롤 애니메이션 구현
  58. const observer = new IntersectionObserver(
  59. (entries) => {
  60. entries.forEach((entry) => {
  61. if (entry.isIntersecting) {
  62. setTimeout(() => {
  63. isVisible.value = true;
  64. }, props.animationDelay);
  65. observer.unobserve(entry.target);
  66. }
  67. });
  68. },
  69. {
  70. threshold: 0.3, // 30% 보일 때 애니메이션 시작
  71. }
  72. );
  73. const element = document.querySelector(".title--visual");
  74. if (element) {
  75. observer.observe(element);
  76. }
  77. // cleanup
  78. return () => {
  79. if (element) {
  80. observer.unobserve(element);
  81. }
  82. };
  83. } else {
  84. isVisible.value = true;
  85. }
  86. });
  87. </script>