在日常工作和学习中,思维导图是一种非常有效的可视化工具,可以帮助我们梳理思路、规划任务、整理知识结构。本文将带你一步步了解如何使用 HTML 和 jsmind 实现一个基础的在线思维导图应用。
效果演示
项目概述
本项目主要包含以下核心功能:
- 初始化思维导图
- 添加子节点、删除节点、编辑节点内容
- 保存数据到本地存储,刷新页面后数据不丢失
- 导出思维导图数据为 JSON 文件
- 导入 JSON 数据
- 保存为图片
准备工作
下载 jsmind 文件,项目中引入以下主要文件:jsmind.css、jsmind.js、jsmind.draggable-node.js、jsmind.screenshot.js。
jsmind 下载地址:https://github.com/hizzgdev/jsmind
<link type="text/css" rel="stylesheet" href="./jsmind.css" />
<script src="./jsmind.js"></script>
<script src="./jsmind.draggable-node.js"></script>
<script src="./jsmind.screenshot.js"></script>
页面结构与样式设计
创建 HTML 结构
<!-- 工具栏 -->
<div class="toolbar"><button onclick="addNode()">添加子节点</button><button onclick="removeNode()">删除节点</button><button onclick="editNode()">编辑节点</button><button onclick="saveDataToLocal()">保存</button><button onclick="downloadData()">导出</button><button onclick="saveAsImage()">保存为图片</button><input type="file" id="importFile" accept=".json" style="display: none;" onchange="importDataFromFile(event)"><label for="importFile" class="button">导入数据</label>
</div>
<!-- 思维导图容器 -->
<div id="jsmind_container"></div>
设计 CSS 样式
body {margin: 0;padding: 0;
}
#jsmind_container {width: 100vw;height: 100vh;overflow: auto;box-sizing: border-box;top: 0;
}
.toolbar {position: fixed;top: 10px;left: 50%;transform: translateX(-50%);z-index: 1000;display: flex;gap: 10px;
}
.toolbar button,.toolbar .button {padding: 8px 16px;border: none;border-radius: 4px;background-color: #007bff;color: white;cursor: pointer;transition: background-color 0.3s ease;
}
.toolbar button:hover {background-color: #0056b3;
}
核心功能实现
初始化思维导图
使用 jsMind 初始化一个可编辑的思维导图实例,支持从浏览器本地存储(localStorage)中加载上次保存的数据。
// 初始化思维导图
let jm = null;
const initMindMap = () => {const options = {container: 'jsmind_container',editable: true,theme: 'white',};// 初始数据let data = {"meta": {"name": "示例思维导图","author": "user","version": "1.0"},"format": "node_tree","data": {"id": "root","topic": "中心主题","children": []}};const savedData = localStorage.getItem('mindmapData');if (savedData) {try {data = JSON.parse(savedData);} catch (e) {console.error("解析本地数据失败", e);}}jm = new jsMind(options);jm.show(data);
}
window.onload = initMindMap;
添加子节点
function addNode() {const selectedNode = jm.get_selected_node();if (selectedNode) {const nodeId = Date.now().toString();jm.add_node(selectedNode.id, nodeId, '新增子节点', {});}
}
删除节点
function removeNode() {const selectedNode = jm.get_selected_node();if (selectedNode && selectedNode.id !== 'root') {jm.remove_node(selectedNode);}
}
编辑节点
function editNode() {const selectedNode = jm.get_selected_node();if (selectedNode) {const newText = prompt('输入新内容', selectedNode.topic);if (newText) {jm.update_node(selectedNode.id, newText);}}
}
保存数据到本地存储
function saveDataToLocal() {const mindData = jm.get_data();localStorage.setItem('mindmapData', JSON.stringify(mindData));
}
导出思维导图数据
将当前数据保存为 .json 文件并触发下载。
function downloadData() {saveDataToLocal();const mindData = jm.get_data();const dataStr = JSON.stringify(mindData, null, 2);const blob = new Blob([dataStr], {type: 'application/json'});const url = URL.createObjectURL(blob);const a = document.createElement('a');a.href = url;a.download = 'mindmap.json';a.click();
}
导入 JSON 数据
可以通过上传已导出的 JSON 文件恢复之前的思维导图结构。
function importDataFromFile(event) {const file = event.target.files[0];if (!file) return;const reader = new FileReader();reader.onload = function(e) {try {const mindData = JSON.parse(e.target.result);jm.show(mindData);alert("导入成功!");} catch (error) {alert("导入失败,请选择有效的JSON文件。");console.error("解析文件失败:", error);}};reader.readAsText(file);
}
保存为图片
利用 jsmind.screenshot.js 插件将整个思维导图截图并保存为图片文件。
function saveAsImage() {saveDataToLocal();jm.screenshot.shootDownload();
}
扩展建议
- 主题切换:通过修改 jm 的 theme 配置,提供多种预设主题
- 节点图标与样式设置
- 快捷键支持
完整代码
<!DOCTYPE html>
<html lang="zh-CN">
<head><title>在线思维导图</title><meta charset="utf-8"><!-- 引入 jsMind 样式 --><link type="text/css" rel="stylesheet" href="./jsmind.css" /><style>body {margin: 0;padding: 0;}#jsmind_container {width: 100vw;height: 100vh;overflow: auto;box-sizing: border-box;top: 0;}.toolbar {position: fixed;top: 10px;left: 50%;transform: translateX(-50%);z-index: 1000;display: flex;gap: 10px;}.toolbar button,.toolbar .button {padding: 8px 16px;border: none;border-radius: 4px;background-color: #007bff;color: white;cursor: pointer;transition: background-color 0.3s ease;}.toolbar button:hover {background-color: #0056b3;}</style>
</head>
<body>
<!-- 工具栏 -->
<div class="toolbar"><button onclick="addNode()">添加子节点</button><button onclick="removeNode()">删除节点</button><button onclick="editNode()">编辑节点</button><button onclick="saveDataToLocal()">保存</button><button onclick="downloadData()">导出</button><button onclick="saveAsImage()">保存为图片</button><input type="file" id="importFile" accept=".json" style="display: none;" onchange="importDataFromFile(event)"><label for="importFile" class="button">导入数据</label>
</div>
<!-- 思维导图容器 -->
<div id="jsmind_container"></div><script src="./jsmind.js"></script>
<script src="./jsmind.draggable-node.js"></script>
<script src="./jsmind.screenshot.js"></script>
<script>// 初始化思维导图let jm = null;const initMindMap = () => {const options = {container: 'jsmind_container',editable: true,theme: 'white',};// 初始数据let data = {"meta": {"name": "示例思维导图","author": "user","version": "1.0"},"format": "node_tree","data": {"id": "root","topic": "中心主题","children": []}};const savedData = localStorage.getItem('mindmapData');if (savedData) {try {data = JSON.parse(savedData);} catch (e) {console.error("解析本地数据失败", e);}}jm = new jsMind(options);jm.show(data);}// 添加子节点function addNode() {const selectedNode = jm.get_selected_node();if (selectedNode) {const nodeId = Date.now().toString();jm.add_node(selectedNode.id, nodeId, '新增子节点', {});}}// 删除节点function removeNode() {const selectedNode = jm.get_selected_node();if (selectedNode && selectedNode.id !== 'root') {jm.remove_node(selectedNode);}}// 编辑节点function editNode() {const selectedNode = jm.get_selected_node();if (selectedNode) {const newText = prompt('输入新内容', selectedNode.topic);if (newText) {jm.update_node(selectedNode.id, newText);}}}// 保存数据到本地存储function saveDataToLocal() {const mindData = jm.get_data();localStorage.setItem('mindmapData', JSON.stringify(mindData));}// 导出数据function downloadData() {saveDataToLocal();const mindData = jm.get_data();const dataStr = JSON.stringify(mindData, null, 2);const blob = new Blob([dataStr], {type: 'application/json'});const url = URL.createObjectURL(blob);const a = document.createElement('a');a.href = url;a.download = 'mindmap.json';a.click();}// 导入数据function importDataFromFile(event) {const file = event.target.files[0];if (!file) return;const reader = new FileReader();reader.onload = function(e) {try {const mindData = JSON.parse(e.target.result);jm.show(mindData);alert("导入成功!");} catch (error) {alert("导入失败,请选择有效的JSON文件。");console.error("解析文件失败:", error);}};reader.readAsText(file);}function saveAsImage() {saveDataToLocal();jm.screenshot.shootDownload();}// 初始化window.onload = initMindMap;
</script>
</body>
</html>