bcf99ec454
deploy werewolf / build-and-deploy (push) Successful in 1m1s
- vite-plugin-pwa(injectManifest)自定义 SW:install 逐个抓取并向页面广播进度, cache-first 服务,导航离线回退 index.html,缓存版本随清单哈希自动淘汰旧缓存 - 全屏 modal 进度条(src/pwa.ts),反映首屏预缓存真实下载进度 - 牌图 mozjpeg 压缩 + 限长边 900px,每张 ≤200K(21.2MB→2.8MB) - 生成 PWA 图标 + manifest + apple-touch meta,index.html 接入 - 新增脚本:npm run gen:icons / compress:images
61 lines
2.1 KiB
JavaScript
61 lines
2.1 KiB
JavaScript
// 把 public/werewolf 下的牌图压到每张 <= 200KB(原地覆盖)。
|
|
// 策略:最长边限 900px,mozjpeg 质量从高到低递减,直到达标。
|
|
// 已经 <= 目标的文件跳过,避免重复编码反复掉质量。
|
|
// 运行: npm run compress:images
|
|
import sharp from 'sharp'
|
|
import { readdir, stat, writeFile } from 'node:fs/promises'
|
|
import { fileURLToPath } from 'node:url'
|
|
import { dirname, resolve, join } from 'node:path'
|
|
|
|
const here = dirname(fileURLToPath(import.meta.url))
|
|
const root = resolve(here, '../public/werewolf')
|
|
|
|
const TARGET = 200 * 1024
|
|
const MAX_EDGE = 900
|
|
const QUALITIES = [82, 76, 70, 64, 58, 52, 46]
|
|
|
|
async function listJpgs(dir) {
|
|
const out = []
|
|
for (const name of await readdir(dir, { withFileTypes: true })) {
|
|
const p = join(dir, name.name)
|
|
if (name.isDirectory()) out.push(...(await listJpgs(p)))
|
|
else if (/\.jpe?g$/i.test(name.name)) out.push(p)
|
|
}
|
|
return out
|
|
}
|
|
|
|
async function compress(file) {
|
|
const before = (await stat(file)).size
|
|
if (before <= TARGET) return { file, skipped: true, before }
|
|
let chosen = null
|
|
for (const quality of QUALITIES) {
|
|
const buf = await sharp(file)
|
|
.rotate() // 按 EXIF 摆正
|
|
.resize({ width: MAX_EDGE, height: MAX_EDGE, fit: 'inside', withoutEnlargement: true })
|
|
.jpeg({ quality, mozjpeg: true })
|
|
.toBuffer()
|
|
chosen = { buf, quality }
|
|
if (buf.length <= TARGET) break
|
|
}
|
|
await writeFile(file, chosen.buf)
|
|
return { file, before, after: chosen.buf.length, quality: chosen.quality }
|
|
}
|
|
|
|
const files = await listJpgs(root)
|
|
let totalBefore = 0
|
|
let totalAfter = 0
|
|
for (const f of files) {
|
|
const r = await compress(f)
|
|
const rel = f.slice(root.length + 1)
|
|
if (r.skipped) {
|
|
totalBefore += r.before
|
|
totalAfter += r.before
|
|
console.log(`skip ${rel} (${(r.before / 1024) | 0}K)`)
|
|
} else {
|
|
totalBefore += r.before
|
|
totalAfter += r.after
|
|
console.log(`ok ${rel} ${(r.before / 1024) | 0}K -> ${(r.after / 1024) | 0}K q${r.quality}`)
|
|
}
|
|
}
|
|
console.log(`\n${files.length} files: ${(totalBefore / 1024 / 1024).toFixed(1)}MB -> ${(totalAfter / 1024 / 1024).toFixed(1)}MB`)
|