§ 背景
Shoka 主题原版没有轮播图功能。hexo-shoka-swiper 是一个专为 Shoka 主题设计的轮播图插件,基于 Swiper 库,提供两种使用场景:
- 首页推广轮播(全局)—— 在首页展示带有
swiper_indexfront-matter 的文章卡片 - 文章内轮播(按文章)—— 通过
{% swiper %}/{% slide %}标签在文章中嵌入图片/视频画廊
插件由 akilarlxh 和 llxlr 共同开发,llxlr 负责后续维护和 Pjax 适配增强。
仓库:llxlr/hexo-shoka-swiper | 版本:0.1.11 | 许可:Apache-2.0
§ 安装
pnpm add hexo-shoka-swiper |
依赖:Swiper CSS/JS 通过插件的 CDN(jsDelivr)自动加载,无需额外安装。依赖 nunjucks-date(Nunjucks 模板中日期格式化)。
§ 架构设计
§ 首页推广轮播
§ 配置
在 _config.shoka.yml 中:
swiper: | |
enable: true # 总开关 | |
enable_page: all # 显示页面:all 或指定路径 | |
exclude: # 排除路径列表 | |
timemode: date # 卡片日期:date 或 updated | |
layout: | |
type: id # 注入容器类型:id 或 class | |
name: main # 容器选择器 | |
index: 0 # class 模式下的索引 | |
insertposition: afterbegin # DOM 插入位置 | |
swiper_title: 推荐阅读 # 标题文字 | |
default_descr: 再怎么看我也不知道怎么描述它的啦! # 默认描述 | |
error_img: /images/loading.gif # 封面图加载失败时的占位图 | |
swiper_css: # 可选:自定义 Swiper CSS CDN | |
swiper_js: # 可选:自定义 Swiper JS CDN | |
custom_css: # 可选:自定义样式 CDN | |
custom_js: # 可选:自定义初始化脚本 CDN |
§ 文章配置
在文章 Front Matter 中添加:
--- | |
title: 值得一读的好文章 | |
swiper_index: 10 # 数值越大越靠前 | |
description: 这篇文章真的很棒 # 卡片描述文字 | |
cover: /images/cover.jpg # 卡片封面图 | |
swiper_type: video # 可选:视频卡片 | |
swiper_video: /videos/demo.mp4 | |
swiper_video_poster: /images/poster.jpg | |
swiper_video_embed: <iframe src="..."></iframe> # 或嵌入视频 | |
link: /other-post/ # 可选:自定义卡片链接 | |
--- |
§ 数据流
文章 Front Matter
swiper_index: 10
cover: /images/cover.jpg
description: "文章描述"
↓ after_generate filter 收集
swiper_list (按 swiper_index 降序排列)
↓ Nunjucks 渲染 lib/slider.njk
HTML(.blog-slider 容器,含 .blog-slider__wrp > .blog-slider__item)
↓ injector 注入 + swiper_init.js 初始化
Swiper 实例(fade 效果 + pagination 圆点 + Pjax 生命周期管理)
§ Pjax 适配
hexo-shoka-swiper 对 Pjax 进行了完整适配,这是本插件最核心的增强:
// swiper_init.js 核心逻辑(简化版) | |
(function () { | |
function initSwiper() { | |
if (window.swiper?.destroy) { | |
window.swiper.destroy(true, true); // 1. 先销毁旧实例 | |
window.swiper = null; | |
} | |
const el = document.querySelector(".blog-slider"); | |
if (!el) return; // 2. DOM 不存在则跳过 | |
window.swiper = new Swiper(".blog-slider", { ... }); // 3. 创建新实例 | |
} | |
function pjaxMount() { | |
const cfg = window.__SWIPER_CONFIG__; | |
if (!cfg) return; | |
const cpage = location.pathname; | |
if (cfg.epage !== "all" && cfg.epage !== cpage) return; // 路径匹配 | |
const parent = cfg.get_layout(); | |
if (parent && !parent.querySelector(".blog-slider")) { | |
parent.insertAdjacentHTML(cfg.insertposition, cfg.html); // 重新注入 HTML | |
} | |
initSwiper(); | |
} | |
initSwiper(); // 首次加载 | |
document.addEventListener("pjax:send", () => { | |
window.swiper?.destroy?.(true, true); // Pjax 前清理 | |
window.swiper = null; | |
}); | |
document.addEventListener("pjax:success", pjaxMount); // Pjax 后重建 | |
})(); |
关键设计要点:
| 要点 | 说明 |
|---|---|
pjax:send 中销毁 | 在 DOM 被替换前释放内存,避免操作即将消失的元素 |
| HTML 重新注入 | Pjax 替换 .pjax 后轮播容器消失,需要重新注入 |
window.__SWIPER_CONFIG__ | 首次加载的 inline 脚本暴露配置到全局,Pjax 时读取 |
| 路径匹配 | 支持 all 或精确路径,排除列表过滤 |
| 视频管理 | slide 切换时暂停所有视频,仅播放当前 slide 的视频(400ms 延迟,匹配 fade 过渡) |
§ 文章内轮播 Tag
文章内轮播通过两对标签实现:{% swiper %} 容器 + {% slide %} 子项。
§ 基本语法
{% swiper style:gallery ratio:16:9 %} | |
{% slide cover:/images/photo1.jpg %} | |
这是第一张照片的描述 | |
{% endslide %} | |
{% slide cover:/images/photo2.jpg link:https://example.com %} | |
第二张,点击可跳转 | |
{% endslide %} | |
{% slide video:/videos/demo.mp4 poster:/images/poster.jpg %} | |
带封面的视频 | |
{% endslide %} | |
{% slide embed:<iframe src="..."></iframe> %} | |
嵌入视频(YouTube / Bilibili) | |
{% endslide %} | |
{% endswiper %} |
§ 参数说明
{% swiper %} 容器参数:
| 参数 | 值 | 说明 |
|---|---|---|
style | gallery (默认) / card | 画廊模式(大图 + 导航箭头)/ 卡片模式(小卡 + fade + 竖排圆点) |
ratio | 16:9 (默认) / 4:3 / 1:1 | 媒体区域宽高比 |
{% slide %} 子项参数:
| 参数 | 说明 |
|---|---|
cover | 图片 URL(图片 slide) |
video | 视频 URL(视频 slide) |
poster | 视频封面图 |
embed | 嵌入 iframe HTML(如 YouTube / Bilibili) |
link | 点击 slide 的跳转链接 |
type | image 或 video(默认自动检测) |
内容(位于 {% slide %}...{% endslide %} 之间):支持 Markdown 的描述文本,渲染后显示在 slide 底部。
§ 自动注入
文章内轮播的 Swiper CSS/JS 资源在首次使用时自动注入到 <head> 和 <body>,无需额外配置。每个 swiper 容器获得唯一 ID(as_N_xxxxxx),同一页面可放置多个独立轮播。
§ Pjax 兼容
文章内轮播通过 window.__articleSwipers 数组管理所有实例:
pjax:send:遍历数组,逐个destroy(),然后清空pjax:success:扫描新页面中的.as-swiper容器,重新初始化
§ 样式定制
插件的 CSS(swiperstyle.css)包含完整的响应式设计:
- 桌面端:大图轮播 + 图文卡片 + 导航箭头 + pagination 圆点
- 移动端(≤600px):紧凑布局,文字缩小,导航箭头隐藏
- 暗黑模式:通过
[data-theme=dark]选择器适配 - 动画:active slide 的标题和描述有分层淡入动画(staggered fade-in)
如需覆盖默认样式,可通过插件的 custom_css 配置项指定自定义样式表路径。
§ 资源文件结构
hexo-shoka-swiper/
├── index.js # 插件入口
├── lib/
│ ├── swiper.min.css # Swiper 核心 CSS
│ ├── swiper.min.js # Swiper 核心 JS
│ ├── swiperstyle.css # 自定义样式(响应式 + 暗黑模式)
│ ├── swiper_init.js # 初始化脚本(含 Pjax 适配)
│ ├── slider.njk # 首页轮播 Nunjucks 模板
│ └── swiper.njk # 文章内轮播 Nunjucks 模板(gallery / card)
└── images/
└── loading.gif # 默认加载占位图
§ 相关文章
- Shoka 主题 Pjax 通用适配指南 — Swiper 是 Pjax 适配的完整案例之一