From 7a0fd5adfb33ce5b2873df60ac928375cfe01639 Mon Sep 17 00:00:00 2001 From: wanglin2 <1013335014@qq.com> Date: Mon, 10 Oct 2022 14:31:43 +0800 Subject: [PATCH] =?UTF-8?q?=E6=94=AF=E6=8C=81=E5=B0=8F=E5=9C=B0=E5=9B=BE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- simple-mind-map/index.js | 6 + simple-mind-map/package.json | 2 +- simple-mind-map/src/Export.js | 27 +-- simple-mind-map/src/MiniMap.js | 175 ++++++++++++++++++ simple-mind-map/src/View.js | 22 +++ simple-mind-map/src/layouts/MindMap.js | 2 +- web/src/pages/Edit/components/Edit.vue | 5 +- web/src/pages/Edit/components/Navigator.vue | 141 ++++++++++++++ .../Edit/components/NavigatorToolbar.vue | 10 +- 9 files changed, 363 insertions(+), 27 deletions(-) create mode 100644 simple-mind-map/src/MiniMap.js create mode 100644 web/src/pages/Edit/components/Navigator.vue diff --git a/simple-mind-map/index.js b/simple-mind-map/index.js index 6ed87c18..c481265f 100644 --- a/simple-mind-map/index.js +++ b/simple-mind-map/index.js @@ -10,6 +10,7 @@ import BatchExecution from './src/BatchExecution' import Export from './src/Export' import Select from './src/Select' import Drag from './src/Drag' +import MiniMap from './src/MiniMap'; import { layoutValueList } from './src/utils/constant' @@ -116,6 +117,11 @@ class MindMap { draw: this.draw }) + // 小地图类 + this.miniMap = new MiniMap({ + mindMap: this + }) + // 导出类 this.doExport = new Export({ mindMap: this diff --git a/simple-mind-map/package.json b/simple-mind-map/package.json index 90e74cf5..d2f177bc 100644 --- a/simple-mind-map/package.json +++ b/simple-mind-map/package.json @@ -1,6 +1,6 @@ { "name": "simple-mind-map", - "version": "0.2.10", + "version": "0.2.11", "description": "一个简单的web在线思维导图", "authors": [ { diff --git a/simple-mind-map/src/Export.js b/simple-mind-map/src/Export.js index 13baac0b..f9b2f405 100644 --- a/simple-mind-map/src/Export.js +++ b/simple-mind-map/src/Export.js @@ -44,28 +44,9 @@ class Export { * @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() - const elRect = this.mindMap.el.getBoundingClientRect() - // 去除放大缩小的变换效果 - draw.scale(1 / origTransform.scaleX, 1 / origTransform.scaleY) - // 获取变换后的位置尺寸信息,其实是getBoundingClientRect方法的包装方法 - const rect = draw.rbox() - // 将svg设置为实际内容的宽高 - svg.size(rect.width, rect.height) - // 把实际内容变换 - draw.translate(-rect.x + elRect.left, -rect.y + elRect.top) - // 克隆一份数据 - const clone = svg.clone() - // 恢复原先的大小和变换信息 - svg.size(origWidth, origHeight) - draw.transform(origTransform) + let { svg, svgHTML } = this.mindMap.miniMap.getMiniMap() // 把图片的url转换成data:url类型,否则导出会丢失图片 - let imageList = clone.find('image') + let imageList = svg.find('image') let task = imageList.map(async (item) => { let imgUlr = item.attr('href') || item.attr('xlink:href') let imgData = await imgToDataUrl(imgUlr) @@ -73,8 +54,8 @@ class Export { }) await Promise.all(task) return { - node: clone, - str: clone.svg() + node: svg, + str: svgHTML } } diff --git a/simple-mind-map/src/MiniMap.js b/simple-mind-map/src/MiniMap.js new file mode 100644 index 00000000..8a68f1dd --- /dev/null +++ b/simple-mind-map/src/MiniMap.js @@ -0,0 +1,175 @@ +// 小地图类 +class MiniMap { + /** + * javascript comment + * @Author: 王林25 + * @Date: 2022-10-10 14:00:45 + * @Desc: 构造函数 + */ + constructor(opt) { + this.mindMap = opt.mindMap; + this.isMousedown = false; + this.mousedownPos = { + x: 0, + y: 0, + }; + this.startViewPos = { + x: 0, + y: 0, + }; + } + + /** + * javascript comment + * @Author: 王林25 + * @Date: 2022-10-10 14:00:43 + * @Desc: 获取小地图相关数据 + */ + getMiniMap() { + const svg = this.mindMap.svg; + const draw = this.mindMap.draw; + // 保存原始信息 + const origWidth = svg.width(); + const origHeight = svg.height(); + const origTransform = draw.transform(); + const elRect = this.mindMap.el.getBoundingClientRect(); + // 去除放大缩小的变换效果 + draw.scale(1 / origTransform.scaleX, 1 / origTransform.scaleY); + // 获取变换后的位置尺寸信息,其实是getBoundingClientRect方法的包装方法 + const rect = draw.rbox(); + // 将svg设置为实际内容的宽高 + svg.size(rect.width, rect.height); + // 把实际内容变换 + draw.translate(-rect.x + elRect.left, -rect.y + elRect.top); + // 克隆一份数据 + const clone = svg.clone(); + // 恢复原先的大小和变换信息 + svg.size(origWidth, origHeight); + draw.transform(origTransform); + + return { + svg: clone, // 思维导图图形的整体svg元素,包括:svg(画布容器)、g(实际的思维导图组) + svgHTML: clone.svg(), // svg字符串 + rect: { + ...rect, // 思维导图图形未缩放时的位置尺寸等信息 + ratio: rect.width / rect.height, // 思维导图图形的宽高比 + }, + origWidth, // 画布宽度 + origHeight, // 画布高度 + scaleX: origTransform.scaleX, // 思维导图图形的水平缩放值 + scaleY: origTransform.scaleY, // 思维导图图形的垂直缩放值 + }; + } + + /** + * javascript comment + * @Author: 王林25 + * @Date: 2022-10-10 14:05:51 + * @Desc: 计算小地图的渲染数据 + * boxWidth:小地图容器的宽度 + * boxHeight:小地图容器的高度 + */ + calculationMiniMap(boxWidth, boxHeight) { + let { svgHTML, rect, origWidth, origHeight, scaleX, scaleY } = + this.getMiniMap(); + // 计算数据 + let boxRatio = boxWidth / boxHeight; + let actWidth = 0; + let actHeight = 0; + if (boxRatio > rect.ratio) { + // 高度以box为准,缩放宽度 + actHeight = boxHeight; + actWidth = rect.ratio * actHeight; + } else { + // 宽度以box为准,缩放高度 + actWidth = boxWidth; + actHeight = actWidth / rect.ratio; + } + // svg图形的缩放及位置 + let miniMapBoxScale = actWidth / rect.width; + let miniMapBoxLeft = (boxWidth - actWidth) / 2; + let miniMapBoxTop = (boxHeight - actHeight) / 2; + // 视口框大小及位置 + let _rectX = rect.x - (rect.width * scaleX - rect.width) / 2; + let _rectX2 = rect.x2 + (rect.width * scaleX - rect.width) / 2; + let _rectY = rect.y - (rect.height * scaleY - rect.height) / 2; + let _rectY2 = rect.y2 + (rect.height * scaleY - rect.height) / 2; + let _rectWidth = rect.width * scaleX; + let _rectHeight = rect.height * scaleY; + let viewBoxStyle = { + left: 0, + top: 0, + right: 0, + bottom: 0, + }; + viewBoxStyle.left = + Math.max(0, (-_rectX / _rectWidth) * actWidth) + miniMapBoxLeft + "px"; + viewBoxStyle.right = + Math.max(0, ((_rectX2 - origWidth) / _rectWidth) * actWidth) + + miniMapBoxLeft + + "px"; + + viewBoxStyle.top = + Math.max(0, (-_rectY / _rectHeight) * actHeight) + miniMapBoxTop + "px"; + viewBoxStyle.bottom = + Math.max(0, ((_rectY2 - origHeight) / _rectHeight) * actHeight) + + miniMapBoxTop + + "px"; + return { + svgHTML, // 小地图html + viewBoxStyle, // 视图框的位置信息 + miniMapBoxScale, // 视图框的缩放值 + miniMapBoxLeft, // 视图框的left值 + miniMapBoxTop, // 视图框的top值 + }; + } + + /** + * javascript comment + * @Author: 王林25 + * @Date: 2022-10-10 14:22:40 + * @Desc: 小地图鼠标按下事件 + */ + onMousedown(e) { + this.isMousedown = true; + this.mousedownPos = { + x: e.clientX, + y: e.clientY, + }; + // 保存视图当前的偏移量 + let transformData = this.mindMap.view.getTransformData(); + this.startViewPos = { + x: transformData.state.x, + y: transformData.state.y, + }; + } + + /** + * javascript comment + * @Author: 王林25 + * @Date: 2022-10-10 14:22:55 + * @Desc: 小地图鼠标移动事件 + */ + onMousemove(e, sensitivityNum = 5) { + if (!this.isMousedown) { + return; + } + let ox = e.clientX - this.mousedownPos.x; + let oy = e.clientY - this.mousedownPos.y; + // 在视图最初偏移量上累加更新量 + this.mindMap.view.translateXTo(ox * sensitivityNum + this.startViewPos.x); + this.mindMap.view.translateYTo(oy * sensitivityNum + this.startViewPos.y); + } + + /** + * javascript comment + * @Author: 王林25 + * @Date: 2022-10-10 14:23:01 + * @Desc: 小地图鼠标松开事件 + */ + onMouseup() { + this.isMousedown = false; + } +} + +export default MiniMap; diff --git a/simple-mind-map/src/View.js b/simple-mind-map/src/View.js index c5e04608..9bc9bfd5 100644 --- a/simple-mind-map/src/View.js +++ b/simple-mind-map/src/View.js @@ -126,6 +126,17 @@ class View { this.transform() } + /** + * javascript comment + * @Author: 王林25 + * @Date: 2022-10-10 14:03:53 + * @Desc: 平移x方式到 + */ + translateXTo(x) { + this.x = x + this.transform() + } + /** * javascript comment * @Author: 王林25 @@ -137,6 +148,17 @@ class View { this.transform() } + /** + * javascript comment + * @Author: 王林25 + * @Date: 2022-10-10 14:04:10 + * @Desc: 平移y方向到 + */ + translateYTo(y) { + this.y = y + this.transform() + } + /** * @Author: 王林 * @Date: 2021-07-04 17:13:14 diff --git a/simple-mind-map/src/layouts/MindMap.js b/simple-mind-map/src/layouts/MindMap.js index fa57f52a..75cc6cce 100644 --- a/simple-mind-map/src/layouts/MindMap.js +++ b/simple-mind-map/src/layouts/MindMap.js @@ -224,7 +224,7 @@ class MindMap extends Base { let y1 = top + height / 2 let x2 = item.dir === 'left' ? item.left + item.width : item.left let y2 = item.top + item.height / 2 - let path = path = `M ${x1},${y1} L ${x1 + _s},${y1} L ${x1 + _s},${y2} L ${x2},${y2}` + let path = `M ${x1},${y1} L ${x1 + _s},${y1} L ${x1 + _s},${y2} L ${x2},${y2}` lines[index].plot(path) style && style(lines[index], item) }) diff --git a/web/src/pages/Edit/components/Edit.vue b/web/src/pages/Edit/components/Edit.vue index 13b79eb0..3591b8de 100644 --- a/web/src/pages/Edit/components/Edit.vue +++ b/web/src/pages/Edit/components/Edit.vue @@ -2,6 +2,7 @@
+ @@ -27,6 +28,7 @@ import ShortcutKey from './ShortcutKey' import Contextmenu from './Contextmenu' import NodeNoteContentShow from './NodeNoteContentShow.vue' import { getData, storeData, storeConfig } from '@/api' +import Navigator from './Navigator.vue'; /** * @Author: 王林 @@ -45,7 +47,8 @@ export default { NavigatorToolbar, ShortcutKey, Contextmenu, - NodeNoteContentShow + NodeNoteContentShow, + Navigator }, data() { return { diff --git a/web/src/pages/Edit/components/Navigator.vue b/web/src/pages/Edit/components/Navigator.vue new file mode 100644 index 00000000..3ff43eab --- /dev/null +++ b/web/src/pages/Edit/components/Navigator.vue @@ -0,0 +1,141 @@ + + + + + diff --git a/web/src/pages/Edit/components/NavigatorToolbar.vue b/web/src/pages/Edit/components/NavigatorToolbar.vue index 4b2c4438..51863e57 100644 --- a/web/src/pages/Edit/components/NavigatorToolbar.vue +++ b/web/src/pages/Edit/components/NavigatorToolbar.vue @@ -1,5 +1,8 @@