年度报告优化 #720

This commit is contained in:
cc
2026-04-19 19:28:14 +08:00
parent bc2e7d616a
commit 682f43bf2f
6 changed files with 93 additions and 16 deletions

View File

@@ -447,6 +447,25 @@
letter-spacing: -0.04em;
margin-top: 10vh;
text-shadow: 0 18px 45px rgba(0, 0, 0, 0.45);
max-width: 90vw;
overflow-wrap: anywhere;
}
#scene-0 .title-year--numeric {
font-size: clamp(6.8rem, 21vw, 18rem);
letter-spacing: -0.04em;
}
#scene-0 .title-year--text {
font-size: clamp(4.8rem, 14vw, 10rem);
letter-spacing: 0.01em;
line-height: 1.08;
}
#scene-0 .title-year--text-long {
font-size: clamp(3.8rem, 10.5vw, 7.5rem);
letter-spacing: 0.02em;
line-height: 1.12;
}
#scene-0 .title-year-wrap {

View File

@@ -1,7 +1,6 @@
import { useState, useEffect, useRef, useCallback } from 'react'
import { useNavigate } from 'react-router-dom'
import { X } from 'lucide-react'
import html2canvas from 'html2canvas'
import {
finishBackgroundTask,
isBackgroundTaskCancelRequested,
@@ -403,6 +402,23 @@ function AnnualReportWindow() {
const formatFileYearLabel = (year: number) => (year === 0 ? '历史以来' : String(year))
const wait = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms))
const waitForNextPaint = () => new Promise<void>((resolve) => {
requestAnimationFrame(() => {
requestAnimationFrame(() => resolve())
})
})
const captureSceneDataUrl = async (): Promise<string> => {
const captureFn = window.electronAPI.annualReport.captureCurrentWindow
if (typeof captureFn !== 'function') {
throw new Error('当前版本未启用原生截图接口,请重启应用后重试')
}
const captureResult = await captureFn()
if (!captureResult.success || !captureResult.dataUrl) {
throw new Error(captureResult.error || '原生截图失败')
}
return captureResult.dataUrl
}
const handleExtract = async () => {
if (isExtracting || !reportData || !containerRef.current) return
@@ -435,26 +451,20 @@ function AnnualReportWindow() {
try {
const images: Array<{ name: string; dataUrl: string }> = []
root.classList.add('exporting-scenes')
await waitForNextPaint()
await wait(120)
// 预检:强制验证主进程已注册原生截图 handler确保导出链路不是旧逻辑。
await captureSceneDataUrl()
for (let i = 0; i < TOTAL_SCENES; i++) {
setCurrentScene(i)
setButtonText(`EXTRACTING ${i + 1}/${TOTAL_SCENES}`)
await wait(2000)
const canvas = await html2canvas(root, {
backgroundColor: '#050505',
scale: 2,
useCORS: true,
allowTaint: true,
logging: false,
onclone: (clonedDoc) => {
clonedDoc.querySelector('.annual-report-window')?.classList.add('exporting-scenes')
}
})
await waitForNextPaint()
await wait(1700)
images.push({
name: `P${String(i).padStart(2, '0')}_${sceneNames[i] || `SCENE_${i}`}.png`,
dataUrl: canvas.toDataURL('image/png')
dataUrl: await captureSceneDataUrl()
})
}
@@ -521,6 +531,13 @@ function AnnualReportWindow() {
const yearTitle = reportData.year === 0 ? '历史以来' : String(reportData.year)
const finalYearLabel = reportData.year === 0 ? 'ALL YEARS' : String(reportData.year)
const compactYearTitle = yearTitle.replace(/\s+/g, '')
const isNumericYearTitle = /^\d+$/.test(compactYearTitle)
const yearTitleVariantClass = isNumericYearTitle
? 'title-year--numeric'
: compactYearTitle.length >= 5
? 'title-year--text-long'
: 'title-year--text'
const topFriends = reportData.coreFriends.slice(0, 3)
const endingPostCount = reportData.snsStats?.totalPosts ?? 0
const endingReceivedChats = reportData.socialInitiative?.receivedChats ?? 0
@@ -570,7 +587,7 @@ function AnnualReportWindow() {
<div className="reveal-inner serif scene0-cn-tag"></div>
</div>
<div className="reveal-wrap title-year-wrap">
<div className="reveal-inner serif title-year delay-1">{yearTitle}</div>
<div className={`reveal-inner serif title-year ${yearTitleVariantClass} delay-1`}>{yearTitle}</div>
</div>
<div className="reveal-wrap desc-text p0-desc">
<div className="reveal-inner serif delay-2 p0-desc-inner"><br/></div>

View File

@@ -883,6 +883,12 @@ export interface ElectronAPI {
dir?: string
error?: string
}>
captureCurrentWindow: () => Promise<{
success: boolean
dataUrl?: string
size?: { width: number; height: number }
error?: string
}>
onAvailableYearsProgress: (callback: (payload: {
taskId: string
years?: number[]