Skip to content

Commit

Permalink
refactor: logo carousel 컴포넌트로 분리
Browse files Browse the repository at this point in the history
  • Loading branch information
yeonju0110 committed May 15, 2024
1 parent f1aeb3f commit a1ad190
Show file tree
Hide file tree
Showing 6 changed files with 92 additions and 58 deletions.
31 changes: 31 additions & 0 deletions src/components/carousel-slider/carousel-slider.module.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
@use '@/styles/abstracts' as *;
@use '@/styles/utils' as *;

$gap: 8px;

@keyframes slide {
0% {
transform: translateX(0);
}
100% {
transform: translateX(calc((var(--slide-item-width) + $gap) * -1 * var(--slide-item-length)));
}
}

.carousel_container {
display: flex;
gap: $gap;
width: calc((var(--slide-item-width) + $gap) * var(--slide-item-length));
animation: 60s linear infinite running slide;

.carousel_item {
flex: 0 0 auto;
width: var(--slide-item-width);
aspect-ratio: 416 / 280;
border-radius: 8px;
}
}

.carousel_container__reverse {
animation: 60s linear infinite reverse slide;
}
49 changes: 49 additions & 0 deletions src/components/carousel-slider/carousel-slider.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import clsx from 'clsx'
import { GatsbyImage, IGatsbyImageData } from 'gatsby-plugin-image'
import { useEffect, useRef } from 'react'

import * as css from './carousel-slider.module.scss'

interface Props {
logos: IGatsbyImageData[]
width?: number | string
reverse?: boolean
}

/**
* 로고들을 캐러셀 형식으로 표시하는 CarouselSlider 컴포넌트입니다. 자동 무한루프 방식으로 동작하며 필요에 따라 방향을 반대로 할 수 있습니다.
*
* @component
* @param {IGatsbyImageData[]} logos 로고 이미지 배열
* @param {number | string} width 캐러샐의 각 아이템의 너비 (기본값: 10%)
* @param {boolean} reverse true인 경우 캐러샐은 반대 방향으로 슬라이드됩니다. (기본값: false)
* @returns
*/
export const CarouselSlider = ({ logos, width = '10%', reverse = false }: Props) => {
const extendSlideItems = [...logos, ...logos.slice(0, 4)] // 끊김 없는 루프 효과를 위해 시야에 보이는 개수인 4개의 항목을 끝에 추가합니다.
const carouselRef = useRef<HTMLDivElement>(null)

useEffect(() => {
if (carouselRef.current) {
// 로고 데이터의 길이와 지정된 너비에 따라 동적으로 캐러셀 스타일을 조정합니다.
carouselRef.current.style.setProperty('--slide-item-length', `${logos.length}`)
carouselRef.current.style.setProperty('--slide-item-width', `${width}`)
}
}, [logos.length, width])

return (
<div ref={carouselRef} className={clsx(css.carousel_container, { [css.carousel_container__reverse]: reverse })}>
{extendSlideItems.map((logo, idx) => (
<GatsbyImage
// eslint-disable-next-line react/no-array-index-key
key={idx}
className={css.carousel_item}
image={logo}
alt="프로덕트 로고 이미지"
objectPosition="50% top"
loading="eager"
/>
))}
</div>
)
}
1 change: 1 addition & 0 deletions src/components/carousel-slider/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './carousel-slider'
1 change: 1 addition & 0 deletions src/components/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
export * from './apply-button'
export * from './aura-circle'
export * from './br'
export * from './carousel-slider'
export * from './floating-button'
export * from './glass-card'
export * from './scroll-reveal-container'
Expand Down
Original file line number Diff line number Diff line change
@@ -1,49 +1,13 @@
@use '@/styles/abstracts' as *;
@use '@/styles/utils' as *;

@keyframes slide {
0% {
transform: translateX(0);
}
100% {
transform: translateX(calc((12% + 8px) * -23));
}
}

@keyframes slide2 {
0% {
transform: translateX(calc((12% + 8px) * -24));
}
100% {
transform: translateX(0);
}
}

.carouselContainer {
overflow: hidden;
width: 100vw;
margin-top: 20px;

.carouselSlide,
.carouselSlide2 {
width: calc((12% + 8px) * 23); // 이미지 하나의 너비 X 이미지 갯수
display: flex;
gap: 8px;
animation: slide 50s linear infinite;

&.carouselSlide2 {
width: calc((12% + 8px) * 24); // 이미지 하나의 너비 X 이미지 갯수
animation-name: slide2;
margin-top: 8px;
}

.carouselItem {
flex: 0 0 auto;
width: 12%;
aspect-ratio: 416 / 280;
border-radius: 8px;
}
}
display: flex;
flex-direction: column;
gap: 8px;
}

.center {
Expand Down
26 changes: 7 additions & 19 deletions src/views/about/components/product-section/product-section.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Link } from 'gatsby'
import { GatsbyImage, IGatsbyImageData } from 'gatsby-plugin-image'
import { IGatsbyImageData } from 'gatsby-plugin-image'

import { Br, ScrollRevealContainer, Section } from '@/components'
import { Br, CarouselSlider, ScrollRevealContainer, Section } from '@/components'
import { getRefinedImage } from '@/utils'

import * as css from './product-section.module.scss'
Expand All @@ -19,22 +19,10 @@ interface Props {
}

export const ProductSection = ({ apps }: Props) => {
const renderImages = (items: AppData[]) =>
items.map((app, index) => (
<GatsbyImage
// eslint-disable-next-line react/no-array-index-key
key={index}
image={getRefinedImage(app.logo?.childImageSharp?.gatsbyImageData)}
alt=""
className={css.carouselItem}
objectPosition="50% top"
loading="eager"
/>
))

const logos = apps.map((app) => getRefinedImage(app.logo?.childImageSharp?.gatsbyImageData))
const middleIndex = Math.floor(apps.length / 2)
const firstSlideItems = [...apps.slice(0, middleIndex), ...apps.slice(0, 4)]
const secondSlideItems = [...apps.slice(middleIndex), ...apps.slice(middleIndex, middleIndex + 4)]
const firstRow = logos.slice(0, middleIndex)
const secondRow = logos.slice(middleIndex)

return (
<Section>
Expand All @@ -49,8 +37,8 @@ export const ProductSection = ({ apps }: Props) => {
/>
</ScrollRevealContainer>
<div className={css.carouselContainer}>
<div className={css.carouselSlide}>{renderImages(firstSlideItems)}</div>
<div className={css.carouselSlide2}>{renderImages(secondSlideItems)}</div>
<CarouselSlider logos={firstRow} />
<CarouselSlider logos={secondRow} reverse />
</div>

<Link to="/project" className={css.moreButton}>
Expand Down

0 comments on commit a1ad190

Please sign in to comment.