diff --git a/src/app/guide/page.tsx b/src/app/guide/page.tsx
index 78482c7e..3b82ad08 100644
--- a/src/app/guide/page.tsx
+++ b/src/app/guide/page.tsx
@@ -12,10 +12,12 @@ import Tag from '@/components/common/Tag';
import Toast from '@/components/common/Toast';
import Letter from '@/components/letter/Letter';
import NewItemPicker from '@/components/signup/NewItemPicker';
+import Picker from '@/components/timecapsule/Picker';
+import ItemPicker from '@/components/timecapsule/Picker';
import { LETTER_DUMMY } from '@/constants/letterDummy';
import { theme } from '@/styles/theme';
import Image from 'next/image';
-import React, { useState } from 'react';
+import React, { useEffect, useState } from 'react';
import useMeasure from 'react-use-measure';
import styled from 'styled-components';
@@ -63,13 +65,17 @@ const GuidePage = () => {
};
//ItemPicker
- const [item, setItem] = useState('item4');
+ const items = ['1', '2', '3', '4', '5', '6', '7', '8'];
+ const [selectedItem, setSelectedItem] = useState(items[0]);
- const items = ['item1', 'item2', 'item3', 'item4', 'item5', 'item6', 'item7'];
const handleItemChange = (item: string) => {
- setItem(item);
+ setSelectedItem(item);
};
+ useEffect(() => {
+ console.log(selectedItem);
+ }, [selectedItem]);
+
//Letter
const letterData = LETTER_DUMMY[0];
const imageData = [
@@ -208,11 +214,12 @@ const GuidePage = () => {
/>
ItemPicker
-
Letter
@@ -317,11 +324,8 @@ const SpaceBetween = styled.div`
`;
const PickedItemContainer = styled.div`
- width: 100%;
- display: flex;
- flex-direction: row;
- justify-content: center;
- position: relative;
+ width: 120px;
+ margin: 0 auto;
`;
const Background = styled.div`
diff --git a/src/components/timecapsule/Picker.tsx b/src/components/timecapsule/Picker.tsx
new file mode 100644
index 00000000..068ca6fd
--- /dev/null
+++ b/src/components/timecapsule/Picker.tsx
@@ -0,0 +1,128 @@
+import React, { useEffect, useRef } from 'react';
+import styled from 'styled-components';
+
+interface ItemPickerProps {
+ items: string[];
+ value: string;
+ onChange: (value: string) => void;
+ height?: number; // 전체 높이, 기본 200
+ visibleCount?: number; // 보이는 아이템 개수, 기본 5
+ itemPadding?: number; // 아이템 위아래 padding(px)
+}
+
+const ItemPicker: React.FC = ({
+ items,
+ value,
+ onChange,
+ height = 200,
+ visibleCount = 5,
+ itemPadding = 10 // 위아래 10px씩
+}) => {
+ const containerRef = useRef(null);
+ const itemHeight = height / visibleCount;
+ const padCount = Math.floor(visibleCount / 2);
+
+ // 선택된 값이 중앙에 오도록 스크롤
+ useEffect(() => {
+ const idx = items.indexOf(value);
+ if (idx !== -1 && containerRef.current) {
+ let scrollPos = (idx + padCount) * itemHeight - (height - itemHeight) / 2;
+ scrollPos = Math.max(scrollPos, 0);
+ containerRef.current.scrollTo({ top: scrollPos, behavior: 'smooth' });
+ }
+ }, [value, items, height, itemHeight, padCount]);
+
+ // 스크롤이 멈췄을 때 중앙값 자동 선택
+ const handleScroll = () => {
+ if (!containerRef.current) return;
+ const scrollTop = containerRef.current.scrollTop;
+ let centerIdx =
+ Math.round((scrollTop + height / 2 - itemHeight / 2) / itemHeight) -
+ padCount;
+ centerIdx = Math.max(centerIdx, 0);
+ if (items[centerIdx] && items[centerIdx] !== value) {
+ onChange(items[centerIdx]);
+ }
+ };
+
+ // 패딩 추가된 배열
+ const paddedItems = [
+ ...Array(padCount).fill(''), // 위쪽 패딩
+ ...items,
+ ...Array(padCount).fill('') // 아래쪽 패딩
+ ];
+
+ return (
+
+ {paddedItems.map((item, idx) => {
+ const realIdx = idx - padCount;
+ const selected = items[realIdx] === value;
+ const distance = Math.abs(realIdx - items.indexOf(value));
+ return (
+
+ {item}
+
+ );
+ })}
+
+ );
+};
+
+export default ItemPicker;
+
+// 스타일
+const PickerContainer = styled.div<{ height: number }>`
+ position: relative;
+ height: ${({ height }) => height}px;
+ overflow-y: auto;
+ scroll-snap-type: y mandatory;
+ -webkit-overflow-scrolling: touch;
+ background: #18181c;
+ border-radius: 16px;
+ padding: 0;
+ box-sizing: border-box;
+ scrollbar-width: none;
+ &::-webkit-scrollbar {
+ display: none;
+ }
+`;
+
+const PickerItem = styled.div<{ + $selected: boolean; + $distance: number; + $itemHeight: number; + $isEmpty: boolean; + $itemPadding: number; +}>`
+ scroll-snap-align: center;
+ height: ${({ $itemHeight }) => $itemHeight}px;
+ padding: ${({ $itemPadding }) => `${$itemPadding}px 0`};
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ font-size: ${({ $selected }) => ($selected ? '1.5rem' : '1.1rem')};
+ font-weight: ${({ $selected }) => ($selected ? 700 : 400)};
+ color: ${({ $selected, $distance, $isEmpty }) =>
+ $isEmpty
+ ? 'transparent'
+ : $selected
+ ? '#fff'
+ : `rgba(255,255,255,${1 - Math.min($distance * 0.4, 0.8)})`};
+ opacity: ${({ $selected, $distance, $isEmpty }) =>
+ $isEmpty ? 0 : $selected ? 1 : 1 - Math.min($distance * 0.3, 0.6)};
+ transition: color 0.3s, opacity 0.3s, font-size 0.3s;
+ cursor: ${({ $isEmpty }) => ($isEmpty ? 'default' : 'pointer')};
+ user-select: none;
+`;