侧边栏壁纸
博主头像
无涯茶榻

茶烟半榻窗竹雨,一卷残书梦里秋

  • 累计撰写 22 篇文章
  • 累计创建 1 个标签
  • 累计收到 0 条评论

目 录CONTENT

文章目录

Halo 首页大图技术拆解:Joe3 主题的 background-image 实现与图片来源

一、写在前面

大多数博客的首页第一屏是一张固定的大图,换主题换模板都得重新配。Joe3 Next 主题把这块做成了一个纯配置项——改一行 JSON 就能换成任意图片地址,不需要碰模板、不需要重启 Halo。

这篇拆解 www.wycha.top 首页大图的完整实现:图到底从哪来、怎么渲染的、改配置要注意什么。

二、渲染机制:CSS background,不是 img 标签

首页模板 index.html 里只有一行判断和一次片段引入:

<th:block th:if="${theme.config.beauty.enable_big_banner}">
  <th:block th:replace="~{modules/macro/big_banner :: big_banner(${title})}" />

真正的实现落在 templates/modules/macro/big_banner.html:

<div id="EvanBigBanner" class="evan-big-banner"
  th:style="'background-image:url('+${theme.config.beauty.big_banner_Photos}+');'
          +${htmlType == 'index'? 'height:calc(100vh - 60px);margin-top: initial;':''}">

三个设计要点:

  • 纯 CSS background-image。整张大图不是 <img> 标签,而是容器元素的背景图。好处是可以用 background-size、background-position 做各种裁切适配,不需要额外的图片标签参与布局。

  • 首屏满屏。首页场景下高度是 calc(100vh - 60px),减掉的 60px 是站点头部导航的高度。这段高度只在 htmlType == 'index' 时拼接,文章页、分类页用的是另一套高度。

  • 底部 SVG 波浪。容器里内嵌了一个 #EvanWaves SVG,用四条 <use> 引用同一个 gentle-wave 路径、分别偏移 0/3/5/7 像素,做出视差滚动的层叠效果。

此外还有两个可选图层:

  • 视频层。enable_big_banner_video 为真时,容器里会叠一个 <video> 元素,样式是 width:100%; height:100%; object-fit:cover,配合 autoplay、loop、muted 实现自动播放的背景视频。本站当前是关闭状态。

  • 文字层。包含站点标题和一个「一言」引用位,标题在模板里写死 opacity: 0 先隐藏,等 JS 加载完成后淡入。

三、图片从哪里来

答案是:来自外部随机图 API,不是本站上传的附件。

配置存在主题的 ConfigMap 里,路径是 beauty 这一组。本站当前的实际配置值如下:

配置键 本站当前值 作用
big_banner_Photos https://imgapi.xl0408.top/index.php 大图地址
big_banner_title 无涯茶榻 大图标题文案
enable_big_banner true 总开关
enable_big_banner_video false 是否启用背景视频
enable_big_banner_hitokoto true 是否显示一言

那个随机图 API 的实际行为

对 https://imgapi.xl0408.top/index.php 连续请求三次,实测结果:

请求 响应体积 结果
第 1 次 306,161 字节 302 跳转到 raw.gitcode.com 的 comic/38.jpg
第 2 次 86,268 字节 换了一张不同的图
第 3 次 1,966,323 字节 又是另一张

可以归纳出这个 API 的三个特征:

  • 每次都换图。不是缓存同一张,而是每次请求随机返回不同的图片。

  • 图源是漫画图。最终 302 跳转到 raw.gitcode.com 上的 comic/xx.jpg 路径,属于从 GitCode 镜像站拉取的图库。

  • 体积波动极大。从 86 KB 到接近 2 MB 都有,图片分辨率是 1920×1080 的 JPEG。这个波动对首屏加载体验影响不小。

四、文字层的数据流

标题和一言都由 assets/js/beauty.js 里的 EvanBigBanner 对象在运行时填充:

this.dom.bigBanner      = $("#EvanBigBanner");
this.dom.bannerTitle    = $("#EvanBigBanner_Title");
this.dom.bannerSubTitle = $("#EvanBigBanner_SubTitle");
this.dom.hitokotoText   = $("#HitokotoText");
this.dom.hitokotoForm   = $("#HitokotoForm");

一言内容来自 hitokoto 的公开接口 https://v1.hitokoto.cn/,实测返回 200 和一段 JSON。

顺带一提,hitokoto 的 v2 接口(v2.hitokoto.cn)实测请求超时,但主题 JS 用的是 v1,所以不影响使用。如果哪天 v1 挂了、页面上的「一言」区域变空,先查这个接口而不是查主题。

五、怎么改:改数据,不改模板

大图的所有可调项都在主题的 ConfigMap 里,编辑端点是:

GET /api/v1alpha1/configmaps/theme-Joe-configMap

注意路径是 /api/ 单数、没有 group 段,和 Halo 其它扩展资源(/apis/content.halo.run/v1alpha1/...)的路径规律相反,按组名推断必然 404。

  • 改设置不用重启 Halo。ConfigMap 是数据不是模板,PUT 完刷新页面就生效。只有改模板文件才需要重启(Thymeleaf 不热重载)。

  • data.beauty 是 JSON 字符串,要解两次。第一层是 ConfigMap 的 data 映射,第二层才是 beauty 组本身的 JSON。

  • PUT 是整体替换。只改 big_banner_Photos 时,beauty 组里其余的键必须原样回写,否则该组所有设置会一起丢失。

六、别和轮播 banner 搞混

首页其实有两套大图机制,很容易混淆:

对比项 首屏大图 轮播 banner
模板文件 modules/macro/big_banner.html modules/macro/banner.html
配置组 beauty.big_banner_Photos carousel.banner_data_group
渲染方式 CSS background <img> 轮播
可调参数 固定一张背景图 方向、切换特效、延迟时长等
内容来源 主题设置里填的图地址 可以放文章封面

首页那些带 lazyload 的 <img> 标签(指向 /upload/*.jpg)是文章列表卡片的封面,跟首屏大图没有关系。

七、两个值得注意的点

1. 首屏依赖第三方服务

大图地址指向站外 API,那个服务挂了或变慢,首屏就会空白。因为用的是 CSS background,浏览器不会因此报错——页面照样 200,只是图出不来。这种故障排查起来很费劲,因为服务端日志里什么都没有。

2. 图片没有落地到本地

每次访问都要打一遍那个 API,再被 302 跳到 GitCode 拉图。首次加载可能几百 KB,也可能接近 2 MB。

想优化的话,把图下载到本站的 /upload/ 目录,再把 big_banner_Photos 指向站内路径就行——background-image:url() 对站内相对路径同样支持,不需要改任何模板代码。

0

评论区