diff --git a/simple-mind-map/example/exampleData.js b/simple-mind-map/example/exampleData.js
index bea996fa..1e71e274 100644
--- a/simple-mind-map/example/exampleData.js
+++ b/simple-mind-map/example/exampleData.js
@@ -1,16 +1,16 @@
const createFullData = () => {
return {
- // "image": "http://aliyuncdn.lxqnsys.com/whbm/enJFNMHnedQTYTESGfDkctCp2",
- // "imageTitle": "图片名称",
- // "imageSize": {
- // "width": 1000,
- // "height": 563
- // },
- // "icon": ['priority_1'],
- // "tag": ["标签1", "标签2"],
- // "hyperlink": "http://lxqnsys.com/",
- // "hyperlinkTitle": "理想青年实验室",
- // "note": "理想青年实验室\n一个有意思的角落"
+ "image": "http://192.168.3.118:8080/enJFNMHnedQTYTESGfDkctCp2.jpeg",
+ "imageTitle": "图片名称",
+ "imageSize": {
+ "width": 1000,
+ "height": 563
+ },
+ "icon": ['priority_1'],
+ "tag": ["标签1", "标签2"],
+ "hyperlink": "http://lxqnsys.com/",
+ "hyperlinkTitle": "理想青年实验室",
+ "note": "理想青年实验室\n一个有意思的角落"
};
}
@@ -22,7 +22,7 @@ const createFullData = () => {
export default {
"root": {
"data": {
- "text": "根节点",
+ "text": "根节点"
},
"children": [
{
@@ -38,7 +38,6 @@ export default {
}, {
"data": {
"text": "子节点1-2",
- ...createFullData()
}
},]
},
@@ -56,31 +55,26 @@ export default {
{
"data": {
"text": "子节点2-1-1",
- ...createFullData()
}
},
{
"data": {
"text": "子节点2-1-2",
- ...createFullData()
},
"children": [
{
"data": {
"text": "子节点2-1-2-1",
- ...createFullData()
}
},
{
"data": {
"text": "子节点2-1-2-2",
- ...createFullData()
},
"children": [
{
"data": {
"text": "子节点2-1-2-2-1",
- ...createFullData()
}
},
{
@@ -92,7 +86,6 @@ export default {
{
"data": {
"text": "子节点2-1-2-2-3",
- ...createFullData()
}
}
]
@@ -108,7 +101,6 @@ export default {
{
"data": {
"text": "子节点2-1-3",
- ...createFullData()
}
}
]
@@ -116,7 +108,6 @@ export default {
{
"data": {
"text": "子节点2-2",
- ...createFullData()
}
}
]
@@ -129,7 +120,6 @@ export default {
{
"data": {
"text": "子节点3-1",
- ...createFullData()
}
},
{
@@ -148,19 +138,16 @@ export default {
{
"data": {
"text": "子节点4-1",
- ...createFullData()
},
"children": [
{
"data": {
"text": "子节点4-1-1",
- ...createFullData()
}
},
{
"data": {
"text": "子节点4-1-2",
- ...createFullData()
}
},
{
@@ -174,7 +161,6 @@ export default {
{
"data": {
"text": "子节点4-2",
- ...createFullData()
}
}
]
diff --git a/simple-mind-map/index.js b/simple-mind-map/index.js
index b04ac4cd..b48f5f02 100644
--- a/simple-mind-map/index.js
+++ b/simple-mind-map/index.js
@@ -7,11 +7,12 @@ import Style from './src/Style'
import KeyCommand from './src/KeyCommand'
import Command from './src/Command'
import BatchExecution from './src/BatchExecution'
+import Export from './src/Export';
import {
SVG
} from '@svgdotjs/svg.js'
-// 默认选项
+// 默认选项配置
const defaultOpt = {
// 布局
layout: 'logicalStructure',
@@ -19,8 +20,12 @@ const defaultOpt = {
theme: 'default', // 内置主题:default(默认主题)
// 主题配置,会和所选择的主题进行合并
themeConfig: {},
- // 放大缩小的增量比例,即step = scaleRatio * width|height
- scaleRatio: 0.1
+ // 放大缩小的增量比例
+ scaleRatio: 0.1,
+ // 设置鼠标左键还是右键按下拖动,1(左键)、2(右键)
+ dragButton: 1,
+ // 最多显示几个标签
+ maxTag: 5
}
/**
@@ -38,7 +43,7 @@ class MindMap {
*/
constructor(opt = {}) {
// 合并选项
- this.opt = merge(defaultOpt, opt)
+ this.opt = this.handleOpt(merge(defaultOpt, opt))
// 容器元素
this.el = this.opt.el
@@ -51,8 +56,9 @@ class MindMap {
this.width = width
this.height = height
- // 画笔
- this.draw = SVG().addTo(this.el).size(width, height)
+ // 画布
+ this.svg = SVG().addTo(this.el).size(width, height)
+ this.draw = this.svg.group()
// 节点id
this.uid = 0
@@ -86,6 +92,11 @@ class MindMap {
draw: this.draw
})
+ // 导出类
+ this.doExport = new Export({
+ mindMap: this
+ })
+
// 批量执行类
this.batchExecution = new BatchExecution()
@@ -96,6 +107,23 @@ class MindMap {
}, 0);
}
+ /**
+ * @Author: 王林
+ * @Date: 2021-07-01 22:15:22
+ * @Desc: 配置参数处理
+ */
+ handleOpt(opt) {
+ // 检查布局配置
+ if (!['logicalStructure'].includes(opt.layout)) {
+ opt.layout = 'logicalStructure'
+ }
+ // 检查主题配置
+ opt.theme = opt.theme && theme[opt.theme] ? opt.theme : 'default'
+ // 检查鼠标键值
+ opt.dragButton = [1, 3].includes(opt.dragButton) ? opt.dragButton : 1
+ return opt
+ }
+
/**
* javascript comment
* @Author: 王林25
@@ -144,7 +172,7 @@ class MindMap {
*/
initTheme() {
// 合并主题配置
- this.themeConfig = merge(this.opt.theme && theme[this.opt.theme] ? theme[this.opt.theme] : theme.default, this.opt.themeConfig)
+ this.themeConfig = merge(theme[this.opt.theme], this.opt.themeConfig)
// 设置背景样式
Style.setBackgroundStyle(this.el, this.themeConfig)
}
@@ -195,6 +223,16 @@ class MindMap {
execCommand(...args) {
this.command.exec(...args)
}
+
+ /**
+ * @Author: 王林
+ * @Date: 2021-07-01 22:06:38
+ * @Desc: 导出
+ */
+ async export(...args) {
+ let result = await this.doExport.export(...args)
+ return result;
+ }
}
export default MindMap
\ No newline at end of file
diff --git a/simple-mind-map/package.json b/simple-mind-map/package.json
index f645920c..9014260a 100644
--- a/simple-mind-map/package.json
+++ b/simple-mind-map/package.json
@@ -5,6 +5,7 @@
"scripts": {},
"dependencies": {
"@svgdotjs/svg.js": "^3.0.16",
+ "canvg": "^3.0.7",
"deepmerge": "^1.5.2",
"eventemitter3": "^4.0.7"
}
diff --git a/simple-mind-map/src/Event.js b/simple-mind-map/src/Event.js
index 3f3ae06b..990f3dec 100644
--- a/simple-mind-map/src/Event.js
+++ b/simple-mind-map/src/Event.js
@@ -55,11 +55,16 @@ class Event extends EventEmitter {
* @Desc: 绑定事件
*/
bind() {
- this.mindMap.draw.on('click', this.onDrawClick)
+ this.mindMap.svg.on('click', this.onDrawClick)
this.mindMap.el.addEventListener('mousedown', this.onMousedown)
window.addEventListener('mousemove', this.onMousemove)
window.addEventListener('mouseup', this.onMouseup)
- this.mindMap.el.addEventListener('mousewheel', this.onMousewheel)
+ // 兼容火狐浏览器
+ if(window.navigator.userAgent.toLowerCase().indexOf("firefox") != -1){
+ this.mindMap.el.addEventListener('DOMMouseScroll', this.onMousewheel)
+ } else {
+ this.mindMap.el.addEventListener('mousewheel', this.onMousewheel)
+ }
}
/**
@@ -91,6 +96,9 @@ class Event extends EventEmitter {
* @Desc: 鼠标按下事件
*/
onMousedown(e) {
+ if (e.which !== this.mindMap.opt.dragButton) {
+ return;
+ }
e.preventDefault()
this.isMousedown = true
this.mousedownPos.x = e.clientX
@@ -137,7 +145,7 @@ class Event extends EventEmitter {
e.stopPropagation()
e.preventDefault()
let dir
- if (e.wheelDeltaY > 0) {
+ if ((e.wheelDeltaY || e.detail) > 0) {
dir = 'up'
} else {
dir = 'down'
diff --git a/simple-mind-map/src/Export.js b/simple-mind-map/src/Export.js
new file mode 100644
index 00000000..4b5dd6b6
--- /dev/null
+++ b/simple-mind-map/src/Export.js
@@ -0,0 +1,203 @@
+import { imgToDataUrl, downloadFile } from './utils';
+const URL = window.URL || window.webkitURL || window
+
+/**
+ * @Author: 王林
+ * @Date: 2021-07-01 22:05:16
+ * @Desc: 导出类
+ */
+class Export {
+ /**
+ * @Author: 王林
+ * @Date: 2021-07-01 22:05:42
+ * @Desc: 构造函数
+ */
+ constructor(opt) {
+ this.mindMap = opt.mindMap
+ }
+
+ /**
+ * @Author: 王林
+ * @Date: 2021-07-02 07:44:06
+ * @Desc: 导出
+ */
+ async export(type, isDownload = true) {
+ if (this[type]) {
+ let result = await this[type]()
+ if (isDownload) {
+ downloadFile(result, '思维导图.' + type)
+ }
+ return result;
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * @Author: 王林
+ * @Date: 2021-07-04 14:57:40
+ * @Desc: 获取svg数据
+ */
+ async getSvgData() {
+ const svg = this.mindMap.svg
+ const draw = this.mindMap.draw
+ // 保存原始信息
+ const origWidth = svg.width()
+ const origHeight = svg.height()
+ const origTransform = draw.transform()
+ // 去除变换效果
+ draw.scale(1 / origTransform.scaleX, 1 / origTransform.scaleY).translate(0, 0)
+ // 获取实际内容当前变换后的位置信息
+ const rect = draw.rbox()
+ // 将svg设置为实际内容的宽高
+ svg.size(rect.wdith, rect.height)
+ // 把实际内容变换
+ draw.translate(-rect.x, -rect.y)
+ // 克隆一份数据
+ const clone = svg.clone()
+ // 恢复原先的大小和变换信息
+ svg.size(origWidth, origHeight)
+ draw.transform(origTransform)
+ // 把图片的url转换成data:url类型,否则导出会丢失图片
+ let imageList = clone.find('image')
+ let task = imageList.map(async (item) => {
+ let imgUlr = item.attr('href') || item.attr('xlink:href')
+ let imgData = await imgToDataUrl(imgUlr)
+ item.attr('href', imgData)
+ })
+ await Promise.all(task)
+ return {
+ node: clone,
+ str: clone.svg()
+ };
+ }
+
+ /**
+ * @Author: 王林
+ * @Date: 2021-07-04 15:25:19
+ * @Desc: svg转png
+ */
+ svgToPng(svgSrc) {
+ return new Promise((resolve, reject) => {
+ const img = new Image()
+ // 跨域图片需要添加这个属性,否则画布被污染了无法导出图片
+ img.setAttribute('crossOrigin', 'anonymous')
+ img.onload = async () => {
+ try {
+ let canvas = document.createElement('canvas')
+ canvas.width = img.width
+ canvas.height = img.height
+ let ctx = canvas.getContext('2d')
+ // 绘制背景
+ await this.drawBackgroundToCanvas(ctx, img.width, img.height)
+ // 图片绘制到canvas里
+ ctx.drawImage(img, 0, 0, img.width, img.height)
+ resolve(canvas.toDataURL())
+ } catch (error) {
+ reject(error)
+ }
+ }
+ img.onerror = (e) => {
+ reject(e)
+ }
+ img.src = svgSrc
+ });
+ }
+
+ /**
+ * @Author: 王林
+ * @Date: 2021-07-04 15:32:07
+ * @Desc: 在canvas上绘制思维导图背景
+ */
+ drawBackgroundToCanvas(ctx, width, height) {
+ return new Promise((resolve, rejct) => {
+ let { backgroundColor = '#fff', backgroundImage, backgroundRepeat = "repeat" } = this.mindMap.themeConfig
+ // 背景颜色
+ ctx.save()
+ ctx.rect(0, 0, width, height)
+ ctx.fillStyle = backgroundColor
+ ctx.fill()
+ ctx.restore()
+ // 背景图片
+ if (backgroundImage && backgroundImage !== 'none') {
+ ctx.save()
+ let img = new Image()
+ img.src = backgroundImage
+ img.onload = () => {
+ let pat = ctx.createPattern(img, backgroundRepeat)
+ ctx.rect(0, 0, width, height)
+ ctx.fillStyle = pat
+ ctx.fill()
+ ctx.restore()
+ resolve()
+ }
+ img.onerror = (e) => {
+ rejct(e)
+ }
+ } else {
+ resolve()
+ }
+ });
+ }
+
+ /**
+ * @Author: 王林
+ * @Date: 2021-07-01 22:09:51
+ * @Desc: 导出为png
+ * 方法1.把svg的图片都转化成data:url格式,再转换
+ * 方法2.把svg的图片提取出来再挨个绘制到canvas里,最后一起转换
+ */
+ async png() {
+ let { str } = await this.getSvgData()
+ // 转换成blob数据
+ let blob = new Blob([str], {
+ type: 'image/svg+xml'
+ });
+ // 转换成data:url数据
+ let svgUrl = URL.createObjectURL(blob);
+ // 绘制到canvas上
+ let imgDataUrl = await this.svgToPng(svgUrl)
+ URL.revokeObjectURL(svgUrl);
+ return imgDataUrl
+ }
+
+ /**
+ * @Author: 王林
+ * @Date: 2021-07-04 15:32:07
+ * @Desc: 在svg上绘制思维导图背景
+ */
+ drawBackgroundToSvg(svg) {
+ return new Promise(async (resolve, rejct) => {
+ let { backgroundColor = '#fff', backgroundImage, backgroundRepeat = "repeat" } = this.mindMap.themeConfig
+ // 背景颜色
+ svg.css('background-color', backgroundColor)
+ // 背景图片
+ if (backgroundImage && backgroundImage !== 'none') {
+ let imgDataUrl = await imgToDataUrl(backgroundImage)
+ svg.css('background-image', `url(${imgDataUrl})`)
+ svg.css('background-repeat', backgroundRepeat)
+ resolve()
+ } else {
+ resolve()
+ }
+ });
+ }
+
+ /**
+ * @Author: 王林
+ * @Date: 2021-07-04 14:54:07
+ * @Desc: 导出为svg
+ */
+ async svg() {
+ let { node } = await this.getSvgData()
+ await this.drawBackgroundToSvg(node)
+ let str = node.svg()
+ // 转换成blob数据
+ let blob = new Blob([str], {
+ type: 'image/svg+xml'
+ });
+ return URL.createObjectURL(blob);
+ }
+}
+
+export default Export
\ No newline at end of file
diff --git a/simple-mind-map/src/Node.js b/simple-mind-map/src/Node.js
index ebf03e2f..e1869bcd 100644
--- a/simple-mind-map/src/Node.js
+++ b/simple-mind-map/src/Node.js
@@ -1,7 +1,8 @@
import Style from './Style'
import {
resizeImgSize,
- copyRenderTree
+ copyRenderTree,
+ imgToDataUrl
} from './utils'
import {
Image,
@@ -215,11 +216,12 @@ class Node {
if (!_data.icon || _data.icon.length <= 0) {
return [];
}
+ let iconSize = this.themeConfig.iconSize
return _data.icon.map((item) => {
return {
- node: SVG(iconsSvg.getNodeIconListIcon(item)).size(this.themeConfig.iconSize, this.themeConfig.iconSize),
- width: this.themeConfig.iconSize,
- height: this.themeConfig.iconSize
+ node: SVG(iconsSvg.getNodeIconListIcon(item)).size(iconSize, iconSize),
+ width: iconSize,
+ height: iconSize
};
});
}
@@ -231,10 +233,7 @@ class Node {
* @Desc: 创建文本节点
*/
createTextNode() {
- if (!this.nodeData.data.text) {
- return
- }
- let node = this.draw.text(this.nodeData.data.text)
+ let node = this.draw.text(this.nodeData.data.text || '')
this.style.text(node)
let {
width,
@@ -255,22 +254,24 @@ class Node {
* @Desc: 创建超链接节点
*/
createHyperlinkNode() {
- if (!this.nodeData.data.hyperlink) {
+ let { hyperlink, hyperlinkTitle } = this.nodeData.data
+ if (!hyperlink) {
return
}
let iconSize = this.themeConfig.iconSize
- let node = this.draw.element('a')
+ let node = this.draw.link(hyperlink).target('_blank')
node.node.addEventListener('click', (e) => {
e.stopPropagation()
})
- node.attr('href', this.nodeData.data.hyperlink).attr('target', '_blank')
- if (this.nodeData.data.hyperlinkTitle) {
- node.attr('title', this.nodeData.data.hyperlinkTitle)
+ if (hyperlinkTitle) {
+ node.attr('title', hyperlinkTitle)
}
- node.add(this.draw.rect(iconSize, iconSize).fill({ color: 'transparent' }))
- node.add(SVG(iconsSvg.hyperlink).size(iconSize, iconSize))
+ node.rect(iconSize, iconSize).fill({ color: 'transparent' })
+ let iconNode = SVG(iconsSvg.hyperlink).size(iconSize, iconSize)
+ this.style.iconNode(iconNode)
+ node.add(iconNode)
return {
- node: this.draw.nested().add(node),
+ node: node,
width: iconSize,
height: iconSize
}
@@ -282,12 +283,13 @@ class Node {
* @Desc: 创建标签节点
*/
createTagNode() {
- if (!this.nodeData.data.tag || this.nodeData.data.tag.length <= 0) {
+ let tagData = this.nodeData.data.tag
+ if (!tagData || tagData.length <= 0) {
return [];
}
let nodes = []
- this.nodeData.data.tag.slice(0, 5).forEach((item, index) => {
- let tag = this.draw.nested()
+ tagData.slice(0, this.mindMap.opt.maxTag).forEach((item, index) => {
+ let tag = this.draw.group()
let text = this.draw.text(item).x(8).cy(10)
this.style.tagText(text, index)
let {
@@ -317,10 +319,12 @@ class Node {
if (!this.nodeData.data.note) {
return null;
}
- let node = this.draw.nested().attr('cursor', 'pointer')
+ let node = this.draw.group().attr('cursor', 'pointer')
let iconSize = this.themeConfig.iconSize
node.add(this.draw.rect(iconSize, iconSize).fill({ color: 'transparent' }))
- node.add(SVG(iconsSvg.note).size(iconSize, iconSize))
+ let iconNode = SVG(iconsSvg.note).size(iconSize, iconSize)
+ this.style.iconNode(iconNode)
+ node.add(iconNode)
let el = document.createElement('div')
el.style.cssText = `
position: absolute;
@@ -377,11 +381,11 @@ class Node {
imgObj.node.cx(left + width / 2).y(top + paddingY)
}
// 内容节点
- let textContentNested = this.draw.nested()
+ let textContentNested = this.draw.group()
let textContentOffsetX = 0
// icon
let iconObjs = this.createIconNode()
- let iconNested = this.draw.nested()
+ let iconNested = this.draw.group()
if (iconObjs && iconObjs.length > 0) {
let iconLeft = 0
iconObjs.forEach((item) => {
@@ -403,13 +407,13 @@ class Node {
// 超链接
let hyperlinkObj = this.createHyperlinkNode()
if (hyperlinkObj) {
- hyperlinkObj.node.x(textContentOffsetX).y((_textContentHeight - hyperlinkObj.height) / 2)
+ hyperlinkObj.node.translate(textContentOffsetX, (_textContentHeight - hyperlinkObj.height) / 2)
textContentNested.add(hyperlinkObj.node)
textContentOffsetX += hyperlinkObj.width + _textContentItemMargin
}
// 标签
let tagObjs = this.createTagNode()
- let tagNested = this.draw.nested()
+ let tagNested = this.draw.group()
if (tagObjs && tagObjs.length > 0) {
let tagLeft = 0
tagObjs.forEach((item) => {
@@ -423,12 +427,15 @@ class Node {
// 备注
let noteObj = this.createNoteNode()
if (noteObj) {
- noteObj.node.x(textContentOffsetX).y((_textContentHeight - noteObj.height) / 2)
+ noteObj.node.translate(textContentOffsetX, (_textContentHeight - noteObj.height) / 2)
textContentNested.add(noteObj.node)
textContentOffsetX += noteObj.width
}
// 文字内容整体
- textContentNested.x(left + width / 2).dx(-textContentNested.bbox().width / 2).y(top + imgHeight + paddingY + (imgHeight > 0 && _textContentHeight > 0 ? this._blockContentMargin : 0))
+ textContentNested.translate(
+ left + width / 2 - textContentNested.bbox().width / 2,
+ top + imgHeight + paddingY + (imgHeight > 0 && _textContentHeight > 0 ? this._blockContentMargin : 0)
+ )
group.add(textContentNested)
// 单击事件
group.click((e) => {
diff --git a/simple-mind-map/src/Style.js b/simple-mind-map/src/Style.js
index 478dc8dd..16fb2668 100644
--- a/simple-mind-map/src/Style.js
+++ b/simple-mind-map/src/Style.js
@@ -13,13 +13,11 @@ class Style {
* @Desc: 设置背景样式
*/
static setBackgroundStyle(el, themeConfig) {
- let { backgroundColor, backgroundImage, backgroundRepeat, backgroundSize, backgroundPosition } = themeConfig
+ let { backgroundColor, backgroundImage, backgroundRepeat } = themeConfig
el.style.backgroundColor = backgroundColor
if (backgroundImage) {
el.style.backgroundImage = `url(${backgroundImage})`
el.style.backgroundRepeat = backgroundRepeat
- el.style.backgroundSize = backgroundSize
- el.style.backgroundPosition = backgroundPosition
}
}
@@ -127,6 +125,17 @@ class Style {
})
}
+ /**
+ * @Author: 王林
+ * @Date: 2021-07-03 22:37:19
+ * @Desc: 内置图标
+ */
+ iconNode(node) {
+ node.attr({
+ fill: this.merge('color')
+ })
+ }
+
/**
* @Author: 王林
* @Date: 2021-04-11 14:50:49
diff --git a/simple-mind-map/src/TextEdit.js b/simple-mind-map/src/TextEdit.js
index fb93573a..49355a9b 100644
--- a/simple-mind-map/src/TextEdit.js
+++ b/simple-mind-map/src/TextEdit.js
@@ -59,9 +59,6 @@ export default class TextEdit {
* @Desc: 显示文本编辑框
*/
show(node) {
- if (!node.nodeData.data.text) {
- return;
- }
this.showEditTextBox(node, node.textNode.node.node.getBoundingClientRect())
}
@@ -98,8 +95,9 @@ export default class TextEdit {
}
this.renderer.activeNodeList.forEach((node) => {
let str = getStrWithBrFromHtml(this.textEditNode.innerHTML)
- node.nodeData.data.text = str
- console.log(8)
+ this.mindMap.execCommand('UPDATE_NODE_DATA', node, {
+ text: str
+ })
this.mindMap.render()
})
this.mindMap.emit('hide_text_edit', this.textEditNode, this.renderer.activeNodeList)
diff --git a/simple-mind-map/src/View.js b/simple-mind-map/src/View.js
index 713a2d4c..a12c6328 100644
--- a/simple-mind-map/src/View.js
+++ b/simple-mind-map/src/View.js
@@ -1,5 +1,3 @@
-import merge from 'deepmerge'
-
/**
* javascript comment
* @Author: 王林25
@@ -16,19 +14,11 @@ class View {
constructor(opt = {}) {
this.opt = opt
this.mindMap = this.opt.mindMap
- this.viewBox = {
- x: 0,
- y: 0,
- width: this.mindMap.width,
- height: this.mindMap.height
- }
- this.cacheViewBox = {
- x: 0,
- y: 0,
- width: this.mindMap.width,
- height: this.mindMap.height
- }
this.scale = 1
+ this.sx = 0
+ this.sy = 0
+ this.x = 0
+ this.y = 0
this.bind()
}
@@ -41,29 +31,35 @@ class View {
bind() {
// 拖动视图
this.mindMap.event.on('mousedown', () => {
- this.cacheViewBox = merge({}, this.viewBox)
+ this.sx = this.x
+ this.sy = this.y
})
this.mindMap.event.on('drag', (e, event) => {
- // 视图放大缩小后拖动的距离也要相应变化
- this.viewBox.x = this.cacheViewBox.x - event.mousemoveOffset.x * this.scale
- this.viewBox.y = this.cacheViewBox.y - event.mousemoveOffset.y * this.scale
- this.setViewBox()
+ this.x = this.sx + event.mousemoveOffset.x
+ this.y = this.sy + event.mousemoveOffset.y
+ this.mindMap.draw.transform({
+ scale: this.scale,
+ origin: 'left center',
+ translate: [this.x, this.y],
+ })
})
// 放大缩小视图
this.mindMap.event.on('mousewheel', (e, dir) => {
- let stepWidth = this.viewBox.width * this.mindMap.opt.scaleRatio
- let stepHeight = this.viewBox.height * this.mindMap.opt.scaleRatio
- // 放大
+ // // 放大
if (dir === 'down') {
this.scale += this.mindMap.opt.scaleRatio
- this.viewBox.width += stepWidth
- this.viewBox.height += stepHeight
} else { // 缩小
- this.scale -= this.mindMap.opt.scaleRatio
- this.viewBox.width -= stepWidth
- this.viewBox.height -= stepHeight
+ if (this.scale - this.mindMap.opt.scaleRatio > 0.1) {
+ this.scale -= this.mindMap.opt.scaleRatio
+ } else {
+ this.scale = 0.1
+ }
}
- this.setViewBox()
+ this.mindMap.draw.transform({
+ scale: this.scale,
+ origin: 'left center',
+ translate: [this.x, this.y],
+ })
})
}
@@ -73,13 +69,10 @@ class View {
* @Date: 2021-04-07 15:43:26
* @Desc: 设置视图
*/
- setViewBox() {
- let {
- x,
- y,
- width,
- height
- } = this.viewBox
+ setViewBox({ x,
+ y,
+ width,
+ height }) {
this.opt.draw.viewbox(x, y, width, height)
}
}
diff --git a/simple-mind-map/src/layouts/Base.js b/simple-mind-map/src/layouts/Base.js
index 35e979d7..9f084320 100644
--- a/simple-mind-map/src/layouts/Base.js
+++ b/simple-mind-map/src/layouts/Base.js
@@ -16,8 +16,6 @@ class Base {
this.mindMap = renderer.mindMap
// 渲染树
this.renderTree = renderer.renderTree
- // 主题配置
- this.themeConfig = this.mindMap.themeConfig
// 绘图对象
this.draw = this.mindMap.draw
// 根节点
@@ -97,7 +95,7 @@ class Base {
* @Desc: 获取节点的marginX
*/
getMarginX(layerIndex) {
- return layerIndex === 1 ? this.themeConfig.second.marginX : this.themeConfig.node.marginX;
+ return layerIndex === 1 ? this.mindMap.themeConfig.second.marginX : this.mindMap.themeConfig.node.marginX;
}
/**
@@ -106,7 +104,7 @@ class Base {
* @Desc: 获取节点的marginY
*/
getMarginY(layerIndex) {
- return layerIndex === 1 ? this.themeConfig.second.marginY : this.themeConfig.node.marginY;
+ return layerIndex === 1 ? this.mindMap.themeConfig.second.marginY : this.mindMap.themeConfig.node.marginY;
}
}
diff --git a/simple-mind-map/src/layouts/LogicalStructure.js b/simple-mind-map/src/layouts/LogicalStructure.js
index 70f096ea..3cf989da 100644
--- a/simple-mind-map/src/layouts/LogicalStructure.js
+++ b/simple-mind-map/src/layouts/LogicalStructure.js
@@ -63,7 +63,7 @@ class LogicalStructure extends Base {
this.root = newNode
} else {
// 非根节点
- let marginX = layerIndex === 1 ? this.themeConfig.second.marginX : this.themeConfig.node.marginX
+ let marginX = layerIndex === 1 ? this.mindMap.themeConfig.second.marginX : this.mindMap.themeConfig.node.marginX
// 定位到父节点右侧
newNode.left = parent._node.left + parent._node.width + marginX
// 互相收集
diff --git a/simple-mind-map/src/themes/default.js b/simple-mind-map/src/themes/default.js
index 81c7997e..769c3529 100644
--- a/simple-mind-map/src/themes/default.js
+++ b/simple-mind-map/src/themes/default.js
@@ -23,10 +23,6 @@ export default {
backgroundImage: 'none',
// 背景重复
backgroundRepeat: 'no-repeat',
- // 背景图像大小
- backgroundSize: 'auto',
- // 背景图像定位
- backgroundPosition: '0% 0%',
// 根节点样式
root: {
fillColor: '#549688',
diff --git a/simple-mind-map/src/utils/index.js b/simple-mind-map/src/utils/index.js
index 47a4d50e..f10116bb 100644
--- a/simple-mind-map/src/utils/index.js
+++ b/simple-mind-map/src/utils/index.js
@@ -131,4 +131,46 @@ export const copyRenderTree = (tree, root) => {
})
}
return tree;
+}
+
+/**
+ * @Author: 王林
+ * @Date: 2021-07-04 09:08:43
+ * @Desc: 图片转成dataURL
+ */
+export const imgToDataUrl = (src) => {
+ return new Promise((resolve, reject) => {
+ const img = new Image()
+ // 跨域图片需要添加这个属性,否则画布被污染了无法导出图片
+ img.setAttribute('crossOrigin', 'anonymous')
+ img.onload = () => {
+ try {
+ let canvas = document.createElement('canvas')
+ canvas.width = img.width
+ canvas.height = img.height
+ let ctx = canvas.getContext('2d')
+ // 图片绘制到canvas里
+ ctx.drawImage(img, 0, 0, img.width, img.height)
+ resolve(canvas.toDataURL())
+ } catch (error) {
+ reject(e)
+ }
+ }
+ img.onerror = (e) => {
+ reject(e)
+ }
+ img.src = src
+ });
+}
+
+/**
+ * @Author: 王林
+ * @Date: 2021-07-04 16:20:06
+ * @Desc: 下载文件
+ */
+export const downloadFile = (file, fileName) => {
+ let a = document.createElement('a')
+ a.href = file
+ a.download = fileName
+ a.click()
}
\ No newline at end of file
diff --git a/web/public/enJFNMHnedQTYTESGfDkctCp2.jpeg b/web/public/enJFNMHnedQTYTESGfDkctCp2.jpeg
new file mode 100644
index 00000000..ac324e43
Binary files /dev/null and b/web/public/enJFNMHnedQTYTESGfDkctCp2.jpeg differ
diff --git a/web/src/config/index.js b/web/src/config/index.js
index 242c65d7..44f6f50f 100644
--- a/web/src/config/index.js
+++ b/web/src/config/index.js
@@ -147,22 +147,6 @@ export const backgroundRepeatList = [
}
]
-// 背景图片大小
-export const backgroundSizeList = [
- {
- name: '自动',
- value: 'auto'
- },
- {
- name: '完全覆盖',
- value: 'cover'
- },
- {
- name: '最合适',
- value: 'contain'
- }
-]
-
// 背景图片定位
export const backgroundPositionList = [
{
diff --git a/web/src/pages/Edit/components/BaseStyle.vue b/web/src/pages/Edit/components/BaseStyle.vue
index 20f841c6..f6f0cd42 100644
--- a/web/src/pages/Edit/components/BaseStyle.vue
+++ b/web/src/pages/Edit/components/BaseStyle.vue
@@ -29,7 +29,7 @@
图片重复