Files
huashu-design/assets/ios_frame.jsx
T
alchainandClaude Opus 4.7 7d7bab4a82 init: huashu-design skill v1.0
花叔设计工作台 —— AI 驱动的 HTML 设计工作流,支持 Claude Code / Codex /
Cursor / Trae / OpenClaw / Hermes Agent 等任意 skill-compatible agent。

四条主干能力:
- HTML 高保真视觉产出(原型/App/幻灯片/动画/变体/信息图)
- 设计方向顾问 Fallback(5 流派 × 20 设计哲学 × 24 预制 showcase)
- Junior Designer 工作流(先 show 假设再迭代)
- 专家级 5 维度评审(哲学/层级/执行/功能/创新)

工程工具链:
- HTML → 25fps/60fps MP4 + palette GIF,含 6 首场景化 BGM
- HTML → PDF / PPTX 导出
- Playwright 截图 + 控制台错误验证
- iOS/Android/macOS/Browser 设备边框 + Stage/Sprite 动画引擎

初版包含 SKILL.md + 13 份 references + 7 个 scripts + 18 类 assets。

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-19 22:41:51 +08:00

193 lines
4.6 KiB
React
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* IosFrame — iPhone设备边框
*
* 参考iPhone 15 Pro(393×852 logical pixels)
* 含:灵动岛 + 状态栏(时间/信号/电池)+ Home Indicator + 圆角
*
* 用法:
* <IosFrame time="9:41" battery={85}>
* <YourAppContent />
* </IosFrame>
*
* 自定义:
* <IosFrame width={390} height={844} darkMode showKeyboard>
* ...
* </IosFrame>
*/
const iosFrameStyles = {
wrapper: {
display: 'inline-block',
padding: 12,
background: '#000',
borderRadius: 60,
boxShadow: '0 0 0 2px #1f2937, 0 20px 60px rgba(0,0,0,0.3)',
position: 'relative',
},
screen: {
position: 'relative',
borderRadius: 48,
overflow: 'hidden',
background: '#fff',
},
statusBar: {
position: 'absolute',
top: 0,
left: 0,
right: 0,
height: 54,
display: 'flex',
alignItems: 'center',
justifyContent: 'space-between',
padding: '0 32px 0 32px',
fontSize: 16,
fontWeight: 600,
fontFamily: '-apple-system, "SF Pro Text", sans-serif',
zIndex: 20,
pointerEvents: 'none',
},
dynamicIsland: {
position: 'absolute',
top: 12,
left: '50%',
transform: 'translateX(-50%)',
width: 124,
height: 36,
background: '#000',
borderRadius: 999,
zIndex: 30,
},
statusIcons: {
display: 'flex',
alignItems: 'center',
gap: 6,
},
signalIcon: {
display: 'flex',
alignItems: 'flex-end',
gap: 2,
height: 12,
},
signalBar: {
width: 3,
background: 'currentColor',
borderRadius: 1,
},
wifiIcon: {
width: 16,
height: 12,
position: 'relative',
},
batteryIcon: {
width: 26,
height: 12,
border: '1.5px solid currentColor',
borderRadius: 3,
padding: 1,
position: 'relative',
opacity: 0.8,
},
batteryCap: {
position: 'absolute',
top: 3,
right: -3,
width: 2,
height: 6,
background: 'currentColor',
borderRadius: '0 1px 1px 0',
},
content: {
position: 'absolute',
top: 54,
left: 0,
right: 0,
bottom: 34,
overflow: 'auto',
},
homeIndicator: {
position: 'absolute',
bottom: 10,
left: '50%',
transform: 'translateX(-50%)',
width: 140,
height: 5,
background: 'rgba(0,0,0,0.3)',
borderRadius: 999,
zIndex: 10,
},
homeIndicatorDark: {
background: 'rgba(255,255,255,0.5)',
},
};
function IosFrame({
children,
width = 393,
height = 852,
time = '9:41',
battery = 100,
darkMode = false,
showStatusBar = true,
showDynamicIsland = true,
showHomeIndicator = true,
}) {
const textColor = darkMode ? '#fff' : '#000';
return (
<div style={iosFrameStyles.wrapper}>
<div style={{
...iosFrameStyles.screen,
width,
height,
background: darkMode ? '#000' : '#fff',
}}>
{showStatusBar && (
<div style={{ ...iosFrameStyles.statusBar, color: textColor }}>
<span>{time}</span>
<div style={iosFrameStyles.statusIcons}>
<div style={iosFrameStyles.signalIcon}>
<div style={{ ...iosFrameStyles.signalBar, height: 4 }} />
<div style={{ ...iosFrameStyles.signalBar, height: 6 }} />
<div style={{ ...iosFrameStyles.signalBar, height: 9 }} />
<div style={{ ...iosFrameStyles.signalBar, height: 11 }} />
</div>
<svg width="16" height="12" viewBox="0 0 16 12" fill="none" style={{ color: textColor }}>
<path d="M8 11.5a1 1 0 100-2 1 1 0 000 2z" fill="currentColor" />
<path d="M3 7.5a7 7 0 0110 0" stroke="currentColor" strokeWidth="1.3" fill="none" strokeLinecap="round" />
<path d="M1 4.5a11 11 0 0114 0" stroke="currentColor" strokeWidth="1.3" fill="none" strokeLinecap="round" opacity="0.7" />
</svg>
<div style={iosFrameStyles.batteryIcon}>
<div style={{
width: `${battery}%`,
height: '100%',
background: 'currentColor',
borderRadius: 1,
opacity: 0.9,
}} />
<div style={iosFrameStyles.batteryCap} />
</div>
</div>
</div>
)}
{showDynamicIsland && <div style={iosFrameStyles.dynamicIsland} />}
<div style={iosFrameStyles.content}>
{children}
</div>
{showHomeIndicator && (
<div style={{
...iosFrameStyles.homeIndicator,
...(darkMode ? iosFrameStyles.homeIndicatorDark : {}),
}} />
)}
</div>
</div>
);
}
if (typeof window !== 'undefined') {
window.IosFrame = IosFrame;
}