全站鼠标指针换成《俺妹》主题:亮色桐乃、深色黑猫

2450 字
12 分钟
全站鼠标指针换成《俺妹》主题:亮色桐乃、深色黑猫
全站鼠标指针换成《俺妹》主题:亮色桐乃、深色黑猫

这次把整站的鼠标指针换了一遍。

亮色主题配高坂桐乃,深色主题配五更琉璃(黑猫),各 28 枚、一共 56 枚。正常选择和链接、正文和表单的文本光标、禁用状态、拖拽、忙碌与加载,以及滚动和调整大小那一批,全都换掉了;代码块除外 —— 那里我特意留着系统原生的 I 型光标,下面会说为什么。切换亮暗主题时整套指针自动跟着换,不需要手动挑。

亮色桐乃 / 深色黑猫,各 28 枚
亮色桐乃 / 深色黑猫,各 28 枚

素材:两张总览图,切成 56 枚#

手上只有两张总览图:一张桐乃、一张黑猫,每张 7 列 4 行的图标阵,底下压着中文标签。要变成能用的素材,得先把 28 个格子一个个切出来、去掉背景、缩到光标尺寸。

剪贴板里存的那份只有 1920 宽,翻回原始文件是 6144×4096,我直接用后者当源。

版面我用投影扫出来的:先按行统计「非白像素」,得到图标带和标签带交替的几条横带;再在图标带里按列扫一遍。这活看着机械,实际有两个坑等着我。

坑一:这批图不能按我的老办法去白底#

我以前处理贴纸用的是「全局去白」—— 把所有接近纯白的像素设成透明,那套在贴纸上很好使。放到这批图上,一跑就废。

桐乃那枚「正常选择」是个深色描边 + 白色填充的箭头,箭头内部是白的,人物的衣服也是白的,而且跟我页面背景一个色号(都是 255,255,255,我数了一下,那一格 63% 的像素是纯白)。全局去白的结果是箭头被掏成空壳,只剩一圈线。

我换了个思路:被描边围住的白色,本来就是图形的一部分。 先用阈值取「墨」(min(R,G,B) < 244),再对墨迹做孔洞填充,只补那些不与图像边缘连通的空白区域:

ink = np.asarray(rgb).min(axis=2) < 244
# 四周先补一圈纯白,保证背景一定与边缘连通
pad = np.zeros((h + 320, w + 320), bool)
pad[160:-160, 160:-160] = ink
filled = ndimage.binary_fill_holes(pad)[160:-160, 160:-160]

这下对了:箭头内部的白色补了回来,人物衣服也保住了,只有外圈那片真正连到边缘的白变成透明。我又给边缘补了一道 1px 高斯羽化,缩放走预乘 alpha(先乘再除回来),免得透明区的白色从描边外侧渗出一圈白边。

坑二:两张图的行距不一样,我一开始偷懒用了同一套#

第一版我给两张图套了同一组裁切框,省事。结果黑猫那张有一半格子报警。

翻回原图才看明白:黑猫那张比桐乃多了一圈外框和一个页脚标,表头也略高一点。它的表头底边正好落在第 1 行裁切框里 —— 那一整行都是墨,填充之后整个框被填满;第 3 行的中文标签顶边又比桐乃那张高 7px,切进去就把「斜向调整大小 2」的标签文字粘成了图形的一部分。

所以裁切框我改成按主题各测一套:行框取「上一行标签底 + 6px」到「本行标签顶 − 6px」,列框取相邻列中心的中点、两端避开图集外框。改完两组各 28 格,零告警。

热点(点击判定点)我也让它跟着自动算:箭头和手指这类有尖端的,取内容区里 x + y 最小的那个像素;圆形、十字、调整大小这类取正中。桐乃和黑猫的指尖差 1~2px,所以两套分别算,不共用。

28 条规则,我只写了一遍#

我要的是「亮色桐乃、深色黑猫」。最直白的写法是每条规则写两遍 —— 一套挂 html, body,一套挂 html.dark,28 个指针就是 56 条,以后改一个热点还得改两处。这种迟早会漂。

我试了一下 CSS 自定义属性,Chromium 里能跑通:

:root {
--ffc-normal-1: url("/images/cursors/kirino/normal-32.png");
--ffc-normal-2: url("/images/cursors/kirino/normal-64.png");
--ffc-normal-hx: 4;
--ffc-normal-hy: 2;
}
html.dark {
--ffc-normal-1: url("/images/cursors/kuroneko/normal-32.png");
--ffc-normal-2: url("/images/cursors/kuroneko/normal-64.png");
/* 黑猫的箭头尖位置不同,热点跟着换 */
--ffc-normal-hx: 4;
--ffc-normal-hy: 2;
}
html,
body {
cursor: var(--ffc-normal-1) var(--ffc-normal-hx) var(--ffc-normal-hy), auto;
cursor: -webkit-image-set(var(--ffc-normal-1) 1x, var(--ffc-normal-2) 2x)
var(--ffc-normal-hx) var(--ffc-normal-hy), auto;
cursor: image-set(var(--ffc-normal-1) 1x, var(--ffc-normal-2) 2x)
var(--ffc-normal-hx) var(--ffc-normal-hy), auto;
}

