Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 1 addition & 21 deletions src/components/group/MyGroupBox.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ export function MyGroupBox({ onMyGroupsClick }: MyGroupProps) {
navigate(`detail/joined/${roomId}`);
};

const { scrollRef, cardRefs, infiniteGroups, current } = useInfiniteCarousel(groups);
const { scrollRef, cardRefs, infiniteGroups } = useInfiniteCarousel(groups);

return (
<Container>
Expand Down Expand Up @@ -97,11 +97,6 @@ export function MyGroupBox({ onMyGroupsClick }: MyGroupProps) {
/>
))}
</Carousel>
<Dots>
{groups.map((_, i) => (
<Dot key={i} active={i === current} />
))}
</Dots>
</>
) : (
<EmptyContainer>
Expand Down Expand Up @@ -155,21 +150,6 @@ const Carousel = styled.div`
scroll-snap-type: x mandatory;
`;

const Dots = styled.div`
display: flex;
justify-content: center;
gap: 12px;
margin: 30px 0;
`;

const Dot = styled.div<{ active: boolean }>`
width: 4px;
height: 4px;
border-radius: 50%;
background: ${({ active }) => (active ? colors.white : colors.grey[300])};
transition: background-color 0.3s;
`;

const LoadingContainer = styled.div`
display: flex;
justify-content: center;
Expand Down
60 changes: 14 additions & 46 deletions src/components/group/RecruitingGroupCarousel.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { useRef, useEffect, useCallback } from 'react';
import styled from '@emotion/styled';
import type { Group } from './MyGroupBox';
import { RecruitingGroupBox } from './RecruitingGroupBox';
import { useInfiniteCarousel } from '@/hooks/useInfiniteCarousel';

export interface Section {
title: string;
Expand All @@ -13,59 +13,26 @@ interface Props {
}

export function RecruitingGroupCarousel({ sections }: Props) {
const scrollRef = useRef<HTMLDivElement>(null);
const itemRefs = useRef<Array<HTMLDivElement | null>>([]);
const sectionGroups = sections.map(sec => ({
...sec.groups[0],
title: sec.title,
groups: sec.groups,
}));

const handleScroll = useCallback(() => {
const container = scrollRef.current;
if (!container) return;

const centerX = container.offsetWidth / 2;
const scrollLeft = container.scrollLeft;

itemRefs.current.forEach(item => {
if (!item) return;
const itemCenter = item.offsetLeft + item.offsetWidth / 2;
const dist = Math.abs(itemCenter - scrollLeft - centerX);
const ratio = Math.min(dist / centerX, 1);
const scale = 1 - ratio * 0.1;
item.style.transform = `scale(${scale})`;
});
}, []);

useEffect(() => {
const container = scrollRef.current;
if (!container || sections.length === 0) return;

const mid = Math.floor(sections.length / 2);
const midItem = itemRefs.current[mid];
if (midItem) {
const centerX = container.offsetWidth / 2;
const targetScroll = midItem.offsetLeft + midItem.offsetWidth / 2 - centerX;
container.scrollTo({ left: targetScroll, behavior: 'auto' });
}
handleScroll();
}, [sections.length, handleScroll]);

useEffect(() => {
const container = scrollRef.current;
if (!container) return;
container.addEventListener('scroll', handleScroll, { passive: true });
return () => {
container.removeEventListener('scroll', handleScroll);
};
}, [handleScroll]);
const { scrollRef, cardRefs, infiniteGroups } = useInfiniteCarousel(sectionGroups, {
scaleAmount: 0.08,
});

return (
<ScrollWrapper ref={scrollRef}>
{sections.map((sec, i) => (
{infiniteGroups.map((g, i) => (
<Item
key={i}
key={`${g.title}-${i}`}
ref={el => {
itemRefs.current[i] = el;
cardRefs.current[i] = el;
}}
>
<RecruitingGroupBox groups={sec.groups} title={sec.title} />
<RecruitingGroupBox groups={g.groups} title={g.title} />
</Item>
))}
</ScrollWrapper>
Expand All @@ -88,4 +55,5 @@ const Item = styled.div`
max-width: 640px;
scroll-snap-align: center;
transition: transform 0.2s;
height: 800px;
`;
8 changes: 5 additions & 3 deletions src/hooks/useInfiniteCarousel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import type { Group } from '../components/group/MyGroupBox';

const CLONE_COUNT = 10;

export function useInfiniteCarousel(groups: Group[]) {
export function useInfiniteCarousel(groups: Group[], options?: { scaleAmount?: number }) {
const scrollRef = useRef<HTMLDivElement>(null);
const cardRefs = useRef<(HTMLDivElement | null)[]>([]);
const [current, setCurrent] = useState(0);
Expand All @@ -14,6 +14,8 @@ export function useInfiniteCarousel(groups: Group[]) {

const middleIndex = useMemo(() => Math.floor(infiniteGroups.length / 2), [infiniteGroups]);

const scaleAmount = options?.scaleAmount ?? 0.17;

const handleScroll = useCallback(() => {
const container = scrollRef.current;
if (!container) return;
Expand All @@ -28,7 +30,7 @@ export function useInfiniteCarousel(groups: Group[]) {
if (!card) return;
const cardCenter = card.offsetLeft + card.offsetWidth / 2;
const distance = Math.abs(center - (cardCenter - scrollLeft));
const scale = Math.max(0.83, 1 - (distance / center) * 0.17);
const scale = Math.max(0.83, 1 - (distance / center) * scaleAmount);
card.style.transform = `scale(${scale})`;

if (distance < minDist) {
Expand All @@ -49,7 +51,7 @@ export function useInfiniteCarousel(groups: Group[]) {
container.scrollLeft = left;
}
}
}, [groups.length, middleIndex]);
}, [groups.length, middleIndex, scaleAmount]);

useEffect(() => {
const container = scrollRef.current;
Expand Down
3 changes: 0 additions & 3 deletions src/pages/group/Group.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,6 @@ const Group = () => {
const [sections, setSections] = useState<Section[]>([
{ title: '마감 임박한 독서 모임방', groups: [] },
{ title: '인기 있는 독서 모임방', groups: [] },
{ title: '인플루언서·작가 독서 모임방', groups: [] },
]);

const fetchAllRoomsData = async () => {
Expand All @@ -62,14 +61,12 @@ const Group = () => {
setSections([
{ title: '마감 임박한 독서 모임방', groups: deadlineRoomsData },
{ title: '인기 있는 독서 모임방', groups: popularRoomsData },
{ title: '인플루언서·작가 독서 모임방', groups: [] },
]);
} catch (error) {
console.error('방 목록 조회 오류:', error);
setSections([
{ title: '마감 임박한 독서 모임방', groups: [] },
{ title: '인기 있는 독서 모임방', groups: [] },
{ title: '인플루언서·작가 독서 모임방', groups: [] },
]);
}
};
Expand Down