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 方法和事件,可以满足各种场景下的需求。通过自定义主题和节点内容,可以创建出高度定制化的思维导图。特别是其自定义节点内容和动态生成节点的功能,使得可以构建出更加复杂和交互性更强的思维导图应用。

相关推荐
♟彦♟1 小时前
web-前端小实验2
前端
G_qingxin1 小时前
前端排序算法
前端·算法·排序算法
He guolin1 小时前
[Vue]的快速上手
前端·javascript·vue.js
flying robot2 小时前
Rust的对web生态的影响
开发语言·前端·rust
艾斯特_2 小时前
window.open 被浏览器拦截解决方案
前端·javascript
2401_897579652 小时前
软件架构的康威定律:AI如何重构团队协作模式
前端·人工智能·重构
小破孩呦2 小时前
Vue3中使用 Vue Flow 流程图方法
前端·vue.js·流程图
周尛先森2 小时前
在 Vue.js 3 中使用 Composition API 的 provide/inject
前端
Vec[95]2 小时前
将光源视角的深度贴图应用于摄像机视角的渲染
前端·人工智能·贴图
zhangfeng11332 小时前
要在Chrome和Firefox中获取LWP格式的cookie文件,可以通过以下步骤实现:
前端·chrome·firefox