폼·입력

[Wheel Picker] 위아래로 굴려 고르는 iOS식 드럼 휠

아이폰 시간 선택기처럼 위아래로 굴리면 가운데 값이 선택되는 원통 휠. CSS 스냅 + 스크롤 위치 계산 + 3D 곡면으로 만들었습니다.

ReactTypeScriptCSS scroll-snapperspectiverequestAnimationFrame
라이브 데모
새 탭에서 열기
데모 불러오는 중…

제작 과정

한 줄 요약 위아래로 굴리면 항목이 가운데에 딱 멈추고, 그 가운데 값이 선택되는 아이폰식 회전 휠이에요.

이럴 때 필요해요

시간수량옵션처럼 정해진 값 중 하나를 빠르게 고르는 상황에서, 긴 드롭다운이나 작은 +/- 버튼보다 휠이 훨씬 직관적이에요. 특히 모바일에서 엄지로 굴려 고르는 손맛이 좋죠. 알람 시간, 타이머, 생년월일, 인원수 선택 같은 곳에 잘 어울려요.

어떻게 동작하나

  1. 세로로 스크롤되는 목록에 CSS scroll-snap을 걸어서, 손을 떼면 항목이 가운데 칸에 자석처럼 딱 멈춰요.

  2. 지금 무엇이 선택됐는지는 따로 추적하지 않고, 스크롤한 거리(scrollTop)를 칸 높이로 나눠 "가운데에 온 항목"을 그때그때 계산해요.

  3. 가운데에서 멀어진 항목일수록 rotateX로 뒤로 눕히고 작게흐리게 만들어요. 그러면 평평한 목록이 둥근 원통(드럼)을 옆에서 보는 것처럼 보여요. 이 효과는 컨테이너의 perspective가 만들어요.

  4. 이 3D 변형은 상태를 바꾸지 않고 각 항목의 style을 직접 칠해서 줘요. 스크롤은 1초에도 수십 번 일어나니, requestAnimationFrame으로 한 프레임에 한 번만 그려 부드럽게 유지해요.

핵심은 이거예요

선택값을 손으로 관리하지 마세요 스크롤 위치 하나가 곧 선택값이에요(가운데 인덱스 = scrollTop 칸높이). 스냅은 CSS가, 멈출 위치 계산은 이 한 줄이 해주니, 우리가 할 일은 그 결과로 화면을 칠하는 것뿐이에요.

놓치기 쉬운 것

  • 첫 항목과 마지막 항목도 가운데에 오려면 위아래에 빈 여백(패딩)을 칸 높이만큼 줘야 해요. 안 그러면 끝값이 가운데까지 안 와요.

  • 매 스크롤마다 모든 항목을 React로 다시 그리면 버벅여요 ref로 DOM을 직접 갱신하고 선택값만 상태로 둬요.

  • 3D rotateX가 보이려면 부모(스크롤 영역)에 perspective가 있어야 해요.

이런 곳에 써요

  • 알람타이머의 시분 선택, 인원/수량 고르기

  • 생년월일날짜 일부를 빠르게 고르는 모바일 폼

  • 옵션이 정해진 설정값(폰트 크기, 반복 횟수 등) 선택

소스 코드

· 데모 페이지에서 자동 추출
import type { Metadata } from "next";
import { WheelPickerDemo } from "./-components/WheelPickerDemo";

export const metadata: Metadata = {
  title: "Wheel Picker (데모)",
  description:
    "iOS식 드럼 휠 선택기 — scroll-snap으로 가운데 스냅 + 스크롤 위치로 선택 감지 + perspective 3D 곡면.",
  robots: { index: false, follow: false },
};

export default function Page() {
  return (
    <main className="flex min-h-[100dvh] items-center justify-center bg-gradient-to-br from-violet-50 via-white to-fuchsia-50/60">
      <WheelPickerDemo />
    </main>
  );
}
32조회수

댓글