feat(web): 文章编辑器手机预览更新为 iPhone 17 Pro Max 设计

- 外壳从钛金属渐变改为航空级铝合金一体化银灰色
- 比例更新为 163.4×78.0mm (2.095:1)
- 背面摄像头从三角形排列改为横向相机条(3镜头+闪光灯横向排列)
- 边框收窄(3px),阴影减弱匹配铝合金质感
- 标签更新为 "iPhone 17 Pro Max"
This commit is contained in:
iven
2026-05-11 03:18:59 +08:00
parent e00ee69d28
commit 00301d2528

View File

@@ -10,11 +10,12 @@ interface ArticlePhonePreviewProps {
} }
/** /**
* iPhone 15 Pro Max 高保真仿真预览 * iPhone 17 Pro Max 高保真仿真预览
* *
* 真实参数比例: * 真实参数比例:
* 物理尺寸 159.9mm × 76.7mm (aspect 2.084) * 物理尺寸 163.4mm × 78.0mm (aspect 2.095)
* Natural Titanium 钛金属直边边框 * 铝合金一体化机身 (7000-series aerospace aluminum)
* 横向相机条 (horizontal camera bar) — 3 镜头
* Dynamic Island: 药丸形 + 前置摄像头 + 接近传感器 * Dynamic Island: 药丸形 + 前置摄像头 + 接近传感器
* 侧边按钮: 音量×2 / Action Button / 电源 * 侧边按钮: 音量×2 / Action Button / 电源
* Home Indicator: 底部圆角横条 * Home Indicator: 底部圆角横条
@@ -43,13 +44,13 @@ export default function ArticlePhonePreview({
const today = useMemo(() => new Date().toLocaleDateString('zh-CN'), []); const today = useMemo(() => new Date().toLocaleDateString('zh-CN'), []);
// ── iPhone 15 Pro Max 缩放参数 ── // ── iPhone 17 Pro Max 缩放参数 ──
// 物理: 159.9 × 76.7 mm → 比例 2.084:1 // 物理: 163.4 × 78.0 mm → 比例 2.095:1
const PHONE_W = 256; const PHONE_W = 256;
const PHONE_H = Math.round(PHONE_W * 2.084); // 533 const PHONE_H = Math.round(PHONE_W * 2.095); // 536
const PHONE_R = 50; const PHONE_R = 50;
const BEZEL = 4; // 边框到屏幕的距离 const BEZEL = 3; // 铝合金一体 → 更窄边框
const SCREEN_R = PHONE_R - BEZEL; // 46 const SCREEN_R = PHONE_R - BEZEL; // 47
// Dynamic Island (物理约 25.4mm × 8.8mm) // Dynamic Island (物理约 25.4mm × 8.8mm)
const ISLAND_W = 82; const ISLAND_W = 82;
@@ -75,6 +76,12 @@ export default function ArticlePhonePreview({
// 侧边按钮突出距离 // 侧边按钮突出距离
const BTN_OUT = 2.5; const BTN_OUT = 2.5;
// ── 铝合金颜色 ──
// iPhone 17 Pro Max: 航空级 7000 系铝合金,一体成型
const AL_BODY = '#B0B2B8'; // 银色铝合金
const AL_DARK = '#9A9CA2';
const AL_LIGHT = '#C4C6CC';
return ( return (
<div <div
style={{ style={{
@@ -99,32 +106,31 @@ export default function ArticlePhonePreview({
letterSpacing: 0.3, letterSpacing: 0.3,
}} }}
> >
· iPhone 15 Pro Max · iPhone 17 Pro Max
</div> </div>
{/* ══════ iPhone 15 Pro Max 外壳 ══════ */} {/* ══════ iPhone 17 Pro Max 外壳 ══════ */}
{/* 外层: 钛金属渐变边框效果 */} {/* 铝合金一体化机身 */}
<div <div
style={{ style={{
width: PHONE_W + BEZEL * 2, width: PHONE_W + BEZEL * 2,
height: PHONE_H + BEZEL * 2, height: PHONE_H + BEZEL * 2,
borderRadius: PHONE_R + BEZEL, borderRadius: PHONE_R + BEZEL,
background: background: `linear-gradient(165deg, ${AL_LIGHT} 0%, ${AL_BODY} 30%, ${AL_DARK} 70%, ${AL_BODY} 100%)`,
'linear-gradient(145deg, #A0A0A4 0%, #7A7A7E 25%, #929296 50%, #6E6E72 75%, #88888C 100%)',
padding: 0, padding: 0,
position: 'relative', position: 'relative',
boxShadow: [ boxShadow: [
// 主阴影 - 桌面投影 // 主阴影
'0 1px 2px rgba(0,0,0,0.08)', '0 1px 2px rgba(0,0,0,0.06)',
'0 4px 12px rgba(0,0,0,0.10)', '0 4px 12px rgba(0,0,0,0.08)',
'0 12px 32px rgba(0,0,0,0.12)', '0 12px 32px rgba(0,0,0,0.10)',
'0 24px 64px rgba(0,0,0,0.08)', '0 24px 64px rgba(0,0,0,0.06)',
// 钛金属高光 - 顶部边缘 // 铝合金高光 顶部
'inset 0 1px 0 rgba(255,255,255,0.25)', 'inset 0 1px 0 rgba(255,255,255,0.35)',
'inset 1px 0 0 rgba(255,255,255,0.10)', 'inset 1px 0 0 rgba(255,255,255,0.15)',
// 钛金属暗部 - 底部边缘 // 铝合金暗部 底部
'inset 0 -1px 0 rgba(0,0,0,0.20)', 'inset 0 -1px 0 rgba(0,0,0,0.15)',
'inset -1px 0 0 rgba(0,0,0,0.10)', 'inset -1px 0 0 rgba(0,0,0,0.08)',
].join(', '), ].join(', '),
}} }}
> >
@@ -134,11 +140,10 @@ export default function ArticlePhonePreview({
style={{ style={{
position: 'absolute', position: 'absolute',
left: -BTN_OUT, left: -BTN_OUT,
top: 115, top: 118,
width: BTN_OUT + 1, width: BTN_OUT + 1,
height: 28, height: 26,
background: background: `linear-gradient(180deg, ${AL_LIGHT}, ${AL_DARK})`,
'linear-gradient(180deg, #A0A0A4, #78787C, #88888C)',
borderRadius: '2px 0 0 2px', borderRadius: '2px 0 0 2px',
zIndex: 5, zIndex: 5,
}} }}
@@ -148,11 +153,10 @@ export default function ArticlePhonePreview({
style={{ style={{
position: 'absolute', position: 'absolute',
left: -BTN_OUT, left: -BTN_OUT,
top: 150, top: 152,
width: BTN_OUT + 1, width: BTN_OUT + 1,
height: 28, height: 26,
background: background: `linear-gradient(180deg, ${AL_LIGHT}, ${AL_DARK})`,
'linear-gradient(180deg, #A0A0A4, #78787C, #88888C)',
borderRadius: '2px 0 0 2px', borderRadius: '2px 0 0 2px',
zIndex: 5, zIndex: 5,
}} }}
@@ -165,8 +169,7 @@ export default function ArticlePhonePreview({
top: 188, top: 188,
width: 11, width: 11,
height: 11, height: 11,
background: background: `radial-gradient(circle at 40% 40%, ${AL_LIGHT}, ${AL_DARK})`,
'radial-gradient(circle at 40% 40%, #A8A8AC, #78787C)',
borderRadius: '50%', borderRadius: '50%',
zIndex: 5, zIndex: 5,
}} }}
@@ -176,11 +179,10 @@ export default function ArticlePhonePreview({
style={{ style={{
position: 'absolute', position: 'absolute',
right: -BTN_OUT, right: -BTN_OUT,
top: 145, top: 148,
width: BTN_OUT + 1, width: BTN_OUT + 1,
height: 38, height: 36,
background: background: `linear-gradient(180deg, ${AL_LIGHT}, ${AL_DARK})`,
'linear-gradient(180deg, #A0A0A4, #78787C, #88888C)',
borderRadius: '0 2px 2px 0', borderRadius: '0 2px 2px 0',
zIndex: 5, zIndex: 5,
}} }}
@@ -211,7 +213,6 @@ export default function ArticlePhonePreview({
background: '#000', background: '#000',
borderRadius: ISLAND_R, borderRadius: ISLAND_R,
zIndex: 30, zIndex: 30,
// Dynamic Island 微妙的凹陷感
boxShadow: 'inset 0 0.5px 1px rgba(0,0,0,0.5)', boxShadow: 'inset 0 0.5px 1px rgba(0,0,0,0.5)',
}} }}
> >
@@ -226,8 +227,7 @@ export default function ArticlePhonePreview({
height: SENSOR_SIZE, height: SENSOR_SIZE,
borderRadius: '50%', borderRadius: '50%',
background: '#1a1a2e', background: '#1a1a2e',
boxShadow: boxShadow: 'inset 0 0 1px rgba(80,80,120,0.4)',
'inset 0 0 1px rgba(80,80,120,0.4)',
}} }}
/> />
{/* 前置摄像头 (右侧) */} {/* 前置摄像头 (右侧) */}
@@ -240,7 +240,6 @@ export default function ArticlePhonePreview({
width: CAM_SIZE, width: CAM_SIZE,
height: CAM_SIZE, height: CAM_SIZE,
borderRadius: '50%', borderRadius: '50%',
// 摄像头镜头效果: 外环 + 内部蓝紫反光
background: background:
'radial-gradient(circle at 35% 35%, #3a3a5c, #1a1a2e 50%, #0d0d1a)', 'radial-gradient(circle at 35% 35%, #3a3a5c, #1a1a2e 50%, #0d0d1a)',
boxShadow: [ boxShadow: [
@@ -252,7 +251,7 @@ export default function ArticlePhonePreview({
/> />
</div> </div>
{/* ── 状态栏 (iOS 17 风格) ── */} {/* ── 状态栏 (iOS 26 风格) ── */}
<div <div
style={{ style={{
position: 'absolute', position: 'absolute',
@@ -286,53 +285,15 @@ export default function ArticlePhonePreview({
}} }}
> >
{/* 蜂窝信号 (4格) */} {/* 蜂窝信号 (4格) */}
<svg <svg width="15" height="11" viewBox="0 0 17 12" fill="none">
width="15" <rect x="0" y="9" width="3" height="3" rx="0.6" fill="#fff" />
height="11" <rect x="4.5" y="6" width="3" height="6" rx="0.6" fill="#fff" />
viewBox="0 0 17 12" <rect x="9" y="3" width="3" height="9" rx="0.6" fill="#fff" />
fill="none" <rect x="13.5" y="0" width="3" height="12" rx="0.6" fill="#fff" />
>
<rect
x="0"
y="9"
width="3"
height="3"
rx="0.6"
fill="#fff"
/>
<rect
x="4.5"
y="6"
width="3"
height="6"
rx="0.6"
fill="#fff"
/>
<rect
x="9"
y="3"
width="3"
height="9"
rx="0.6"
fill="#fff"
/>
<rect
x="13.5"
y="0"
width="3"
height="12"
rx="0.6"
fill="#fff"
/>
</svg> </svg>
{/* WiFi */} {/* WiFi */}
<svg <svg width="14" height="10" viewBox="0 0 20 15" fill="#fff">
width="14"
height="10"
viewBox="0 0 20 15"
fill="#fff"
>
<circle cx="10" cy="13" r="1.4" /> <circle cx="10" cy="13" r="1.4" />
<path <path
d="M6 10.5a5.5 5.5 0 018 0" d="M6 10.5a5.5 5.5 0 018 0"
@@ -351,13 +312,7 @@ export default function ArticlePhonePreview({
</svg> </svg>
{/* 电池 */} {/* 电池 */}
<svg <svg width="24" height="11" viewBox="0 0 27 13" fill="none">
width="24"
height="11"
viewBox="0 0 27 13"
fill="none"
>
{/* 电池外壳 */}
<rect <rect
x="0.5" x="0.5"
y="0.5" y="0.5"
@@ -367,7 +322,6 @@ export default function ArticlePhonePreview({
stroke="rgba(255,255,255,0.55)" stroke="rgba(255,255,255,0.55)"
strokeWidth="1" strokeWidth="1"
/> />
{/* 电量 */}
<rect <rect
x="2" x="2"
y="2" y="2"
@@ -376,7 +330,6 @@ export default function ArticlePhonePreview({
rx="1.2" rx="1.2"
fill="#fff" fill="#fff"
/> />
{/* 电池头 */}
<rect <rect
x="23.5" x="23.5"
y="3.5" y="3.5"
@@ -540,8 +493,7 @@ export default function ArticlePhonePreview({
src={coverImage} src={coverImage}
alt="封面" alt="封面"
onError={(e) => { onError={(e) => {
(e.target as HTMLImageElement).style.display = (e.target as HTMLImageElement).style.display = 'none';
'none';
}} }}
/> />
)} )}
@@ -595,57 +547,70 @@ export default function ArticlePhonePreview({
/> />
</div> </div>
{/* ── 背面摄像头模组暗示 (左侧上方微阴影) ── */} {/* ── 背面横向相机条 (iPhone 17 Pro Max 标志性设计) ── */}
{/* 铝合金外壳上方的相机条凸起暗示 */}
<div <div
style={{ style={{
position: 'absolute', position: 'absolute',
top: BEZEL + 8, top: BEZEL + 10,
left: BEZEL + 8, left: BEZEL + 6,
width: 68, width: 110,
height: 68, height: 28,
borderRadius: 16, borderRadius: 8,
// 通过微妙的内阴影在正面也能感受到背面的摄像头凸起
boxShadow: boxShadow:
'inset 0 0 6px rgba(0,0,0,0.06), 0 0 3px rgba(0,0,0,0.04)', 'inset 0 0 4px rgba(0,0,0,0.06), 0 0 2px rgba(0,0,0,0.03)',
zIndex: 0, zIndex: 0,
pointerEvents: 'none', pointerEvents: 'none',
}} }}
> >
{/* 三个镜头位置的微暗圆点 */} {/* 三个镜头位置 — 横向排列 */}
<div <div
style={{ style={{
position: 'absolute', position: 'absolute',
top: 8, top: 5,
left: 8, left: 10,
width: 18, width: 18,
height: 18, height: 18,
borderRadius: '50%', borderRadius: '50%',
background: background:
'radial-gradient(circle, rgba(0,0,0,0.08), transparent)', 'radial-gradient(circle, rgba(0,0,0,0.07), transparent)',
}} }}
/> />
<div <div
style={{ style={{
position: 'absolute', position: 'absolute',
top: 8, top: 5,
right: 10, left: 34,
width: 18, width: 18,
height: 18, height: 18,
borderRadius: '50%', borderRadius: '50%',
background: background:
'radial-gradient(circle, rgba(0,0,0,0.08), transparent)', 'radial-gradient(circle, rgba(0,0,0,0.07), transparent)',
}} }}
/> />
<div <div
style={{ style={{
position: 'absolute', position: 'absolute',
bottom: 10, top: 5,
left: 8, left: 58,
width: 18, width: 18,
height: 18, height: 18,
borderRadius: '50%', borderRadius: '50%',
background: background:
'radial-gradient(circle, rgba(0,0,0,0.08), transparent)', 'radial-gradient(circle, rgba(0,0,0,0.07), transparent)',
}}
/>
{/* 闪光灯位置 */}
<div
style={{
position: 'absolute',
top: 9,
right: 8,
width: 10,
height: 10,
borderRadius: '50%',
background:
'radial-gradient(circle, rgba(0,0,0,0.04), transparent)',
}} }}
/> />
</div> </div>