Compare commits

...
2 Commits
Author SHA1 Message Date
xuyueandClaude Opus 5.5 0c94f2db87 perf(暗色): 每次打开页面不再白白强制重算两次整页样式
Deploy / deploy (push) Successful in 49s
上一版全站共用一个 useDark 之后,它一进页面仍要把 <html> 的 class 同步两遍,每遍都插
style + getComputedStyle 逼整页重算。class 已经对了就跳过,只有真要切换时才走原来那套。
CPU 降速 6 倍下每页省约 25ms;切换暗色、暗色下刷新、切回亮色都核过。

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-03 14:36:11 +08:00
xuyueandClaude Opus 5.5 f01870c072 fix(烟花): 「表情包」那一种在机房电脑上卡成幻灯片
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 <noreply@anthropic.com>
2026-11-03 14:22:42 +08:00
2 changed files with 27 additions and 9 deletions

No files matched your search

@@ -14,28 +14,36 @@ const EMOJI_SVGS = [
// 将 emoji SVG 转成 confetti 可用的彩色位图 Shape,缓存后供重复调用复用
let emojiShapesPromise: Promise<confetti.Shape[]> | 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<confetti.Shape> {
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,
+10 -1
View File
@@ -13,4 +13,13 @@ import { createGlobalState, useDark as useVueUseDark } from '@vueuse/core'
* 切筛选再来 30 次,右栏换一条又是三四次。2026-11 实测(生产构建、CPU 降速 6 倍)首屏
* 光这一处就占了 1.3 秒。全站共用一个之后只在第一次挂载时同步,后面的调用都是读同一个 ref。
*/
export const useDark = createGlobalState(() => useVueUseDark())
export const useDark = createGlobalState(() =>
useVueUseDark({
// 剩下的那一次(一进页面它还要同步两遍)也省掉:<html> 上的 class 已经对了就不动。
// 只有真要切换时才走它原来那套(关过渡 + 重算),切暗色不会有颜色慢慢渐变的过渡
onChanged(dark, defaultHandler, mode) {
if (document.documentElement.classList.contains('dark') === dark) return
defaultHandler(mode)
},
}),
)