사용자 경험을 극대화하기 위해 인터랙티브한 요소와 재사용가능한 컴포넌트를 활용했습니다.
- Swiper를 사용하여 다양한 영화 정보를 제공했습니다.
- 각 영화의 재생 버튼을 클릭하여 모달 창을 통해 예고편을 바로 볼 수 있도록 구성했습니다.
- 극장 목록은 scroll-x animation을 적용하여 사용자가 다양한 극장을 한눈에 볼 수 있도록 구성했습니다.
- Flex와 transition을 활용하여 이벤트에 인터랙티브한 레이아웃을 구성했습니다.
• 배포 주소: https://cgv-re.netlify.app/
Development
Config
Environment
1️⃣ 이미지 포맷을 jpg에서 webp로 변환
-
성능 : 64 → 71
2️⃣ useMemo를 활용하여 SwiperSlide를 동적으로 생성(코드의 반복성 줄임) & loading="lazy"로 초기 로딩 성능 개선
-
성능 : 71 -> 73
- swiper
/* Intro */
import { Swiper, SwiperSlide } from "swiper/react";
import { EffectFade, Autoplay, Navigation } from "swiper/modules";
import "swiper/css";
import "swiper/css/navigation";
import "swiper/css/effect-fade";
<Swiper
loop={true}
navigation={true}
modules={[EffectFade, Navigation, Autoplay]}
autoplay={{ delay: 6000 }}
effect={"fade"}
>
- modal
// Trailer Modal
const [showModal, setShowModal] = useState(false);
const [videoSource, setVideoSource] = useState("");
const openModal = useCallback((source) => {
setVideoSource(source);
setShowModal(true);
}, []);
const handleCloseModal = useCallback(() => {
setVideoSource("");
setShowModal(false);
}, []);
{
/* 모달 */
}
{
showModal && (
<div className="modal">
<div className="inner">
<div>
<button onClick={handleCloseModal}>
<FontAwesomeIcon icon={faXmark} />
</button>
</div>
<video controls autoPlay loop muted>
<source src={videoSource} type="video/mp4" />
</video>
</div>
</div>
);
}
<div
className="btn"
onClick={() =>
openModal(
"https://h.vod.cgv.co.kr/vodCGVa/88267/88267_226464_1200_128_960_540.mp4"
)
}
>
<img className="play" src={play} alt="play" loading="lazy" />
</div>
- Flex & animation
{/* section : theater */}
<section className="theater">
<div className="inner">
<div className="list">
<div className="list-group">
<img src="{theater01}" alt="theater01" loading="lazy" />
<img src="{theater02}" alt="theater02" loading="lazy" />
<img src="{theater03}" alt="theater03" loading="lazy" />
<img src="{theater04}" alt="theater04" loading="lazy" />
<img src="{theater05}" alt="theater05" loading="lazy" />
</div>
<div aria-hidden="true" className="list-group">
<img src="{theater01}" alt="theater01" loading="lazy" />
<img src="{theater02}" alt="theater02" loading="lazy" />
<img src="{theater03}" alt="theater03" loading="lazy" />
<img src="{theater04}" alt="theater04" loading="lazy" />
<img src="{theater05}" alt="theater05" loading="lazy" />
</div>
</div>
<div className="list list-reverse">
<div className="list-group">
<img src="{theater06}" alt="theater06" loading="lazy" />
<img src="{theater07}" alt="theater07" loading="lazy" />
<img src="{theater08}" alt="theater08" loading="lazy" />
<img src="{theater09}" alt="theater09" loading="lazy" />
<img src="{theater10}" alt="theater10" loading="lazy" />
<img src="{theater11}" alt="theater11" loading="lazy" />
</div>
<div aria-hidden="true" className="list-group">
<img src="{theater06}" alt="theater06" loading="lazy" />
<img src="{theater07}" alt="theater07" loading="lazy" />
<img src="{theater08}" alt="theater08" loading="lazy" />
<img src="{theater09}" alt="theater09" loading="lazy" />
<img src="{theater10}" alt="theater10" loading="lazy" />
<img src="{theater11}" alt="theater11" loading="lazy" />
</div>
</div>
</div>
</section>
.theater {
margin-top: 126px;
overflow: hidden;
.inner {
display: flex;
flex-direction: column;
gap: 16px;
margin: auto;
.list {
display: flex;
user-select: none;
gap: 16px;
.list-group {
display: flex;
justify-content: space-around;
gap: 16px;
animation: scroll-x var(--duration) linear infinite;
}
}
.list-reverse {
.list-group {
animation-direction: reverse;
animation-delay: -3s;
}
}
}
img {
width: 382px;
height: 212px;
border-radius: 12px;
}
}
@media (prefers-reduced-motion: reduce) {
.list {
animation-play-state: paused;
}
}
@keyframes scroll-x {
from {
transform: translateX(var(--scroll-start));
}
to {
transform: translateX(var(--scroll-end));
}
}
- tabBackgrounds 배열에서 현재 활성화된 탭('activeTab')의 배경 목록 표시
<div className="lists" data-aos="fade-up" data-aos-duration="1500">
<ul>
{tabBackgrounds[activeTab].map((bg, index) => (
<li key={index} className={`background-${bg}`}>
<div>
<button onClick={() => handleNavigation("/making")}>상세보기</button>
</div>
</li>
))}
</ul>
</div>
- Flex를 활용하여 동적 리스트 항목 크기 조절 및 애니메이션 효과 구현
.event {
.inner {
.contents {
.lists {
ul {
list-style: none;
padding: 0;
margin: 0;
display: flex;
gap: 6px;
overflow: hidden;
li {
border-radius: 12px;
position: relative;
width: calc(1200px / 7);
height: 530px;
flex: 1;
filter: grayscale(1);
transition: 0.3s;
cursor: pointer;
overflow: hidden;
&:hover {
flex: 5;
filter: grayscale(0);
div {
opacity: 1;
transition: 0.3s;
}
}
}
}
}
}
}
}