最近亲手打造了一款纯前端实现的AI藏头诗生成器,实际效果非常理想。本文将完整拆解设计思路与代码实现过程,带你深入理解如何用JavaScript构建智能诗歌创作工具。
效果演示




一、项目背景与设计理念
1.1 藏头诗的文化价值
藏头诗是中国古典诗歌中独具魅力的形式,又称“藏头格”,其核心在于将特定含义隐藏在每句诗的第一个字中。这种传统文体融合了文学性与趣味性,自古以来深受文人雅士推崇。
传统藏头诗的核心特点:
- 每句诗的首字连读,构成完整的词语或句子
- 既要确保藏头内容的完整性,又要兼顾诗歌的韵律与意境
- 常用于含蓄表达情感、祝福或隐秘信息
例如明代才子唐伯虎的《我画蓝江水悠悠》:
这首诗每句首字连起来即为“我画蓝江”,构思精妙,意蕴深远。
1.2 为什么需要AI藏头诗生成器
在数字时代,我们希望通过技术手段让藏头诗创作变得更加便捷高效:
- 降低创作门槛:传统藏头诗创作需要深厚的文学功底和大量时间投入
- 即时生成:用户只需输入藏头文字,AI便能在秒级内输出符合要求的作品
- 多样化选择:支持多种体裁与风格,满足不同场景需求
- 文化传承:借助现代技术弘扬传统文化,让古典诗词走进日常生活
1.3 技术选型
我们选择纯前端实现方案,理由如下:
| 技术方案 | 优势 | 劣势 | 适用性 |
|---|---|---|---|
| 纯前端JavaScript | 无需后端支持、响应迅速、部署简便 | 生成能力相对有限 | 适合规则驱动型生成 |
| 后端AI模型 | 生成质量高、能力强 | 依赖服务器、成本较高 | 适合复杂生成任务 |
| 混合方案 | 兼顾质量与性能 | 架构复杂 | 大型应用 |
由于藏头诗具有明确的规则约束(藏头字固定、格律要求),纯前端方案完全能够胜任。我们通过精心设计的规则引擎与素材库,实现了高质量的藏头诗自动生成。
二、系统架构设计
2.1 整体架构
系统采用经典的三层架构设计:
┌─────────────────────────────────────────────────────────────┐│表现层 (UI Layer) ││┌─────────────┐┌─────────────┐┌─────────────────────┐│││输入面板 ││展示面板 ││ 历史记录面板 │││└─────────────┘└─────────────┘└─────────────────────┘│└─────────────────────────────────────────────────────────────┘│▼┌─────────────────────────────────────────────────────────────┐│业务逻辑层 (Service Layer)││┌─────────────┐┌─────────────┐┌─────────────────────┐│││ 诗歌生成器││ 主题切换器││工具函数 │││ └─────────────┘└─────────────┘└─────────────────────┘│└─────────────────────────────────────────────────────────────┘│▼┌─────────────────────────────────────────────────────────────┐│数据层 (Data Layer)││┌─────────────────────────┐┌────────────────────────────┐│││ 诗词素材数据库 ││本地存储 (LocalStorage) │││ │ poemThemes / poemTypes││poemHistory / poemStats│││ └─────────────────────────┘└────────────────────────────┘│└─────────────────────────────────────────────────────────────┘
2.2 核心模块划分
2.2.1 配置模块 (config.js)
负责存储和管理静态数据,包括:
- poemThemes:六大主题素材库,每个主题包含关键字和诗句模板
- poemTypes:诗歌体裁配置,定义各体裁的句数和每句字数
- poemThemes:韵脚分组,用于押韵判断
- 工具函数:随机选择、数组乱序等辅助方法
2.2.2 AI模块 (ai.js)
核心生成逻辑,包含:
- generateAcrostic():主生成函数,协调各子模块
- generateLine():单句诗生成
- buildLineFromTemplate():基于模板构建诗句
- fillTemplate():模板填充逻辑
- renderPoemDisplay():诗歌渲染函数
- 历史记录管理:增删改查功能
2.2.3 控制器 (battle.js)
用户交互和事件处理:
- initApp():应用初始化入口
- generatePoem():生成按钮点击处理
- copyPoem():复制功能
- readPoem():朗读功能
- 主题切换:主题切换系统
- 事件绑定:各类交互事件
2.3 数据流设计
用户输入藏头文字 │ ▼输入验证 (长度检查:2-10字) │ ▼获取用户配置 (体裁、风格、押韵等) │ ▼调用 generateAcrostic() 生成诗歌 │ ├─→ 遍历每个藏头字 ││ │▼ │ 生成对应诗句 ││ │▼ │ 检查素材库是否有现成诗句 ││ │├─→ 有 → 直接使用 │└─→ 无 → 模板填充 │ ▼构建诗歌对象 │ ▼保存到历史记录 (LocalStorage) │ ▼渲染展示 (高亮藏头字)
三、核心代码详解
3.1 诗歌生成核心逻辑
3.1.1 主生成函数
function generateAcrostic(hiddenWords, options = {}) {const {theme = 'romantic',type = 'qijue',useRhyme = true,usePingze = false} = options;// 验证输入if (!hiddenWords || hiddenWords.length < 2) {throw new Error('请输入至少2个字的藏头文字');}if (hiddenWords.length > 10) {throw new Error('藏头文字最多10个字');}// 获取主题素材const themeData = poemThemes[theme] || poemThemes.romantic;const config = poemTypes[type] || poemTypes.qijue;// 生成诗句const lines = [];for (let i = 0; i < hiddenWords.length; i++) {const hiddenWord = hiddenWords[i];const line = generateLine(hiddenWord, themeData, config, i);lines.push(line);}// 构建诗歌对象const poem = {id: Date.now(),title: generateTitle(hiddenWords),hiddenWords: hiddenWords,lines: lines,theme: theme,type: type,useRhyme: useRhyme,usePingze: usePingze,createdAt: new Date().toISOString()};return poem;}
设计要点:
- 参数解构与默认值:使用ES6解构赋值,提供合理的默认配置,用户只需关注核心输入
- 输入验证:双重检查确保数据有效性
- 核心循环:遍历每个藏头字,逐个生成对应诗句
- 对象封装:将生成结果封装为标准对象,便于后续处理
3.1.2 单句生成函数
function generateLine(hiddenWord, themeData, config, index) {const patterns = [`${hiddenWord}XXXXXXX`,// 七言`${hiddenWord}XXXXX`,// 五言`${hiddenWord}XXXXXXX`,// 通用`${hiddenWord}XXXXXX`// 词];// 选择合适的模板let pattern;if (config.syllables === 7) {pattern = patterns[0];} else if (config.syllables === 5) {pattern = patterns[1];} else if (config.type === 'ci') {pattern = patterns[3];} else {pattern = patterns[2];}// 从主题库中选择或生成句子const suitableLines = themeData.lines.filter(line => line.startsWith(hiddenWord));if (suitableLines.length > 0) {// 找到以藏头字开头的诗句return randomChoice(suitableLines);} else {// 生成新句子return buildLineFromTemplate(hiddenWord, themeData, config, index);}}
设计要点:
- 优先级策略:优先从素材库选择现成诗句,保证质量
- 模式匹配:根据体裁选择合适的字符长度模板
- 智能拼接:藏头字+主题相关字词,确保意境连贯
- 降级方案:素材库没有时,调用模板填充生成
3.2 主题素材数据库
3.2.1 数据结构设计
const poemThemes = {romantic: {name: '浪漫抒情',words: ['花', '月', '情', '思', '梦', '缘', '爱', '恋', ...],lines: ['花开四季香满庭','月照西楼影自怜','情意绵绵似水流','思念悠悠如云烟',...]},heroic: {name: '豪迈壮阔',words: ['天', '地', '山', '河', '剑', '酒', '风', '云', ...],lines: ['天高地阔任我行','地势坤厚载物情','山高路远志不移','河水滔滔向东流',...]},// ... 其他主题};
数据结构说明:
| 字段 | 类型 | 说明 |
|---|---|---|
| name | string | 主题显示名称 |
| words | array | 主题相关关键字,用于填充 |
| lines | array | 预置诗句,每句以关键字开头 |
3.2.2 体裁配置
const poemTypes = {qijue: { name: '七言绝句', lines: 4, syllables: 7 },wujue: { name: '五言绝句', lines: 4, syllables: 5 },qilv: { name: '七言律诗', lines: 8, syllables: 7 },wulv: { name: '五言律诗', lines: 8, syllables: 5 },ci: { name: '词·长短句', lines: 6, syllables: 7 },free: { name: '自由体', lines: 0, syllables: 0 }};
体裁参数说明:
| 体裁 | 句数 | 每句字数 | 特点 |
|---|---|---|---|
| 七言绝句 | 4 | 7 | 短小精悍,常见格式 |
| 五言绝句 | 4 | 5 | 简洁含蓄,古风浓厚 |
| 七言律诗 | 8 | 7 | 气势恢宏,格律严格 |
| 五言律诗 | 8 | 5 | 工整对仗,意境深远 |
| 词 | 6 | 7 | 长短句结合,音乐性强 |
| 自由体 | 自定 | 自定 | 灵活自由,无严格限制 |
3.3 模板填充算法
function fillTemplate(template, hiddenWord, themeData) {let result = hiddenWord;let templateIndex = 1;while (result.length < 7 && templateIndex < template.length) {const char = template[templateIndex];if (char === 'X') {result += randomChoice(themeData.words);} else if (char !== 'X') {result += char;}templateIndex++;}// 补充到完整长度while (result.length < 7) {result += randomChoice(themeData.words);}return result;}
算法流程:
开始│▼result = 藏头字, templateIndex = 1│▼┌─────────────────────────────┐│result.length < 7 且│◀────────┐│templateIndex < 模板长度 ││└─────────────────────────────┘│ ││ ├─ 是 ──→ 获取模板字符│ ││ ▼│┌──────────────┐ ││ 字符是 'X'? │ │└──────────────┘ │ │││ ├─ 是 ──→ 随机选择主题词填充│ │││ ├─ 否 ──→ 直接追加字符│ │││ ▼▼│ templateIndex++ ──────────────────────┘ │ ▼┌──────────────────┐│ result.length < 7 │◀────────┐└──────────────────┘│ │ │ └─ 是 ──→ 随机填充 ────┘ │ ▼返回结果
3.4 渲染与展示
function renderPoemDisplay(poem) {const display = document.getElementById('poemDisplay');if (!poem) {display.innerHTML = `
