Skip to content

Commit 9883462

Browse files
Vimalinxclaude
andcommitted
feat(mobile): 添加移动端图片展示和优化控制区域
## 新增功能 ### 移动端图片展示 - ✅ 在左侧内容区底部添加项目图片展示 - ✅ 图片占据底部 40% 空间,带渐变遮罩 - ✅ 项目切换时图片有平滑的淡入淡出动画 - ✅ 使用项目的第一张图片作为预览 ### 重新设计的移动端控制区域 - ✅ 独立的 45% 高度控制面板 - ✅ 顶部显示项目信息和序号 * 左侧:Current Project 标签 + 项目名称 * 右侧:大号序号显示(01/04) - ✅ 中部:可点击的进度指示条 * 当前项目:长条形白色(w-12) * 其他项目:短条形半透明(w-2) - ✅ 底部:大尺寸导航按钮(16×16) * 左右切换按钮 * 中间提示:"滑动切换" + 动画点 ## 布局优化 ### 移动端垂直布局 - 上部 55%:文字内容区域 * 项目信息 * 描述文字 * 按钮和标签 - 底部 40%:图片展示区 * 当前项目图片预览 * 渐变遮罩效果 - 下部 45%:控制面板 * 项目信息 * 进度指示 * 导航按钮 ### 桌面端保持不变 - 浮动画廊继续工作 - 轮盘控制保持原样 ## 用户体验改进 ### 视觉反馈 - ✅ 项目图片实时展示 - ✅ 清晰的进度指示 - ✅ 大按钮易于点击 ### 交互优化 - ✅ 点击进度条快速跳转 - ✅ 大尺寸导航按钮 - ✅ 流畅的切换动画 ### 空间利用 - 移动端完整的 100% 高度利用 - 图片、内容、控制三分天下 - 每个区域职责清晰 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
1 parent 554e97e commit 9883462

1 file changed

Lines changed: 107 additions & 60 deletions

File tree

src/components/ProjectWheel.tsx

Lines changed: 107 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -112,16 +112,16 @@ export const ProjectWheel = ({ lang }: ProjectWheelProps) => {
112112
>
113113

114114
{/* 1. Left Side: Immersive Background & Content */}
115-
<motion.div
115+
<motion.div
116116
variants={{
117117
hidden: { opacity: 0, x: -50 },
118-
visible: {
119-
opacity: 1,
118+
visible: {
119+
opacity: 1,
120120
x: 0,
121121
transition: { duration: 0.8, ease: [0.25, 0.1, 0.25, 1] }
122122
}
123123
}}
124-
className="relative h-1/2 w-full md:h-full md:w-2/3 lg:w-3/4 overflow-hidden"
124+
className="relative h-[55%] w-full md:h-full md:w-2/3 lg:w-3/4 overflow-hidden"
125125
>
126126

127127
{/* Background Base */}
@@ -154,8 +154,31 @@ export const ProjectWheel = ({ lang }: ProjectWheelProps) => {
154154
)}
155155
</div>
156156

157+
{/* Mobile Image Gallery (Mobile Only) */}
158+
<div className="md:hidden absolute bottom-0 left-0 right-0 z-10 h-[40%] bg-gradient-to-t from-black via-black/80 to-transparent">
159+
<AnimatePresence mode="wait">
160+
{currentProject.gallery && currentProject.gallery.length > 0 && (
161+
<motion.div
162+
key={`mobile-gallery-${activeIndex}`}
163+
initial={{ opacity: 0 }}
164+
animate={{ opacity: 1 }}
165+
exit={{ opacity: 0 }}
166+
transition={{ duration: 0.5 }}
167+
className="relative h-full w-full"
168+
>
169+
<img
170+
src={currentProject.gallery[0]}
171+
alt={`${currentProject.title} preview`}
172+
className="h-full w-full object-cover object-top"
173+
/>
174+
<div className="absolute inset-0 bg-gradient-to-t from-black via-black/20 to-transparent" />
175+
</motion.div>
176+
)}
177+
</AnimatePresence>
178+
</div>
179+
157180
{/* Gradient Mask: Left-to-Right Fade for Text Readability */}
158-
<div className="absolute inset-y-0 left-0 w-[80%] z-10 pointer-events-none bg-gradient-to-r from-black via-black/60 to-transparent" />
181+
<div className="absolute inset-y-0 left-0 w-[80%] z-10 pointer-events-none bg-gradient-to-r from-black via-black/60 to-transparent md:bg-gradient-to-r" />
159182

160183
{/* Content Overlay */}
161184
<div className="absolute inset-0 z-20 flex flex-col justify-end p-6 md:justify-center md:p-16 lg:p-24 pointer-events-none">
@@ -207,65 +230,89 @@ export const ProjectWheel = ({ lang }: ProjectWheelProps) => {
207230
))}
208231
</div>
209232
</div>
210-
211-
{/* Mobile Navigation Controls (Hidden on Desktop) */}
212-
<div className="mt-8 flex flex-col gap-6 lg:hidden">
213-
{/* Progress Indicator */}
214-
<div className="flex items-center justify-center gap-2">
215-
{config.projects.map((_, idx) => (
216-
<button
217-
key={idx}
218-
onClick={(e) => { e.stopPropagation(); triggerSwitch(idx); }}
219-
className={`h-1 rounded-full transition-all duration-300 ${
220-
idx === activeIndex
221-
? 'w-8 bg-white'
222-
: 'w-2 bg-white/30'
223-
}`}
224-
/>
225-
))}
226-
</div>
227-
228-
{/* Navigation Buttons */}
229-
<div className="flex items-center justify-center gap-4">
230-
<button
231-
onClick={(e) => { e.stopPropagation(); triggerSwitch(activeIndex === 0 ? config.projects.length - 1 : activeIndex - 1); }}
232-
className="flex h-14 w-14 items-center justify-center rounded-full border border-white/10 bg-white/5 text-white backdrop-blur-md active:scale-95 transition-all hover:bg-white/10"
233-
>
234-
<ChevronLeft className="h-6 w-6" />
235-
</button>
236-
<div className="flex flex-col items-center">
237-
<span className="text-2xl font-bold text-white">
238-
{String(activeIndex + 1).padStart(2, '0')}
239-
</span>
240-
<span className="text-xs text-gray-500 font-mono">
241-
/ {String(config.projects.length).padStart(2, '0')}
242-
</span>
243-
</div>
244-
<button
245-
onClick={(e) => { e.stopPropagation(); triggerSwitch(activeIndex === config.projects.length - 1 ? 0 : activeIndex + 1); }}
246-
className="flex h-14 w-14 items-center justify-center rounded-full border border-white/10 bg-white/5 text-white backdrop-blur-md active:scale-95 transition-all hover:bg-white/10"
247-
>
248-
<ChevronRight className="h-6 w-6" />
249-
</button>
250-
</div>
251-
252-
{/* Project Name Indicator */}
253-
<motion.div
254-
key={currentProject.id}
255-
initial={{ opacity: 0, y: 10 }}
256-
animate={{ opacity: 1, y: 0 }}
257-
className="text-center"
258-
>
259-
<span className="text-sm font-medium text-purple-400">
260-
{currentProject.title}
261-
</span>
262-
</motion.div>
263-
</div>
264233
</motion.div>
265234
</div>
266235
</motion.div>
267236

