simple-mind-map 实战:打造交互式思维导图

前言

最近接收到一个打造知识图库的需求,类似于实现一个思维导图,具体的功能是:

  1. 展示一个树状的思维导图,节点的样式和内容是自定义的;
  2. 可以实现增加和删除节点;
  3. 像chatgpt一样动态的生成节点;
  4. 在节点中进行操作(勾选);

自己实现的话太过复杂,在网上搜索找到了simple-mind-map这个库。

simple-mind-map 是一个轻量级、可定制、无依赖的思维导图 JavaScript 库;基本的使用可以查看文档wanglin2.github.io/mind-map-do...

记录下来是因为踩了很多坑以及具体实现的过程中有许多困难的点,希望可以帮助大家避开。

难点

难点一:自定义节点

要实现每一层节点样式不同,有的是图片加文字,有的是添加背景等,在原节点的基础上设置样式是行不通的,这时候就要用到isUseCustomNodeContent和customCreateNodeContent。 isUseCustomNodeContent: 是否使用自定义节点内容,如果设置为 true,则需要使用 customCreateNodeContent 选项创建节点内容。官方提供了在vue和react中使用的示例,地址:wanglin2.github.io/mind-map-do...

其次,连接的线条的样式和颜色可以通过设置自定义主题来设置:github.com/wanglin2/mi...

难点二:包含输入框的节点无法输入

需要注意的是:在使用了自定义节点并设置节点包含输入框时,输入框无法输入!

官方给出了提示,但是我在第一次看的时候漏掉了这句话,导致浪费了好多时间😑 具体的写法是:

ini 复制代码
<Input autoFocus onMouseDown={(e) => { e.stopPropagation(); }} />

难点三:像AI一样动态生成节点

官方给出了使用updateData的方法wanglin2.github.io/mind-map-do...

但是具体的业务是我已经拿到了这棵树的所有数据,只需要一个一个展示出来,怎么样把手中的树一层一层的给到mindMap实例呢,这里提供一种方法:

ini 复制代码
function* simulateGenerateNodes(fullData) {
  /**
   * 生成器函数,用于模拟逐层或逐个节点生成思维导图的数据。
   *
   * @param {object} fullData - 完整的思维导图数据,包含根节点和所有子节点。
   *                           fullData 的数据结构应符合 simple-mind-map 的数据格式要求,
   *                           即包含 data 和 children 属性,其中 data 属性建议包含 uid 和 parentUid。
   * @yields {object} - 每次调用 next() 方法时,会返回当前的思维导图数据状态。
   *                    初始状态只包含根节点,后续每次迭代会逐步添加节点,直到所有节点都被添加。
   */
  // 初始化根节点,只包含根节点的 data 数据,children 为空数组
  const root = {
    data: { ...fullData.data },
    children: [],
  };

  // 缓存当前层级的节点,初始值为根节点
  let currentLevelNodes = [root];
  // 缓存下一层级的数据,初始值为根节点的 children 数据
  let nextLevelData = fullData.children;

  // 循环直到 nextLevelData 为空或长度为 0
  while (nextLevelData && nextLevelData.length > 0) {
    // 缓存下一层级的节点
    const nextLevelNodes = [];
    // 遍历当前层级的所有节点
    for (let i = 0; i < currentLevelNodes.length; i++) {
      const currentNode = currentLevelNodes[i];
      // 根据 parentUid 过滤出当前节点的直接子节点数据
      const currentData = nextLevelData.filter(
        (item) => item.data.parentUid === currentNode.data.uid,
      );
      // 遍历当前节点的直接子节点数据
      for (let j = 0; j < currentData.length; j++) {
        // 创建新的节点对象,只包含 data 数据,children 为空数组
        const newNode = {
          data: { ...currentData[j].data },
          children: [],
        };
        // 将新节点添加到当前节点的 children 数组中
        currentNode.children.push(newNode);
        // 将新节点添加到 nextLevelNodes 数组中
        nextLevelNodes.push(newNode);
        // 注释掉这行代码,因为我们希望每次生成一层之后再 yield,而不是每个节点
        // yield JSON.parse(JSON.stringify(root)); // Yield after adding each node
      }
    }
    // 当添加完一层后,yield 当前的树状态
    yield JSON.parse(JSON.stringify(root));
    // 更新 currentLevelNodes 为下一层级的节点
    currentLevelNodes = nextLevelNodes;
    // 更新 nextLevelData 为下一层级节点的 children 数据
    nextLevelData = nextLevelData.flatMap((node) => node.children || []);
  }
}

使用示例

import 复制代码
const generator = simulateGenerateNodes(fullMindMapData);

