1617 字
8 分钟
互动文章详情readme
本博客的互动文章详细实现及参考.完结完结 呼
2026-01-14
-
-
-
互动文章创作指南
1. 什么是互动文章
互动文章是一种允许读者通过选择不同分支来影响故事发展的文章类型。每篇文章都有自己的剧情树,读者的选择会导致不同的结局。
2. 核心概念
2.1 Group
互动文章以Group为单位组织,每个Group包含一系列相关的文章,共享同一个剧情树。
2.2 ID系统
每个互动文章都有一个唯一的ID,用于标识其在剧情树中的位置。ID格式为数字,如:
- ID 0: 故事起点
- ID 1: 分支1
- ID 2: 分支2
- …

注意文件路径均推荐使用英文,防止报错
2.3 剧情树(Story Tree)
剧情树是互动文章的核心,定义了故事的所有可能分支和结局。它由以下元素组成:
- 节点(Node):代表故事中的一个状态
- 边(Edge):代表节点之间的连接关系
- 指令(Command):定义节点的行为
3. 创作流程
3.1 设计剧情树
使用Canvas文件设计剧情:
- 在
src/content/groups/[group]/目录下创建main.canvas文件 - 设计剧情节点和连接关系
Canvas节点类型:
| 节点类型 | 格式 | 说明 |
|---|---|---|
| 开启ID | OPEN ID x | 允许访问ID为x的文章 |
| 条件判断 | IF condition | 如 IF random<=50,根据条件选择分支 |
| 真分支 | T | 条件为真时的分支 |
| 假分支 | F | 条件为假时的分支 |
| 初始化变量 | INIT variable value | 如 INIT a 0,初始化变量a为0 |
| 变量赋值 | A expression | 如 A a+1,将a+1的值赋给a |

