
HY
折腾进行时
Hello, I'm HY.
这里记录这个博客从上线、写作到页面迭代的每一个重要节点,每一次都按真实改动整理,展开即可查看完整变更说明。
版本规则:Major 代表结构级升级 · Minor 代表新功能 · Patch 代表修复、内容与维护
在 blog.amamo.top 的友链页看到的效果:鼠标悬上去,卡片顶部长出一条首页截图。对方是直接把卡片重写成竖版、把图塞进 friends.astro 里的,我这边不能动主题源文件,所以换了个做法 —— 卡片结构留给主题,封面用一段独立脚本在浏览器里贴上去。截图没有走第三方截图 API(首次要等 5~15 秒,还绕一圈国外),而是新增了一个脚本用本机 chrome-headless-shell 把每条友链截一遍、sharp 转成 640 宽的 webp 存进 public,所以加载时走的是本站 CDN。悬停前连请求都不发,第一次真悬停才赋值 src
友链卡片悬停时在顶部展开首页截图(高 7rem,截取页面上半部分而不是居中裁剪,更像「首页」)
封面默认不加载,首次悬停 / 聚焦才发请求;脚本本体也只在 /friends/ 才按需拉取,其他页面只多十几行判断
新增 scripts/generate-friends-covers.ts,批量截取所有启用友链的首页,转成 640×512 比例的 webp(单张 10~21 KB),并生成 src/data/friends-covers.json 清单
新增 src/pages/api/friends-covers.json.ts(构建期生成静态 JSON)与 public/assets/js|css/friends-covers.*,主题源文件零改动,上游合并无冲突
昨天归档时把「原图」照单全收进相册,事后翻相册发现两张碍眼的:一是手机壁纸那张品红纯色底的原始版(壁纸池里用的已是抠成透明通道的版本,相册里再留一个刺眼的红底没意义),二是「贴纸」相册里那张从九宫格切出来的草帽 Q 版——它和单张高清版是同款,九宫格那格是 JPEG 压缩过的格子源,放大后帽子和描边都是噪点,留着只是重复占位。两张都从相册目录删掉,LQIP 占位色同步清干净
「移动壁纸」相册删掉 kuroneko-shh.png(品红底原始版),现在只剩 1 / 2 / 3 / shana-1 四张
「贴纸」相册删掉 kuroneko-hat.png(九宫格 JPEG 源,与更清晰的单张版 kuroneko-dress 同款),相册 22 张
这批素材是一套挂件图:九宫格整版切开抠底出 9 张 Q 版贴纸,另有一张单张高清版一起进首页贴纸池(两者同款的那张后来删掉了)。桌面端随机抽 10 张的机制不用动坐标,手机端自动装箱。横版夏装图直接进桌面壁纸轮播;竖版立绘原来是刺眼的品红纯色底,索性按同色匹配整体抠掉、只留透明通道不做替代底,背后交给页面自身底色,浅色深色各自适应 —— 手机端从单图升级成两张轮播。原图照例全部归档:贴纸画稿进「贴纸」相册,两张壁纸原图分别进桌面 / 移动壁纸相册
首页贴纸新增:九宫格切出 kirino-sit / kirino-glasses / kirino-maid / kuroneko-swim / kuroneko-maid / ayase-swim / kanako-swim / maid-glasses,外加单张 kuroneko-dress,全部透明底、高 192px、均 8–12KB
手机壁纸池新增 kuroneko-shh:把刺眼的品红纯色底整体抠掉,直接保留透明通道(1080 宽 q90 约 194KB),背后露出的是页面底色,浅色/深色模式各自适应;mobile 配置从单图字符串升级为两张数组,手机端开始走轮播
原图全部归档:10 张贴纸画稿进「贴纸」相册,两张壁纸原图分别进「桌面壁纸」「移动壁纸」相册(移动那张保留的是抠底前的品红底原版)
桌面壁纸池新增 kuroneko-summer(1920×1080,q90 约 157KB),追加进轮播数组末尾
删掉两张贴纸:「夏娜·持剑(红发)」(shana-sword-red)与重复的「黑猫·草帽」(kuroneko-hat,与单张版 kuroneko-dress 同款,留下更清晰的单张版);贴纸池最终从 11 张扩到 19 张,被删的两张原始画稿仍留在「贴纸」相册
昨天开的 /bilibili/ 页只解决「收藏了什么」,而真正想让人看到的是「看过什么」—— 那部分记录都在 bgm 上,把 B站 的「看过」清单批量补录过去之后,B站 这页就没什么存在必要了。主题其实自带一对平级的追番页,bangumi 那页一直是关着的,而且默认指向第三方镜像 api.bangumi.pro,实测这个域名连 TCP 都建不起来,是条死链。换回官方 api.bgm.tv 之后就能直接用:官方放开了跨域,浏览器可以直连,于是开 dynamic 模式,访客打开页面时现拉数据,收藏有变动不必重新部署
siteConfig 里 pages.bilibili 关回 false、pages.bangumi 打开:/bilibili/ 重新重定向到 /404/,「我的」菜单里那一项换成「番组计划」(两者都绑了 pageKey,开关一关自动隐藏)
数据用 dynamic 模式:构建期不请求任何外部接口,访客打开时由浏览器拉取(官方接口带 Access-Control-Allow-Origin,实测直连正常)。代价是首屏靠 JS 渲染,好处是收藏更新不用重新部署
bangumi.apiUrl 由已失效的第三方 api.bangumi.pro 换回官方 api.bgm.tv;详情页地址同步从 api.bangumi.pro/subject/ 改为 bgm.tv/subject/,卡片点进去落在 bgm.tv 而不是代理域名
noReferrerDomains 增加 *.bgm.tv —— bgm 的封面图床同样有防盗链,不加会整页裂图
页面文件和导航菜单项一直都在,只是 siteConfig 里的页面开关是 false,访问会被直接重定向到 /404/,导航里那一项又因为绑了 pageKey 被自动隐藏,等于整页处于「装好了但没通电」的状态。把开关打开后,构建时会请求 B 站 space/bangumi/follow/list 接口,追番(type=1)与追剧(type=2)一起抓,带评分、简介和最新一集的更新进度。封面走的是 hdslb 域名,站点早就配了 noReferrerDomains,不会被防盗链挡回来
siteConfig.pages.bilibili 由 false 改为 true,导航栏「关于 → 哔哩哔哩追番」入口恢复显示(菜单项绑 pageKey,开关关闭时自动隐藏)
首次拉取到 221 条条目(追番 + 追剧),均分 8.7;数据为构建期抓取,静态渲染,不随 B 站实时变动
统计页里那些画——页首樱花横幅、推送节奏和访客活跃两张角色立绘、概览区四张卡片插画——散在页面各处,没法单独看。这回把它们归进相册:三张 PNG 立绘与横幅转成 90 质量的 webp,四张概览插画本来就是压缩好的 webp,字节原样拷过去不做二次压缩;原始 PNG 归档在仓库 gallery-originals/analytics/,配套 _manifest.json 记录来源与尺寸
新增「统计页插画」相册(/gallery/analytics/),收录 /analytics/ 页面的 7 张插画:01 页首横幅、02-03 两张角色立绘(保留透明通道)、04-07 四张概览卡片插画,封面用推送节奏立绘
三张 PNG 转 webp(质量 90,不放大,保留透明通道);四张概览 webp 字节原样拷贝,避免二次有损压缩。原始 PNG 归档至 gallery-originals/analytics/ 并附 _manifest.json
上一版整份搬过来,功能是齐的,但用起来处处别扭:整页是参考站的蓝,跟本站的橙黄对不上;推送热力图显示「最近 1690 次」,而我一年的提交明明只有两百多;两个状态角标被 space-between 顶到最右,正好压在角色插画的脸上;DATA SOURCE 那里的 Umami 点进去是登录页。这些都是「能跑但不像自己的」—— 尤其第二项是个实打实的错:这个仓库是 fork,git log 不加过滤会把上游的历史一起算进来,1695 条里属于我的只有 252 条,差了七倍。一条条改完之后才算顺眼
整页主色由写死的 #2d7cff 换成 var(--primary),kicker、小标题、折线图、时间范围切换一次性全部跟随主题色;访客活跃时间热力图的色阶也改成从主色派生,暗色不用再写一组
推送热力图改用 GitHub 原版绿阶(不跟主题色,这个模块本来就在模仿 GitHub 贡献图);「访客活跃时间」那块仍跟主题色,两者有意区分开
两个状态角标(hero 的「已更新」和推送节奏的「已记录」)从右上角挪进标题文案块 —— 那两处 header 都是 space-between 布局,右侧恰好都是角色插画,角标会被顶上去压住它们。现在插画完整露出来了
推送统计只算本人提交:git log 加 --author、GitHub API 加 author 参数。这个仓库是 fork,不过滤会把上游 CuteLeaf/Firefly 的历史全算进来(1695 条 vs 本人 252 条)。另给 API 每页加了重试,避免网络抖动让热力图整块变空
状态文字改成语义绿(本地预览 / 已更新 / 已记录),并给推送状态加了 data-state,失败时仍然是红色而不是一律绿
DATA SOURCE 里的 Umami 变成外链,指向公开分享页而不是实例首页(首页对访客来说只有登录框);地址由 shareApiBase 与 shareId 拼出,换实例自动跟随
首页那张卡片——头像、站名、快捷导航、彩色社交标签——是整块壁纸上最抢眼的东西,但壁纸本身也是自己一张张挑的。于是想要个「开场」:先让卡片亮两秒,然后把位置让出来,只留壁纸和贴纸。真正难缠的不是动画,是动画的终态:卡片入场走的是 CSS animation,fill: both 会把终态一直挂在元素上,优先级高于普通声明,不先把这段动画摘掉,后面 transition 怎么写都推不动,表现就是「点了没反应,卡片直接瞬移」。另一个决定是只做水平位移、不做缩放——手机端那套按卡片位置算贴纸落点的布局读的是卡片的 rect,只横着挪的话上下两个值完全不变,收起前后贴纸一张都不会跳。最后是「只播一次」的判定:没用 sessionStorage,因为那样会把刷新也算进同一次会话,和想要的正好相反
新增「入场后自动收起」:卡片完整停留约 2 秒后整体向右滑出屏幕,露出纯壁纸。配置在 homeCardConfig.autoCollapse,只填「停留多久」一个数(hold: 2000)—— 入场动画什么时候跑完由脚本监听 animationend 现算,不用写「从入场开始算」的总时长,以后调整入场动画的节奏不必回来改这个数
收起后右边缘挂出一根竖排小签(默认「关于我」),带一枚与卡片身份行同款的小方块当视觉抓手;它是一个真正的 button,点击、键盘 Enter / 空格都能把卡片滑回来,带 aria-expanded。竖条停在 slideTo 同侧,卡片从右边走、签子也从右边出来
下滑箭头不再被贴纸挡住:贴纸层 z-index(25) 压在箭头(10) 上面,居中那张小人正好把它盖死。没有去动箭头的层级(那样中央会多出一颗压在贴纸身上的圆钮),改成让贴纸给它让路——桌面端底部这排从 11 张素材里随机抽 10 张,左右各 5 张分列,正中央空出一段(homeCardConfig.desktopStickerRow,count / centerGap 可调),箭头落在空地里。每次刷新换一批,抽中的仍按原本的相对顺序排,不会左右跨位
收起只做水平位移、不做缩放:手机端贴纸布局读的是卡片 rect 的上下边界,横向挪动不改这两个值。实测收起前后 11 张(手机 4 张)贴纸坐标零漂移,不需要重排
改动全部落在自己新增的组件与配置里(HomeWallpaperDecor.astro + homeCardConfig.ts),主题源文件一个没动;补了 prefers-reduced-motion 兜底(减弱动态效果时不做位移,直接落到终态)
卡片右上角新增一颗半透明 − :点它立刻收起,不用等自动到点。用减号不用叉,是因为这按钮是「先把卡片收起来」而不是「永久关掉」——收完还能从边缘小竖条叫回来
右下角新增「隐藏贴纸」开关:点一下把首页那排小人收走,再点一下放回来。收起时从左往右一张张淡出、放回时从右往左接上(错开 40ms,可在 homeCardConfig.stickerToggle 调成 0 = 整排一起动),状态存 localStorage,刷新与切页都记得住
卡片入场动画的 fill: both 终态会压住后续的 transform / opacity,transition 完全推不动。收起前先给容器加 is-entrance-done 摘掉卡片自身的动画并强制一次样式计算,位移才真正走得动
「只播一次」用脚本作用域里的内存变量,而不是 sessionStorage:脚本带 data-swup-ignore-script 只执行一次、壁纸区又在 Swup 容器外不重渲染,于是刷新会重播、从文章返回首页不会重播,卡片保持收起;sessionStorage 会连刷新一起算掉,正好相反
上一条刚给页脚塞了两行数字,转头又看上了 rainzt.cn 的 /analytics/ —— 那不是两行数字,是整整一页看板:四张彩色总览卡、带 7 / 30 / 90 天切换的访问脉冲、按 GitHub 推送时间排的热力图、24 小时活跃分布、设备与地域的环形图,还有一张中国地图和最近评论。第一版我照着截图自己设计了一套,能跑,但一看就不是那个味道:配色、留白、动效节奏全都对不上。被一句话点醒之后才去翻 Aemeath 的仓库——人家根本不是单个文件,页面将近四千行,外加两个组件,其中专门渲染总览数字的那个要是漏了,页面其余模块全部正常、只有四个数字永远停在「加载中」。整份搬过来才算真正对上,顺带把导航入口从「关于」子菜单提到了顶级
新增 /analytics/ 站点统计页;导航入口放顶级而不是藏在「关于」子菜单里——藏起来等于没有
数据全部走 Umami 公开分享接口,浏览器直连;取数要同时带 x-umami-share-token 与 x-umami-share-context 两个头,少一个就 401。配置集中在 analyticsConfig.umamiAnalytics 的 shareId / shareApiBase / historicalStats 三项
页面用到的 --text-color 与 --font-active-sans 上游主题没有,做在页面自身作用域里而不是改 src/styles/variables.styl,避免为两个变量动主题源文件
同步这次的实现记录(/posts/blog/analyticsdashboard/):从自研翻车、到怎么发现漏了一个组件,都写了
七个模块:四色总览卡、访问脉冲(7 / 30 / 90 天切换 + 手动刷新 + 数字滚动)、推送节奏热力图(按 GitHub 提交时间,带贪吃蛇动画)、访客活跃时间、设备与浏览器环形图、地域分布(中国地图 + 流量来源占比)、Waline 最近评论
实现整份取自同源 fork(Aemeath),含页面、侧栏组件、指标组件与全部插画、地图资源;只把署名和 localStorage 前缀从 rainzt 换成自己的,其余保持原样,便于以后跟着上游一起更新
补上「推送节奏」依赖的 GitHub 提交数据接口(构建期生成静态 JSON:全量 clone 读 git 历史,shallow clone 回退到 GitHub API 分页),并去掉参考实现里「每日扣除 2 次定时 Actions」的算法——那是人家自己的定时机器人,照抄会把提交数算少
又是在刷 rainzt.cn 的时候看上的——他页脚挂着两行东西:一行「本站已运行 X 天 X 小时 X 分 X 秒」在跳秒,一行是今日与全站的访客/访问四个数字。跳秒那行纯前端就能算,另一行卡了我一会儿:博客是跑在 Vercel 上的纯静态站,没有服务端替我去读 Umami。抱着试试看的心态 curl 了一下他那个分享链接,响应头里明晃晃一个 Access-Control-Allow-Origin: * —— Umami 的分享接口本来就是给外部页面调用的。真正的坑在第二步,取数据的接口要同时带 x-umami-share-token 和 x-umami-share-context 两个头,少一个就 401,我按「只带 token」试了一轮才摸对。落点还是 Firefly 原生的 FooterConfig.html 注入点,这次同样一个主题源文件都没动
新增建站运行时间(天/小时/分/秒),秒数逐秒跳动;切到后台标签页直接停掉定时器,切回来立刻重算,避免在看不见的地方空转 setInterval
接入方式:先 GET /api/share/<slug> 换一枚 JWT(缓存 24 小时),再 GET /api/websites/<id>/stats?startAt=&endAt= 取数。两个头缺一不可——x-umami-share-token 加 x-umami-share-context: 1,只带前者返回 401
请求挂在 window.load 之后的 requestIdleCallback 上,不进首屏关键路径;两个接口并发,靠令牌与结果双缓存把首次之后的开销压到 0
同步这次的实现记录(/posts/blog/footerstats/):从探接口、踩 401 的坑,到落点选择与缓存策略都写了
新增今日访客、今日访问、本站访客、本站访问四个指标,数字从 0 滚到目标值(900ms 三次方缓动),系统开了「减少动态效果」就跳过动画直接落值
缓存分三层:分享令牌 24 小时、今日数据 60 秒、全站数据 5 分钟,都写在 localStorage;页面停留期间每 90 秒静默刷新一次,document.hidden 时一次请求都不发
取数失败时整行保持 hidden,既不显示 0 也不显示「加载中」,不在页脚留半截残骸;控制台只留一条 warn
当前页面没有目录
当前页面没有目录
当前页面没有目录