diff --git a/docs/art/visual-direction.md b/docs/art/visual-direction.md new file mode 100644 index 0000000..efc652d --- /dev/null +++ b/docs/art/visual-direction.md @@ -0,0 +1,20 @@ +# 视觉方向与影像来源 + +## 当前界面 + +剧情采用“考古档案影像”作为视觉语言:深墨绿背景、青铜绿的档案标注、朱红的主操作,宋体标题与清晰的正文字体。图片铺满场景,文本区保持不透明度与对比度,供长篇阅读和选择操作。手机上内容自然滚动,不截断长文本。 + +当前四张照片均为**殷墟相关历史参考影像**,不是虚构“苍梧”遗址、人物或剧情现场照片。画面内标注“影像参考 / 殷墟考古资料”。这是可用的临时影像方案,不应作为正式章节插画向用户宣称。正式美术需求见 [插图生成清单](illustration-prompts.md),其 57 张插画尚未生成;后续应按剧情节点及分支逐张替换,不可用同一张史料图误导性地代表所有分支。 + +## 已加载的图片 + +图片置于 `public/images/`,均取自 Wikimedia Commons 的 CC0 文件页面;下载的是缩放至 1280px 宽的 JPEG,应用从本地路径加载,不依赖运行时第三方请求。 + +| 本地路径 | 原始文件页面 | 用途 | +|---|---|---| +| `ruins.jpg` | [Yinxu Palace Foundation](https://commons.wikimedia.org/wiki/File:Yinxu_Palace_Foundation_(45144971564).jpg) | 第一章背景 | +| `excavation.jpg` | [Sacrificial Pits of the Palace-Temple Complex of Yinxu](https://commons.wikimedia.org/wiki/File:Sacrificial_Pits_of_the_Palace-Temple_Complex_of_Yinxu_(45144967314).jpg) | 第二章背景 | +| `bronze.jpg` | [Shang Bronze Gui, Yinxu](https://commons.wikimedia.org/wiki/File:Shang_Bronze_Gui,_Yinxu.jpg) | 过场与特定剧情节点 | +| `inscription.jpg` | [Ancient Chinese Writing on Ox Scapula](https://commons.wikimedia.org/wiki/File:Ancient_Chinese_Writing_on_Ox_Scapula,_Shang_Dynasty_Oracle_Bone,_Yinxu,_Anyang.jpg) | 第三章与特定剧情节点 | + +正式发布前应再次核对各来源页面的授权与署名元数据,并让 UI 的图片来源说明可直接访问。现阶段 UI 使用概括性说明,此表提供精确来源。 diff --git a/public/images/bronze.jpg b/public/images/bronze.jpg new file mode 100644 index 0000000..eb0b89c Binary files /dev/null and b/public/images/bronze.jpg differ diff --git a/public/images/excavation.jpg b/public/images/excavation.jpg new file mode 100644 index 0000000..100cc61 Binary files /dev/null and b/public/images/excavation.jpg differ diff --git a/public/images/inscription.jpg b/public/images/inscription.jpg new file mode 100644 index 0000000..8e841b7 Binary files /dev/null and b/public/images/inscription.jpg differ diff --git a/public/images/ruins.jpg b/public/images/ruins.jpg new file mode 100644 index 0000000..9512f6c Binary files /dev/null and b/public/images/ruins.jpg differ diff --git a/src/App.css b/src/App.css index f01d27e..001b779 100644 --- a/src/App.css +++ b/src/App.css @@ -1,374 +1,89 @@ -.app { - max-width: 720px; - margin: 0 auto; - min-height: 100vh; - display: flex; - flex-direction: column; -} - -/* ---- 影游场景 ---- */ -.scene { - flex: 1; - display: flex; - flex-direction: column; - justify-content: flex-end; - padding: 24px; - background: linear-gradient(160deg, #14101e 30%, #2a2338 100%); - border-radius: 12px; - min-height: 80vh; - transition: background 0.8s ease; -} - -.scene-header { - position: fixed; - top: 0; - left: 0; - right: 0; - display: flex; - justify-content: space-between; - padding: 12px 20px; - font-size: 13px; - color: #b0a490; - background: rgba(20, 16, 30, 0.8); -} - -.variant-tag { - color: #d4a96a; -} - -.dialogue-box { - display: flex; - gap: 16px; - background: rgba(20, 16, 30, 0.85); - border: 1px solid #3a3248; - border-radius: 12px; - padding: 20px; - cursor: pointer; -} - -.sprite { - flex-shrink: 0; - width: 72px; - height: 72px; - border-radius: 8px; - display: flex; - align-items: center; - justify-content: center; - font-size: 28px; - color: #f0e6d2; -} - -.speaker-name { - font-weight: 700; - color: #d4a96a; - margin-bottom: 6px; -} - -.dialogue-text { - line-height: 1.8; - margin: 0 0 8px; -} - -.hint { - font-size: 12px; - color: #8a7f6a; -} - -/* ---- 选择点 ---- */ -.choice-box, -.narration-box { - background: rgba(20, 16, 30, 0.85); - border: 1px solid #3a3248; - border-radius: 12px; - padding: 20px; -} - -.choice-prompt, -.narration-prompt { - font-size: 17px; - line-height: 1.7; - color: #f0e6d2; -} - -.choice-options { - display: flex; - flex-direction: column; - gap: 10px; - margin-top: 12px; -} - -.choice-btn { - text-align: left; - padding: 12px 16px; - border-radius: 8px; - border: 1px solid #4a3f5a; - background: #241e33; - color: #e8dcc8; - font-size: 15px; - cursor: pointer; - transition: border-color 0.2s, background 0.2s; -} - -.choice-btn:hover { - border-color: #d4a96a; - background: #2e2642; -} - -/* ---- 讲述点 ---- */ -.mechanism-tag { - display: inline-block; - font-size: 12px; - color: #1a1626; - background: #d4a96a; - border-radius: 4px; - padding: 2px 8px; - margin-bottom: 10px; -} - -.hold-btn { - width: 100%; - margin-top: 12px; - padding: 18px; - border-radius: 999px; - border: 2px solid #d4a96a; - background: transparent; - color: #d4a96a; - font-size: 16px; - cursor: pointer; - user-select: none; - touch-action: none; -} - -.hold-btn.holding { - background: #d4a96a; - color: #1a1626; -} - -/* ---- 过场 ---- */ -.cutscene { - text-align: center; - cursor: pointer; -} - -.cutscene-placeholder { - border: 2px dashed #4a3f5a; - border-radius: 12px; - padding: 80px 20px; - font-size: 20px; - color: #8a7f6a; - margin-bottom: 12px; -} - -/* ---- 通用按钮 ---- */ -button.primary, -button.secondary { - margin-top: 16px; - padding: 12px 24px; - border-radius: 8px; - font-size: 15px; - cursor: pointer; -} - -button.primary { - border: none; - background: #d4a96a; - color: #1a1626; - font-weight: 700; -} - -button.secondary { - border: 1px solid #4a3f5a; - background: transparent; - color: #b0a490; - width: 100%; - margin-top: 10px; -} - -.chapter-end { - text-align: center; - padding: 40px 0; -} - -/* ---- 画像报告 ---- */ -.report { - padding: 32px 24px; -} - -.report h1 { - color: #d4a96a; -} - -.badge-banner { - display: flex; - gap: 20px; - align-items: center; - background: rgba(212, 169, 106, 0.08); - border: 1px solid #d4a96a; - border-radius: 12px; - padding: 20px; - margin: 16px 0 24px; -} - -.badge-medal { - width: 72px; - height: 72px; - border-radius: 50%; - background: #d4a96a; - color: #1a1626; - display: flex; - align-items: center; - justify-content: center; - font-size: 32px; - font-weight: 700; - flex-shrink: 0; -} - -.badge-note { - font-size: 13px; - color: #8a7f6a; -} - -.dimension-list { - display: flex; - flex-direction: column; - gap: 12px; - margin-bottom: 28px; -} - -.dimension-row { - display: grid; - grid-template-columns: 140px 1fr 190px; - align-items: center; - gap: 12px; -} - -.dimension-label { - color: #e8dcc8; -} - -.dimension-bar { - height: 14px; - background: #2a2338; - border-radius: 7px; - overflow: hidden; -} - -.dimension-fill { - height: 100%; - background: #d4a96a; -} - -.dimension-value { - font-size: 13px; - color: #b0a490; -} - -/* ---- 分享卡 ---- */ -.share-card { - text-align: center; - margin-bottom: 16px; -} - -.share-canvas { - max-width: 320px; - width: 100%; - border-radius: 12px; - border: 1px solid #3a3248; - display: block; - margin: 0 auto 12px; -} - -/* ---- 语音输入与身份登记 ---- */ -.voice-input { - margin-top: 12px; -} - -.voice-live { - margin-top: 10px; - padding: 10px 14px; - background: rgba(212, 169, 106, 0.1); - border-left: 3px solid #d4a96a; - border-radius: 4px; - color: #e8dcc8; - line-height: 1.7; -} - -.voice-textarea { - width: 100%; - box-sizing: border-box; - padding: 12px 14px; - border-radius: 8px; - border: 1px solid #4a3f5a; - background: #241e33; - color: #e8dcc8; - font-size: 15px; - line-height: 1.7; - resize: vertical; - margin-bottom: 8px; -} - -.identity-box { - background: rgba(20, 16, 30, 0.85); - border: 1px solid #3a3248; - border-radius: 12px; - padding: 20px; -} - -.identity-field { - display: grid; - grid-template-columns: 80px 1fr; - align-items: center; - gap: 12px; - margin: 10px 0; - color: #d8ccb8; -} - -.identity-field input { - padding: 10px 12px; - border-radius: 8px; - border: 1px solid #4a3f5a; - background: #241e33; - color: #e8dcc8; - font-size: 15px; -} - -/* ---- 同意门(ADR-0004) ---- */ -.consent-box { - background: rgba(20, 16, 30, 0.85); - border: 1px solid #3a3248; - border-radius: 12px; - padding: 24px; - max-width: 560px; - margin: 48px auto; -} - -.consent-box h2 { - color: #f0e6d2; - font-size: 20px; - margin: 8px 0 16px; -} - -.consent-list { - color: #d8ccb8; - font-size: 15px; - line-height: 1.8; - padding-left: 20px; - margin-bottom: 20px; -} - -.consent-list li { - margin-bottom: 8px; -} - -.consent-note { - color: #d8ccb8; - font-size: 15px; - line-height: 1.7; - margin-bottom: 16px; -} - -.consent-box .primary, -.consent-box .secondary { - margin-right: 10px; - margin-bottom: 10px; +.app { min-height: 100svh; display: flex; flex-direction: column; } +.scene { position: relative; isolation: isolate; min-height: 100svh; display: flex; flex-direction: column; overflow: hidden; background: #17201d; } +.scene-media, .scene-shade { position: absolute; inset: 0; width: 100%; height: 100%; pointer-events: none; } +.scene-media { object-fit: cover; object-position: center; z-index: -2; filter: saturate(.75) contrast(1.06); } +.scene-shade { z-index: -1; background: linear-gradient(90deg, rgba(11,22,20,.72), rgba(11,22,20,.14) 80%), linear-gradient(0deg, #101917 0%, rgba(16,25,23,.9) 20%, rgba(16,25,23,.08) 72%); } +.scene-content { width: min(100% - 64px, 1100px); min-height: 100svh; margin-inline: auto; display: flex; flex-direction: column; padding-bottom: clamp(28px,5vh,64px); } +.scene-header { display: flex; align-items: center; justify-content: space-between; gap: 24px; border-bottom: 1px solid rgba(233,231,223,.34); padding-block: 20px; font-size: 13px; color: #e8e8dd; } +.scene-brand { font-family: 'Noto Serif CJK SC','Songti SC',serif; font-size: 18px; white-space: nowrap; } +.scene-brand small { display: inline-block; margin-left: 16px; font: 11px/1.5 'Noto Sans CJK SC','PingFang SC',sans-serif; color: #b7c8bb; } +.scene-chapter { color: #d6d3c8; text-align: right; } +.scene-stage { margin-top: auto; margin-bottom: 32px; } +.scene-index, .scene-credit { font-size: 12px; color: #b9d2c6; } +.scene-index { letter-spacing: 1px; } +.scene-stage h1 { margin: 6px 0 10px; font-size: clamp(36px,5vw,68px); line-height: 1.2; color: #f5f0e4; text-shadow: 0 2px 24px rgba(0,0,0,.38); } +.scene-credit { display: block; margin-top: 8px; } +.dialogue-box, .choice-box, .narration-box, .identity-box, .cutscene, .chapter-end { width: min(100%,760px); padding: 24px 28px; border: 1px solid rgba(201,219,203,.34); border-left: 3px solid #a8bda5; border-radius: 3px; background: rgba(13,25,23,.94); box-shadow: 0 16px 40px rgba(0,0,0,.2); backdrop-filter: blur(12px); } +.dialogue-box { display: flex; align-items: flex-start; gap: 18px; text-align: left; color: inherit; } +.dialogue-box:hover { border-color: #d9bc94; } +.speaker-mark { flex: 0 0 46px; width: 46px; height: 46px; display: grid; place-items: center; border: 1px solid #8ca696; border-radius: 50%; font-family: 'Noto Serif CJK SC','Songti SC',serif; color: #d2d7c8; } +.dialogue-body { display: block; min-width: 0; flex: 1; } +.speaker-name { display: block; margin-bottom: 8px; color: #d8b58a; font-weight: 600; } +.dialogue-text { display: block; line-height: 1.9; white-space: pre-line; } +.continue-hint { display: block; margin-top: 16px; color: #adc5b4; font-size: 12px; text-align: right; } +.choice-prompt, .narration-prompt, .cutscene p { color: #f2f0e7; font-size: 18px; line-height: 1.75; } +.choice-options { display: grid; gap: 9px; margin-top: 18px; } +.choice-btn { width: 100%; padding: 14px 18px; border: 1px solid #546d60; border-radius: 3px; background: rgba(34,54,47,.7); color: #f1eee4; text-align: left; transition: background .2s,border-color .2s; } +.choice-btn:hover { background: #344d40; border-color: #d8b58a; } +.mechanism-tag { display: inline-block; margin-bottom: 12px; padding: 3px 8px; border: 1px solid #799b82; color: #c2d8c5; font-size: 12px; } +.hint { color: #aabbb0; font-size: 12px; line-height: 1.6; } +button.primary, button.secondary, .hold-btn { min-height: 44px; padding: 10px 20px; border-radius: 3px; font-size: 15px; transition: background .2s,border-color .2s; } +button.primary { border: 1px solid #c36e55; background: #a94732; color: #fff9ed; font-weight: 600; } +button.primary:hover { background: #c05d43; } +button.secondary { border: 1px solid #829989; background: transparent; color: #e6eae0; } +button.secondary:hover { border-color: #d6b48b; background: rgba(168,189,165,.12); } +.cutscene .primary, .chapter-end .primary { margin-top: 12px; } +.voice-input { margin-top: 16px; } +.voice-controls { max-width: 280px; } +.hold-btn { width: 100%; border: 1px solid #91ae99; background: #294638; color: #edf1e7; touch-action: none; } +.hold-btn.holding { background: #a94732; border-color: #c36e55; } +.voice-live { padding: 10px 14px; border-left: 2px solid #b2c6ac; color: #d8e3d8; background: rgba(74,106,85,.23); } +.voice-textarea, .identity-field input { width: 100%; padding: 12px 14px; border: 1px solid #718f7c; border-radius: 3px; background: #172b25; color: #f0f0e7; line-height: 1.6; } +.voice-textarea { display: block; margin-bottom: 12px; resize: vertical; } +.voice-textarea::placeholder, .identity-field input::placeholder { color: #9eb1a3; } +.voice-actions { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; } +.voice-actions > button:not(.primary) { border: 0; background: transparent; color: #c5d2c2; text-decoration: underline; } +.identity-field { display: grid; grid-template-columns: 80px minmax(0,1fr); align-items: center; gap: 12px; margin: 10px 0; } +.identity-box .primary, .narration-box .secondary { margin-top: 12px; } +.consent-box { width: min(100% - 40px,700px); margin: auto; padding: clamp(24px,5vw,56px); border-top: 2px solid #9bb69b; background: #1b2a24; box-shadow: 0 16px 60px rgba(0,0,0,.25); } +.app:has(.consent-box) { padding-block: 32px; background: radial-gradient(ellipse at 80% 0%,#335444,#101917 65%); } +.consent-box h2 { font-size: clamp(24px,3vw,36px); margin: 8px 0 20px; } +.consent-list { padding-left: 22px; line-height: 1.8; color: #d7e0d6; } +.consent-list li { margin-bottom: 10px; } +.consent-note { color: #d7e0d6; } +.consent-box .primary { margin-right: 12px; } +.consent-box .hint { margin-top: 20px; } +.report { width: min(100% - 40px,880px); margin-inline: auto; padding: clamp(32px,5vw,72px) 0; } +.report h1 { font-size: clamp(27px,4vw,42px); color: #f1eee3; } +.badge-banner { display: flex; align-items: center; gap: 24px; margin: 28px 0 36px; padding: 22px 0; border-block: 1px solid #5a7762; } +.badge-banner h2 { color: #e7c297; margin-bottom: 8px; } +.badge-banner p { margin-bottom: 6px; } +.badge-medal { flex: 0 0 70px; height: 70px; display: grid; place-items: center; border: 2px solid #bd9570; border-radius: 50%; color: #eed7af; font: 34px 'Noto Serif CJK SC','Songti SC',serif; } +.badge-note { color: #a9baad; font-size: 13px; } +.dimension-list { display: grid; gap: 18px; margin-bottom: 42px; } +.dimension-row { display: grid; grid-template-columns: 140px minmax(60px,1fr) 185px; align-items: center; gap: 14px; } +.dimension-label { color: #e6ede1; } +.dimension-bar { height: 8px; background: #35463b; } +.dimension-fill { height: 100%; background: #a8c1a3; } +.dimension-value { color: #bdcfc0; font-size: 13px; text-align: right; } +.share-card { text-align: center; margin-bottom: 28px; } +.share-canvas { display: block; width: min(100%,320px); height: auto; margin: 0 auto 16px; border: 1px solid #698573; } +.report > .secondary { display: block; margin: auto; } +@media (max-width: 640px) { + .scene-content { width: min(100% - 32px,480px); padding-bottom: 20px; } + .scene-header { padding-block: 14px; align-items: flex-start; } + .scene-brand { font-size: 15px; } + .scene-brand small { display: block; margin-left: 0; margin-top: 3px; font-size: 10px; } + .scene-chapter { font-size: 11px; } + .scene-stage { margin-bottom: 20px; } + .scene-stage h1 { font-size: 38px; } + .dialogue-box, .choice-box, .narration-box, .identity-box, .cutscene, .chapter-end { padding: 18px; } + .speaker-mark { flex-basis: 36px; width: 36px; height: 36px; } + .dialogue-box { gap: 12px; } + .choice-prompt, .narration-prompt { font-size: 16px; } + .dimension-row { grid-template-columns: minmax(0,1fr) auto; gap: 4px 10px; } + .dimension-bar { grid-column: 1 / -1; grid-row: 2; } + .dimension-value { font-size: 12px; } } +@media (max-height: 700px) { .scene-stage { margin-top: 48px; } } +@media (prefers-reduced-motion: reduce) { .choice-btn, button.primary, button.secondary, .hold-btn { transition: none; } } diff --git a/src/index.css b/src/index.css index a75c89f..58979cc 100644 --- a/src/index.css +++ b/src/index.css @@ -1,31 +1,21 @@ :root { - font: 16px/1.6 system-ui, 'PingFang SC', 'Segoe UI', sans-serif; - color: #d8ccb8; - background: #14101e; + font: 16px/1.6 'Noto Sans CJK SC', 'PingFang SC', 'Microsoft YaHei', sans-serif; + color: #e9e7df; + background: #111a19; color-scheme: dark; font-synthesis: none; text-rendering: optimizeLegibility; -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; } -body { - margin: 0; -} - -#root { - min-height: 100svh; -} - -h1 { - font-size: 28px; -} - -h2 { - font-size: 20px; - margin: 0 0 4px; -} - -p { - margin: 0 0 6px; -} +* { box-sizing: border-box; } +body { margin: 0; min-width: 320px; } +#root { min-height: 100svh; } +button, input, textarea { font: inherit; } +button { cursor: pointer; } +button:disabled { opacity: .48; cursor: not-allowed; } +:focus-visible { outline: 2px solid #dfad78; outline-offset: 3px; } +h1, h2, p { margin-top: 0; } +h1, h2 { font-family: 'Noto Serif CJK SC', 'Songti SC', 'STSong', serif; font-weight: 500; } +p { margin-bottom: .65em; } +::selection { background: #a44735; color: #fff; } diff --git a/src/ui/StoryPlayer.tsx b/src/ui/StoryPlayer.tsx index 868e307..71f80a8 100644 --- a/src/ui/StoryPlayer.tsx +++ b/src/ui/StoryPlayer.tsx @@ -13,14 +13,10 @@ const FOLLOW_UP_LABELS: Record = { emotion: '情绪', }; -/** speaker/sprite → 立绘占位色(无图片素材,CSS 色块) */ -const SPRITE_COLORS: Record = { - curator: '#6a5a8a', - shenduo: '#4a6a8a', - atang: '#c46a5a', - wenjian: '#7a9a8a', - laogui: '#8a7a4a', - scene: '#3a3a4a', +const CHAPTER_IMAGES: Record = { + chapter1: '/images/ruins.jpg', + chapter2: '/images/excavation.jpg', + chapter3: '/images/inscription.jpg', }; interface Props { @@ -28,7 +24,7 @@ interface Props { onFinish: () => void; } -/** 最简影游界面:对话框 / 选项按钮 / 讲述点语音输入 / 身份登记 / 场景变体渐变 */ +/** 对话 / 选择 / 讲述 / 身份登记与章节过场 */ export function StoryPlayer({ engine, onFinish }: Props) { // tick 只是触发重渲染;引擎是唯一事实源 const [tick, setTick] = useState(0); @@ -37,33 +33,34 @@ export function StoryPlayer({ engine, onFinish }: Props) { const node = engine.currentNode(); const chapter = engine.currentChapter(); - const variant = engine.state.activeVariant; + const image = node.type === 'cutscene' || (chapter.id === 'chapter2' && node.id === 'c2-05') + ? '/images/bronze.jpg' + : node.id === 'c1-05' ? '/images/inscription.jpg' : CHAPTER_IMAGES[chapter.id]; return ( -
-
- {chapter.title} - {variant && 场景变体:{variant.label}} -
+
+ +
+
+
+ 苍梧疑冢 时空档案馆 / ACT {chapter.act} + {chapter.title} +
+
+ ARCHIVE / 0{chapter.act} +