Canvas边类型:(这个不用管,有脚本自己造,颜色不影响效果)
- 普通边:默认连接
- 真分支边:颜色为4(绿色)
- 假分支边:颜色为1(红色)
- 变量边:颜色为3(黄色)
- 赋值边:颜色为5(蓝色)
3.2 生成剧情数据
运行以下命令生成剧情相关文件:
# 从Canvas文件生成story-tree.jsonnode scripts/extract-canvas-tree.js
# 从story-tree.json生成id-tree.jsonnode scripts/generate-id-tree.js
# 更新文章中的::post指令node scripts/update-story-posts.js这些脚本会生成:
story-tree.json:剧情树数据id-tree.json:ID映射关系- 自动更新文章中的互动分支
- 在build的时候会自动运行相关脚本
3.3 创建互动文章
Frontmatter配置:
title: "文章标题" # 文章封面图片,可以留空published: 2025-11-21T23:33:04 # 可以自己写description: "文章描述" "---重要!!!关于选项显示"image: "" #可以显示在卡片上,填入相对路径tags: - 其他 #随便写,不显示category: test # 随便写,不显示draft: false # 是否在dev环境下渲染,尽量写falselang: zh_CN # 语言group: "2" # Group IDgroupID: 0 # 文章在Group中的IDuid: "00000003" # 唯一标识,用于生成固定链接,可以留空等待build赋值使用::post指令创建互动分支:
(可以等待update-story-posts.js自动生成)
::post{uid="00000004" text="A" groupID="3"}::post{uid="00000005" text="B" groupID="2"}# 你要问我text留空显示啥?废话,当然是显示title属性啦~uid:目标文章的唯一标识text:显示在卡片上的文字(覆盖title)groupID:目标文章的Group ID::post命令之间需要空格
4. 变量系统
4.1 内置变量
random:范围1-100的随机数,每次访问记录变化时更新
4.2 自定义变量
- 在Canvas中使用
INIT variable value初始化变量 - 使用
A expression修改变量值 - 支持算术运算:
a+1、a-1、a*2、a/2
4.3 变量持久化
变量值保存在localStorage中,即使刷新页面也不会丢失。
5. 条件判断
5.1 条件格式
IF condition5.2 支持的条件
- 数值比较:
random<=50、a>=2 - 逻辑运算:
a>0 && b<10 - 支持的运算符:
<=,>=,<,>,==,!=,&&,||
6. 锁块功能
6.1 工作原理
- 所有互动卡片默认处于锁定状态
- 根据剧情树和访问记录,计算可访问的ID列表
- 为可访问的卡片添加
data-accessible="true"属性 - CSS样式根据该属性控制卡片的显示状态
6.2 锁块样式
- 锁定状态:半透明、不可点击、显示禁止光标
- 解锁状态:完全不透明、可点击、显示指针光标
- 平滑过渡动画
7. 创作最佳实践
7.1 剧情设计
- 清晰的分支结构:避免过于复杂的分支,保持剧情流畅
- 有意义的选择:每个选择都应该对故事发展产生明显影响
- 合理的变量使用:不要滥用变量,保持逻辑清晰
- 明确的结局:每个分支都应该有明确的结局
7.2 技术实现
- 使用Canvas设计:Canvas提供直观的可视化设计界面
- 定期生成数据:每次修改Canvas后,记得重新生成story-tree和id-tree
- 测试所有分支:确保每个分支都能正常工作
- 合理使用变量:变量用于追踪故事状态,不要用于复杂计算
8. 示例:简单互动故事
8.1 使用obsidian设计Canvas
{ "nodes": [ {"id":"c99e6cf9d5ec8f64","type":"text","text":"OPEN ID 0","x":-192,"y":-33,"width":250,"height":60,"color":"6"}, {"id":"aea345450b268a69","type":"text","text":"OPEN ID 2","x":167,"y":-33,"width":250,"height":60,"color":"6"}, {"id":"f5defe68147d9720","type":"text","text":"OPEN ID 1","x":167,"y":-160,"width":250,"height":60,"color":"6"}, {"id":"34a8193e89a5a317","type":"text","text":"IF random<=50","x":500,"y":-340,"width":250,"height":60,"color":"2"}, {"id":"94fffa3f23f5b996","type":"text","text":"T","x":825,"y":-340,"width":250,"height":60,"color":"4"}, {"id":"f490a129e27d0c1f","type":"text","text":"F","x":825,"y":-260,"width":250,"height":60,"color":"1"} ], "edges": [ {"id":"83d1c0e66c614adf","fromNode":"c99e6cf9d5ec8f64","fromSide":"right","toNode":"aea345450b268a69","toSide":"left"}, {"id":"1af9be442ca8869e","fromNode":"c99e6cf9d5ec8f64","fromSide":"right","toNode":"f5defe68147d9720","toSide":"left"}, {"id":"c56f753c1f038d11","fromNode":"f5defe68147d9720","fromSide":"right","toNode":"34a8193e89a5a317","toSide":"left","color":"3"}, {"id":"d9cd3da50807f828","fromNode":"34a8193e89a5a317","fromSide":"right","toNode":"94fffa3f23f5b996","toSide":"left","color":"4"}, {"id":"875b7c8cfea02c11","fromNode":"34a8193e89a5a317","fromSide":"right","toNode":"f490a129e27d0c1f","toSide":"left","color":"1"} ]}8.2 使用obsidian设计文章内容
---title: 故事起点group: "2"groupID: 0uid: "00000003"---
# 欢迎来到互动故事你好你好巴拉巴拉
--- #脚本生成的选择项↓↓↓# your の choice
你选择
::post{uid="00000004" text="A" groupID="1"}
::post{uid="00000005" text="B" groupID="2"}9. 常见问题
9.1 卡片不显示
- 检查Canvas中是否有
OPEN ID x节点 - 确保
::post指令的uid和groupID正确 - 检查是否生成了最新的
story-tree.json和id-tree.json - 你换行了吗?
9.2 选择后没有反应
- 检查Canvas中的边连接是否正确
- 确保条件判断的语法正确
- 检查变量初始化是否正确
9.3 变量值不正确
- 检查变量初始化和赋值语句
- 确保变量名一致
- 注意变量类型,避免类型不匹配
10. 扩展功能
10.1 自定义样式
可以在 src/pages/groups/[group]/[slug].astro 中修改卡片样式:
.group-post-container a.card-github { /* 自定义锁定样式 */}
.group-post-container a.card-github[data-accessible="true"] { /* 自定义解锁样式 */}10.2 添加新功能
可以扩展 InteractiveStory 类,添加新的指令类型和功能。
11. 总结
互动文章为读者提供了一种全新的阅读体验,让他们能够参与到故事发展中。通过合理设计剧情树、使用变量系统和条件判断,可以创建出丰富多样的互动内容。
希望本指南能帮助你创作出精彩的互动文章!
12. 参考资源
互动文章详情readme