From 17bbf3fd47214576df9842207b770efb7eab0ee9 Mon Sep 17 00:00:00 2001 From: chao Date: Fri, 24 Jan 2025 23:40:28 +0800 Subject: [PATCH] feats: add word wrap --- .../src/constants/defaultOptions.js | 2 + .../core/render/node/nodeCreateContents.js | 71 ++++++++++++++----- 2 files changed, 56 insertions(+), 17 deletions(-) diff --git a/simple-mind-map/src/constants/defaultOptions.js b/simple-mind-map/src/constants/defaultOptions.js index 01aa0244..b6b5ab33 100644 --- a/simple-mind-map/src/constants/defaultOptions.js +++ b/simple-mind-map/src/constants/defaultOptions.js @@ -53,6 +53,8 @@ export const defaultOpt = { */ // 达到该宽度文本自动换行 textAutoWrapWidth: 500, + // 以单词为最小单位计算自动换行 + textAutoWrapBreakWord: false, // 自定义鼠标滚轮事件处理 // 可以传一个函数,回调参数为事件对象 customHandleMousewheel: null, diff --git a/simple-mind-map/src/core/render/node/nodeCreateContents.js b/simple-mind-map/src/core/render/node/nodeCreateContents.js index 6598f607..54eadbe2 100644 --- a/simple-mind-map/src/core/render/node/nodeCreateContents.js +++ b/simple-mind-map/src/core/render/node/nodeCreateContents.js @@ -219,6 +219,31 @@ function createRichTextNode(specifyText) { } } +function wrapTextByUnit(units, maxWidth, style, joinChar = '') { + const lines = [] + let line = [] + + while (units.length) { + const unit = units.shift() + const text = [...line, unit].join(joinChar) + + if (measureText(text, style).width <= maxWidth) { + line.push(unit) + } else { + if (line.length > 0) { + lines.push(line.join(joinChar)) + } + line = [unit] + } + } + + if (line.length > 0) { + lines.push(line.join(joinChar)) + } + + return lines +} + // 创建文本节点 function createTextNode(specifyText) { if (this.getData('needUpdate')) { @@ -240,32 +265,42 @@ function createTextNode(specifyText) { if (!isUndef(text)) { textArr = String(text).split(/\n/gim) } - const { textAutoWrapWidth: maxWidth, emptyTextMeasureHeightText } = - this.mindMap.opt + + const { + textAutoWrapWidth: maxWidth, + emptyTextMeasureHeightText, + textAutoWrapBreakWord + } = this.mindMap.opt + let isMultiLine = textArr.length > 1 + textArr.forEach((item, index) => { - let arr = item.split('') - let lines = [] - let line = [] - while (arr.length) { - let str = arr.shift() - let text = [...line, str].join('') - if (measureText(text, this.style).width <= maxWidth) { - line.push(str) - } else { - lines.push(line.join('')) - line = [str] - } - } - if (line.length > 0) { - lines.push(line.join('')) + let lines + + if (textAutoWrapBreakWord) { + const words = item.split(/\s+/) + lines = wrapTextByUnit(words, maxWidth, this.style, ' ') + + lines = lines.flatMap((line) => { + if (measureText(line, this.style).width > maxWidth) { + const chars = line.split('') + return wrapTextByUnit(chars, maxWidth, this.style, '') + } + return [line] + }) + } else { + const chars = item.split('') + lines = wrapTextByUnit(chars, maxWidth, this.style, '') } + if (lines.length > 1) { isMultiLine = true } textArr[index] = lines.join('\n') }) + textArr = textArr.join('\n').replace(/\n$/g, '').split(/\n/gim) + textArr.forEach((item, index) => { // 避免尾部的空行不占宽度 // 同时解决该问题:https://github.com/wanglin2/mind-map/issues/1037 @@ -289,11 +324,13 @@ function createTextNode(specifyText) { const tmpBbox = tmpNode.bbox() height = tmpBbox.height } + width = Math.min(Math.ceil(width), maxWidth) height = Math.ceil(height) g.attr('data-width', width) g.attr('data-height', height) g.attr('data-ismultiLine', isMultiLine || textArr.length > 1) + return { node: g, width,