268-
{/* 2. Right Side: The Wheel Control */}
237+
{/* 2. Right Side: The Wheel Control (Desktop) & Mobile Controls Area */}
238+
<motion.div
239+
variants={{
240+
hidden: { opacity: 0, scale: 0.8, x: 50 },
241+
visible: {
242+
opacity: 1,
243+
scale: 1,
244+
x: 0,
245+
transition: { duration: 0.8, ease: [0.34, 1.56, 0.64, 1] }
246+
}
247+
}}
248+
className="relative h-[45%] w-full md:hidden flex flex-col justify-between p-6 bg-gradient-to-b from-black/50 to-black"
249+
>
250+
{/* Mobile Project Info */}
251+
<div className="flex items-center justify-between">
252+
<div>
253+
<span className="text-xs text-gray-500 uppercase tracking-widest">Current Project</span>
254+
<motion.div
255+
key={currentProject.id}
256+
initial={{ opacity: 0, y: 10 }}
257+
animate={{ opacity: 1, y: 0 }}
258+
className="text-lg font-bold text-white mt-1"
259+
>
260+
{currentProject.title}
261+
</motion.div>
262+
</div>
263+
<div className="text-right">
264+
<span className="text-3xl font-bold text-white">
265+
{String(activeIndex + 1).padStart(2, '0')}
266+
</span>
267+
<span className="text-xs text-gray-500 block">
268+
/ {String(config.projects.length).padStart(2, '0')}
269+
</span>
270+
</div>
271+
</div>
272+
273+
{/* Progress Dots */}
274+
<div className="flex items-center justify-center gap-3 py-4">
275+
{config.projects.map((_, idx) => (
276+
<button
277+
key={idx}
278+
onClick={(e) => { e.stopPropagation(); triggerSwitch(idx); }}
279+
className={`transition-all duration-300 ${
280+
idx === activeIndex
281+
? 'w-12 h-1.5 bg-white rounded-full'
282+
: 'w-2 h-1.5 bg-white/30 rounded-full'
283+
}`}
284+
/>
285+
))}
286+
</div>
287+
288+
{/* Navigation Buttons */}
289+
<div className="flex items-center justify-center gap-6">
290+
<button
291+
onClick={(e) => { e.stopPropagation(); triggerSwitch(activeIndex === 0 ? config.projects.length - 1 : activeIndex - 1); }}
292+
className="flex h-16 w-16 items-center justify-center rounded-full border-2 border-white/20 bg-white/5 text-white backdrop-blur-md active:scale-95 transition-all hover:bg-white/10 hover:border-white/40"
293+
>
294+
<ChevronLeft className="h-7 w-7" />
295+
</button>
296+
<div className="flex flex-col items-center">
297+
<span className="text-xs text-gray-500 uppercase tracking-widest mb-1">
298+
{lang === 'zh' ? '滑动切换' : 'Swipe'}
299+
</span>
300+
<div className="flex items-center gap-1">
301+
<div className="w-1 h-1 bg-white/30 rounded-full animate-bounce" style={{ animationDelay: '0ms' }} />
302+
<div className="w-1 h-1 bg-white/30 rounded-full animate-bounce" style={{ animationDelay: '150ms' }} />
303+
<div className="w-1 h-1 bg-white/30 rounded-full animate-bounce" style={{ animationDelay: '300ms' }} />
304+
</div>
305+
</div>
306+
<button
307+
onClick={(e) => { e.stopPropagation(); triggerSwitch(activeIndex === config.projects.length - 1 ? 0 : activeIndex + 1); }}
308+
className="flex h-16 w-16 items-center justify-center rounded-full border-2 border-white/20 bg-white/5 text-white backdrop-blur-md active:scale-95 transition-all hover:bg-white/10 hover:border-white/40"
309+
>
310+
<ChevronRight className="h-7 w-7" />
311+
</button>
312+
</div>
313+
</motion.div>
314+
315+
{/* Desktop Wheel Control */}
269316
<motion.div
270317
variants={{
271318
hidden: { opacity: 0, scale: 0.8, x: 50 },

0 commit comments

Comments
 (0)