From b7910c466590801340009151986959bba76e0308 Mon Sep 17 00:00:00 2001
From: wanglin2 <1013335014@qq.com>
Date: Sat, 22 Apr 2023 14:24:05 +0800
Subject: [PATCH 1/8] =?UTF-8?q?=E4=BC=98=E5=8C=96=E8=8A=82=E7=82=B9?=
=?UTF-8?q?=E7=BC=96=E8=BE=91?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
simple-mind-map/src/RichText.js | 15 ++++++++-------
simple-mind-map/src/Style.js | 4 ++--
simple-mind-map/src/TextEdit.js | 7 ++++---
simple-mind-map/src/utils/nodeCreateContents.js | 5 +++++
4 files changed, 19 insertions(+), 12 deletions(-)
diff --git a/simple-mind-map/src/RichText.js b/simple-mind-map/src/RichText.js
index f419f151..284a8674 100644
--- a/simple-mind-map/src/RichText.js
+++ b/simple-mind-map/src/RichText.js
@@ -65,14 +65,15 @@ class RichText {
.ql-container.ql-snow {
border: none;
}
+
+ .smm-richtext-node-wrap p {
+ font-family: auto;
+ }
+
+ .smm-richtext-node-edit-wrap p {
+ font-family: auto;
+ }
`
- // .smm-richtext-node-wrap p {
- // display: flex;
- // }
-
- // .smm-richtext-node-edit-wrap p {
- // display: flex;
- // }
this.styleEl = document.createElement('style')
this.styleEl.type = 'text/css'
this.styleEl.innerHTML = cssText
diff --git a/simple-mind-map/src/Style.js b/simple-mind-map/src/Style.js
index 72f109c9..b0f95fd3 100644
--- a/simple-mind-map/src/Style.js
+++ b/simple-mind-map/src/Style.js
@@ -120,11 +120,11 @@ class Style {
}
// html文字节点
- domText(node, fontSizeScale = 1, textLines) {
+ domText(node, fontSizeScale = 1, isMultiLine) {
node.style.fontFamily = this.merge('fontFamily')
node.style.fontSize = this.merge('fontSize') * fontSizeScale + 'px'
node.style.fontWeight = this.merge('fontWeight') || 'normal'
- node.style.lineHeight = textLines === 1 ? 'normal' : this.merge('lineHeight')
+ node.style.lineHeight = !isMultiLine ? 'normal' : this.merge('lineHeight')
node.style.fontStyle = this.merge('fontStyle')
}
diff --git a/simple-mind-map/src/TextEdit.js b/simple-mind-map/src/TextEdit.js
index adb2c9de..80d7e90b 100644
--- a/simple-mind-map/src/TextEdit.js
+++ b/simple-mind-map/src/TextEdit.js
@@ -110,7 +110,8 @@ export default class TextEdit {
let lineHeight = node.style.merge('lineHeight')
let fontSize = node.style.merge('fontSize')
let textLines = (this.cacheEditingText || node.nodeData.data.text).split(/\n/gim)
- node.style.domText(this.textEditNode, scale, textLines.length)
+ let isMultiLine = node._textData.node.attr('data-ismultiLine') === 'true'
+ node.style.domText(this.textEditNode, scale, isMultiLine)
this.textEditNode.style.zIndex = this.mindMap.opt.nodeTextEditZIndex
this.textEditNode.innerHTML = textLines.join('
')
this.textEditNode.style.minWidth = rect.width + 10 + 'px'
@@ -119,8 +120,8 @@ export default class TextEdit {
this.textEditNode.style.top = rect.top + 'px'
this.textEditNode.style.display = 'block'
this.textEditNode.style.maxWidth = this.mindMap.opt.textAutoWrapWidth * scale + 'px'
- if (textLines.length > 1 && lineHeight !== 1) {
- this.textEditNode.style.transform = `translateY(${-((lineHeight * fontSize - fontSize) / 2 - 2) * scale}px)`
+ if (isMultiLine && lineHeight !== 1) {
+ this.textEditNode.style.transform = `translateY(${-((lineHeight * fontSize - fontSize) / 2) * scale}px)`
}
this.showTextEdit = true
// 选中文本
diff --git a/simple-mind-map/src/utils/nodeCreateContents.js b/simple-mind-map/src/utils/nodeCreateContents.js
index 9c507af1..4e729947 100644
--- a/simple-mind-map/src/utils/nodeCreateContents.js
+++ b/simple-mind-map/src/utils/nodeCreateContents.js
@@ -96,6 +96,7 @@ function createTextNode() {
let textStyle = this.style.getTextFontStyle()
let textArr = this.nodeData.data.text.split(/\n/gim)
let maxWidth = this.mindMap.opt.textAutoWrapWidth
+ let isMultiLine = false
textArr.forEach((item, index) => {
let arr = item.split('')
let lines = []
@@ -113,6 +114,9 @@ function createTextNode() {
if (line.length > 0) {
lines.push(line.join(''))
}
+ if (lines.length > 1) {
+ isMultiLine = true
+ }
textArr[index] = lines.join('\n')
})
textArr = textArr.join('\n').split(/\n/gim)
@@ -127,6 +131,7 @@ function createTextNode() {
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,
From 2cbfe4f0e7d3fdb5ea9f0a9f7d3d3cb3912d7d8d Mon Sep 17 00:00:00 2001
From: wanglin2 <1013335014@qq.com>
Date: Sun, 23 Apr 2023 14:09:41 +0800
Subject: [PATCH 2/8] =?UTF-8?q?Feature=EF=BC=9A=E5=AF=8C=E6=96=87=E6=9C=AC?=
=?UTF-8?q?=E6=A8=A1=E5=BC=8F=E5=AF=BC=E5=87=BA=E6=94=B9=E4=B8=BA=E4=BD=BF?=
=?UTF-8?q?=E7=94=A8html2canvas=E8=BD=AC=E6=8D=A2=E6=95=B4=E4=B8=AAsvg?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
simple-mind-map/index.js | 6 +-
simple-mind-map/src/Export.js | 41 ++++-----
simple-mind-map/src/RichText.js | 108 ++++++++---------------
web/src/lang/en_us.js | 2 +-
web/src/lang/zh_cn.js | 2 +-
web/src/pages/Edit/components/Export.vue | 40 +++++----
6 files changed, 83 insertions(+), 116 deletions(-)
diff --git a/simple-mind-map/index.js b/simple-mind-map/index.js
index da5beaaa..737a2103 100644
--- a/simple-mind-map/index.js
+++ b/simple-mind-map/index.js
@@ -298,7 +298,11 @@ class MindMap {
this.execCommand('CLEAR_ACTIVE_NODE')
this.command.clearHistory()
this.command.addHistory()
- this.renderer.renderTree = data
+ if (this.richText) {
+ this.renderer.renderTree = this.richText.handleSetData(data)
+ } else {
+ this.renderer.renderTree = data
+ }
this.reRender()
}
diff --git a/simple-mind-map/src/Export.js b/simple-mind-map/src/Export.js
index aab5b1dc..245d8617 100644
--- a/simple-mind-map/src/Export.js
+++ b/simple-mind-map/src/Export.js
@@ -27,7 +27,7 @@ class Export {
}
// 获取svg数据
- async getSvgData(domToImage) {
+ async getSvgData() {
let { exportPaddingX, exportPaddingY } = this.mindMap.opt
let { svg, svgHTML } = this.mindMap.getSvgData({
paddingX: exportPaddingX,
@@ -44,24 +44,14 @@ class Export {
if (imageList.length > 0) {
svgHTML = svg.svg()
}
- // 如果开启了富文本编辑,需要把svg中的dom元素转换成图片
- let nodeWithDomToImg = null
- if (domToImage && this.mindMap.richText) {
- let res = await this.mindMap.richText.handleSvgDomElements(svg)
- if (res) {
- nodeWithDomToImg = res.svg
- svgHTML = res.svgHTML
- }
- }
return {
node: svg,
- str: svgHTML,
- nodeWithDomToImg
+ str: svgHTML
}
}
// svg转png
- svgToPng(svgSrc) {
+ svgToPng(svgSrc, transparent) {
return new Promise((resolve, reject) => {
const img = new Image()
// 跨域图片需要添加这个属性,否则画布被污染了无法导出图片
@@ -73,7 +63,9 @@ class Export {
canvas.height = img.height + this.exportPadding * 2
let ctx = canvas.getContext('2d')
// 绘制背景
- await this.drawBackgroundToCanvas(ctx, canvas.width, canvas.height)
+ if (!transparent) {
+ await this.drawBackgroundToCanvas(ctx, canvas.width, canvas.height)
+ }
// 图片绘制到canvas里
ctx.drawImage(
img,
@@ -140,8 +132,14 @@ class Export {
* 方法1.把svg的图片都转化成data:url格式,再转换
* 方法2.把svg的图片提取出来再挨个绘制到canvas里,最后一起转换
*/
- async png() {
- let { str } = await this.getSvgData(true)
+ async png(name, transparent = false) {
+ let { node, str } = await this.getSvgData()
+ // 如果开启了富文本,则使用htmltocanvas转换为图片
+ if (this.mindMap.richText) {
+ let res = await this.mindMap.richText.handleExportPng(node.node)
+ let imgDataUrl = await this.svgToPng(res, transparent)
+ return imgDataUrl
+ }
// 转换成blob数据
let blob = new Blob([str], {
type: 'image/svg+xml'
@@ -149,7 +147,7 @@ class Export {
// 转换成data:url数据
let svgUrl = URL.createObjectURL(blob)
// 绘制到canvas上
- let imgDataUrl = await this.svgToPng(svgUrl)
+ let imgDataUrl = await this.svgToPng(svgUrl, transparent)
URL.revokeObjectURL(svgUrl)
return imgDataUrl
}
@@ -209,15 +207,12 @@ class Export {
}
// 导出为svg
- // domToImage:是否将svg中的dom节点转换成图片的形式
// plusCssText:附加的css样式,如果svg中存在dom节点,想要设置一些针对节点的样式可以通过这个参数传入
- async svg(name, domToImage = false, plusCssText) {
- let { node, nodeWithDomToImg } = await this.getSvgData(domToImage)
+ async svg(name, plusCssText) {
+ let { node } = await this.getSvgData()
// 开启了节点富文本编辑
if (this.mindMap.richText) {
- if (domToImage) {
- node = nodeWithDomToImg
- } else if (plusCssText) {
+ if (plusCssText) {
let foreignObjectList = node.find('foreignObject')
if (foreignObjectList.length > 0) {
foreignObjectList[0].add(SVG(``))
diff --git a/simple-mind-map/src/RichText.js b/simple-mind-map/src/RichText.js
index 284a8674..59cdbc6d 100644
--- a/simple-mind-map/src/RichText.js
+++ b/simple-mind-map/src/RichText.js
@@ -397,81 +397,29 @@ class RichText {
return data
}
- // 将svg中嵌入的dom元素转换成图片
- async _handleSvgDomElements(svg) {
- svg = svg.clone()
- let foreignObjectList = svg.find('foreignObject')
- let task = foreignObjectList.map(async item => {
- let clone = item.first().node.cloneNode(true)
- let div = document.createElement('div')
- div.style.cssText = `position: fixed; left: -999999px;`
- div.appendChild(clone)
- this.mindMap.el.appendChild(div)
- let canvas = await html2canvas(clone, {
- backgroundColor: null
- })
- this.mindMap.el.removeChild(div)
- let imgNode = new SvgImage()
- .load(canvas.toDataURL())
- .size(canvas.width, canvas.height)
- item.replace(imgNode)
- })
- await Promise.all(task)
- return {
- svg: svg,
- svgHTML: svg.svg()
+ // 处理导出为图片
+ async handleExportPng(node) {
+ let el = document.createElement('div')
+ el.style.position = 'absolute'
+ el.style.left = '-9999999px'
+ el.appendChild(node)
+ this.mindMap.el.appendChild(el)
+ // 遍历所有节点,将它们的margin和padding设为0
+ let walk = (root) => {
+ root.style.margin = 0
+ root.style.padding = 0
+ if (root.hasChildNodes()) {
+ Array.from(root.children).forEach((item) => {
+ walk(item)
+ })
+ }
}
- }
-
- // 将svg中嵌入的dom元素转换成图片
- handleSvgDomElements(svg) {
- return new Promise((resolve, reject) => {
- svg = svg.clone()
- let foreignObjectList = svg.find('foreignObject')
- let index = 0
- let len = foreignObjectList.length
- let transform = async () => {
- this.mindMap.emit('transforming-dom-to-images', index, len)
- try {
- let item = foreignObjectList[index++]
- let parent = item.parent()
- let clone = item.first().node.cloneNode(true)
- let div = document.createElement('div')
- div.style.cssText = `position: fixed; left: -999999px;`
- div.appendChild(clone)
- this.mindMap.el.appendChild(div)
- let canvas = await html2canvas(clone, {
- backgroundColor: null
- })
- // 优先使用原始宽高,因为当设备的window.devicePixelRatio不为1时,html2canvas输出的图片会更大
- let imgNodeWidth = parent.attr('data-width') || canvas.width
- let imgNodeHeight = parent.attr('data-height') || canvas.height
- this.mindMap.el.removeChild(div)
- let imgNode = new SvgImage()
- .load(canvas.toDataURL())
- .size(imgNodeWidth, imgNodeHeight)
- .x((parent ? parent.attr('data-offsetx') : 0) || 0)
- item.replace(imgNode)
- if (index <= len - 1) {
- setTimeout(() => {
- transform()
- }, 0)
- } else {
- resolve({
- svg: svg,
- svgHTML: svg.svg()
- })
- }
- } catch (error) {
- reject(error)
- }
- }
- if (len > 0) {
- transform()
- } else {
- resolve(null)
- }
+ walk(node)
+ let canvas = await html2canvas(el, {
+ backgroundColor: null
})
+ this.mindMap.el.removeChild(el)
+ return canvas.toDataURL()
}
// 将所有节点转换成非富文本节点
@@ -499,6 +447,20 @@ class RichText {
this.mindMap.render(null, CONSTANTS.TRANSFORM_TO_NORMAL_NODE)
}
+ // 处理导入数据
+ handleSetData(data) {
+ let walk = (root) => {
+ root.data.richText = true
+ if (root.children && root.children.length > 0) {
+ Array.from(root.children).forEach((item) => {
+ walk(item)
+ })
+ }
+ }
+ walk(data)
+ return data
+ }
+
// 插件被移除前做的事情
beforePluginRemove() {
this.transformAllNodesToNormalNode()
diff --git a/web/src/lang/en_us.js b/web/src/lang/en_us.js
index 0d1d1c6d..f66a24ea 100644
--- a/web/src/lang/en_us.js
+++ b/web/src/lang/en_us.js
@@ -90,7 +90,7 @@ export default {
pdfFile: 'pdf file',
markdownFile: 'markdown file',
tips: 'tips: .smm and .json file can be import',
- domToImage: 'Whether to convert rich text nodes in svg into pictures',
+ isTransparent: 'Background is transparent',
pngTips: 'tips: Exporting pictures in rich text mode is time-consuming. It is recommended to export to svg format',
svgTips: 'tips: Exporting pictures in rich text mode is time-consuming',
transformingDomToImages: 'Converting nodes: ',
diff --git a/web/src/lang/zh_cn.js b/web/src/lang/zh_cn.js
index 74477611..407866ef 100644
--- a/web/src/lang/zh_cn.js
+++ b/web/src/lang/zh_cn.js
@@ -90,7 +90,7 @@ export default {
pdfFile: 'pdf文件',
markdownFile: 'markdown文件',
tips: 'tips:.smm和.json文件可用于导入',
- domToImage: '是否将svg中富文本节点转换成图片',
+ isTransparent: '背景是否透明',
pngTips: 'tips:富文本模式导出图片非常耗时,建议导出为svg格式',
svgTips: 'tips:富文本模式导出图片非常耗时',
transformingDomToImages: '正在转换节点:',
diff --git a/web/src/pages/Edit/components/Export.vue b/web/src/pages/Edit/components/Export.vue
index 15227835..3a9b879d 100644
--- a/web/src/pages/Edit/components/Export.vue
+++ b/web/src/pages/Edit/components/Export.vue
@@ -23,12 +23,6 @@
style="margin-left: 12px"
>{{ $t('export.include') }}
-