From f01870c07279eed3790feed4ae2d6d226ecf3a77 Mon Sep 17 00:00:00 2001 From: yuetsh <517252939@qq.com> Date: Tue, 3 Nov 2026 14:22:42 +0800 Subject: [PATCH] =?UTF-8?q?fix(=E7=83=9F=E8=8A=B1):=20=E3=80=8C=E8=A1=A8?= =?UTF-8?q?=E6=83=85=E5=8C=85=E3=80=8D=E9=82=A3=E4=B8=80=E7=A7=8D=E5=9C=A8?= =?UTF-8?q?=E6=9C=BA=E6=88=BF=E7=94=B5=E8=84=91=E4=B8=8A=E5=8D=A1=E6=88=90?= =?UTF-8?q?=E5=B9=BB=E7=81=AF=E7=89=87?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit canvas-confetti 画位图粒子是每帧按「位图边长 × scalar」填一个图案矩形,和表情实际多大 无关。原来位图 64px、scalar 5,表情显示 50px,每个粒子每帧却填 320×320,两团 32 个 粒子一帧三百多万像素。改成位图直接画成 50px、scalar 1、矩阵不缩放:显示大小和轨迹 不变(scalar 只管大小不管运动),填的正好是 50×50。 同样画法在无 GPU 的 Chrome 里复现一帧:17ms → 0.5ms(快的 CPU 核上,旧的已经吃满一帧)。 其余九种是圆点、星星、对勾、爱心这类路径,只填图形本身,没有这个问题。 Co-Authored-By: Claude Opus 5.5 --- .../oj/problem/composables/useFireworks.ts | 25 +++++++++++++------ 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/apps/web/src/oj/problem/composables/useFireworks.ts b/apps/web/src/oj/problem/composables/useFireworks.ts index 0460066..0f3fc42 100644 --- a/apps/web/src/oj/problem/composables/useFireworks.ts +++ b/apps/web/src/oj/problem/composables/useFireworks.ts @@ -14,28 +14,36 @@ const EMOJI_SVGS = [ // 将 emoji SVG 转成 confetti 可用的彩色位图 Shape,缓存后供重复调用复用 let emojiShapesPromise: Promise | null = null +/** + * 表情在屏幕上的边长(px)。位图就按这个大小画,放的时候 `scalar: 1`、矩阵不缩放。 + * + * **别改回「位图画大、矩阵缩小、scalar 放大」**:canvas-confetti 画位图粒子是每帧 + * `fillRect(位图边长 × scalar)` 再用图案填,填的矩形和表情实际多大无关。原来位图 64、 + * scalar 5,表情显示 50px,每个粒子每帧却要填 320×320 —— 两团 32 个粒子一帧三百多万像素, + * 机房电脑(显卡弱、Chrome 常走软件渲染)放到这一种就卡(用户 2026-11 反馈)。 + * 现在填的正好是 50×50,同样大小、同样轨迹,像素少到四十分之一。 + */ +const EMOJI_PX = 50 + function svgToBitmapShape(svg: string): Promise { return new Promise((resolve, reject) => { const blob = new Blob([svg], { type: 'image/svg+xml' }) const url = URL.createObjectURL(blob) const img = new Image() img.onload = async () => { - const renderSize = 64 - const baseUnit = 10 const canvas = document.createElement('canvas') - canvas.width = renderSize - canvas.height = renderSize + canvas.width = EMOJI_PX + canvas.height = EMOJI_PX const ctx = canvas.getContext('2d')! - ctx.drawImage(img, 0, 0, renderSize, renderSize) + ctx.drawImage(img, 0, 0, EMOJI_PX, EMOJI_PX) URL.revokeObjectURL(url) try { const bitmap = await createImageBitmap(canvas) - const scale = baseUnit / renderSize resolve({ type: 'bitmap', bitmap, - matrix: [scale, 0, 0, scale, (-renderSize * scale) / 2, (-renderSize * scale) / 2], + matrix: [1, 0, 0, 1, -EMOJI_PX / 2, -EMOJI_PX / 2], } as unknown as confetti.Shape) } catch (e) { reject(e) @@ -415,7 +423,8 @@ export function useFireworks() { const options: confetti.Options = { shapes: emojiShapes, flat: true, - scalar: 5, + // 大小在位图里(EMOJI_PX),这里放大的话填的矩形跟着放大,见 EMOJI_PX + scalar: 1, particleCount: 16, spread: 360, startVelocity: 40,