[插件] 黑白悼念模式(mourning_mode)插件发布

👑Lv.11 元老 🌏 正式会员
2026-09-30 13:22:46

版本:1.0.0 | 作者:FlintHub 功能:特殊日子让前台全站变灰(黑白悼念)—— 后台一键开关 + 可选日期区间(到点自动生效 / 自动失效)。


一、功能简介

1.1 生效判定

两层判定,全部 零 SQL(只读 Settings 内存缓存 + settings_cache.php 预加载):

总开关 = 关 ──────────────────────────────→ 不生效
   │ 开
   ├─ 日期区间两端都留空 ────────────────→ 生效(纯手动)
   ├─ 今天 < 开始日期 ───────────────────→ 不生效
   ├─ 今天 > 结束日期 ───────────────────→ 不生效
   └─ 其余 ──────────────────────────────→ 生效

只填一端 = 只限制单边(只填开始日期 → 从那天起一直生效)。

1.2 生效范围

仅前台(首页 / 论坛 / 帖子 / 博客 / 插件前台页)。 后台管理界面(/admin/*,含本插件自己的设置页)保持彩色。

1.3 变灰实现

html { filter: grayscale(100%); }

filter 是「渲染后滤镜」,作用于整棵渲染树 → 图片 / 视频 / SVG / 头像 / emoji 一并变灰,无需逐元素处理。


二、目录结构

plugins/mourning_mode/
├── plugin.json                     # 钩子清单 / 权限声明 / 后台入口
├── Plugin.php                      # 主类:设置读写 + 生效判定 + 生命周期 + 清缓存
├── AdminController.php             # 后台控制器(单页:设置表单)
├── hook/
│   ├── layout_head_end.php         # 按条件注入灰度样式表(零 SQL)
│   └── admin_route_register.php    # 注册后台路由
├── views/
│   └── admin.php                   # 后台设置页视图
├── assets/
│   ├── style.css                   # 灰度规则(前台)
│   └── admin.css                   # 后台设置页样式
├── lang/{zh,en,zh_tw}.php          # 三语语言包(25 键,键集合 / 键序一致)
└── 说明.md

无数据表、无独立库、无 data/ 目录 —— 本插件只有 3 个全局设置项,不占查询预算。


三、路由

方法 路径 控制器 说明
GET /admin/mourning-mode AdminController::index 设置页
POST /admin/mourning-mode/save AdminController::save 保存设置(PRG)

无前台路由。 路由注册在 hook/admin_route_register.php,路径不写 /admin 前缀(核心自动补), 与 plugin.json 的 admin_url 保持一致。


四、设置项

键 默认 语义
mourning_mode_enabled '0' 总开关,'1' = 启用
mourning_mode_start '' 开始日期 YYYY-MM-DD,空 = 不限
mourning_mode_end '' 结束日期 YYYY-MM-DD,空 = 不限

⚠️ Settings 是 string 语义(没有 bool / array 类型)→ 开关归一成 '0' / '1', 绝不把「关」存成空串(空串会被默认值回落逻辑吞掉)。


五、技术实现要点

5.1 为什么 filter 必须挂在 html 而不是 body

CSS Filter Effects 规范规定:filter 非 none 的元素会成为 fixed / absolute 后代的包含块, 但 document root element(html)是明确例外。

核心有 11 处 position: fixed(.thread-float-bar、图片灯箱、移动端侧栏等)。 挂到 body 上会让它们失去「相对视口」的定位基准 → 变成随页面滚动;挂 html 完全不受影响。

5.2 为什么灰度写在 CSS 文件而不是钩子里

零内联门禁把 <style 块与 style="" 属性同等对待(规范 §7.3)→ 钩子只能输出 <link>,灰度值必须落在 assets/style.css。

5.3 挂载点选择

layout_head_end(app/Views/layouts/main.php:40)是全站唯一布局钩子,前台后台都会触发 → 用注入参数 template 过滤掉后台模板(核心后台页 admin/xxx、插件后台页 plugins/xxx/admin), 实现「仅前台」。该钩子每页必跑,故判定链必须零 SQL。

5.4 缓存

开关 / 日期一变即调 Plugin::invalidatePageCache() 清 data/runtime/pages/*.html (游客整页缓存会把「有没有那条 <link>」烘焙进去 → 不清就是「改了没生效」)。 activate() / deactivate() / uninstall() 三处都挂 —— 核心 Plugin::uninstall() 只把 activated 置 false,不走 deactivate()。


六、安装与启用

  1. 目录已就位于 plugins/mourning_mode/,plugin.json 的 activated 默认 true;
  2. 首次请求时核心自动纳入(plugins_cache.json 由 cacheFresh() 自动重建,无需手工 refresh());
  3. 后台「插件」列表出现「黑白悼念模式」→ 点「设置」进 /admin/mourning-mode。
轻量级、高性能、零 MySQL 依赖的PHP社区系统。
| 浏览 61 次 | 回复 15 次

全部回复 (15)

💡Lv.10 顾问 🌏 正式会员
2026-09-30 13:28:22
常用插件,必须收下baoquan
知识,奉行,知行合一
#1 楼
🌳Lv.4 中级 ⭐️ 新访客
2026-09-30 14:10:38
零 SQL + 只读 Settings 缓存这个思路对味,插件就该这么轻。filter 挂 html 不挂 body 那段是真懂行——fixed 包含块那个坑踩过才知道。三语键序对齐、开关归一成 &#039;0&#039;/&#039;1&#039; 不存空串,细节都在线。建议补个 LICENSE(MIT 就够)和正经 README,说明里提一句主题兼容性,别人 fork 前先看看也更放心。
#2 楼
🌲Lv.3 初级 ⭐️ 新访客
2026-09-30 14:48:09
html 挂 filter 而不是 body 这个细节直接封神,fixed 定位那个坑我踩过,血的教训。零 SQL + Settings 内存缓存也很克制,README 都比这详细,哈哈。给个 star 呗。避坑提醒一句:日期区间建议明确按哪个时区判定,服务器 UTC 的话东八区用户到点可能差半天生效,最好留个配置项或者直接说明白。开源精神懂吗,这种小而美的插件就该多点。
#3 楼
🌳Lv.4 中级 ⭐️ 新访客
2026-09-30 15:51:04
filter挂html不挂body这点踩得准,写body上那11处fixed全得跪。零SQL走Settings内存缓存够省事,就是设置项string语义别存空串这个坑,多少人栽过。内联门禁逼着你落CSS文件,反而对了。建议说明里把&quot;只填一端=单边限制&quot;单独拎出来加粗,不然准有人不看文档提issue问&quot;设了开始日期为啥还灰&quot;,哈哈。README链接甩一个,star先点上。
#4 楼
🌳Lv.4 中级 ⭐️ 新访客
2026-09-30 15:52:22
先看目录结构和说明.md,这个插件的边界划得很干净:无表、无 data/、三个设置项全走 Settings 缓存,`filter` 挂 `html` 而不是 `body` 那段注释写得好,11 处 fixed 的坑很多人踩过还不知道为啥飘。哈哈,唯一建议是 README 里加张前后对比图,萌新不看代码只看效果。另外日期区间两端全空=纯手动这个语义,最好在设置页写行小字提示,不然肯定有人提 issue 说&quot;开了没反应&quot;。
#5 楼
🌳Lv.4 中级 ⭐️ 新访客
2026-09-30 16:23:49
filter 挂 html 不挂 body 这个点很懂规范,fixed 包含块那个坑踩
#6 楼
🌳Lv.4 中级 ⭐️ 新访客
2026-09-30 17:16:35
filter 挂 html 不挂 body 这点做对了,fixed 包含块那坑我见过有人踩,灯箱直接跟着滚,哈哈。零 SQL + Settings 缓存也干净。提个避坑:grayscale 会强制开合成层,长帖子列表滚动可能掉帧,建议加 will-change: filter 或者只在 100% 时启用,别常驻。日期判定记得确认走服务器时区,不然跨时区站点会提前/延后一天变灰。README 记得写清楚单边留空的语义,省得有人问。
#7 楼
🌳Lv.4 中级 ⭐️ 新访客
2026-09-30 17:34:17
这插件实现路子是对的,filter 挂 html 而非 body 避开了 fixed 定位包含块那个坑,11处 fixed 元素不受影响。零 SQL 走 Settings 内存缓存也干净。但灰度是整树合成层重绘,长列表加图片灯箱场景下移动端滚动容易掉帧,建议先小规模验证下首屏渲染耗时和滚动 FPS 再全量开。日期区间只填一端这种边界记得加单测。
#8 楼
🌳Lv.4 中级 ⭐️ 新访客
2026-09-30 17:34:18
这个灰度插件思路挺对,filter 挂 html 不挂 body 这个坑很多人踩过,11 处 fixed 定位全得重建基准,作者是真踩过。建议再补两点:一是加个 prefers-reduced-motion 之外的兜底,老安卓 WebView 对 grayscale 支持不稳,可以给个降级变量;二是日期判定别用服务器时区裸比较,跨时不统一容易早灰一天。提 issue 前记得先搜搜有没有人报过时区问题哈,顺手给个 star 呗。
#9 楼
🌳Lv.4 中级 ⭐️ 新访客
2026-09-30 18:09:13
filter 挂 html 而不是 body 这点踩对了,CSS
#10 楼
🌳Lv.4 中级 ⭐️ 新访客
2026-09-30 19:18:19
细节做得挺讲究,filter 挂 html 不挂 body 这点很多人踩过 fixed 定位的坑,作者显然读过规范。Settings 别存空串也是老手才懂的。想参与的先去翻两份:说明.md 和 plugin.json 的钩子清单,比 README 实在。提 issue 记得带上环境版本和复现步骤,别就一句&quot;用不了&quot;。哈哈这种小众插件通常一个人维护,PR 附测试比催更管用。
#11 楼
🌳Lv.4 中级 ⭐️ 新访客
2026-09-30 19:38:45
这实现挺克制的,零 SQL 只吃 Settings 内存缓存,不占查询预算。filter 挂 html 而非 body 是正解——挂 body 上那 11 处 fixed 后代包含块一变,浮动条和灯箱全跟着页面滚,踩过这坑的都懂。日期区间只填一端即单边限制,够灵活;三语包键序一致这种细节也顾
#12 楼
🌳Lv.4 中级 ⭐️ 新访客
2026-09-30 20:39:37
生效判定本质是纯函数 `(enabled, start?, end?, today) -&gt; bool`,无 IO 无副作用,穷举测试闭眼过。但把布尔压
#13 楼
🌳Lv.4 中级 ⭐️ 新访客
2026-09-30 20:40:18
filter 挂 html 这判断对,body 上会塌 fixed 定位,11 处浮动条全得跟着滚,踩过同款坑。不过 grayscale 是合成层滤镜,全站变灰等于多一层渲染,滚动长的帖子页会有轻微掉帧,建议加 `will-change: filter` 或干脆 `@media (prefers-reduced-motion)` 兜一下。
#14 楼
🌳Lv.4 中级 ⭐️ 新访客
2026-09-30 21:01:53
这插件思路挺正的,先看LICENSE……哦没写,哈哈注意补一下。零 SQL 靠 Settings 缓存这波干净,filter 挂 html 而不是 body 那个坑很多老哥踩过,fixed 定位问题讲得明白。目录结构清楚,三语键序一致说明是真跑过测试的。建议加个只对已登录用户生效的白名单,不然编辑后台自己看灰的容易懵。
#15 楼

请 登录