const intervalId = setInterval(() => {
  const next = generator.next();
  if (!next.done) {
    setMindMapData(next.value); // 更新思维导图数据
  } else {
    clearInterval(intervalId);
  }
}, 100); // 每 100 毫秒更新一次

基础的使用

1、鼠标悬浮节点,出现删除/新增按钮

在customCreateNodeContent中添加鼠标悬浮事件:

customCreateNodeContent: 复制代码
      let div = document.createElement('div');
      // 添加鼠标悬浮事件监听
        div.addEventListener('mouseenter', function (e) {
          console.log(div.getBoundingClientRect());
          const { top, left, width, height } = div.getBoundingClientRect();
          addIcon.style.top = `${top + height / 2 - 10}px`;
          addIcon.style.left = `${left + width - 4}px`;
          addIcon.style.display = 'block';
          addIcon.addEventListener('click', function (e) {
            setCreateNodeParent(node);
            setIsModalOpen(true);
          });
        });
        addIcon.addEventListener('mouseenter', function (e) {
          addIcon.style.display = 'block';
        });

        addIcon.addEventListener('mouseleave', function (e) {
          addIcon.style.display = 'none';
        });

        // 添加鼠标离开事件监听
        div.addEventListener('mouseleave', function () {
          addIcon.style.display = 'none';
        });
        })

    div.className = 'node_1';
        div.style.cssText = `xxx`;
        div.innerHTML = `
            <div style='display:flex;flex-direction: column;align-items: center;font-size: 22rem'>
                <img src=${sysImg[count]} style='width: 120rem;height: 100rem;'></img>
                <div style=';border-radius: 4px;
                  background: #0BE59D;color:#19232D;padding:4rem 18rem;font-size:16rem;margin-top:-20rem
                    '>${node.nodeData.data.text}</div>
            </div>
      `;
      return div;
    }

鼠标悬浮的过程中滚动鼠标会出现增加/删除按钮移位的情况,

可以这样解决:

ini 复制代码
mindMap.on('mousewheel', () => {
      addIcon.style.display = 'none';
    });

2、删除节点

const 复制代码
mindMap.execCommand('REMOVE_NODE', node);

3、新增节点

php 复制代码
// 在根节点下插入一个子节点
mindMap.execCommand('INSERT_CHILD_NODE', {
  data: {
    text: '新的子节点',
    uid: 'xxx', // uid如果在数据中不设置,simple-mind-map会自动生成,为了后续操作更方便,最好自己设置
  },
});

// 在指定节点下插入一个子节点
const parentNode = mindMap.renderer.findNodeByUid('指定节点的uid');
mindMap.execCommand('INSERT_CHILD_NODE', true, parentNode, {
  data: {
    text: '新的子节点',
    uid: 'xxx', 
    parentUid: parentNode.data.uid 
  },
});

4、查找节点

ini 复制代码
const node = mindMap.renderer.findNodeByUid('节点的uid'); // 通过 uid 查找
const node = mindMap.renderer.findNodeByData('text', '节点文本'); // 通过 data 属性查找

5、修改节点

ini 复制代码
const node = mindMap.renderer.findNodeByUid('要修改的节点的uid');
mindMap.execCommand('UPDATE_NODE_TEXT', node, '新的节点文本');

总结:

simple-mind-map 是一个功能强大且易于使用的思维导图库。它提供了丰富的配置选项、API 方法和事件,可以满足各种场景下的需求。通过自定义主题和节点内容,可以创建出高度定制化的思维导图。特别是其自定义节点内容和动态生成节点的功能,使得可以构建出更加复杂和交互性更强的思维导图应用。

相关推荐
Boilermaker199226 分钟前
【Java EE】SpringIoC
前端·数据库·spring
中微子38 分钟前
JavaScript 防抖与节流:从原理到实践的完整指南
前端·javascript
天天向上10241 小时前
Vue 配置打包后可编辑的变量
前端·javascript·vue.js
芬兰y1 小时前
VUE 带有搜索功能的穿梭框(简单demo)
前端·javascript·vue.js
好果不榨汁1 小时前
qiankun 路由选择不同模式如何书写不同的配置
前端·vue.js
小蜜蜂dry1 小时前
Fetch 笔记
前端·javascript
拾光拾趣录1 小时前
列表分页中的快速翻页竞态问题
前端·javascript
小old弟1 小时前
vue3,你看setup设计详解,也是个人才
前端
Lefan1 小时前
一文了解什么是Dart
前端·flutter·dart
Patrick_Wilson1 小时前
青苔漫染待客迟
前端·设计模式·架构