有两处是我先单独验过才敢往下写的。

一是变量能承载整个 <image>,也能承载热点那两个数字 —— cursor: <image> <number> <number>, <keyword> 里的每一段都可以是 var(),替换发生在计算值阶段,解析器不挑。于是「用哪张图」和「点在哪儿」全进了变量,切主题只换变量,28 条规则写一遍就够。顺手也确认了变量名写错时的兜底:整条声明失效、回退到 auto,不会把光标搞成一个奇怪的样子。

二是两种倍率共用同一组 CSS px 热点。2x 图渲染时也缩到 32 CSS px,所以热点只写一份(3 2 / 6 4 / 16 16),不用跟着倍率换算 —— 这也是我没选「直接塞一张 64px 大图」的原因:CSS 的 cursor 压根不能声明图片的固有尺寸,塞进去会得到一个 64px 的巨无霸光标。

每条规则我写了三档声明:纯 url() 兜底 → -webkit-image-set() → 标准 image-set()。不认识 image-set 的浏览器自动落回 1x 位图,而不是把整条声明丢掉。

代码块为什么没换#

正文和表单的文本光标我换成了魔法棒,但 pre、code 我特意留了系统原生的 I 型光标 —— 代码块是选命令行、选配置去复制的地方,32px 的位图不如一根细十字好瞄。

本来一句 pre, code { cursor: text } 就该收工,结果我一看 pre 上的 computed style,是 body 那个黄箭头。查下来是 expressive-code 自带的一条规则:

.expressive-code :not(:is(svg, svg *)) {
cursor: revert;
}

revert 会把属性回滚到用户代理那一层 —— 对 pre 来说等于「我没写过」,于是它顺着继承链一路向上,继承到了 body 的箭头。它的特异性是 0,1,1,正好压过我那条 0,0,1 的 pre。

好在 revert 属于非 important 声明,所以 pre, code, kbd, samp, pre *, code * { cursor: text !important } 才压得住。pre 定住之后,内部那些同样写着 revert 的后代自然一路继承下来;而 expressive-code 的复制按钮在 <figcaption> 里、不在 pre 内,不会被这条误伤 —— 我单独回查了一遍,它仍然是链接指针。

怎么确认它真的生效了#

这一环比实现麻烦:光标是浏览器在合成层上画出来的一层东西,不属于页面内容,任何截图方式都抓不到它。所以「指针换了没有」没法用一张图自证,能拿到的只有计算样式和图片能不能解码。

于是我把验证脚本写成这样:拿目标元素的 getComputedStyle(el).cursor,把里面那个文件路径抓出来,new Image() 加载一遍,确认 naturalWidth 是 32 —— 这一条同时证明了「声明命中了」和「图片存在且格式正确」。

在本地 dev 上跑下来:

元素结果
bodykirino/normal 热点 4 2,兜底 auto
akirino/link 热点 8 3,兜底 pointer
p / h1 / td / input / textareakirino/text 热点 16 16,兜底 text
pre / pre code / .ec-linetext(原生 I 型光标,没被覆盖)
abbr[title]kirino/help 兜底 help
:disabledkirino/unavailable 兜底 not-allowed
[draggable]kirino/move 兜底 move
.expressive-code buttonkirino/link(复制按钮没被 pre 那条误伤)
28 个 [data-cursor="…"]全部命中对应图标
加上 html.dark 之后整套换成 kuroneko,热点跟着变

112 个 PNG(28 枚 × 2 套 × 2 倍率)我逐个请求了一遍,全是 200,没有一个 404。这类「路径拼错、但因为一直没人点所以从没暴露」的问题,不挨个点一遍是发现不了的。

28 枚我全都挂上了 [data-cursor="…"] 点名,其中十来个有天然触发点、不用手写属性:

28 枚指针的落地范围
28 枚指针的落地范围

深色主题下同一页换整套素材,热点也跟着换:

切到深色主题后换成黑猫那套
切到深色主题后换成黑猫那套

既然拍不到光标本身,我至少能拍「鼠标划过去会变成什么」。顺手做了个自包含的单文件演示页,56 张图全部内嵌成 data URI,打开就能划,右侧那个按钮切主题 —— 这篇的配图也是从那上面截的。

演示页:空白、链接、按钮、正文、代码块各是什么指针
演示页:空白、链接、按钮、正文、代码块各是什么指针