{chapter.title}

+ 影像参考 / 殷墟考古资料 +
{node.type === 'dialogue' && ( -
{ engine.advance(); if (engine.state.finished) onFinish(); else rerender(); }}> -
- {node.speaker.slice(0, 1)} -
-
-
{node.speaker}
-

{engine.renderText(node.text)}

- 点击继续 ▸ -
-
+ )} {node.type === 'choice' && ( @@ -84,10 +81,10 @@ export function StoryPlayer({ engine, onFinish }: Props) { {node.type === 'identity' && } {node.type === 'cutscene' && ( -
{ engine.advance(); rerender(); }}> -
▶ {node.video}
+
+ 场景记录 / {chapter.act}

{engine.renderText(node.caption)}

- 点击继续 ▸ +
)} @@ -106,7 +103,8 @@ export function StoryPlayer({ engine, onFinish }: Props) {
)} -
+
+
); } diff --git a/src/ui/report/ShareCard.tsx b/src/ui/report/ShareCard.tsx index ac99fb0..1968705 100644 --- a/src/ui/report/ShareCard.tsx +++ b/src/ui/report/ShareCard.tsx @@ -27,31 +27,30 @@ export function ShareCard({ badgeDimension, scores }: Props) { const badge = BADGE_MAP[badgeDimension] ?? { title: '档案采集员', flavor: '' }; const dim = PROFILE_DIMENSIONS.find((d) => d.id === badgeDimension); - // 背景渐变(美术占位) - const bg = ctx.createLinearGradient(0, 0, W, H); - bg.addColorStop(0, '#1a1626'); - bg.addColorStop(1, '#3a2a4a'); - ctx.fillStyle = bg; + ctx.fillStyle = '#14231d'; ctx.fillRect(0, 0, W, H); + ctx.strokeStyle = '#8ba58e'; + ctx.lineWidth = 2; + ctx.strokeRect(32, 32, W - 64, H - 64); ctx.textAlign = 'center'; - ctx.fillStyle = '#c9b48a'; + ctx.fillStyle = '#d0dfcd'; ctx.font = '28px serif'; ctx.fillText('苍梧疑冢 · 人格画像', W / 2, 80); - // 勋章圆 ctx.beginPath(); ctx.arc(W / 2, 220, 70, 0, Math.PI * 2); - ctx.fillStyle = '#d4a96a'; - ctx.fill(); - ctx.fillStyle = '#1a1626'; + ctx.strokeStyle = '#bfa47c'; + ctx.lineWidth = 3; + ctx.stroke(); + ctx.fillStyle = '#eed6aa'; ctx.font = 'bold 44px serif'; ctx.fillText('勋', W / 2, 236); - ctx.fillStyle = '#f0e6d2'; + ctx.fillStyle = '#f1eee4'; ctx.font = 'bold 40px serif'; ctx.fillText(badge.title, W / 2, 360); - ctx.fillStyle = '#b0a490'; + ctx.fillStyle = '#b3c6b4'; ctx.font = '22px serif'; ctx.fillText(badge.flavor, W / 2, 400); if (dim) { @@ -63,19 +62,19 @@ export function ShareCard({ badgeDimension, scores }: Props) { ctx.textAlign = 'left'; scores.forEach((s, i) => { const y = 510 + i * 56; - ctx.fillStyle = '#d8ccb8'; + ctx.fillStyle = '#dce7d9'; ctx.font = '22px serif'; ctx.fillText(s.label, 80, y); - ctx.fillStyle = '#4a3f5a'; + ctx.fillStyle = '#385044'; ctx.fillRect(260, y - 16, 240, 18); - ctx.fillStyle = '#d4a96a'; + ctx.fillStyle = '#a8c1a3'; ctx.fillRect(260, y - 16, (240 * s.score) / s.max, 18); - ctx.fillStyle = '#d8ccb8'; + ctx.fillStyle = '#dce7d9'; ctx.fillText(s.score.toFixed(1), 520, y); }); ctx.textAlign = 'center'; - ctx.fillStyle = '#8a7f6a'; + ctx.fillStyle = '#acc0af'; ctx.font = '18px serif'; ctx.fillText('每个选择都是实测,没有自报。', W / 2, H - 40); return canvas;