feat(v1.2.5): 优化首页滚动顺滑性、恢复整屏吸附并提升 Halo 要求至 2.27

- 首页固定模糊半径只改 opacity;Hero 离屏停绘;Navbar 滚动状态去重
- 恢复首屏整屏吸附:仅接管 0 ↔ 内容区顶部边界,scrollend + 600ms 兜底解锁
- CSS scroll-snap proximity 实际不吸附(缺页面顶部吸附点),已移除避免与 scrollTo 冲突
- 版本 1.2.5,requires >=2.27.0;源码仓库与问题反馈改为 git.nxxy335.top
- 更新 README / FAQ / 开发指南 / 更新日志,补充性能与发版说明
This commit is contained in:
nxxy335 committed 2026-11-10 21:22:37 +08:00
1 parent 6c612911b3
commit c0ad7331d4
19 files changed
+264 -120

No files matched your search

+4 -4
View File
@@ -6,7 +6,7 @@
## 环境要求 ## 环境要求
- Halo **2.26.0 及以上**(页面布局契约、微信读书页面均依赖 2.26 的新能力) - Halo **2.27.0 及以上**(页面布局契约、微信读书页面均依赖 2.27 的新能力)
- v1.2.0 起内置主题配色系统(纯 CSS 变量实现);更低版本的 Halo 请使用主题 v1.1.x - v1.2.0 起内置主题配色系统(纯 CSS 变量实现);更低版本的 Halo 请使用主题 v1.1.x
## 特点 ## 特点
@@ -15,7 +15,7 @@
2. **插件兼容性** — 适配 11 款常用 Halo 插件,安装即用;提供页面布局契约,插件前台页面自动复用主题外观 2. **插件兼容性** — 适配 11 款常用 Halo 插件,安装即用;提供页面布局契约,插件前台页面自动复用主题外观
3. **内置灯箱** — 主题内置 LightGallery 灯箱,支持 EXIF 信息展示;安装 lightgallery.js 插件后自动禁用 3. **内置灯箱** — 主题内置 LightGallery 灯箱,支持 EXIF 信息展示;安装 lightgallery.js 插件后自动禁用
4. **可配置性** — 17 个设置分组、122 个配置项,几乎所有模块可独立开关 4. **可配置性** — 17 个设置分组、122 个配置项,几乎所有模块可独立开关
5. **性能优化** — 服务端字数统计(API 扩展包)、图片懒加载与响应式缩略图、滚动吸附、CSS/HTML 压缩 5. **性能优化** — 服务端字数统计(API 扩展包)、图片懒加载与响应式缩略图、首屏整屏吸附(仅首屏边界接管,内容区保留原生滚动)、Hero 离屏停绘、CSS/HTML 压缩;详见[开发指南 - 性能与滚动顺滑](docs/customize/development.md)
6. **页面布局契约** — 提供 `templates/layout.html`,插件页面不再回退到 Halo 内置布局 6. **页面布局契约** — 提供 `templates/layout.html`,插件页面不再回退到 Halo 内置布局
## 预览 ## 预览
@@ -56,7 +56,7 @@
- Hero 首屏:明暗双背景图、大标题/副标题独立配色、一言(支持自动刷新与淡入淡出过渡)、下滑箭头平滑滚动 - Hero 首屏:明暗双背景图、大标题/副标题独立配色、一言(支持自动刷新与淡入淡出过渡)、下滑箭头平滑滚动
- 欢迎弹窗:基于访客 IP 地理定位的欢迎提示,支持 5 种位置、自定义 API 与会话去重开关 - 欢迎弹窗:基于访客 IP 地理定位的欢迎提示,支持 5 种位置、自定义 API 与会话去重开关
- 滚动吸附:首屏与内容区之间滚轮/触摸自动吸附 - 滚动吸附:首屏与内容区之间滚轮/触摸自动吸附,进入内容区后恢复原生自由滚动
- 文章卡片:可配置显示字数(依赖 API 扩展包)、阅读量、分类、标签、摘要行数 - 文章卡片:可配置显示字数(依赖 API 扩展包)、阅读量、分类、标签、摘要行数
### 文章 ### 文章
@@ -177,7 +177,7 @@ pnpm smoke
## 技术栈 ## 技术栈
- [Halo](https://halo.run) 2.26+ - [Halo](https://halo.run) 2.27+
- [Astro](https://astro.build) 6(构建输出 Thymeleaf 模板) - [Astro](https://astro.build) 6(构建输出 Thymeleaf 模板)
- [Thymeleaf](https://www.thymeleaf.org) 模板引擎 - [Thymeleaf](https://www.thymeleaf.org) 模板引擎
- [Sass](https://sass-lang.com/) 样式 - [Sass](https://sass-lang.com/) 样式
+1
View File
@@ -0,0 +1 @@
@charset "UTF-8";:root{--bg: #faf7f2;--bg-raised: #f3ede5;--bg-overlay: color-mix(in srgb, var(--bg) 85%, transparent);--ink: #2c2420;--ink-2: #7a6e64;--ink-3: #b5a99e;--rule: #e8e0d6;--accent: #d4764e;--accent-hover: #c4613a;--accent-bg: #fdf0e8;--mist-pink: #f0e4de;--sea-salt: #e8e0d6;--caramel: #8b6f5e;--glass-bg: color-mix(in srgb, var(--bg) 72%, transparent);--glass-border: color-mix(in srgb, var(--rule) 50%, transparent);--shadow-sm: 0 1px 3px color-mix(in srgb, var(--ink) 6%, transparent);--shadow-md: 0 4px 16px color-mix(in srgb, var(--ink) 8%, transparent);--shadow-lg: 0 8px 32px color-mix(in srgb, var(--ink) 12%, transparent);--shadow-glow: 0 0 40px color-mix(in srgb, var(--accent) 15%, transparent)}html.dark{--bg: #1e1c1a;--bg-raised: #292623;--bg-overlay: color-mix(in srgb, var(--bg) 85%, transparent);--ink: #e9e4dd;--ink-2: #aaa096;--ink-3: #7c7268;--rule: #3b362f;--accent: #e89a64;--accent-hover: #f2ad79;--accent-bg: #2c231b;--mist-pink: #2e2823;--sea-salt: #302b26;--caramel: #a38b7b;--glass-bg: color-mix(in srgb, var(--bg) 72%, transparent);--glass-border: color-mix(in srgb, var(--rule) 50%, transparent);--shadow-sm: 0 1px 3px rgba(0, 0, 0, .24);--shadow-md: 0 4px 16px rgba(0, 0, 0, .3);--shadow-lg: 0 8px 32px rgba(0, 0, 0, .4);--shadow-glow: 0 0 40px rgba(232, 154, 100, .12)}html[data-palette=matcha]{--bg: #f6f8f1;--bg-raised: #ecf1e3;--ink: #26302a;--ink-2: #6d7a6e;--ink-3: #a8b3a4;--rule: #dde5d4;--accent: #6f9d52;--accent-hover: #5d8943;--accent-bg: #ecf3e2;--mist-pink: #e7eddc;--sea-salt: #dfe8d3;--caramel: #7d8a68}html[data-palette=matcha].dark{--bg: #1b1f1a;--bg-raised: #252b22;--ink: #e6e9e0;--ink-2: #a3ab9c;--ink-3: #767e70;--rule: #363d33;--accent: #9cc47f;--accent-hover: #aed493;--accent-bg: #242c1f;--mist-pink: #262d22;--sea-salt: #282f24;--caramel: #98a387}html[data-palette=indigo]{--bg: #f5f7fa;--bg-raised: #eaeff5;--ink: #232a33;--ink-2: #66707d;--ink-3: #a3adb8;--rule: #dce2ea;--accent: #4a6fa5;--accent-hover: #3d5d8c;--accent-bg: #e9eff8;--mist-pink: #e2e8f0;--sea-salt: #dbe4ee;--caramel: #707c8e}html[data-palette=indigo].dark{--bg: #191d23;--bg-raised: #222831;--ink: #e4e7ec;--ink-2: #9aa4b1;--ink-3: #6d7683;--rule: #323a45;--accent: #7fa3d1;--accent-hover: #93b4dc;--accent-bg: #1f2632;--mist-pink: #232830;--sea-salt: #252b34;--caramel: #8b95a6}html[data-palette=sakura]{--bg: #faf5f6;--bg-raised: #f3eaec;--ink: #322a2c;--ink-2: #7d6e72;--ink-3: #b4a6aa;--rule: #ecdfe2;--accent: #d97387;--accent-hover: #c85d72;--accent-bg: #fbeef1;--mist-pink: #f2e2e6;--sea-salt: #eddfe3;--caramel: #96757e}html[data-palette=sakura].dark{--bg: #201b1c;--bg-raised: #2a2425;--ink: #ece5e6;--ink-2: #b0a4a7;--ink-3: #7f7276;--rule: #3c3436;--accent: #e89aab;--accent-hover: #f0aec0;--accent-bg: #2d2225;--mist-pink: #2f2729;--sea-salt: #31282a;--caramel: #a88d94}html[data-palette=violet]{--bg: #f7f5fa;--bg-raised: #eeeaf5;--ink: #2b2733;--ink-2: #6f6a7c;--ink-3: #aaa5b6;--rule: #e2ddeb;--accent: #8a6fc0;--accent-hover: #7759ae;--accent-bg: #f0eaf9;--mist-pink: #e8e2f0;--sea-salt: #e3dcee;--caramel: #7f7590}html[data-palette=violet].dark{--bg: #1c1a21;--bg-raised: #26222f;--ink: #e8e5ee;--ink-2: #a49eaf;--ink-3: #746e80;--rule: #373241;--accent: #b49ae0;--accent-hover: #c3ace8;--accent-bg: #262033;--mist-pink: #292434;--sea-salt: #2b2536;--caramel: #948caa}html[data-palette=sea]{--bg: #f3f8f7;--bg-raised: #e8f0ee;--ink: #21302d;--ink-2: #647872;--ink-3: #9fb1ac;--rule: #d9e6e3;--accent: #3d9c8f;--accent-hover: #2f8578;--accent-bg: #e5f3f0;--mist-pink: #dee9e6;--sea-salt: #d6e5e1;--caramel: #6c8680}html[data-palette=sea].dark{--bg: #181f1e;--bg-raised: #212a28;--ink: #e3eae8;--ink-2: #9aaba6;--ink-3: #6d7d79;--rule: #2f3b39;--accent: #6cc3b4;--accent-hover: #82d0c2;--accent-bg: #1d2a27;--mist-pink: #222c2a;--sea-salt: #242e2c;--caramel: #869894}html[data-palette=amber]{--bg: #faf7ef;--bg-raised: #f3eddc;--ink: #322b1e;--ink-2: #7d7360;--ink-3: #b5ab94;--rule: #eae1cb;--accent: #b97f1c;--accent-hover: #9f6a0e;--accent-bg: #f9efd8;--mist-pink: #f0e8d2;--sea-salt: #ebe2ca;--caramel: #96835c}html[data-palette=amber].dark{--bg: #201d16;--bg-raised: #2a261d;--ink: #ebe6d9;--ink-2: #b0a893;--ink-3: #7f7866;--rule: #3c3729;--accent: #e0a83e;--accent-hover: #ebba5c;--accent-bg: #2c251a;--mist-pink: #2f2a1e;--sea-salt: #312c20;--caramel: #a89a76}html[data-palette=plum]{--bg: #f9f4f5;--bg-raised: #f1e8ea;--ink: #2f2427;--ink-2: #796a6f;--ink-3: #b1a2a7;--rule: #e9dde0;--accent: #a4506b;--accent-hover: #8d3f59;--accent-bg: #f7e9ee;--mist-pink: #ecdee2;--sea-salt: #e5d8db;--caramel: #8d6f77}html[data-palette=plum].dark{--bg: #1f1a1b;--bg-raised: #292223;--ink: #eae2e4;--ink-2: #ada0a4;--ink-3: #7c6e73;--rule: #3a3133;--accent: #d98aa3;--accent-hover: #e39fb5;--accent-bg: #2b2023;--mist-pink: #2d2527;--sea-salt: #2f2729;--caramel: #a08890}html[data-palette=graphite]{--bg: #f7f7f6;--bg-raised: #eeedeb;--ink: #262626;--ink-2: #6e6e6c;--ink-3: #ababa8;--rule: #e0e0de;--accent: #3f3f3e;--accent-hover: #262626;--accentLine truncated
+1
View File
@@ -0,0 +1 @@
@charset "UTF-8";:root{--bg: #faf7f2;--bg-raised: #f3ede5;--bg-overlay: color-mix(in srgb, var(--bg) 85%, transparent);--ink: #2c2420;--ink-2: #7a6e64;--ink-3: #b5a99e;--rule: #e8e0d6;--accent: #d4764e;--accent-hover: #c4613a;--accent-bg: #fdf0e8;--mist-pink: #f0e4de;--sea-salt: #e8e0d6;--caramel: #8b6f5e;--glass-bg: color-mix(in srgb, var(--bg) 72%, transparent);--glass-border: color-mix(in srgb, var(--rule) 50%, transparent);--shadow-sm: 0 1px 3px color-mix(in srgb, var(--ink) 6%, transparent);--shadow-md: 0 4px 16px color-mix(in srgb, var(--ink) 8%, transparent);--shadow-lg: 0 8px 32px color-mix(in srgb, var(--ink) 12%, transparent);--shadow-glow: 0 0 40px color-mix(in srgb, var(--accent) 15%, transparent)}html.dark{--bg: #1e1c1a;--bg-raised: #292623;--bg-overlay: color-mix(in srgb, var(--bg) 85%, transparent);--ink: #e9e4dd;--ink-2: #aaa096;--ink-3: #7c7268;--rule: #3b362f;--accent: #e89a64;--accent-hover: #f2ad79;--accent-bg: #2c231b;--mist-pink: #2e2823;--sea-salt: #302b26;--caramel: #a38b7b;--glass-bg: color-mix(in srgb, var(--bg) 72%, transparent);--glass-border: color-mix(in srgb, var(--rule) 50%, transparent);--shadow-sm: 0 1px 3px rgba(0, 0, 0, .24);--shadow-md: 0 4px 16px rgba(0, 0, 0, .3);--shadow-lg: 0 8px 32px rgba(0, 0, 0, .4);--shadow-glow: 0 0 40px rgba(232, 154, 100, .12)}html[data-palette=matcha]{--bg: #f6f8f1;--bg-raised: #ecf1e3;--ink: #26302a;--ink-2: #6d7a6e;--ink-3: #a8b3a4;--rule: #dde5d4;--accent: #6f9d52;--accent-hover: #5d8943;--accent-bg: #ecf3e2;--mist-pink: #e7eddc;--sea-salt: #dfe8d3;--caramel: #7d8a68}html[data-palette=matcha].dark{--bg: #1b1f1a;--bg-raised: #252b22;--ink: #e6e9e0;--ink-2: #a3ab9c;--ink-3: #767e70;--rule: #363d33;--accent: #9cc47f;--accent-hover: #aed493;--accent-bg: #242c1f;--mist-pink: #262d22;--sea-salt: #282f24;--caramel: #98a387}html[data-palette=indigo]{--bg: #f5f7fa;--bg-raised: #eaeff5;--ink: #232a33;--ink-2: #66707d;--ink-3: #a3adb8;--rule: #dce2ea;--accent: #4a6fa5;--accent-hover: #3d5d8c;--accent-bg: #e9eff8;--mist-pink: #e2e8f0;--sea-salt: #dbe4ee;--caramel: #707c8e}html[data-palette=indigo].dark{--bg: #191d23;--bg-raised: #222831;--ink: #e4e7ec;--ink-2: #9aa4b1;--ink-3: #6d7683;--rule: #323a45;--accent: #7fa3d1;--accent-hover: #93b4dc;--accent-bg: #1f2632;--mist-pink: #232830;--sea-salt: #252b34;--caramel: #8b95a6}html[data-palette=sakura]{--bg: #faf5f6;--bg-raised: #f3eaec;--ink: #322a2c;--ink-2: #7d6e72;--ink-3: #b4a6aa;--rule: #ecdfe2;--accent: #d97387;--accent-hover: #c85d72;--accent-bg: #fbeef1;--mist-pink: #f2e2e6;--sea-salt: #eddfe3;--caramel: #96757e}html[data-palette=sakura].dark{--bg: #201b1c;--bg-raised: #2a2425;--ink: #ece5e6;--ink-2: #b0a4a7;--ink-3: #7f7276;--rule: #3c3436;--accent: #e89aab;--accent-hover: #f0aec0;--accent-bg: #2d2225;--mist-pink: #2f2729;--sea-salt: #31282a;--caramel: #a88d94}html[data-palette=violet]{--bg: #f7f5fa;--bg-raised: #eeeaf5;--ink: #2b2733;--ink-2: #6f6a7c;--ink-3: #aaa5b6;--rule: #e2ddeb;--accent: #8a6fc0;--accent-hover: #7759ae;--accent-bg: #f0eaf9;--mist-pink: #e8e2f0;--sea-salt: #e3dcee;--caramel: #7f7590}html[data-palette=violet].dark{--bg: #1c1a21;--bg-raised: #26222f;--ink: #e8e5ee;--ink-2: #a49eaf;--ink-3: #746e80;--rule: #373241;--accent: #b49ae0;--accent-hover: #c3ace8;--accent-bg: #262033;--mist-pink: #292434;--sea-salt: #2b2536;--caramel: #948caa}html[data-palette=sea]{--bg: #f3f8f7;--bg-raised: #e8f0ee;--ink: #21302d;--ink-2: #647872;--ink-3: #9fb1ac;--rule: #d9e6e3;--accent: #3d9c8f;--accent-hover: #2f8578;--accent-bg: #e5f3f0;--mist-pink: #dee9e6;--sea-salt: #d6e5e1;--caramel: #6c8680}html[data-palette=sea].dark{--bg: #181f1e;--bg-raised: #212a28;--ink: #e3eae8;--ink-2: #9aaba6;--ink-3: #6d7d79;--rule: #2f3b39;--accent: #6cc3b4;--accent-hover: #82d0c2;--accent-bg: #1d2a27;--mist-pink: #222c2a;--sea-salt: #242e2c;--caramel: #869894}html[data-palette=amber]{--bg: #faf7ef;--bg-raised: #f3eddc;--ink: #322b1e;--ink-2: #7d7360;--ink-3: #b5ab94;--rule: #eae1cb;--accent: #b97f1c;--accent-hover: #9f6a0e;--accent-bg: #f9efd8;--mist-pink: #f0e8d2;--sea-salt: #ebe2ca;--caramel: #96835c}html[data-palette=amber].dark{--bg: #201d16;--bg-raised: #2a261d;--ink: #ebe6d9;--ink-2: #b0a893;--ink-3: #7f7866;--rule: #3c3729;--accent: #e0a83e;--accent-hover: #ebba5c;--accent-bg: #2c251a;--mist-pink: #2f2a1e;--sea-salt: #312c20;--caramel: #a89a76}html[data-palette=plum]{--bg: #f9f4f5;--bg-raised: #f1e8ea;--ink: #2f2427;--ink-2: #796a6f;--ink-3: #b1a2a7;--rule: #e9dde0;--accent: #a4506b;--accent-hover: #8d3f59;--accent-bg: #f7e9ee;--mist-pink: #ecdee2;--sea-salt: #e5d8db;--caramel: #8d6f77}html[data-palette=plum].dark{--bg: #1f1a1b;--bg-raised: #292223;--ink: #eae2e4;--ink-2: #ada0a4;--ink-3: #7c6e73;--rule: #3a3133;--accent: #d98aa3;--accent-hover: #e39fb5;--accent-bg: #2b2023;--mist-pink: #2d2527;--sea-salt: #2f2729;--caramel: #a08890}html[data-palette=graphite]{--bg: #f7f7f6;--bg-raised: #eeedeb;--ink: #262626;--ink-2: #6e6e6c;--ink-3: #ababa8;--rule: #e0e0de;--accent: #3f3f3e;--accent-hover: #262626;--accentLine truncated
+1
View File
@@ -0,0 +1 @@
.hero[data-astro-cid-nlow4r3u]{position:fixed;inset:0;z-index:-1;display:flex;align-items:center;justify-content:center;overflow:hidden;background:linear-gradient(160deg,var(--bg) 0%,color-mix(in srgb,var(--bg) 85%,var(--accent) 15%) 40%,color-mix(in srgb,var(--bg) 90%,var(--mist-pink) 10%) 70%,var(--bg) 100%);width:100%}.hero__bg[data-astro-cid-nlow4r3u]{position:absolute;inset:0;pointer-events:none}.hero__blur-overlay[data-astro-cid-nlow4r3u]{position:absolute;inset:0;z-index:1;background:color-mix(in srgb,var(--bg) 26%,transparent);backdrop-filter:blur(12px);-webkit-backdrop-filter:blur(12px);opacity:0;will-change:opacity;pointer-events:none}.hero[data-astro-cid-nlow4r3u].wi-hero--offscreen{visibility:hidden}.hero[data-astro-cid-nlow4r3u].wi-hero--offscreen .hero__orb[data-astro-cid-nlow4r3u],.hero[data-astro-cid-nlow4r3u].wi-hero--offscreen .hero__scroll-indicator[data-astro-cid-nlow4r3u]{animation-play-state:paused}.hero__orb[data-astro-cid-nlow4r3u]{position:absolute;border-radius:50%;filter:blur(80px);animation:breathe 5s ease-in-out infinite}.hero__orb--1[data-astro-cid-nlow4r3u]{width:420px;height:420px;top:-10%;right:-5%;background:radial-gradient(circle,rgba(212,118,78,.4) 0%,transparent 70%);animation-delay:0s;animation-duration:5s}.hero__orb--2[data-astro-cid-nlow4r3u]{width:350px;height:350px;bottom:-8%;left:-5%;background:radial-gradient(circle,rgba(220,140,160,.3) 0%,transparent 70%);animation-delay:-1.8s;animation-duration:6s}.hero__orb--3[data-astro-cid-nlow4r3u]{width:300px;height:300px;top:40%;left:50%;transform:translate(-50%);background:radial-gradient(circle,rgba(180,150,200,.2) 0%,transparent 70%);animation-delay:-3.2s;animation-duration:4.5s}.hero__bg-image[data-astro-cid-nlow4r3u]{position:absolute;inset:0;background-size:cover;background-position:center;opacity:1}.hero__bg-image--dark[data-astro-cid-nlow4r3u],html.dark .hero__bg-image--light[data-astro-cid-nlow4r3u]{display:none}html.dark .hero__bg-image--dark[data-astro-cid-nlow4r3u]{display:block}.hero__content[data-astro-cid-nlow4r3u]{position:relative;z-index:2;display:flex;flex-direction:column;align-items:center;text-align:center;padding:var(--space-xl);gap:var(--space-lg);transition:opacity .4s ease,transform .4s ease}.hero__title[data-astro-cid-nlow4r3u]{font-family:var(--font-sans);font-size:clamp(3rem,8vw,6rem);font-weight:700;letter-spacing:-.03em;color:var(--hero-title-text, var(--ink));line-height:var(--leading-tight)}.hero__subtitle[data-astro-cid-nlow4r3u]{font-family:var(--font-body);font-size:var(--text-xl);color:var(--hero-subtitle-text, var(--ink-2));letter-spacing:.05em;line-height:var(--leading-normal)}.hero__description[data-astro-cid-nlow4r3u]{font-size:var(--text-lg);color:var(--hero-quote-text, var(--ink-2));line-height:var(--leading-relaxed);max-width:560px;text-align:center;transition:opacity .3s ease}.hero--fx-shadow[data-astro-cid-nlow4r3u] .hero__title[data-astro-cid-nlow4r3u],.hero--fx-shadow[data-astro-cid-nlow4r3u] .hero__subtitle[data-astro-cid-nlow4r3u],.hero--fx-shadow[data-astro-cid-nlow4r3u] .hero__description[data-astro-cid-nlow4r3u]{text-shadow:0 1px 2px rgba(0,0,0,.22),0 2px 10px rgba(0,0,0,.16)}html.dark .hero--fx-shadow[data-astro-cid-nlow4r3u] .hero__title[data-astro-cid-nlow4r3u],html.dark .hero--fx-shadow[data-astro-cid-nlow4r3u] .hero__subtitle[data-astro-cid-nlow4r3u],html.dark .hero--fx-shadow[data-astro-cid-nlow4r3u] .hero__description[data-astro-cid-nlow4r3u]{text-shadow:0 1px 3px rgba(0,0,0,.6),0 2px 12px rgba(0,0,0,.4)}.hero--fx-glow[data-astro-cid-nlow4r3u] .hero__title[data-astro-cid-nlow4r3u],.hero--fx-glow[data-astro-cid-nlow4r3u] .hero__subtitle[data-astro-cid-nlow4r3u],.hero--fx-glow[data-astro-cid-nlow4r3u] .hero__description[data-astro-cid-nlow4r3u]{text-shadow:0 0 10px rgba(255,255,255,.85),0 0 26px rgba(255,255,255,.5)}html.dark .hero--fx-glow[data-astro-cid-nlow4r3u] .hero__title[data-astro-cid-nlow4r3u],html.dark .hero--fx-glow[data-astro-cid-nlow4r3u] .hero__subtitle[data-astro-cid-nlow4r3u],html.dark .hero--fx-glow[data-astro-cid-nlow4r3u] .hero__description[data-astro-cid-nlow4r3u]{text-shadow:0 0 8px rgba(255,255,255,.45),0 0 24px rgba(255,255,255,.22)}.hero--fx-scrim[data-astro-cid-nlow4r3u] .hero__content[data-astro-cid-nlow4r3u]{background:#ffffff73;backdrop-filter:blur(10px);-webkit-backdrop-filter:blur(10px);border-radius:24px;padding:var(--space-2xl)}html.dark .hero--fx-scrim[data-astro-cid-nlow4r3u] .hero__content[data-astro-cid-nlow4r3u]{background:#0006}.hero--fx-gradient[data-astro-cid-nlow4r3u] .hero__content[data-astro-cid-nlow4r3u]{background:linear-gradient(135deg,#ffffff8c,#ffffff2e);border-radius:24px;padding:var(--space-2xl)}html.dark .hero--fx-gradient[data-astro-cid-nlow4r3u] .hero__content[data-astro-cid-nlow4r3u]{background:linear-gradient(135deg,#00000080,#00000029)}.hero--fx-frame[data-astro-cid-nlow4r3u] .hero__content[data-astro-cid-nlow4r3u]{border:1px solid color-mix(in srgb,var(--ink) 22%,transparent);background:color-mix(in srgb,var(Line truncated
+1 -1
View File
@@ -80,7 +80,7 @@ export default defineConfig({
], ],
socialLinks: [ socialLinks: [
{ icon: 'github', link: 'https://github.com/nxxy335top/halo-theme-WarmIsland' } { icon: 'github', link: 'https://git.nxxy335.top/nxxy335/halo-theme-WarmIsland', ariaLabel: '源码仓库' }
], ],
search: { search: {
+25
View File
@@ -1,5 +1,30 @@
# 更新日志 # 更新日志
## v1.2.5
### 兼容性与元数据
- ⬆️ Halo 版本要求提升至 **2.27.0+**(页面布局契约、微信读书页面依赖 2.27 的能力)
- 🔗 源码仓库与问题反馈地址统一为 `https://git.nxxy335.top/nxxy335/halo-theme-WarmIsland`
- 📚 完善安装、开发、FAQ 文档,修正过时的技术栈与构建打包说明
### 性能优化
- 🚀 **首页滚动顺滑性**:移除逐帧改写全屏 `backdrop-filter` 模糊半径的逻辑。
旧实现让 Chromium 每个滚动帧都重做一次全屏背景采样 + 高斯模糊,是首页掉帧主因;
现改为固定模糊半径 + 只调整 `opacity`(合成层操作),并缓存进度值避免重复写 DOM
- 🚀 **首屏离屏即停**:滚过首屏后 Hero 已被不透明内容区完全盖住,此时隐藏 Hero
并暂停光球动画,长页面后半段不再有额外绘制
- 🚀 **首屏整屏吸附回归(顺滑版)**:v1.2.5 初版改用 CSS `scroll-snap: y proximity` 后吸附
实际失效——内容区顶部是唯一吸附点、页面顶部 `0` 没有吸附点,`proximity` 几乎不触发;
换成 `mandatory` 又会把长内容反复拉回内容区顶部。现改为**边界吸附**:只在
`0 ↔ 内容区顶部` 接管一次滚轮 / 触摸,进入内容区后完全交给原生滚动惯性;解锁用
`scrollend` + 600ms 兜底(原为固定 800ms 输入锁),已在目标位置时不再接管
- 🚀 **导航栏滚动状态去重**:只在状态真正变化时写 class,不再每帧触发样式重算
- ♿ 补充 `prefers-reduced-motion` 支持:减少动态效果时关闭平滑滚动与光球 / 指示箭头动画,
吸附改为即时定位(不再关闭吸附)
- 🖼️ 首页文章列表继续使用 `content-visibility: auto` 跳过视口外渲染,图片保持懒加载
## v1.2.4 ## v1.2.4
### 问题修复 ### 问题修复
+1 -1
View File
@@ -80,7 +80,7 @@ ICP 备案号通常需要链接到工信部的备案查询页面。主题会自
footer_socials: footer_socials:
- platform: GitHub - platform: GitHub
icon: fa-brands fa-github icon: fa-brands fa-github
url: https://github.com/your-username url: https://git.nxxy335.top/nxxy335/halo-theme-WarmIsland
- platform: 微博 - platform: 微博
icon: fa-brands fa-weibo icon: fa-brands fa-weibo
url: https://weibo.com/your-username url: https://weibo.com/your-username
+41 -11
View File
@@ -27,8 +27,8 @@ git --version # git version 2.x.x
## 克隆与安装 ## 克隆与安装
```bash ```bash
git clone https://github.com/your-username/WarmIsland.git git clone https://git.nxxy335.top/nxxy335/halo-theme-WarmIsland.git
cd WarmIsland cd halo-theme-WarmIsland
pnpm install pnpm install
``` ```
@@ -66,6 +66,36 @@ ZIP 根目录直接放 `theme.yaml` / `settings.yaml` / `templates/`,不要额
> `Template index was not found.` 并让首页 404。现在打包脚本会先校验 > `Template index was not found.` 并让首页 404。现在打包脚本会先校验
> `index.html` 等模板、资源目录与版本号,不满足即报错退出。 > `index.html` 等模板、资源目录与版本号,不满足即报错退出。
## 性能与滚动顺滑
首页滚动曾经卡顿,根因不是 DOM 数量,而是「每帧高成本绘制 + 劫持滚动输入」:
| 问题 | 原因 | 现在的做法 |
|------|------|------------|
| 全屏模糊逐帧重算 | 滚动回调每帧改写 `backdrop-filter: blur(Npx)`,Chromium 每帧重做一次全屏背景采样 + 高斯模糊 | 模糊半径固定,滚动只改整层 `opacity`(合成层操作) |
| 吸附吞掉滚动惯性 | 旧实现在 `wheel` / `touchend` 上 `preventDefault()` 并固定锁 800ms | 保留整屏吸附,但只在 `0 ↔ 内容区顶部` 边界接管一次;进入内容区后完全交给原生滚动。解锁用 `scrollend` + 600ms 兜底,已在目标位置时不再接管 |
| Hero 一直绘制 | 固定全屏 Hero 的光球动画与模糊层在滚过首屏后仍在跑 | 内容区盖满视口后加 `wi-hero--offscreen` 隐藏并暂停动画 |
| 状态反复写入 | 导航栏每个滚动帧都 `classList.add/remove` | 缓存状态,只在变化时写 |
**为什么吸附不用 CSS `scroll-snap`**:v1.2.5 初版试过 `html { scroll-snap-type: y proximity }`
加内容区 `scroll-snap-align: start`,实际几乎不吸附——只有内容区顶部一个吸附点,页面顶部
`0` 没有吸附点,`proximity` 只在滚动结束位置接近吸附点时才生效;换成 `mandatory` 则会在
长内容的每次滚动后把页面拉回内容区顶部,正文无法阅读。`prefers-reduced-motion` 下 CSS 方案
还会直接关闭吸附。因此改为脚本的边界吸附,CSS 侧不再设置 `scroll-snap`,避免与 `scrollTo`
平滑动画抢夺滚动位置。
涉及文件:`src/pages/index.astro`(滚动渐隐 + 边界吸附脚本)、`src/components/HeroSection.astro`(模糊层与离屏规则)、
`src/components/Navbar.astro`(状态去重)、`src/styles/main.scss`(平滑滚动与 reduced-motion)。
**可调项**:后台「总体样式 → 启用动效」可一键关闭滚动渐入,「Hero 首屏」可关闭整个 Hero;
系统开启「减少动态效果」(`prefers-reduced-motion: reduce`)时,平滑滚动与光球 / 箭头动画停用,
吸附改为即时定位(仍然可用)。长列表使用 `content-visibility: auto` 跳过视口外渲染,
图片统一 `loading="lazy" decoding="async"`。
> 改动滚动逻辑后建议用浏览器 Performance 面板录制一次首页滚动:滚动帧里不应出现大面积
> `Paint` / `Update Layer Tree`;也不要在 `scroll` 回调里读取 `getBoundingClientRect()` 之类
> 会强制同步布局的属性(当前实现只在初始化 / resize 时测量内容区位置)。
## 项目结构 ## 项目结构
``` ```
@@ -130,26 +160,26 @@ WarmIsland/
| 技术 | 版本 | 用途 | | 技术 | 版本 | 用途 |
|------|------|------| |------|------|------|
| [Astro](https://astro.build) | 5.x | 静态站点生成,组件开发 | | [Halo](https://www.halo.run/) | 2.27+ | 博客运行时,提供 Finder API 与主题渲染 |
| [vite-plugin-halo-theme](https://github.com/halo-sigs/vite-plugin-halo-theme) | - | Halo 主题开发集成 | | [Astro](https://astro.build) | 6.x | 组件开发,构建输出静态 `.html` 模板 |
| [Thymeleaf](https://www.thymeleaf.org/) | - | Halo 服务端模板引擎 | | [Thymeleaf](https://www.thymeleaf.org/) | - | Halo 服务端模板引擎(源码里直接写 `th:*`) |
### 前端技术 ### 前端技术
| 技术 | 用途 | | 技术 | 用途 |
|------|------| |------|------|
| [Vue 3](https://vuejs.org/) | 交互组件(目录、点赞、无限滚动等) | | [SCSS](https://sass-lang.com/) | 样式预处理,CSS 变量驱动的配色系统 |
| [SCSS](https://sass-lang.com/) | 样式预处理 | | 原生 JavaScript | 滚动、菜单、主题切换、无限滚动等交互,无前端框架运行时 |
| [Prism.js](https://prismjs.com/) | 代码高亮 | | [LightGallery](https://www.lightgalleryjs.com/) | 图片灯箱(可选) |
| [LightGallery](https://www.lightgalleryjs.com/) | 图片灯箱 | | Halo 插件生态 | 评论、代码高亮、服务端字数统计等由 Halo 与可选插件提供 |
### 构建工具 ### 构建工具
| 技术 | 用途 | | 技术 | 用途 |
|------|------| |------|------|
| [Vite](https://vitejs.dev/) | 构建打包 | | [Vite](https://vitejs.dev/)(Astro 内置) | 资源打包与内容哈希 |
| [pnpm](https://pnpm.io/) | 包管理 | | [pnpm](https://pnpm.io/) | 包管理 |
| [TypeScript](https://www.typescriptlang.org/) | 类型安全 | | [TypeScript](https://www.typescriptlang.org/) | 类型定义(`env.d.ts` / `pages.ts`) |
## 模板语法 ## 模板语法
+2 -2
View File
@@ -24,7 +24,7 @@ ZIP 里缺少 `templates/index.html` 时,渲染首页找不到 `index` 模板
检查以下几点: 检查以下几点:
1. 确认 Halo 版本 >= 2.26.0 1. 确认 Halo 版本 >= 2.27.0
2. 确认主题已启用 2. 确认主题已启用
3. 清除浏览器缓存后重试(Ctrl+Shift+R 强制刷新) 3. 清除浏览器缓存后重试(Ctrl+Shift+R 强制刷新)
4. 检查 Halo 后台日志是否有报错 4. 检查 Halo 后台日志是否有报错
@@ -170,7 +170,7 @@ v1.2.1 起安装包会保留各版本的哈希资源(v1.1.7 之后全部覆盖
### 如何报告 Bug? ### 如何报告 Bug?
请在 [GitHub Issues](https://github.com/sunny-335/halo-theme-WarmIsland/issues) 中提交,包含以下信息: 请在 [源码仓库 Issues](https://git.nxxy335.top/nxxy335/halo-theme-WarmIsland/issues) 中提交,包含以下信息:
- Halo 版本 - Halo 版本
- 主题版本 - 主题版本
+1 -1
View File
@@ -27,7 +27,7 @@ WarmIsland(暖屿)是一款为 [Halo](https://halo.run) 打造的博客主
### 方式二:本地上传安装 ### 方式二:本地上传安装
1. 从 [GitHub Releases](https://github.com/nxxy335top/halo-theme-WarmIsland/releases) 下载最新版本的 `.zip` 安装包 1. 从 [源码仓库](https://git.nxxy335.top/nxxy335/halo-theme-WarmIsland) 的 Releases / 发行版页面下载最新版本的 `.zip` 安装包
2. 登录 Halo 后台管理控制台 2. 登录 Halo 后台管理控制台
3. 进入 **主题** → **安装** → 选择 **本地上传** 3. 进入 **主题** → **安装** → 选择 **本地上传**
4. 上传下载的 `.zip` 文件 4. 上传下载的 `.zip` 文件
+2 -2
View File
@@ -13,8 +13,8 @@ hero:
text: 配置项 text: 配置项
link: /config/basic link: /config/basic
- theme: alt - theme: alt
text: GitHub text: 源码仓库
link: https://github.com/nxxy335top/halo-theme-WarmIsland link: https://git.nxxy335.top/nxxy335/halo-theme-WarmIsland
features: features:
- title: 🎨 精致视觉 - title: 🎨 精致视觉
+1 -1
View File
@@ -1,5 +1,5 @@
{ {
"version": "1.2.4", "version": "1.2.5",
"type": "module", "type": "module",
"engines": { "engines": {
"node": ">=22.12.0" "node": ">=22.12.0"
+30 -3
View File
@@ -229,12 +229,28 @@
position: absolute; position: absolute;
inset: 0; inset: 0;
z-index: 1; z-index: 1;
backdrop-filter: blur(0px); /* 模糊半径固定,滚动时只改这层的 opacity(合成层操作)。
-webkit-backdrop-filter: blur(0px); 逐帧改写 backdrop-filter 的 px 值会让浏览器每帧重做全屏背景采样,
will-change: backdrop-filter; 是首页滚动掉帧的主因;固定值 + opacity 可复用已合成的结果。 */
background: color-mix(in srgb, var(--bg) 26%, transparent);
backdrop-filter: blur(12px);
-webkit-backdrop-filter: blur(12px);
opacity: 0;
will-change: opacity;
pointer-events: none; pointer-events: none;
} }
/* 滚过首屏后 Hero 已被不透明内容区完全盖住,隐藏它可停掉
光球动画与模糊层的持续绘制(由首页滚动脚本切换 wi-hero--offscreen) */
.hero.wi-hero--offscreen {
visibility: hidden;
}
.hero.wi-hero--offscreen .hero__orb,
.hero.wi-hero--offscreen .hero__scroll-indicator {
animation-play-state: paused;
}
.hero__orb { .hero__orb {
position: absolute; position: absolute;
border-radius: 50%; border-radius: 50%;
@@ -454,6 +470,17 @@
animation: none; animation: none;
} }
@media (prefers-reduced-motion: reduce) {
.hero__orb,
.hero__scroll-indicator {
animation: none;
}
.hero__content {
transition: none;
}
}
html.dark .hero { html.dark .hero {
background: linear-gradient( background: linear-gradient(
160deg, 160deg,
+24 -19
View File
@@ -97,29 +97,34 @@ import ThemeSwitcher from "./ThemeSwitcher.astro";
const navbar = document.getElementById("wi-navbar"); const navbar = document.getElementById("wi-navbar");
let lastScrollY = window.scrollY; let lastScrollY = window.scrollY;
let ticking = false; let ticking = false;
/* 缓存状态:只在状态真正变化时才写 class,避免每个滚动帧都触发样式重算 */
let scrolled = null;
let hidden = null;
function onScroll() { function onScroll() {
if (!ticking) { if (ticking) return;
requestAnimationFrame(() => {
const currentScrollY = window.scrollY;
if (currentScrollY > 20) {
navbar?.classList.add("wi-navbar--scrolled");
} else {
navbar?.classList.remove("wi-navbar--scrolled");
}
if (currentScrollY > lastScrollY && currentScrollY > 80) {
navbar?.classList.add("wi-navbar--hidden");
} else if (currentScrollY < lastScrollY) {
navbar?.classList.remove("wi-navbar--hidden");
}
lastScrollY = currentScrollY;
ticking = false;
});
ticking = true; ticking = true;
requestAnimationFrame(() => {
ticking = false;
const currentScrollY = window.scrollY;
const nextScrolled = currentScrollY > 20;
const nextHidden =
currentScrollY > lastScrollY && currentScrollY > 80
? true
: currentScrollY < lastScrollY
? false
: hidden;
if (nextScrolled !== scrolled) {
scrolled = nextScrolled;
navbar?.classList.toggle("wi-navbar--scrolled", scrolled);
} }
if (nextHidden !== hidden) {
hidden = nextHidden;
navbar?.classList.toggle("wi-navbar--hidden", !!hidden);
}
lastScrollY = currentScrollY;
});
} }
window.addEventListener("scroll", onScroll, { passive: true }); window.addEventListener("scroll", onScroll, { passive: true });
-1
View File
@@ -21,7 +21,6 @@ const fragmentAttr = layoutFragment ? "html (head, content)" : undefined;
<!doctype html> <!doctype html>
<html <html
lang="zh-CN" lang="zh-CN"
class={home ? 'wi-home-snap' : ''}
style="overflow-x:hidden" style="overflow-x:hidden"
th:classappend="${theme.config?.animation?.animation_enabled == false} ? 'wi-no-animations'" th:classappend="${theme.config?.animation?.animation_enabled == false} ? 'wi-no-animations'"
th:attr="data-palette=${theme.config?.style?.color_palette ?: 'warm-island'}" th:attr="data-palette=${theme.config?.style?.color_palette ?: 'warm-island'}"
+115 -69
View File
@@ -277,106 +277,152 @@ import WelcomePopup from "../components/WelcomePopup.astro";
if (!hero || !blurOverlay) return; if (!hero || !blurOverlay) return;
var maxBlur = 20; /* 首屏滚动渐隐只写 opacity / transform 这类合成属性。
var triggerDistance = 300; 旧实现每帧改写全屏 backdrop-filter 的模糊半径,Chromium 每帧都要
重做一次全屏背景采样 + 高斯模糊,是首页滚动掉帧的主因。 */
var fadeStart = 200; var fadeStart = 200;
var fadeEnd = 500; var fadeEnd = 500;
var ticking = false; var ticking = false;
var lastProgress = -1;
var lastHidden = null;
var content = document.querySelector(".wi-home-content--with-hero");
var contentTop = window.innerHeight;
var reduceMotion =
window.matchMedia && window.matchMedia("(prefers-reduced-motion: reduce)").matches;
/* 内容区顶部位置只在初始化 / resize 时测量,避免滚动帧里读布局 */
function measure() {
if (content) {
contentTop = content.getBoundingClientRect().top + (window.scrollY || window.pageYOffset);
}
}
function update() {
ticking = false;
var scrollY = window.scrollY || window.pageYOffset;
/* 滚过首屏后 Hero 已被不透明内容区完全盖住,直接隐藏:
光球动画、模糊层都不再绘制,长页面后半段零额外开销。 */
var hidden = scrollY >= contentTop - 1;
if (hidden !== lastHidden) {
lastHidden = hidden;
hero.classList.toggle("wi-hero--offscreen", hidden);
}
if (hidden) return;
var progress = Math.max(0, Math.min(1, (scrollY - fadeStart) / (fadeEnd - fadeStart)));
if (progress === lastProgress) return;
lastProgress = progress;
/* 模糊半径固定,只调整整层不透明度:GPU 合成,无需重新采样背景 */
blurOverlay.style.opacity = String(Math.min(1, scrollY / 300));
if (heroContent) {
heroContent.style.opacity = String(1 - progress);
if (!reduceMotion) {
heroContent.style.transform = "translateY(" + (-20 * progress) + "px)";
}
}
if (scrollIndicator) {
scrollIndicator.style.opacity = String(1 - progress);
}
}
function onScroll() { function onScroll() {
if (ticking) return; if (ticking) return;
ticking = true; ticking = true;
requestAnimationFrame(function () { requestAnimationFrame(update);
ticking = false;
var scrollY = window.scrollY || window.pageYOffset;
var currentBlur = Math.min(scrollY / triggerDistance * maxBlur, maxBlur);
blurOverlay.style.backdropFilter = "blur(" + currentBlur + "px)";
blurOverlay.style.webkitBackdropFilter = "blur(" + currentBlur + "px)";
if (heroContent) {
var progress = Math.max(0, Math.min(1, (scrollY - fadeStart) / (fadeEnd - fadeStart)));
heroContent.style.opacity = String(1 - progress);
heroContent.style.transform = "translateY(" + (-20 * progress) + "px)";
} }
if (scrollIndicator) { function onResize() {
var indicatorProgress = Math.max(0, Math.min(1, (scrollY - fadeStart) / (fadeEnd - fadeStart))); measure();
scrollIndicator.style.opacity = String(1 - indicatorProgress); onScroll();
}
});
} }
window.addEventListener("scroll", onScroll, { passive: true }); window.addEventListener("scroll", onScroll, { passive: true });
onScroll(); window.addEventListener("resize", onResize, { passive: true });
})(); measure();
</script> update();
<script is:inline th:inline="none"> /* ---- 首屏整屏吸附:只接管 0 ↔ 内容区顶部这一个边界 ----
(function () { 纯 CSS scroll-snap 表达不了这个行为:内容区顶部是唯一吸附点,页面
var hero = document.getElementById("wi-hero"); 顶部 0 没有吸附点,proximity 几乎不会触发;改成 mandatory 又会在
var content = document.querySelector(".wi-home-content--with-hero"); 长内容的每一次滚动后把页面拉回内容区顶部,导致正文无法阅读。
if (!hero || !content) return; 因此只在首屏边界接管一次滚轮 / 触摸,进入内容区后完全交给原生滚动。
掉帧主因(逐帧改写 backdrop-filter)已在上面的 update() 中修复,
这里只做一次 scrollTo,不参与滚动帧绘制。 */
var snapping = false;
var snapTimer = null;
var SNAP_EDGE = 8;
var isSnapping = false; function endSnap() {
var heroHeight = window.innerHeight; snapping = false;
if (snapTimer) {
clearTimeout(snapTimer);
snapTimer = null;
}
window.removeEventListener("scrollend", endSnap);
}
window.addEventListener("resize", function () { function snapTo(top) {
heroHeight = window.innerHeight; if (snapping) return;
}, { passive: true }); snapping = true;
window.scrollTo({ top: top, behavior: reduceMotion ? "auto" : "smooth" });
function snapTo(targetY) { /* scrollend 精确解锁;短定时器兜底,避免不支持 scrollend 时锁死输入 */
if (isSnapping) return;
isSnapping = true;
window.scrollTo({ top: targetY, behavior: "smooth" });
var timer = setTimeout(function () {
isSnapping = false;
}, 800);
if ("onscrollend" in window) { if ("onscrollend" in window) {
window.addEventListener( window.addEventListener("scrollend", endSnap, { once: true });
"scrollend",
function () {
clearTimeout(timer);
isSnapping = false;
},
{ once: true }
);
} }
snapTimer = setTimeout(endSnap, reduceMotion ? 100 : 600);
} }
window.addEventListener("wheel", function (e) { window.addEventListener(
if (isSnapping) { "wheel",
function (e) {
if (!content || e.ctrlKey || e.defaultPrevented) return;
var y = window.scrollY || window.pageYOffset;
/* 内容区内部不再干预,保留原生惯性与长文阅读体验 */
if (y > contentTop + SNAP_EDGE) return;
if (snapping) {
e.preventDefault(); e.preventDefault();
return; return;
} }
var y = window.scrollY; if (Math.abs(e.deltaY) < 2) return;
if (y < heroHeight && e.deltaY > 0) { var target = e.deltaY > 0 ? contentTop : 0;
if (Math.abs(target - y) < 2) return;
e.preventDefault(); e.preventDefault();
snapTo(heroHeight); snapTo(target);
} else if (y > 0 && y <= heroHeight && e.deltaY < 0) { },
e.preventDefault(); { passive: false }
snapTo(0); );
}
}, { passive: false });
var touchStartY = 0; var touchStartY = 0;
window.addEventListener("touchstart", function (e) { window.addEventListener(
"touchstart",
function (e) {
touchStartY = e.touches[0].clientY; touchStartY = e.touches[0].clientY;
}, { passive: true }); },
{ passive: true }
);
window.addEventListener("touchend", function (e) { window.addEventListener(
if (isSnapping) return; "touchend",
function (e) {
if (!content || snapping) return;
var y = window.scrollY || window.pageYOffset;
if (y > contentTop + SNAP_EDGE) return;
var diff = touchStartY - e.changedTouches[0].clientY; var diff = touchStartY - e.changedTouches[0].clientY;
var y = window.scrollY; if (Math.abs(diff) < 30) return;
if (y < heroHeight && diff > 30) { var target = diff > 0 ? contentTop : 0;
snapTo(heroHeight); if (Math.abs(target - y) < 2) return;
} else if (y > 0 && y <= heroHeight && diff < -30) { snapTo(target);
snapTo(0); },
} { passive: true }
}, { passive: true }); );
})(); })();
</script> </script>
<!-- 首屏整屏吸附由上面的脚本在 0 ↔ 内容区顶部边界接管;
CSS 侧不再设置 scroll-snap,避免与 scrollTo 平滑动画互相抢夺滚动位置。 -->
<style> <style>
.wi-home-content { .wi-home-content {
position: relative; position: relative;
+1 -1
View File
@@ -3,7 +3,7 @@ import Layout from "../layouts/Layout.astro";
--- ---
<!-- <!--
页面布局契约(Halo 2.26+) 页面布局契约(Halo 2.27+)
供插件前台页面通过 ~{layout :: html(head = ..., content = ...)} 复用主题外观, 供插件前台页面通过 ~{layout :: html(head = ..., content = ...)} 复用主题外观,
避免这些页面回退到 Halo 内置的 fallback 布局导致风格不一致。 避免这些页面回退到 Halo 内置的 fallback 布局导致风格不一致。
+9
View File
@@ -27,6 +27,15 @@ html {
hanging-punctuation: first last; hanging-punctuation: first last;
} }
/* 首页整屏吸附由 src/pages/index.astro 的边界吸附脚本处理:CSS scroll-snap
无法只吸附 0 ↔ 内容区顶部这一个边界(见脚本注释)。这里只保留全局平滑滚动。 */
@media (prefers-reduced-motion: reduce) {
html {
scroll-behavior: auto;
}
}
body { body {
margin: 0; margin: 0;
padding: 0; padding: 0;
+4 -4
View File
@@ -10,8 +10,8 @@ spec:
description: 暖如灯火,静如留白。暖屿以柔和的色调和舒缓的节奏回归内容本身,为记录与阅读留出一方安静从容的角落。 description: 暖如灯火,静如留白。暖屿以柔和的色调和舒缓的节奏回归内容本身,为记录与阅读留出一方安静从容的角落。
logo: /themes/warm-island/assets/logo.png logo: /themes/warm-island/assets/logo.png
homepage: https://nxxy335.top/WarmIsland/ homepage: https://nxxy335.top/WarmIsland/
repo: https://github.com/sunny-335/halo-theme-WarmIsland repo: https://git.nxxy335.top/nxxy335/halo-theme-WarmIsland
issues: https://github.com/sunny-335/halo-theme-WarmIsland/issues issues: https://git.nxxy335.top/nxxy335/halo-theme-WarmIsland/issues
settingName: "warm-island-setting" settingName: "warm-island-setting"
configMapName: "warm-island-configMap" configMapName: "warm-island-configMap"
customTemplates: customTemplates:
@@ -28,8 +28,8 @@ spec:
description: 微信读书书架(依赖微信读书插件) description: 微信读书书架(依赖微信读书插件)
screenshot: screenshot:
file: weread.html file: weread.html
version: "1.2.4" version: "1.2.5"
requires: ">=2.26.0" requires: ">=2.27.0"
license: license:
- name: "GPL-3.0" - name: "GPL-3.0"
url: "https://www.gnu.org/licenses/gpl-3.0.en.html" url: "https://www.gnu.org/licenses/gpl-3.0.en.html"