From a1ad19040c7cf3fff6d44c3aa446cb9a35dea6e2 Mon Sep 17 00:00:00 2001 From: Yeonju Jo Date: Wed, 15 May 2024 19:05:54 +0900 Subject: [PATCH] =?UTF-8?q?refactor:=20logo=20carousel=20=EC=BB=B4?= =?UTF-8?q?=ED=8F=AC=EB=84=8C=ED=8A=B8=EB=A1=9C=20=EB=B6=84=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../carousel-slider.module.scss | 31 ++++++++++++ .../carousel-slider/carousel-slider.tsx | 49 +++++++++++++++++++ src/components/carousel-slider/index.ts | 1 + src/components/index.ts | 1 + .../product-section.module.scss | 42 ++-------------- .../product-section/product-section.tsx | 26 +++------- 6 files changed, 92 insertions(+), 58 deletions(-) create mode 100644 src/components/carousel-slider/carousel-slider.module.scss create mode 100644 src/components/carousel-slider/carousel-slider.tsx create mode 100644 src/components/carousel-slider/index.ts diff --git a/src/components/carousel-slider/carousel-slider.module.scss b/src/components/carousel-slider/carousel-slider.module.scss new file mode 100644 index 0000000..0b5b8f3 --- /dev/null +++ b/src/components/carousel-slider/carousel-slider.module.scss @@ -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; +} diff --git a/src/components/carousel-slider/carousel-slider.tsx b/src/components/carousel-slider/carousel-slider.tsx new file mode 100644 index 0000000..b49a1ef --- /dev/null +++ b/src/components/carousel-slider/carousel-slider.tsx @@ -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(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 ( +
+ {extendSlideItems.map((logo, idx) => ( + + ))} +
+ ) +} diff --git a/src/components/carousel-slider/index.ts b/src/components/carousel-slider/index.ts new file mode 100644 index 0000000..93e80ac --- /dev/null +++ b/src/components/carousel-slider/index.ts @@ -0,0 +1 @@ +export * from './carousel-slider' diff --git a/src/components/index.ts b/src/components/index.ts index 8e5fd84..b6a08ac 100644 --- a/src/components/index.ts +++ b/src/components/index.ts @@ -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' diff --git a/src/views/about/components/product-section/product-section.module.scss b/src/views/about/components/product-section/product-section.module.scss index 628c0c0..0fc21fe 100644 --- a/src/views/about/components/product-section/product-section.module.scss +++ b/src/views/about/components/product-section/product-section.module.scss @@ -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 { diff --git a/src/views/about/components/product-section/product-section.tsx b/src/views/about/components/product-section/product-section.tsx index c819556..e2f16fa 100644 --- a/src/views/about/components/product-section/product-section.tsx +++ b/src/views/about/components/product-section/product-section.tsx @@ -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' @@ -19,22 +19,10 @@ interface Props { } export const ProductSection = ({ apps }: Props) => { - const renderImages = (items: AppData[]) => - items.map((app, index) => ( - - )) - + 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 (
@@ -49,8 +37,8 @@ export const ProductSection = ({ apps }: Props) => { />
-
{renderImages(firstSlideItems)}
-
{renderImages(secondSlideItems)}
+ +