Feat:1.优化节点样式设置,如果设置的是连线样式不触发节点重新创建;2.优化富文本模式下同时对大量节点调用setStyle方法修改文本样式非常慢的问题

This commit is contained in:
街角小林 2024-11-27 19:06:14 +08:00
parent 7258ed9ea7
commit 508d8fe357
5 changed files with 82 additions and 52 deletions

View File

@ -336,7 +336,7 @@ class MindMap {
this.opt.themeConfig = config this.opt.themeConfig = config
if (!notRender) { if (!notRender) {
// 检查改变的是否是节点大小无关的主题属性 // 检查改变的是否是节点大小无关的主题属性
let res = checkIsNodeSizeIndependenceConfig(changedConfig) const res = checkIsNodeSizeIndependenceConfig(changedConfig)
this.render(null, res ? '' : CONSTANTS.CHANGE_THEME) this.render(null, res ? '' : CONSTANTS.CHANGE_THEME)
} }
} }

View File

@ -34,7 +34,8 @@ import {
formatGetNodeGeneralization, formatGetNodeGeneralization,
sortNodeList, sortNodeList,
throttle, throttle,
checkClipboardReadEnable checkClipboardReadEnable,
isNodeNotNeedRenderData
} from '../../utils' } from '../../utils'
import { shapeList } from './node/Shape' import { shapeList } from './node/Shape'
import { lineStyleProps } from '../../theme/default' import { lineStyleProps } from '../../theme/default'
@ -1580,14 +1581,15 @@ class Render {
// 设置节点样式 // 设置节点样式
setNodeStyle(node, prop, value) { setNodeStyle(node, prop, value) {
let data = { const data = {
[prop]: value [prop]: value
} }
// 如果开启了富文本,则需要应用到富文本上 // 如果开启了富文本,则需要应用到富文本上
if (this.hasRichTextPlugin()) { if (
this.mindMap.richText.setNotActiveNodeStyle(node, { this.hasRichTextPlugin() &&
[prop]: value this.mindMap.richText.isHasRichTextStyle(data)
}) ) {
data.resetRichText = true
} }
this.setNodeDataRender(node, data) this.setNodeDataRender(node, data)
// 更新了连线的样式 // 更新了连线的样式
@ -1598,10 +1600,13 @@ class Render {
// 设置节点多个样式 // 设置节点多个样式
setNodeStyles(node, style) { setNodeStyles(node, style) {
let data = { ...style } const data = { ...style }
// 如果开启了富文本,则需要应用到富文本上 // 如果开启了富文本,则需要应用到富文本上
if (this.hasRichTextPlugin()) { if (
this.mindMap.richText.setNotActiveNodeStyle(node, style) this.hasRichTextPlugin() &&
this.mindMap.richText.isHasRichTextStyle(data)
) {
data.resetRichText = true
} }
this.setNodeDataRender(node, data) this.setNodeDataRender(node, data)
// 更新了连线的样式 // 更新了连线的样式
@ -1964,6 +1969,10 @@ class Render {
// 设置节点数据,并判断是否渲染 // 设置节点数据,并判断是否渲染
setNodeDataRender(node, data, notRender = false) { setNodeDataRender(node, data, notRender = false) {
this.mindMap.execCommand('SET_NODE_DATA', node, data) this.mindMap.execCommand('SET_NODE_DATA', node, data)
if (isNodeNotNeedRenderData(data)) {
this.mindMap.emit('node_tree_render_end')
return
}
this.reRenderNodeCheckChange(node, notRender) this.reRenderNodeCheckChange(node, notRender)
} }

View File

@ -57,6 +57,14 @@ class RichText {
this.isCompositing = false this.isCompositing = false
this.textNodePaddingX = 6 this.textNodePaddingX = 6
this.textNodePaddingY = 4 this.textNodePaddingY = 4
this.supportStyleProps = [
'fontFamily',
'fontSize',
'fontWeight',
'fontStyle',
'textDecoration',
'color'
]
this.initOpt() this.initOpt()
this.extendQuill() this.extendQuill()
this.appendCss() this.appendCss()
@ -675,14 +683,7 @@ class RichText {
// 再将样式恢复为当前主题改节点的默认样式 // 再将样式恢复为当前主题改节点的默认样式
const style = {} const style = {}
if (this.node) { if (this.node) {
;[ this.supportStyleProps.forEach(key => {
'fontFamily',
'fontSize',
'fontWeight',
'fontStyle',
'textDecoration',
'color'
].forEach(key => {
style[key] = this.node.style.merge(key) style[key] = this.node.style.merge(key)
}) })
} }
@ -713,14 +714,7 @@ class RichText {
if (!this.node) return if (!this.node) return
if (clear) { if (clear) {
// 清除文本样式 // 清除文本样式
;[ this.supportStyleProps.forEach(prop => {
'fontFamily',
'fontSize',
'fontWeight',
'fontStyle',
'textDecoration',
'color'
].forEach(prop => {
delete this.node.nodeData.data[prop] delete this.node.nodeData.data[prop]
}) })
} else { } else {
@ -795,6 +789,18 @@ class RichText {
return data return data
} }
// 判断一个对象是否包含了富文本支持的样式字段
isHasRichTextStyle(obj) {
const keys = Object.keys(obj)
for (let i = 0; i < keys.length; i++) {
const key = keys[i]
if (this.supportStyleProps.includes(key)) {
return true
}
}
return false
}
// 给未激活的节点设置富文本样式 // 给未激活的节点设置富文本样式
setNotActiveNodeStyle(node, style) { setNotActiveNodeStyle(node, style) {
const config = this.normalStyleToRichTextStyle(style) const config = this.normalStyleToRichTextStyle(style)
@ -807,17 +813,9 @@ class RichText {
// 检查指定节点是否存在自定义的富文本样式 // 检查指定节点是否存在自定义的富文本样式
checkNodeHasCustomRichTextStyle(node) { checkNodeHasCustomRichTextStyle(node) {
const list = [
'fontFamily',
'fontSize',
'fontWeight',
'fontStyle',
'textDecoration',
'color'
]
const nodeData = node instanceof MindMapNode ? node.getData() : node const nodeData = node instanceof MindMapNode ? node.getData() : node
for (let i = 0; i < list.length; i++) { for (let i = 0; i < this.supportStyleProps.length; i++) {
if (nodeData[list[i]] !== undefined) { if (nodeData[this.supportStyleProps[i]] !== undefined) {
return true return true
} }
} }

View File

@ -15,6 +15,12 @@ export default {
lineColor: '#549688', lineColor: '#549688',
// 连线样式 // 连线样式
lineDasharray: 'none', lineDasharray: 'none',
// 连线是否开启流动效果仅在虚线时有效需要注册LineFlow插件
lineFlow: false,
// 流动效果一个周期的时间单位s
lineFlowDuration: 1,
// 流动方向是否是从父节点到子节点
lineFlowForward: true,
// 连线风格 // 连线风格
lineStyle: 'straight', // 曲线curve【仅支持logicalStructure、mindMap、verticalTimeline三种结构】、直线straight、直连direct【仅支持logicalStructure、mindMap、organizationStructure、verticalTimeline四种结构】 lineStyle: 'straight', // 曲线curve【仅支持logicalStructure、mindMap、verticalTimeline三种结构】、直线straight、直连direct【仅支持logicalStructure、mindMap、organizationStructure、verticalTimeline四种结构】
// 曲线连接时,根节点和其他节点的连接线样式保持统一,默认根节点为 ( 型,其他节点为 { 型设为true后都为 { 型。仅支持logicalStructure、mindMap两种结构 // 曲线连接时,根节点和其他节点的连接线样式保持统一,默认根节点为 ( 型,其他节点为 { 型设为true后都为 { 型。仅支持logicalStructure、mindMap两种结构
@ -88,8 +94,15 @@ export default {
hoverRectColor: '', hoverRectColor: '',
// 点鼠标hover和激活时显示的矩形边框的圆角大小 // 点鼠标hover和激活时显示的矩形边框的圆角大小
hoverRectRadius: 5 hoverRectRadius: 5
// paddingX: 15, // 下列样式也支持给节点设置,用于覆盖最外层的设置
// paddingY: 5 // paddingX,
// paddingY,
// lineWidth,
// lineColor,
// lineDasharray,
// lineFlow,
// lineFlowDuration,
// lineFlowForward
}, },
// 二级节点样式 // 二级节点样式
second: { second: {
@ -115,8 +128,6 @@ export default {
lineMarkerDir: 'end', lineMarkerDir: 'end',
hoverRectColor: '', hoverRectColor: '',
hoverRectRadius: 5 hoverRectRadius: 5
// paddingX: 15,
// paddingY: 5
}, },
// 三级及以下节点样式 // 三级及以下节点样式
node: { node: {
@ -142,8 +153,6 @@ export default {
lineMarkerDir: 'end', lineMarkerDir: 'end',
hoverRectColor: '', hoverRectColor: '',
hoverRectRadius: 5 hoverRectRadius: 5
// paddingX: 15,
// paddingY: 5
}, },
// 概要节点样式 // 概要节点样式
generalization: { generalization: {
@ -168,8 +177,6 @@ export default {
endDir: [1, 0], endDir: [1, 0],
hoverRectColor: '', hoverRectColor: '',
hoverRectRadius: 5 hoverRectRadius: 5
// paddingX: 15,
// paddingY: 5
} }
} }
@ -197,14 +204,12 @@ const nodeSizeIndependenceList = [
'rootLineKeepSameInCurve', 'rootLineKeepSameInCurve',
'rootLineStartPositionKeepSameInCurve', 'rootLineStartPositionKeepSameInCurve',
'showLineMarker', 'showLineMarker',
'gradientStyle',
'lineRadius', 'lineRadius',
'startColor',
'endColor',
'startDir',
'endDir',
'hoverRectColor', 'hoverRectColor',
'hoverRectRadius' 'hoverRectRadius',
'lineFlow',
'lineFlowDuration',
'lineFlowForward'
] ]
export const checkIsNodeSizeIndependenceConfig = config => { export const checkIsNodeSizeIndependenceConfig = config => {
let keys = Object.keys(config) let keys = Object.keys(config)
@ -220,9 +225,13 @@ export const checkIsNodeSizeIndependenceConfig = config => {
return true return true
} }
// 连线的样式
export const lineStyleProps = [ export const lineStyleProps = [
'lineColor', 'lineColor',
'lineDasharray', 'lineDasharray',
'lineWidth', 'lineWidth',
'lineMarkerDir' 'lineMarkerDir',
'lineFlow',
'lineFlowDuration',
'lineFlowForward'
] ]

View File

@ -6,6 +6,7 @@ import {
import MersenneTwister from './mersenneTwister' import MersenneTwister from './mersenneTwister'
import { ForeignObject } from '@svgdotjs/svg.js' import { ForeignObject } from '@svgdotjs/svg.js'
import merge from 'deepmerge' import merge from 'deepmerge'
import { lineStyleProps } from '../theme/default'
// 深度优先遍历树 // 深度优先遍历树
export const walk = ( export const walk = (
@ -507,13 +508,14 @@ export const addHtmlStyle = (html, tag, style) => {
if (!addHtmlStyleEl) { if (!addHtmlStyleEl) {
addHtmlStyleEl = document.createElement('div') addHtmlStyleEl = document.createElement('div')
} }
const tags = Array.isArray(tag) ? tag : [tag]
addHtmlStyleEl.innerHTML = html addHtmlStyleEl.innerHTML = html
let walk = root => { let walk = root => {
let childNodes = root.childNodes let childNodes = root.childNodes
childNodes.forEach(node => { childNodes.forEach(node => {
if (node.nodeType === 1) { if (node.nodeType === 1) {
// 元素节点 // 元素节点
if (node.tagName.toLowerCase() === tag) { if (tags.includes(node.tagName.toLowerCase())) {
node.style.cssText = style node.style.cssText = style
} else { } else {
walk(node) walk(node)
@ -790,6 +792,18 @@ export const checkIsNodeStyleDataKey = key => {
return false return false
} }
// 判断一个对象是否不需要触发节点重新创建
export const isNodeNotNeedRenderData = config => {
const list = [...lineStyleProps] // 节点连线样式
const keys = Object.keys(config)
for (let i = 0; i < keys.length; i++) {
if (!list.includes(keys[i])) {
return false
}
}
return true
}
// 合并图标数组 // 合并图标数组
// const data = [ // const data = [
// { type: 'priority', name: '优先级图标', list: [{ name: '1', icon: 'a' }, { name: 2, icon: 'b' }] }, // { type: 'priority', name: '优先级图标', list: [{ name: '1', icon: 'a' }, { name: 2, icon: 'b' }] },