라이브러리를 사용하지 않고 3D carousel 구현
carousel 은 라이브러리로 구현하면 사용하기 편리하지만, 이 구현을 한 포트폴리오 프로젝트는 최소한의 라이브러리를 사용해서 직접 구현하고자 정했기에, next.js와 css와 html 만으로 구현하였다.
2d 형식의 슬라이드는 너무 못생겼고 간단했기에, 3d로 구현해보았다.
핵심 원리를 요약하자면,
- Transform 속성 활용: 각 캐러셀 아이템에 transform CSS 속성을 사용하여 3D 회전(rotateY)과 깊이(translateZ)를 조정해 입체적인 효과를 구현
- 조건부 렌더링과 인덱스 관리: active 상태 변수를 사용하여 현재 활성화된 캐러셀 아이템을 추적하고, 이를 바탕으로 각 아이템의 위치와 스타일을 동적으로 변경
- 사용자 상호작용: 화살표 버튼을 클릭하면 setActive 함수를 통해 active 상태가 업데이트되며, 이는 캐러셀 아이템의 위치와 모양을 변경하여 사용자에게 상호작용 가능한 인터페이스를 제공
이렇게 된다.
아래의 코드의 각 줄마다 상세 설명을 주석으로 남겨두었다.
Next.js 14와 타입스크립트를 사용하였지만, React에도 "use client" 를 제거하면 그대로 사용 가능하다.
"use client";
import React from "react";
import { useState } from "react";
import { IoIosArrowBack, IoIosArrowForward } from "react-icons/io";
export default function Carousel({ children }: { children: React.ReactNode }) {
const [active, setActive] = useState(0);
const count = React.Children.count(children);
return (
<div
className="relative w-full h-[17rem] overflow-hidden mt-2 mb-4 select-none"
style={{
perspective: `600px`,
transformStyle: `preserve-3d`,
}}
>
{active > 0 && (
<button
className="absolute top-1/2 -translate-y-1/2 z-30 cursor-pointer select-none"
style={{
transform: `translateX(130%) translatey(-80%)`,
}}
onClick={() => setActive((i) => i - 1)}
>
<IoIosArrowBack className="hover:text-yellow-200 text-6xl" />
</button>
)}
{React.Children.map(children, (child, i) => {
const isActive = i === active;
return (
<div
className="absolute w-full h-full transition-all duration-300 ease-out select-none"
style={{
transform: `rotateY(${(active - i) * 30}deg) scaleY(${
1 + Math.abs(active - i) * -0.2
}) translateZ(${Math.abs(active - i) * -30}rem) translateX(${
Math.sign(active - i) * -3
}rem)`,
filter: `blur(${Math.abs(active - i)}rem)`,
zIndex: isActive ? 25 : 20 - Math.abs(active - i),
}}
>
{child}
</div>
);
})}
{active < count - 1 && (
<button
className="absolute top-1/2 -translate-y-1/2 right-0 z-30 cursor-pointer select-none"
style={{
transform: `translateX(-130%) translatey(-80%)`,
}}
onClick={() => setActive((i) => i + 1)}
>
<IoIosArrowForward className="hover:text-yellow-200 text-6xl" />
</button>
)}
</div>
);
}
import Carousel from "../Carousel";
import PersonalProjectCard from "../PersonalProjectCard/PersonalProjectCard";
import ProjectCard from "../ProjectCard/ProjectCard";
import { mainProjectsData, personalSideProjectsData } from "./projectsData";
import style from "./Projects.module.css";
export default function Projects() {
return (
<section className={`${style.container}`}>
<div className="scroll-point h-12 w-full" id="projects"></div>
<div>
{/* mt-12는 네비게이션바의 높이. h2가 가려지지 않도록 설정함 */}
<h2 className={`${style.head2}`}>Projects</h2>
{/* 데이터 가져와서 map 으로 그리기.map의 index를 사용해서 index % 2 ? RightProjectCard : LeftProjectCard 로 그리기 */}
{mainProjectsData.map((project, index) => (
<ProjectCard
key={index}
projectName={project.projectName}
imageUrl={project.imageUrl}
techs={project.techs}
description={project.description}
githubCodeUrl={project.githubCodeUrl}
deploymentUrl={project.deploymentUrl}
reverse={index % 2 !== 0}
myWork={project.myWork}
/>
))}
</div>
<p className={`${style.toyProjects}`}>Toy Projects</p>
<Carousel>
{personalSideProjectsData.map((project, index) => (
<PersonalProjectCard
key={index}
projectName={project.projectName}
imageUrl={project.imageUrl}
description={project.description}
githubCodeUrl={project.githubCodeUrl}
myWork={project.myWork}
techs={project.techs}
deploymentUrl={project.deploymentUrl}
/>
))}
</Carousel>
</section>
);
}
.container {
display: flex;
flex-direction: column;
justify-content: center;
width: 100%;
margin-bottom: 2rem;
}
.head2 {
margin-bottom: 2rem;
line-height: 1;
}
.toyProjects {
font-size: 2rem;
font-weight: 600;
text-align: center;
margin-bottom: 1rem;
}
@media screen and (min-width: 450px) {
.head2 {
margin-bottom: 3rem;
}
}
@media screen and (min-width: 1024px) {
.container {
margin-bottom: 4rem;
}
.head2 {
margin-bottom: 4rem;
}
}
'Next.js > 개발 노트' 카테고리의 다른 글
[React/Next.js] 라이브러리 없이 구현하는 카드 회전 및 광택 효과 (1) | 2023.12.31 |
---|---|
[React/Next.js] 라이브러리 없이 구현하는 카드 조명 효과 (1) | 2023.12.31 |
[React/Next.js] 클릭 또는 몇 초 후에 카드 뒤집기 효과 (card flip) (2) | 2023.12.31 |
[React/Next.js] 여러 줄의 타이핑(multi line typing effect) 효과 (0) | 2023.12.31 |
Next.js 13에서 TypeScript를 사용한 WebSocket 구현 (2) | 2023.12.05 |
댓글