我把它落在哪儿#

文件状态干什么
src/config/FooterConfig.html改(追加)光标样式块:变量定义 + 28 条规则 + 代码块兜底
public/images/cursors/kirino/*新增桐乃 28 枚 × 2 倍率
public/images/cursors/kuroneko/*新增黑猫 28 枚 × 2 倍率
public/assets/js/dynamic-comment-enhance.js改STYLE_IDS 补一条,让动态评论 iframe 也吃同一套

还是本站用惯的路子 —— 注入点仍在 src/config/FooterConfig.html,一个主题源文件都没动。素材总共 700 KB,单张 1.4~9 KB,浏览器按需取用。

最后一条我想单独说。动态页的评论区跑在一个同源 iframe 里,而那个页面走的是不带页脚的 Layout,页脚注入到不了它。之前给回复折叠和回复引用补过一个「父页面代投」,所以这次我只往那份代投名单里加了一个 ff-cursor-style 就完事 —— 那个样式块内部自己分了两套主题,iframe 那边同样从 localStorage 拿到 dark 类,两边自然一致。我点开动态页的评论面板回查过,iframe 里的 body 和链接也都是俺妹指针。

最后#

两张总览图我也收进了相册(俺妹指针主题),原图字节一致、零压缩。

支持与分享

如果这篇文章对你有帮助,欢迎分享给更多人或打赏支持!

打赏
全站鼠标指针换成《俺妹》主题:亮色桐乃、深色黑猫
https://www.9ll.uk/posts/blog/cursortheme/
作者
HY
发布于
2026-10-16
许可协议
CC BY-NC-SA 4.0
相关文章智能推荐
1
全站加了个「最近评论」:数据直连 Waline,主题文件一个没改
博客在参考站的右栏看到「最近评论」组件,想在自己站上也挂一个,而且要求全站都有。评论数据就在自己的 Waline 后端里,直接调它的公开接口;DOM 和样式走页脚注入,脚本全站加载,桌面挂右栏、移动端挂底部组件栏。踩了三个坑:Waline 的 orig 字段是渲染前的 HTML、类名和外包装撞车吃掉 32px 内边距、窄栏 flex 布局把昵称挤成一个字。
2
全站加了个「天气预报」:接口免费,而且按访客 IP 定位
博客刷参考站时看到他右栏挂着一张天气预报卡,从标题竖条到「收起」按钮整个搬了过来。接口在 uapis.cn,免费、不用 key,而且不传参数时按访客 IP 定位 —— 谁来串门看到的就是谁头顶上的天。踩了两个坑:参考站那套图标是 Astro 构建期渲染的 symbol、动态插进去取不到;指标网格的 auto-fit 在 280px 右栏只排得出一列,卡片被顶到 925px。
3
首页加了一条会滚的动态:参考楠枝小筑,一个主题文件都没改
博客在楠枝小筑首页看到一条会一直横滚的「瞬间」条,想在自己站上也挂一个。他用的是 Ethereal 主题,代码没法照抄,所以只搬机制:Memos 数据在客户端拉,滚动靠 CSS 把轨道复制成两份,DOM 走页脚注入再加一点点搬运。最后改动落在两个文件里,主题源码一个没碰。
4
评论区回复默认展开 2 条,长串收在「展开更多」后面
博客友链页有条评论下面挂了 13 条回复,往下翻要滑过整屏;可全收起来又太狠,访客第一眼什么都看不到。最后定成默认展示 2 条,多出来的才收着,还有剩就给个「展开更多」,摊完能收回去。做法照旧只加文件不动上游。踩坑集中在一点上:Waline 是 Vue 渲染的,DOM 会重绘、会随 swup 换页被整体替换,所以折叠状态不能只靠一次性的 DOM 操作。
5
回复带上引用:别再让读者猜「@昵称」在回哪一句
博客Waline 的二级回复只渲染一行「@昵称:」,谁在回谁很清楚,回的是哪一句全靠猜。翻 DOM 发现它其实留了线索 —— 回复里那个 @昵称 的链接 href 就是被回复评论的 objectId。顺着锚点把目标评论捞回来,摘成引用块插在下面,点它还能滚过去高亮。踩了个坑:剥 @ 前缀时忘了 clone,把页面上那行整个删了。
随机文章随机推荐

评论区

Profile Image of the Author
HY
Hello, I'm HY.
公告
迷茫.... 为了访问速度,本站将会在11月上旬至中旬迁移到0dm.cn并完成备案
分类
标签
最新动态
站点统计
文章
74
分类
8
标签
192
总字数
161,979
运行时长
0 天
最后活动
0 天前
站点信息
构建平台
Vercel
博客版本
Firefly v6.16.8
文章许可
CC BY-NC-SA 4.0
文章目录