维护记录#
完成的工作#
- 为博客新增页面装饰背景:自动读取
static/bg/ 下所有图片,在视口左下用「周围透明、中心不透明」的径向渐变显示,多张图片轮流淡入淡出 ✅ - 新增
layouts/baseof.html 覆盖主题的 baseof,使用 readDir "static/bg" 自动发现图片(也可通过 site.Params.bg 显式覆盖);无需任何配置,新增/删除图片直接生效 - 新增
assets/css/extended/bg.css,PaperMod 自动加载此目录。关键效果:position: fixed + z-index: -1 + 透明 body,确保背景永远在最底层、不干扰阅读mask-image: radial-gradient(circle at 60% 40%, #000 0%, #000 32%, rgba(0,0,0,.6) 55%, transparent 82%) 实现「周围透明中心不透明」的渐变- 24 秒一个循环、3 张图错开 8s 延迟轮流淡入淡出
- 暗色模式降低亮度饱和度,移动端缩小尺寸,
prefers-reduced-motion 下停止动画只保留首张
hugo --destination /tmp/hugo-bg-test 构建验证:首页、文章详情页、归档页均正确渲染 .page-bg 容器,3 张 jpg 全部复制到 public/bg/
遇到的问题#
- 起初考虑把背景放在
z-index: -1 但 PaperMod 在 body 上画了 var(--theme) / var(--code-bg),会盖住子元素的负 z-index 背景。解决方案:将 body 背景搬到 html,body 设为透明,让 .page-bg 在 html 背景之上渲染 ⚠️ - 在 assets/css/extended/bg.css 中以 html { background-color: var(--theme); } body, body.list { background-color: transparent; } 覆盖;extended 样式在 core.css 之后 concat,优先级足够
下次建议#
- 若后续想支持点击/hover 暂停轮播,可在 baseof.html 加
onmouseenter / onmouseleave 控制 .page-bg__img 的 animation-play-state - 暗色模式可考虑提供
bg/ 下的"暗色专用"图片(用 front matter 或子目录区分),通过 site.Params.bgDark 控制 - 若图片较多(>5),当前 5 个
:nth-of-type 延迟档位不够,可改成 SCSS 生成
维护记录(续)#
完成的工作#
- 根据用户反馈调整背景行为 ✅
- 轮播 → 随机单图:移除
@keyframes page-bg-fade 与 :nth-of-type 延迟,改为 inline script 每次刷新随机挑一张加 .is-active - 左下 → 左中:容器从
bottom: -3vh 改为 top: 50%; transform: translateY(-50%),垂直居中于视口 - 方形 → 圆形 + 渐变更强:mask 从
radial-gradient(circle at 60% 40%, ..., transparent 82%) 改为更陡的 circle at 50% 50%(实心区从 32% 收缩到 25%,终点 55% → 82%),最终可见区域是圆形
- 配套调整:
html 初始带 class="no-js";脚本启动后移除并改加 js,让无 JS fallback(CSS :not(.js) 选择器)只在禁用 JS 时生效
遇到的问题#
- 旧 keyframes /
animation-delay 残留检查:grep page-bg-fade 在新 CSS 中匹配数 = 0,animation-delay 已全部清除 ✅
下次建议#
- 若想每次刷新换图但保持 “会话内不重复”(同一浏览上下文内不重复同张),可改用
sessionStorage 记上次 idx,rand 到相同则 +1 - 圆形 mask 在极窄屏幕(< 360px)下
min(190px, 50vw) 仍偏大,可再加 @media (max-width: 480px) 把 top: 50% 改成更靠上的位置避免被 main 内容遮挡
维护记录(再续)#
完成的工作#
- 修复背景图片被截断的 bug
- 根本原因:三张源图都是纵向长方形(H/W = 1.30~1.37),但上一版容器是
280x280 正方形 + background-size: cover,等于把图片裁成方形(丢失顶部/底部共约 27% 高度),再被圆形 mask 切出来,所以用户看到的永远是原图的中段 53% - 修复:容器改为
aspect-ratio: 5 / 7(比最瘦长的图 1 略高一点,1.4 vs 1.373)的纵向矩形 + background-size: contain,三张图全部完整显示,最坏情况有 5% 的上下微小留白,但留白落在圆形 mask 透明带内完全不可见 - 用 CSS 变量
--page-bg-w / --page-bg-ratio 统一管理尺寸,移动端断点只覆盖 --page-bg-w,避免重复规则
遇到的问题#
- 调试时
sips 输出的图片尺寸证实了纵向比例;awk 单行格式化因引号转义失败,改用 python3 拼字符串 - Hugo 的 fast render 模式在 CSS 改动后只 rebuild 用了该 CSS 的页面,86ms 完成
下次建议#
- 如果以后会增加横向图片(如 banner 截图),单一
aspect-ratio: 5 / 7 会让横向图上下留白过多。可在 baseof.html 里读取每张图的尺寸(Hugo imageConfig),按宽高比输出内联 style="aspect-ratio: W/H" 覆盖默认比例 - 如果之后想加图片懒加载(虽然只有 3 张图不必要),需把 background-image 重构为真实
img 元素才能用 loading=lazy
维护记录(三续)#
完成的工作#
- 调整背景定位语义:视口 fixed 改为文档 absolute
- 需求:图片不要一直锁在视口左中部,改为只有滚动到文档最底部才看到
- 改动:
.page-bg 容器 position: fixed 改为 position: absolute + inset: 0(撑满整个文档高度);.page-bg__img 移除 top: 50%; transform: translateY(-50%),改为 bottom: 0 - 原理:absolute 元素在没有
position: relative 的祖先时会解析到根 html 节点(即整个文档),bottom: 0 就等于文档底部;用户不滚到最下面图片就在视口外
下次建议#
- 短页面(如 tags / archives 单页)因为文档本身可能比视口还短,图片可能一加载就直接可见 —— 这其实是想要的行为,但如果以后想让所有页面都必须滚动后才看到,可加一段 JS:
window.scrollY === 0 时给 .page-bg 加 opacity: 0、滚动开始后移除
维护记录(四续)#
完成的工作#
- 修正背景定位语义为 fixed + JS 门控
- 上一版用
position: absolute; bottom: 0,图片在首次渲染时被钉死在文档底部某个像素位置,文档长度变化或用户视角不同时不会跟随 —— 用户反馈“只会固定页面第一次渲染的位置” - 这一版恢复
position: fixed,让图片始终钉在视口左下角;显示条件改为 JS 控制:scroll 到文档底部时才加 html.at-bottom class,CSS 根据这个 class 才把 .is-active 的图片 opacity 从 0 提到 0.92 - 门控逻辑:
scrollTop + innerHeight >= scrollHeight - 8 判定为到底部;滚动/缩放/passive 监听;初始加 setTimeout(50) 跑一次防止字体加载后布局变化
遇到的问题#
- 上一轮验证误以为 JS 没生效,实际是 Hugo 把 inline script minify 了(
checkAtBottom → n、imgs → t),grep 原名匹配为 0,但函数调用关系完整 ⚠️ - 下次验证用逻辑特征(at-bottom class、classList.toggle)而非变量名
下次建议#
- 如果想“滚到底出现,往上滚不消失(锁住显示)”的语义,把 CSS 改成只在
at-bottom 由 false 变 true 那一次加 is-revealed class,之后一直保持。代码量几乎不变 - 当前 8px 阈值偏严格;如果发现用户“明明滚到底部却看不到”可能是设备pixel ratio 导致,可调为
Math.max(8, innerHeight * 0.01) 更宽容
维护记录(五续)#
完成的工作#
- 修复背景渐变只覆盖四个角的 bug
- 根本原因:旧 mask 用
radial-gradient(circle at 50% 50%, ...),CSS 默认 closest-side 尺寸 —— mask 圆半径 = min(宽,高)/2 = 160px。矩形 5/7(320×448)的上下中点距圆心 224px > 160px,完全在 mask 外被强制透明 → “只有四个角被渐变、中间是硬切” - 修复:mask 改为
radial-gradient(circle farthest-corner at 50% 50%, ...) —— mask 圆半径 = 角部距离 ≈ 275px,整张矩形都在 mask 范围内,矩形上下中点变成 9.4% 不透明、左右中点 34% 不透明、四个角 0%,全部柔和过渡 - 同时:中心实心区从 35% 半径(112px 硬核)收缩到 6% 半径(33px 小硬核),衰减曲线增加中间驻点让过渡更顺
遇到的问题#
- 几何验证:手算后角部距离 275px、矩形上下边中点 81.4%、左右边中点 58.1%、与渐变 stops 插值后与 CSS 渲染预期一致 ⚠️
下次建议#
- 既然 mask 现在覆盖整张矩形,如果后续觉得“硬心 33px 仍有点硬”,可以试
0% 4%(不要完全实心),让中心也有轻微渐变 - 如果想让背景“雾化”更强(低对比),可以叠加
backdrop-filter: blur(4px) 到 .page-bg__img,让背景在 mask 透明区也能影响主体内容(需测试性能)
维护记录(六续)#
完成的工作#
- 上手动抠图:用
@imgly/background-removal-node 处理 3 张图- 环境准备:
npm config set registry https://registry.npmmirror.com 切淘宝镜像,装包 18s;模型首次加载含下载 ~80MB ONNX - 处理速度:3 张图总计 5 秒(模型已缓存后 1.5-1.9s/张)
- 抠图质量:
- 图 1 (800×1098):主体 53.7%,背景 41.5% 完全透明,边缘羽化 4.8%
- 图 2 (800×1046):主体 37.1%,背景 60.4% 完全透明,边缘羽化 2.5%
- 图 3 (700×909):主体 34.1%,背景 61.8% 完全透明,边缘羽化 4.2%
- 体积增量:原 JPG 总 580KB → 抠图 PNG 总 663KB(+14%,+83KB)
- baseof.html 重构:从“列出全部图片”改为“每个 basename 优先 PNG/WebP/SVG、忽略同名 JPG”,所以原 JPG 保留在磁盘作为回滚、不参加部署
- mix-blend-mode 保留(现在 PNG 透明 + blend 双重保障)
遇到的问题#
- 模板变量名
replaceRE 中特殊字符需要转义,调试两次后 \\.(jpg|...)$ 才正确 ⚠️ - Hugo Go template 中 \\ 在字符串里才是 \ 转义 - 第一版去重 key 错用了
"tier|base",导致同名 PNG/JPG 被认为不同 entry 同时进入 map。修正为仅用 base 作 key,额外逻辑判断“PNG 是否替换现有 JPG” ⚠️
下次建议#
- 原 JPG 现在还在
static/bg/ 里,如果不再需要回滚可手动删除(节省 580KB 部署体积) - 如果有图抠图质量不好(例如 5/7 中的图 1 主体比例较低,可能背景复杂),可用 sharp 对 PNG 后处理(边缘收缩、降噪、转为 indexed 颜色以减小体积)
- 抠图脚本
/tmp/bg-remove/remove-bg.js 可复制到 scripts/ 作为正式维护脚本,但项目主力是 Deno —— 可用 deno task remove-bg 包装 Python 或 Node 调用
维护记录(七续)#
完成的工作#
- 放宽 CSS mask 渐变曲线
- 原因:抠图后主体已透明边缘干净,不再需要硬核中心 + 严进渐变
- 改动:从 7 stops (#000 6% / .78 22% / .5 45% / .22 68% / .06 85%) 放宽为 5 stops (#000 30% / .92 50% / .65 75% / .25 90%)
- 效果:中心 50% 半径(直径 ~140px)完全实,整张图片大部分清晰可见,仅矩形外圈 ~25% 半径处开始柔和淡出,角部 0% 融入背景
下次建议#
- 如果还觉得图片过渡太柔,可继续抬高 75% 、90% 的不透明度(如 .8 / .45)
- 如果觉得边缘还是过锐(PNG 抠图边缘有时会有可见的轮廓),可以在
.page-bg__img 上加 filter: drop-shadow(0 0 6px rgba(0,0,0,0.15)) 柔化
维护记录(八续)#
完成的工作#
- 删除文章封面系统(commit 1278dd7,pushed to master)
- 删除文件(6 个):
scripts/gen-covers.ts(452 行)、scripts/sync-covers.ts(63 行)、2 个对应测试、layouts/_partials/cover.html、.github/workflows/sync-covers.yml - 修改脚本:从
validate-posts 移除 coverExists + cover 校验、从 rename-posts 移除 cover 重命名逻辑、从 git-commit-push 移除 chore(covers) 分类 - 修改配置:
deno.json 移除 gen-covers/gen-covers-dry/sync-covers/sync-covers-dry 4 个 task;hugo.toml 移除 [params.cover];.github/workflows/deploy.yml 移除 Generate covers and inject front matter 步骤 - 修改 lib:移除
paths.ts 的 COVERS_DIR 常量、更新 fs.ts 和 frontmatter.ts 顶部的注释 - 批量修改 225 个 post front matter:删除
cover: 块(4 行:cover/image/alt/hidden);处理了 1 个远程 URL 特殊 case(只有 cover + image 两行) - 总计:245 files changed,+99/-2423
遇到的问题#
- 修复了 3 个 pre-existing bug(都是 cover 删除任务“顺便”发现的):
extractBody 返回正文会多一个前导换行(slice 后未去首 \n)—— 修normalizeDate 测试有错误的 double-quoted 用例(parseFrontMatter 已 unquote)—— 删expect.ts 缺 toBeLessThanOrEqual —— 补
- deno 不可用,用
mise install deno@latest 装到 ~/.local/share/mise/installs/deno/2.9.7,加 ~/.deno/bin 到 PATH;prettier@3.9.6 用 deno install -g 装全局(AGENTS.md 要求)
下次建议#
static/images/covers/ 下 224 个 SVG 还在磁盘上、untracked 状态;如需彻底清理 3.1MB,运行 rm -rf static/images/covers ✅ 已清理(commit 5ff5088)—— 原来这些 SVG 是 tracked 的(从仓库历史一直有),删除产生了 224 个 D: 状态,需 commit + push 才彻底从 HEAD 中拿走- 225 个 post 里以前引用的
aliases: ["/posts/legacy-name/"] 现在保留 —— 这些 alias 跳转仍然有效(除非本身就是错位的别名),未来若需完全删除可在 validate-posts 里加一个“alias 对应的旧路径未被引用过则报错”检查 - 回滚:tag
before-remove-cover 还在指向 0d49a15(push 前创建的);如需完全回滚 git reset --hard before-remove-cover && git push --force——但现在增加了 commit 5ff5088,需用 --force-with-lease 或先 push tag