diff --git a/simple-mind-map/src/Command.js b/simple-mind-map/src/Command.js index 0d1880e2..7b49ef0d 100644 --- a/simple-mind-map/src/Command.js +++ b/simple-mind-map/src/Command.js @@ -85,7 +85,7 @@ class Command { this.history = this.history.slice(0, this.activeHistoryIndex + 1) this.history.push(simpleDeepClone(data)) this.activeHistoryIndex = this.history.length - 1 - this.mindMap.emit('data_change', data) + this.mindMap.emit('data_change', this.removeDataUid(data)) this.mindMap.emit( 'back_forward', this.activeHistoryIndex, @@ -106,7 +106,7 @@ class Command { this.history.length ) let data = simpleDeepClone(this.history[this.activeHistoryIndex]) - this.mindMap.emit('data_change', data) + this.mindMap.emit('data_change', this.removeDataUid(data)) return data } } @@ -119,9 +119,9 @@ class Command { let len = this.history.length if (this.activeHistoryIndex + step <= len - 1) { this.activeHistoryIndex += step - this.mindMap.emit('back_forward', this.activeHistoryIndex) + this.mindMap.emit('back_forward', this.activeHistoryIndex, this.history.length) let data = simpleDeepClone(this.history[this.activeHistoryIndex]) - this.mindMap.emit('data_change', data) + this.mindMap.emit('data_change', this.removeDataUid(data)) return data } } @@ -130,6 +130,21 @@ class Command { getCopyData() { return copyRenderTree({}, this.mindMap.renderer.renderTree, true) } + + // 移除节点数据中的uid + removeDataUid(data) { + data = simpleDeepClone(data) + let walk = (root) => { + delete root.data.uid + if (root.children && root.children.length > 0) { + root.children.forEach((item) => { + walk(item) + }) + } + } + walk(data) + return data + } } export default Command diff --git a/simple-mind-map/src/Render.js b/simple-mind-map/src/Render.js index 633a1b49..9e55f9f6 100644 --- a/simple-mind-map/src/Render.js +++ b/simple-mind-map/src/Render.js @@ -34,6 +34,8 @@ class Render { this.renderTree = merge({}, this.mindMap.opt.data || {}) // 是否重新渲染 this.reRender = false + this.nodeCache = {} + this.lastNodeCache = {} // 触发render的来源 this.renderSource = '' // 当前激活的节点列表 @@ -235,12 +237,29 @@ class Render { // 渲染 render(callback = () => {}, source) { + // 触发当前重新渲染的来源 this.renderSource = source + // 节点缓存 + this.lastNodeCache = this.nodeCache + this.nodeCache = {} + // 重新渲染需要清除激活状态 if (this.reRender) { this.clearActive() } + // 计算布局 this.layout.doLayout(root => { + // 删除本次渲染时不再需要的节点 + Object.keys(this.lastNodeCache).forEach((uid) => { + if (!this.nodeCache[uid]) { + this.lastNodeCache[uid].destroy() + if (this.lastNodeCache[uid].parent) { + this.lastNodeCache[uid].parent.removeLine() + } + } + }) + // 更新根节点 this.root = root + // 渲染节点 this.root.render(() => { this.mindMap.emit('node_tree_render_end') callback && callback() @@ -326,7 +345,7 @@ class Render { let data = this.mindMap.command.back(step) if (data) { this.renderTree = data - this.mindMap.reRender() + this.mindMap.render() } } @@ -336,7 +355,7 @@ class Render { let data = this.mindMap.command.forward(step) if (data) { this.renderTree = data - this.mindMap.reRender() + this.mindMap.render() } } @@ -656,12 +675,14 @@ class Render { } // 如果开启了富文本,则需要应用到富文本上 if (this.mindMap.richText) { - this.mindMap.richText.showEditText(node) let config = this.mindMap.richText.normalStyleToRichTextStyle({ [prop]: value }) - this.mindMap.richText.formatAllText(config) - this.mindMap.richText.hideEditText() + if (Object.keys(config).length > 0) { + this.mindMap.richText.showEditText(node) + this.mindMap.richText.formatAllText(config) + this.mindMap.richText.hideEditText() + } } this.setNodeDataRender(node, data) // 更新了连线的样式 @@ -716,7 +737,7 @@ class Render { 0, 0 ) - this.mindMap.reRender() + this.mindMap.render() } // 收起所有 @@ -735,7 +756,7 @@ class Render { 0, 0 ) - this.mindMap.reRender() + this.mindMap.render() } // 展开到指定层级 @@ -752,7 +773,7 @@ class Render { 0, 0 ) - this.mindMap.reRender() + this.mindMap.render() } // 切换激活节点的展开状态 diff --git a/simple-mind-map/src/layouts/Base.js b/simple-mind-map/src/layouts/Base.js index 517f7582..7aad590f 100644 --- a/simple-mind-map/src/layouts/Base.js +++ b/simple-mind-map/src/layouts/Base.js @@ -13,6 +13,7 @@ class Base { this.draw = this.mindMap.draw // 根节点 this.root = null + this.nodePool = {} } // 计算节点位置 @@ -33,30 +34,70 @@ class Base { // 概要节点 renderGeneralization() {} + // 通过uid缓存节点 + cacheNode(uid, node) { + // 记录本次渲染时的节点 + this.renderer.nodeCache[uid] = node + // 记录所有渲染时的节点 + this.nodePool[uid] = node + // 如果总缓存数量达到1000,直接清空 + if (Object.keys(this.nodePool).length > 1000) { + this.nodePool = {} + } + } + + // 检查当前来源是否需要重新计算节点大小 + checkIsNeedResizeSources() { + return [CONSTANTS.CHANGE_THEME, CONSTANTS.TRANSFORM_TO_NORMAL_NODE].includes(this.renderer.renderSource) + } + // 创建节点实例 createNode(data, parent, isRoot, layerIndex) { // 创建节点 let newNode = null - // 复用节点 + // 数据上保存了节点引用,那么直接复用节点 if (data && data._node && !this.renderer.reRender) { newNode = data._node newNode.reset() newNode.layerIndex = layerIndex + this.cacheNode(data._node.uid, newNode) // 主题或主题配置改变了需要重新计算节点大小和布局 - if (this.renderer.renderSource === CONSTANTS.CHANGE_THEME) { + if (this.checkIsNeedResizeSources()) { + newNode.getSize() + newNode.needLayout = true + } + } else if (this.nodePool[data.data.uid]) { + // 数据上没有保存节点引用,但是通过uid找到了缓存的节点,也可以复用 + newNode = this.nodePool[data.data.uid] + // 保存该节点上一次的数据 + let lastData = JSON.stringify(newNode.nodeData.data) + newNode.reset() + newNode.nodeData = newNode.handleData(data || {}) + newNode.layerIndex = layerIndex + this.cacheNode(data.data.uid, newNode) + data._node = newNode + // 主题或主题配置改变了需要重新计算节点大小和布局 + let isResizeSource = this.checkIsNeedResizeSources() + // 节点数据改变了需要重新计算节点大小和布局 + let isNodeDataChange = lastData !== JSON.stringify(data.data) + if (isResizeSource || isNodeDataChange) { newNode.getSize() newNode.needLayout = true } } else { // 创建新节点 + let uid = this.mindMap.uid++ newNode = new Node({ data, - uid: this.mindMap.uid++, + uid, renderer: this.renderer, mindMap: this.mindMap, draw: this.draw, layerIndex }) + // uid保存到数据上,为了节点复用 + data.data.uid = uid + this.cacheNode(uid, newNode) // 数据关联实际节点 data._node = newNode if (data.data.isActive) { diff --git a/simple-mind-map/src/utils/index.js b/simple-mind-map/src/utils/index.js index 3f0bf725..3dade497 100644 --- a/simple-mind-map/src/utils/index.js +++ b/simple-mind-map/src/utils/index.js @@ -141,13 +141,14 @@ export const copyNodeTree = (tree, root, removeActiveState = false, keepId = fal tree.data = simpleDeepClone(root.nodeData ? root.nodeData.data : root.data) // 去除节点id,因为节点id不能重复 if (tree.data.id && !keepId) delete tree.data.id + if (tree.data.uid) delete tree.data.uid if (removeActiveState) { tree.data.isActive = false } tree.children = [] if (root.children && root.children.length > 0) { root.children.forEach((item, index) => { - tree.children[index] = copyNodeTree({}, item, removeActiveState) + tree.children[index] = copyNodeTree({}, item, removeActiveState, keepId) }) } else if ( root.nodeData && @@ -155,7 +156,7 @@ export const copyNodeTree = (tree, root, removeActiveState = false, keepId = fal root.nodeData.children.length > 0 ) { root.nodeData.children.forEach((item, index) => { - tree.children[index] = copyNodeTree({}, item, removeActiveState) + tree.children[index] = copyNodeTree({}, item, removeActiveState, keepId) }) } return tree