Compare commits

...

26 Commits
0.6.2 ... 0.6.5

Author SHA1 Message Date
wanglin2
a48e52f1f4 打包0.6.5 2023-06-28 16:37:19 +08:00
wanglin2
f8a345d8de Doc: update 2023-06-28 16:28:47 +08:00
wanglin2
d900c2f122 Feat:节点图片支持拖拽调整大小 2023-06-28 15:03:15 +08:00
wanglin2
443a714549 Feat:支持配置鼠标滚轮方向对应的缩放行为 2023-06-27 16:46:37 +08:00
wanglin2
74618a8a2b Feat:打包后的库支持获取内置常量、主题等数据 2023-06-27 16:27:46 +08:00
wanglin2
a25bd4c556 Fix:修复xmind导入报错 2023-06-27 16:15:42 +08:00
wanglin2
efe205ae70 打包0.6.4-fix.1 2023-06-27 09:08:45 +08:00
wanglin2
a0d7473b1f Doc: update 2023-06-27 09:05:39 +08:00
wanglin2
7821781f20 Feat:鼠标滚轮缩放时默认以鼠标当前位置为中心进行缩放,可以通过配置关闭该特性 2023-06-27 09:05:25 +08:00
街角小林
b90d4ddf8a Merge pull request #152 from F-star/feat/scale-in-wheel-curor
Feat: 支持滚轮情况下,以光标为中心进行缩放
2023-06-27 08:47:40 +08:00
Hao Huang
314562c167 Feat: 支持滚轮情况下,以光标为中心进行缩放 2023-06-26 13:32:33 +00:00
wanglin2
5f0a9a7ce2 打包0.6.4 2023-06-26 16:20:38 +08:00
wanglin2
98e27841ad Doc: update 2023-06-26 16:15:20 +08:00
wanglin2
1b6467728c Feat:优化指定中心点缩放 2023-06-26 15:57:45 +08:00
街角小林
4bb349b2df Merge pull request #151 from F-star/main
Feat: 以画布为中心进行缩放
2023-06-26 15:25:08 +08:00
Hao Huang
b262336f08 Feat: 以画布为中心进行缩放 2023-06-26 05:47:06 +00:00
wanglin2
2b59087461 Merge branch 'feature' into main 2023-06-26 10:33:03 +08:00
wanglin2
66c9805efc Doc: update 2023-06-26 10:32:36 +08:00
wanglin2
710128901a 更新群二维码 2023-06-26 09:56:10 +08:00
wanglin2
61be0f7ac4 Merge branch 'feature' into main 2023-06-24 17:08:18 +08:00
wanglin2
7289b3a0ad Doc:update 2023-06-24 17:06:52 +08:00
wanglin2
25243e2053 Merge branch 'feature' into main 2023-06-21 15:56:20 +08:00
wanglin2
060a448cd5 打包0.6.3 2023-06-21 15:55:38 +08:00
wanglin2
bdb6078df6 '更新文档' 2023-06-21 15:33:51 +08:00
wanglin2
749a4d0e81 Feat:支持自定义节点内容 2023-06-21 14:57:22 +08:00
wanglin2
1749705694 Fix:修复概要节点会响应快捷键添加节点的问题 2023-06-21 10:50:26 +08:00
53 changed files with 1331 additions and 220 deletions

View File

@@ -92,6 +92,8 @@ MIT
<img src="./qrcode.jpg" style="width: 300px" />
如果已过期,可以微信添加`wanglinguanfang`拉你入群。
# 请作者喝杯咖啡
> 厚椰乳一盒 + 纯牛奶半盒 + 冰块 + 咖啡液 = 生椰拿铁 yyds

File diff suppressed because one or more lines are too long

Binary file not shown.

Before

Width:  |  Height:  |  Size: 49 KiB

After

Width:  |  Height:  |  Size: 49 KiB

View File

@@ -8,13 +8,21 @@ import Drag from './src/plugins/Drag.js'
import Select from './src/plugins/Select.js'
import AssociativeLine from './src/plugins/AssociativeLine'
import RichText from './src/plugins/RichText'
import NodeImgAdjust from './src/plugins/NodeImgAdjust.js'
import TouchEvent from './src/plugins/TouchEvent.js'
import xmind from './src/parse/xmind.js'
import markdown from './src/parse/markdown.js'
import icons from './src/svg/icons.js'
import * as constants from './src/constants/constant.js'
import themes from './src/themes/index.js'
import * as defaultTheme from './src/themes/default.js'
MindMap.xmind = xmind
MindMap.markdown = markdown
MindMap.iconList = icons.nodeIconList
MindMap.constants = constants
MindMap.themes = themes
MindMap.defaultTheme = defaultTheme
MindMap
.usePlugin(MiniMap)
@@ -26,5 +34,7 @@ MindMap
.usePlugin(Select)
.usePlugin(AssociativeLine)
.usePlugin(RichText)
.usePlugin(TouchEvent)
.usePlugin(NodeImgAdjust)
export default MindMap

View File

@@ -11,127 +11,7 @@ import { layoutValueList, CONSTANTS } from './src/constants/constant'
import { SVG } from '@svgdotjs/svg.js'
import { simpleDeepClone } from './src/utils'
import defaultTheme, { checkIsNodeSizeIndependenceConfig } from './src/themes/default'
// 默认选项配置
const defaultOpt = {
// 是否只读
readonly: false,
// 布局
layout: CONSTANTS.LAYOUT.LOGICAL_STRUCTURE,
// 如果结构为鱼骨图,那么可以通过该选项控制倾斜角度
fishboneDeg: 45,
// 主题
theme: 'default', // 内置主题default默认主题
// 主题配置,会和所选择的主题进行合并
themeConfig: {},
// 放大缩小的增量比例
scaleRatio: 0.1,
// 最多显示几个标签
maxTag: 5,
// 导出图片时的内边距
exportPadding: 20,
// 展开收缩按钮尺寸
expandBtnSize: 20,
// 节点里图片和文字的间距
imgTextMargin: 5,
// 节点里各种文字信息的间距,如图标和文字的间距
textContentMargin: 2,
// 多选节点时鼠标移动到边缘时的画布移动偏移量
selectTranslateStep: 3,
// 多选节点时鼠标移动距边缘多少距离时开始偏移
selectTranslateLimit: 20,
// 自定义节点备注内容显示
customNoteContentShow: null,
/*
{
show(){},
hide(){}
}
*/
// 是否开启节点自由拖拽
enableFreeDrag: false,
// 水印配置
watermarkConfig: {
text: '',
lineSpacing: 100,
textSpacing: 100,
angle: 30,
textStyle: {
color: '#999',
opacity: 0.5,
fontSize: 14
}
},
// 达到该宽度文本自动换行
textAutoWrapWidth: 500,
// 自定义鼠标滚轮事件处理
// 可以传一个函数,回调参数为事件对象
customHandleMousewheel: null,
// 鼠标滚动的行为如果customHandleMousewheel传了自定义函数这个属性不生效
mousewheelAction: CONSTANTS.MOUSE_WHEEL_ACTION.ZOOM,// zoom放大缩小、move上下移动
// 当mousewheelAction设为move时可以通过该属性控制鼠标滚动一下视图移动的步长单位px
mousewheelMoveStep: 100,
// 默认插入的二级节点的文字
defaultInsertSecondLevelNodeText: '二级节点',
// 默认插入的二级以下节点的文字
defaultInsertBelowSecondLevelNodeText: '分支主题',
// 展开收起按钮的颜色
expandBtnStyle: {
color: '#808080',
fill: '#fff'
},
// 自定义展开收起按钮的图标
expandBtnIcon: {
open: '',// svg字符串
close: ''
},
// 是否只有当鼠标在画布内才响应快捷键事件
enableShortcutOnlyWhenMouseInSvg: true,
// 是否开启节点动画过渡
enableNodeTransitionMove: true,
// 如果开启节点动画过渡可以通过该属性设置过渡的时间单位ms
nodeTransitionMoveDuration: 300,
// 初始根节点的位置
initRootNodePosition: null,
// 导出png、svg、pdf时的图形内边距
exportPaddingX: 10,
exportPaddingY: 10,
// 节点文本编辑框的z-index
nodeTextEditZIndex: 3000,
// 节点备注浮层的z-index
nodeNoteTooltipZIndex: 3000,
// 是否在点击了画布外的区域时结束节点文本的编辑状态
isEndNodeTextEditOnClickOuter: true,
// 最大历史记录数
maxHistoryCount: 1000,
// 是否一直显示节点的展开收起按钮,默认为鼠标移上去和激活时才显示
alwaysShowExpandBtn: false,
// 扩展节点可插入的图标
iconList: [
// {
// name: '',// 分组名称
// type: '',// 分组的值
// list: [// 分组下的图标列表
// {
// name: '',// 图标名称
// icon:''// 图标可以传svg或图片
// }
// ]
// }
],
// 节点最大缓存数量
maxNodeCacheCount: 1000,
// 关联线默认文字
defaultAssociativeLineText: '关联',
// 思维导图适应画布大小时的内边距
fitPadding: 50,
// 是否开启按住ctrl键多选节点功能
enableCtrlKeyNodeSelection: true,
// 设置为左键多选节点,右键拖动画布
useLeftKeySelectionRightKeyDrag: false,
// 节点即将进入编辑前的回调方法如果该方法返回true以外的值那么将取消编辑函数可以返回一个值或一个Promise回调参数为节点实例
beforeTextEdit: null
}
import { defaultOpt } from './src/constants/defaultOptions'
// 思维导图
class MindMap {

View File

@@ -1,6 +1,6 @@
{
"name": "simple-mind-map",
"version": "0.6.2",
"version": "0.6.5",
"description": "一个简单的web在线思维导图",
"authors": [
{

View File

@@ -0,0 +1,130 @@
import { CONSTANTS } from './constant'
// 默认选项配置
export const defaultOpt = {
// 是否只读
readonly: false,
// 布局
layout: CONSTANTS.LAYOUT.LOGICAL_STRUCTURE,
// 如果结构为鱼骨图,那么可以通过该选项控制倾斜角度
fishboneDeg: 45,
// 主题
theme: 'default', // 内置主题default默认主题
// 主题配置,会和所选择的主题进行合并
themeConfig: {},
// 放大缩小的增量比例
scaleRatio: 0.2,
// 鼠标缩放是否以鼠标当前位置为中心点,否则以画布中心点
mouseScaleCenterUseMousePosition: true,
// 最多显示几个标签
maxTag: 5,
// 导出图片时的内边距
exportPadding: 20,
// 展开收缩按钮尺寸
expandBtnSize: 20,
// 节点里图片和文字的间距
imgTextMargin: 5,
// 节点里各种文字信息的间距,如图标和文字的间距
textContentMargin: 2,
// 多选节点时鼠标移动到边缘时的画布移动偏移量
selectTranslateStep: 3,
// 多选节点时鼠标移动距边缘多少距离时开始偏移
selectTranslateLimit: 20,
// 自定义节点备注内容显示
customNoteContentShow: null,
/*
{
show(){},
hide(){}
}
*/
// 是否开启节点自由拖拽
enableFreeDrag: false,
// 水印配置
watermarkConfig: {
text: '',
lineSpacing: 100,
textSpacing: 100,
angle: 30,
textStyle: {
color: '#999',
opacity: 0.5,
fontSize: 14
}
},
// 达到该宽度文本自动换行
textAutoWrapWidth: 500,
// 自定义鼠标滚轮事件处理
// 可以传一个函数,回调参数为事件对象
customHandleMousewheel: null,
// 鼠标滚动的行为如果customHandleMousewheel传了自定义函数这个属性不生效
mousewheelAction: CONSTANTS.MOUSE_WHEEL_ACTION.ZOOM, // zoom放大缩小、move上下移动
// 当mousewheelAction设为move时可以通过该属性控制鼠标滚动一下视图移动的步长单位px
mousewheelMoveStep: 100,
// 当mousewheelAction设为zoom时默认向前滚动是缩小向后滚动是放大如果该属性设为true那么会反过来
mousewheelZoomActionReverse: false,
// 默认插入的二级节点的文字
defaultInsertSecondLevelNodeText: '二级节点',
// 默认插入的二级以下节点的文字
defaultInsertBelowSecondLevelNodeText: '分支主题',
// 展开收起按钮的颜色
expandBtnStyle: {
color: '#808080',
fill: '#fff'
},
// 自定义展开收起按钮的图标
expandBtnIcon: {
open: '', // svg字符串
close: ''
},
// 是否只有当鼠标在画布内才响应快捷键事件
enableShortcutOnlyWhenMouseInSvg: true,
// 是否开启节点动画过渡
enableNodeTransitionMove: true,
// 如果开启节点动画过渡可以通过该属性设置过渡的时间单位ms
nodeTransitionMoveDuration: 300,
// 初始根节点的位置
initRootNodePosition: null,
// 导出png、svg、pdf时的图形内边距
exportPaddingX: 10,
exportPaddingY: 10,
// 节点文本编辑框的z-index
nodeTextEditZIndex: 3000,
// 节点备注浮层的z-index
nodeNoteTooltipZIndex: 3000,
// 是否在点击了画布外的区域时结束节点文本的编辑状态
isEndNodeTextEditOnClickOuter: true,
// 最大历史记录数
maxHistoryCount: 1000,
// 是否一直显示节点的展开收起按钮,默认为鼠标移上去和激活时才显示
alwaysShowExpandBtn: false,
// 扩展节点可插入的图标
iconList: [
// {
// name: '',// 分组名称
// type: '',// 分组的值
// list: [// 分组下的图标列表
// {
// name: '',// 图标名称
// icon:''// 图标可以传svg或图片
// }
// ]
// }
],
// 节点最大缓存数量
maxNodeCacheCount: 1000,
// 关联线默认文字
defaultAssociativeLineText: '关联',
// 思维导图适应画布大小时的内边距
fitPadding: 50,
// 是否开启按住ctrl键多选节点功能
enableCtrlKeyNodeSelection: true,
// 设置为左键多选节点,右键拖动画布
useLeftKeySelectionRightKeyDrag: false,
// 节点即将进入编辑前的回调方法如果该方法返回true以外的值那么将取消编辑函数可以返回一个值或一个Promise回调参数为节点实例
beforeTextEdit: null,
// 是否开启自定义节点内容
isUseCustomNodeContent: false,
// 自定义返回节点内容的方法
customCreateNodeContent: null
}

View File

@@ -425,6 +425,9 @@ class Render {
let { defaultInsertSecondLevelNodeText, defaultInsertBelowSecondLevelNodeText } = this.mindMap.opt
let list = appointNodes.length > 0 ? appointNodes : this.activeNodeList
let first = list[0]
if (first.isGeneralization) {
return
}
if (first.isRoot) {
this.insertChildNode(openEdit, appointNodes, appointData)
} else {
@@ -455,6 +458,9 @@ class Render {
let { defaultInsertSecondLevelNodeText, defaultInsertBelowSecondLevelNodeText } = this.mindMap.opt
let list = appointNodes.length > 0 ? appointNodes : this.activeNodeList
list.forEach(node => {
if (node.isGeneralization) {
return
}
if (!node.nodeData.children) {
node.nodeData.children = []
}
@@ -863,13 +869,14 @@ class Render {
}
// 设置节点图片
setNodeImage(node, { url, title, width, height }) {
setNodeImage(node, { url, title, width, height, custom = false }) {
this.setNodeDataRender(node, {
image: url,
imageTitle: title || '',
imageSize: {
width,
height
height,
custom
}
})
}

View File

@@ -66,10 +66,15 @@ export default class TextEdit {
// 显示文本编辑框
async show(node) {
if (typeof this.mindMap.opt.beforeTextEdit === 'function') {
// 使用了自定义节点内容那么不响应编辑事件
if (node.isUseCustomNodeContent()) {
return
}
let { beforeTextEdit } = this.mindMap.opt
if (typeof beforeTextEdit === 'function') {
let isShow = false
try {
isShow = await this.mindMap.opt.beforeTextEdit(node)
isShow = await beforeTextEdit(node)
} catch (error) {
isShow = false
}

View File

@@ -1,7 +1,7 @@
import Style from './Style'
import Shape from './Shape'
import { asyncRun } from '../../../utils'
import { G, Rect } from '@svgdotjs/svg.js'
import { asyncRun, nodeToHTML } from '../../../utils'
import { G, Rect, ForeignObject, SVG } from '@svgdotjs/svg.js'
import nodeGeneralizationMethods from './nodeGeneralization'
import nodeExpandBtnMethods from './nodeExpandBtn'
import nodeCommandWrapsMethods from './nodeCommandWraps'
@@ -59,6 +59,7 @@ class Node {
this.group = null
this.shapeNode = null // 节点形状节点
// 节点内容对象
this._customNodeContent = null
this._imgData = null
this._iconData = null
this._textData = null
@@ -154,6 +155,13 @@ class Node {
// 创建节点的各个内容对象数据
createNodeData() {
// 自定义节点内容
let { isUseCustomNodeContent, customCreateNodeContent } = this.mindMap.opt
if (isUseCustomNodeContent && customCreateNodeContent) {
this._customNodeContent = customCreateNodeContent(this)
}
// 如果没有返回内容,那么还是使用内置的节点内容
if (this._customNodeContent) return
this._imgData = this.createImgNode()
this._iconData = this.createIconNode()
this._textData = this.createTextNode()
@@ -176,6 +184,14 @@ class Node {
// 计算节点尺寸信息
getNodeRect() {
// 自定义节点内容
if (this.isUseCustomNodeContent()) {
let rect = this.measureCustomNodeContentSize(this._customNodeContent)
return {
width: rect.width,
height: rect.height
}
}
// 宽高
let imgContentWidth = 0
let imgContentHeight = 0
@@ -266,6 +282,15 @@ class Node {
if (this.isGeneralization && this.generalizationBelongNode) {
this.group.addClass('generalization_' + this.generalizationBelongNode.uid)
}
// 如果存在自定义节点内容,那么使用自定义节点内容
if (this.isUseCustomNodeContent()) {
let foreignObject = new ForeignObject()
foreignObject.width(width)
foreignObject.height(height)
foreignObject.add(SVG(this._customNodeContent))
this.group.add(foreignObject)
return
}
// 图片节点
let imgHeight = 0
if (this._imgData) {

View File

@@ -17,6 +17,15 @@ function createImgNode() {
node.on('dblclick', e => {
this.mindMap.emit('node_img_dblclick', this, e)
})
node.on('mouseenter', e => {
this.mindMap.emit('node_img_mouseenter', this, node, e)
})
node.on('mouseleave', e => {
this.mindMap.emit('node_img_mouseleave', this, node, e)
})
node.on('mousemove', e => {
this.mindMap.emit('node_img_mousemove', this, node, e)
})
return {
node,
width: imgSize[0],
@@ -26,9 +35,12 @@ function createImgNode() {
// 获取图片显示宽高
function getImgShowSize() {
const { custom, width, height } = this.nodeData.data.imageSize
// 如果是自定义了图片的宽高,那么不受最大宽高限制
if (custom) return [width, height]
return resizeImgSize(
this.nodeData.data.imageSize.width,
this.nodeData.data.imageSize.height,
width,
height,
this.mindMap.themeConfig.imgMaxWidth,
this.mindMap.themeConfig.imgMaxHeight
)
@@ -89,7 +101,7 @@ function createRichTextNode() {
el.style.maxWidth = this.mindMap.opt.textAutoWrapWidth + 'px'
this.mindMap.el.appendChild(div)
let { width, height } = el.getBoundingClientRect()
width = Math.ceil(width)
width = Math.ceil(width) + 1// 修复getBoundingClientRect方法对实际宽度是小数的元素获取到的值是整数导致宽度不够文本发生换行的问题
height = Math.ceil(height)
g.attr('data-width', width)
g.attr('data-height', height)
@@ -280,6 +292,32 @@ function createNoteNode() {
}
}
// 测量自定义节点内容元素的宽高
let warpEl = null
function measureCustomNodeContentSize (content) {
if (!warpEl) {
warpEl = document.createElement('div')
warpEl.style.cssText = `
position: fixed;
left: -99999px;
top: -99999px;
`
this.mindMap.el.appendChild(warpEl)
}
warpEl.innerHTML = ''
warpEl.appendChild(content)
let rect = warpEl.getBoundingClientRect()
return {
width: rect.width,
height: rect.height
}
}
// 是否使用的是自定义节点内容
function isUseCustomNodeContent() {
return !!this._customNodeContent
}
export default {
createImgNode,
getImgShowSize,
@@ -288,5 +326,7 @@ export default {
createTextNode,
createHyperlinkNode,
createTagNode,
createNoteNode
createNoteNode,
measureCustomNodeContentSize,
isUseCustomNodeContent
}

View File

@@ -60,29 +60,38 @@ class View {
})
// 放大缩小视图
this.mindMap.event.on('mousewheel', (e, dir, event, isTouchPad) => {
let {
customHandleMousewheel,
mousewheelAction,
mouseScaleCenterUseMousePosition,
mousewheelMoveStep,
mousewheelZoomActionReverse
} = this.mindMap.opt
// 是否自定义鼠标滚轮事件
if (
this.mindMap.opt.customHandleMousewheel &&
typeof this.mindMap.opt.customHandleMousewheel === 'function'
customHandleMousewheel &&
typeof customHandleMousewheel === 'function'
) {
return this.mindMap.opt.customHandleMousewheel(e)
return customHandleMousewheel(e)
}
if (
this.mindMap.opt.mousewheelAction === CONSTANTS.MOUSE_WHEEL_ACTION.ZOOM
) {
// 鼠标滚轮事件控制缩放
if (mousewheelAction === CONSTANTS.MOUSE_WHEEL_ACTION.ZOOM) {
let cx = mouseScaleCenterUseMousePosition ? e.clientX : undefined
let cy = mouseScaleCenterUseMousePosition ? e.clientY : undefined
switch (dir) {
// 鼠标滚轮,向上和向左,都是缩小
case CONSTANTS.DIR.UP:
case CONSTANTS.DIR.LEFT:
this.narrow()
mousewheelZoomActionReverse ? this.enlarge(cx, cy) : this.narrow(cx, cy)
break
// 鼠标滚轮,向下和向右,都是放大
case CONSTANTS.DIR.DOWN:
case CONSTANTS.DIR.RIGHT:
this.enlarge()
mousewheelZoomActionReverse ? this.narrow(cx, cy) : this.enlarge(cx, cy)
break
}
} else {
let step = this.mindMap.opt.mousewheelMoveStep
} else {// 鼠标滚轮事件控制画布移动
let step = mousewheelMoveStep
if (isTouchPad) {
step = 5
}
@@ -170,8 +179,8 @@ class View {
// 应用变换
transform() {
this.mindMap.draw.transform({
origin: [0, 0],
scale: this.scale,
// origin: 'center center',
translate: [this.x, this.y]
})
this.mindMap.emit('view_data_change', this.getTransformData())
@@ -190,26 +199,43 @@ class View {
}
// 缩小
narrow() {
if (this.scale - this.mindMap.opt.scaleRatio > 0.1) {
this.scale -= this.mindMap.opt.scaleRatio
} else {
this.scale = 0.1
}
narrow(cx, cy) {
const scale = Math.max(this.scale - this.mindMap.opt.scaleRatio, 0.1)
this.scaleInCenter(scale, cx, cy)
this.transform()
this.mindMap.emit('scale', this.scale)
}
// 放大
enlarge() {
this.scale += this.mindMap.opt.scaleRatio
enlarge(cx, cy) {
const scale = this.scale + this.mindMap.opt.scaleRatio
this.scaleInCenter(scale, cx, cy)
this.transform()
this.mindMap.emit('scale', this.scale)
}
// 设置缩放
setScale(scale) {
// 基于指定中心进行缩放cxcy 可不指定,此时会使用画布中心点
scaleInCenter(scale, cx, cy) {
if (cx === undefined || cy === undefined) {
cx = this.mindMap.width / 2
cy = this.mindMap.height / 2
}
const prevScale = this.scale
const ratio = 1 - scale / prevScale
const dx = (cx - this.x) * ratio
const dy = (cy - this.y) * ratio
this.x += dx
this.y += dy
this.scale = scale
}
// 设置缩放
setScale(scale, cx, cy) {
if (cx !== undefined && cy !== undefined) {
this.scaleInCenter(scale, cx, cy)
} else {
this.scale = scale
}
this.transform()
this.mindMap.emit('scale', this.scale)
}

View File

@@ -44,7 +44,8 @@ const transformXmind = content => {
}
// 节点备注
if (node.notes) {
newNode.data.note = (node.notes.realHTML || node.notes.plain).content
let notesData = node.notes.realHTML || node.notes.plain
newNode.data.note = notesData ? notesData.content || '' : ''
}
// 超链接
if (node.href && /^https?:\/\//.test(node.href)) {

View File

@@ -0,0 +1,222 @@
// 节点图片大小调整插件
import { resizeImgSizeByOriginRatio } from '../utils/index'
import btnsSvg from '../svg/btns'
class NodeImgAdjust {
// 构造函数
constructor({ mindMap }) {
this.mindMap = mindMap
this.resizeBtnSize = 26 // 调整按钮的大小
this.handleEl = null // 自定义元素,用来渲染临时图片、调整按钮
this.isShowHandleEl = false // 自定义元素是否在显示中
this.node = null // 当前节点实例
this.img = null // 当前节点的图片节点
this.rect = null // 当前图片节点的尺寸信息
this.isMousedown = false // 当前是否是按住调整按钮状态
this.currentImgWidth = 0 // 当前拖拽实时图片的大小
this.currentImgHeight = 0
this.isAdjusted = false // 是否是拖拽结束后的渲染期间
this.bindEvent()
}
// 监听事件
bindEvent() {
this.onNodeImgMouseleave = this.onNodeImgMouseleave.bind(this)
this.onNodeImgMousemove = this.onNodeImgMousemove.bind(this)
this.onMousemove = this.onMousemove.bind(this)
this.onMouseup = this.onMouseup.bind(this)
this.onRenderEnd = this.onRenderEnd.bind(this)
this.mindMap.on('node_img_mouseleave', this.onNodeImgMouseleave)
this.mindMap.on('node_img_mousemove', this.onNodeImgMousemove)
this.mindMap.on('mousemove', this.onMousemove)
this.mindMap.on('mouseup', this.onMouseup)
this.mindMap.on('node_mouseup', this.onMouseup)
this.mindMap.on('node_tree_render_end', this.onRenderEnd)
}
// 解绑事件
unBindEvent() {
this.mindMap.off('node_img_mouseleave', this.onNodeImgMouseleave)
this.mindMap.off('node_img_mousemove', this.onNodeImgMousemove)
this.mindMap.off('mousemove', this.onMousemove)
this.mindMap.off('mouseup', this.onMouseup)
this.mindMap.off('node_mouseup', this.onMouseup)
this.mindMap.off('node_tree_render_end', this.onRenderEnd)
}
// 节点图片鼠标移动事件
onNodeImgMousemove(node, img) {
// 如果当前正在拖动调整中那么直接返回
if (this.isMousedown || this.isAdjusted) return
// 如果在当前节点内移动,以及自定义元素已经是显示状态,那么直接返回
if (this.node === node && this.isShowHandleEl) return
// 更新当前节点信息
this.node = node
this.img = img
this.rect = this.img.rbox()
// 显示自定义元素
this.showHandleEl()
}
// 节点图片鼠标移出事件
onNodeImgMouseleave() {
if (this.isMousedown) return
this.hideHandleEl()
}
// 隐藏节点实际的图片
hideNodeImage() {
if (!this.img) return
this.img.hide()
}
// 显示节点实际的图片
showNodeImage() {
if (!this.img) return
this.img.show()
}
// 显示自定义元素
showHandleEl() {
if (!this.handleEl) {
this.createResizeBtnEl()
}
this.setHandleElRect()
document.body.appendChild(this.handleEl)
this.isShowHandleEl = true
}
// 隐藏自定义元素
hideHandleEl() {
if (!this.isShowHandleEl) return
this.isShowHandleEl = false
document.body.removeChild(this.handleEl)
this.handleEl.style.backgroundImage = ``
this.handleEl.style.width = 0
this.handleEl.style.height = 0
this.handleEl.style.left = 0
this.handleEl.style.top = 0
}
// 设置自定义元素尺寸位置信息
setHandleElRect() {
let { width, height, x, y } = this.rect
this.handleEl.style.left = `${x}px`
this.handleEl.style.top = `${y}px`
this.currentImgWidth = width
this.currentImgHeight = height
this.updateHandleElSize()
}
// 更新自定义元素宽高
updateHandleElSize() {
this.handleEl.style.width = `${this.currentImgWidth}px`
this.handleEl.style.height = `${this.currentImgHeight}px`
}
// 创建调整按钮元素
createResizeBtnEl() {
// 容器元素
this.handleEl = document.createElement('div')
this.handleEl.style.cssText = `
pointer-events: none;
position: fixed;
background-size: cover;
`
// 调整按钮元素
const btnEl = document.createElement('div')
btnEl.innerHTML = btnsSvg.imgAdjust
btnEl.style.cssText = `
position: absolute;
right: 0;
bottom: 0;
pointer-events: auto;
background-color: rgba(0, 0, 0, 0.3);
width: ${this.resizeBtnSize}px;
height: ${this.resizeBtnSize}px;
display: flex;
justify-content: center;
align-items: center;
cursor: nwse-resize;
`
this.handleEl.appendChild(btnEl)
// 给按钮元素绑定事件
btnEl.addEventListener('mouseenter', () => {
// 移入按钮,会触发节点图片的移出事件,所以需要再次显示按钮
this.showHandleEl()
})
btnEl.addEventListener('mouseleave', () => {
// 移除按钮,需要隐藏按钮
if (this.isMousedown) return
this.hideHandleEl()
})
btnEl.addEventListener('mousedown', e => {
this.onMousedown(e)
})
}
// 鼠标按钮按下事件
onMousedown() {
this.isMousedown = true
// 隐藏节点实际图片
this.hideNodeImage()
// 将节点图片渲染到自定义元素上
this.handleEl.style.backgroundImage = `url(${this.node.nodeData.data.image})`
}
// 鼠标移动
onMousemove(e) {
if (!this.isMousedown) return
e.preventDefault()
// 计算当前拖拽位置对应的图片的实时大小
let { width: imageOriginWidth, height: imageOriginHeight } =
this.node.nodeData.data.imageSize
let newWidth = e.clientX - this.rect.x
let newHeight = e.clientY - this.rect.y
if (newWidth <= 0 || newHeight <= 0) return
let [actWidth, actHeight] = resizeImgSizeByOriginRatio(
imageOriginWidth,
imageOriginHeight,
newWidth,
newHeight
)
this.currentImgWidth = actWidth
this.currentImgHeight = actHeight
this.updateHandleElSize()
}
// 鼠标松开
onMouseup() {
if (!this.isMousedown) return
// 显示节点实际图片
this.showNodeImage()
// 隐藏自定义元素
this.hideHandleEl()
// 更新节点图片为新的大小
let { image, imageTitle } = this.node.nodeData.data
this.mindMap.execCommand('SET_NODE_IMAGE', this.node, {
url: image,
title: imageTitle,
width: this.currentImgWidth,
height: this.currentImgHeight,
custom: true // 代表自定义了图片大小
})
this.isAdjusted = true
this.isMousedown = false
}
// 渲染完成事件
onRenderEnd() {
if (!this.isAdjusted) return
this.isAdjusted = false
}
// 插件被移除前做的事情
beforePluginRemove() {
this.unBindEvent()
}
}
NodeImgAdjust.instanceName = 'nodeImgAdjust'
export default NodeImgAdjust

View File

@@ -50,16 +50,20 @@ class TouchEvent {
} else if (len === 2) {
let touch1 = e.touches[0]
let touch2 = e.touches[1]
let distance = Math.sqrt(
Math.pow(touch1.clientX - touch2.clientX, 2) +
Math.pow(touch1.clientY - touch2.clientY, 2)
)
let ox = touch1.clientX - touch2.clientX
let oy = touch1.clientY - touch2.clientY
let distance = Math.sqrt(Math.pow(ox, 2) + Math.pow(oy, 2))
// 以两指中心点进行缩放
let { x: touch1ClientX, y: touch1ClientY } = this.mindMap.toPos(touch1.clientX, touch1.clientY)
let { x: touch2ClientX, y: touch2ClientY } = this.mindMap.toPos(touch2.clientX, touch2.clientY)
let cx = (touch1ClientX + touch2ClientX) / 2
let cy = (touch1ClientY + touch2ClientY) / 2
if (distance > this.doubleTouchmoveDistance) {
// 放大
this.mindMap.view.enlarge()
this.mindMap.view.enlarge(cx, cy)
} else {
// 缩小
this.mindMap.view.narrow()
this.mindMap.view.narrow(cx, cy)
}
this.doubleTouchmoveDistance = distance
}

View File

@@ -4,7 +4,11 @@ const open = `<svg t="1618141562310" class="icon" viewBox="0 0 1024 1024" versio
// 收缩按钮
const close = `<svg t="1618141589243" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="13611" width="200" height="200"><path d="M512 105.472c225.28 0 407.04 181.76 407.04 407.04s-181.76 407.04-407.04 407.04-407.04-181.76-407.04-407.04 181.76-407.04 407.04-407.04z m0-74.24c-265.216 0-480.768 215.552-480.768 480.768s215.552 480.768 480.768 480.768 480.768-215.552 480.768-480.768-215.552-480.768-480.768-480.768z" p-id="13612"></path><path d="M252.928 474.624h518.144v74.24h-518.144z" p-id="13613"></path></svg>`
// 图片调整按钮
const imgAdjust = `<svg width="12px" height="12px" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg"><path fill="#ffffff" d="M1008.128 614.4a25.6 25.6 0 0 0-27.648 5.632l-142.848 142.848L259.072 186.88 401.92 43.52A25.6 25.6 0 0 0 384 0h-358.4a25.6 25.6 0 0 0-25.6 25.6v358.4a25.6 25.6 0 0 0 43.52 17.92l143.36-142.848 578.048 578.048-142.848 142.848a25.6 25.6 0 0 0 17.92 43.52h358.4a25.6 25.6 0 0 0 25.6-25.6v-358.4a25.6 25.6 0 0 0-15.872-25.088z" /></svg>`
export default {
open,
close
close,
imgAdjust
}

View File

@@ -50,6 +50,26 @@ export const bfsWalk = (root, callback) => {
}
}
// 按原比例缩放图片
export const resizeImgSizeByOriginRatio = (
width,
height,
newWidth,
newHeight
) => {
let arr = []
let nRatio = width / height
let mRatio = newWidth / newHeight
if (nRatio > mRatio) {
// 固定高度
arr = [nRatio * newHeight, newHeight]
} else {
// 固定宽度
arr = [newWidth, newWidth / nRatio]
}
return arr
}
// 缩放图片尺寸
export const resizeImgSize = (width, height, maxWidth, maxHeight) => {
let nRatio = width / height
@@ -137,7 +157,12 @@ export const copyRenderTree = (tree, root, removeActiveState = false) => {
}
// 复制节点树数据
export const copyNodeTree = (tree, root, removeActiveState = false, keepId = false) => {
export const copyNodeTree = (
tree,
root,
removeActiveState = false,
keepId = false
) => {
tree.data = simpleDeepClone(root.nodeData ? root.nodeData.data : root.data)
// 去除节点id因为节点id不能重复
if (tree.data.id && !keepId) delete tree.data.id
@@ -236,8 +261,8 @@ export const degToRad = deg => {
return deg * (Math.PI / 180)
}
// 驼峰转连字符
export const camelCaseToHyphen = (str) => {
// 驼峰转连字符
export const camelCaseToHyphen = str => {
return str.replace(/([a-z])([A-Z])/g, (...args) => {
return args[1] + '-' + args[2].toLowerCase()
})
@@ -258,11 +283,8 @@ export const measureText = (text, { italic, bold, fontSize, fontFamily }) => {
}
measureTextContext.save()
measureTextContext.font = font
const {
width,
actualBoundingBoxAscent,
actualBoundingBoxDescent
} = measureTextContext.measureText(text)
const { width, actualBoundingBoxAscent, actualBoundingBoxDescent } =
measureTextContext.measureText(text)
measureTextContext.restore()
const height = actualBoundingBoxAscent + actualBoundingBoxDescent
return { width, height }
@@ -270,7 +292,9 @@ export const measureText = (text, { italic, bold, fontSize, fontFamily }) => {
// 拼接font字符串
export const joinFontStr = ({ italic, bold, fontSize, fontFamily }) => {
return `${italic ? 'italic ' : ''} ${bold ? 'bold ' : ''} ${fontSize}px ${fontFamily} `
return `${italic ? 'italic ' : ''} ${
bold ? 'bold ' : ''
} ${fontSize}px ${fontFamily} `
}
// 在下一个事件循环里执行任务
@@ -336,7 +360,7 @@ export const checkNodeOuter = (mindMap, node) => {
// 提取html字符串里的纯文本
let getTextFromHtmlEl = null
export const getTextFromHtml = (html) => {
export const getTextFromHtml = html => {
if (!getTextFromHtmlEl) {
getTextFromHtmlEl = document.createElement('div')
}
@@ -345,15 +369,26 @@ export const getTextFromHtml = (html) => {
}
// 将blob转成data:url
export const readBlob = (blob) => {
export const readBlob = blob => {
return new Promise((resolve, reject) => {
let reader = new FileReader()
reader.onload = (evt) => {
reader.onload = evt => {
resolve(evt.target.result)
}
reader.onerror = (err) => {
reader.onerror = err => {
reject(err)
}
reader.readAsDataURL(blob)
})
}
}
// 将dom节点转换成html字符串
let nodeToHTMLWrapEl = null
export const nodeToHTML = node => {
if (!nodeToHTMLWrapEl) {
nodeToHTMLWrapEl = document.createElement('div')
}
nodeToHTMLWrapEl.innerHTML = ''
nodeToHTMLWrapEl.appendChild(node)
return nodeToHTMLWrapEl.innerHTML
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 123 KiB

View File

@@ -43,7 +43,10 @@ export default {
associativeLineWidth: 'Width',
associativeLineColor: 'Color',
associativeLineActiveWidth: 'Active width',
associativeLineActiveColor: 'Active color'
associativeLineActiveColor: 'Active color',
mousewheelZoomActionReverse: 'Mouse Wheel Zoom',
mousewheelZoomActionReverse1: 'Zoom out forward and zoom in back',
mousewheelZoomActionReverse2: 'Zoom in forward and zoom out back'
},
color: {
moreColor: 'More color'

View File

@@ -43,7 +43,10 @@ export default {
associativeLineWidth: '粗细',
associativeLineColor: '颜色',
associativeLineActiveWidth: '激活粗细',
associativeLineActiveColor: '激活颜色'
associativeLineActiveColor: '激活颜色',
mousewheelZoomActionReverse: '鼠标滚轮缩放',
mousewheelZoomActionReverse1: '向前缩小向后放大',
mousewheelZoomActionReverse2: '向前放大向后缩小'
},
color: {
moreColor: '更多颜色'

View File

@@ -11,7 +11,7 @@ let langList = [
}
]
let StartList = ['introduction', 'start', 'deploy', 'client', 'translate', 'changelog']
let CourseList = new Array(19).fill(0).map((_, index) => {
let CourseList = new Array(20).fill(0).map((_, index) => {
return 'course' + (index + 1)
})
let APIList = [
@@ -31,6 +31,7 @@ let APIList = [
'watermark',
'associativeLine',
'touchEvent',
'nodeImgAdjust',
'xmind',
'markdown',
'utils'

View File

@@ -1,5 +1,27 @@
# Changelog
## 0.6.5
Fix: 1.Fix the issue of xmind file import errors. 2.Fixed a rare issue where line breaks occur when the width of the node text is decimal.
New: 1.The packaged library supports obtaining built-in constants, themes, and other data. 2.Supports configuring the zoom behavior corresponding to the direction of the mouse wheel. 3.Node images support dragging and resizing.
## 0.6.4-fix.1
New: 1.When zooming with the mouse wheel, the default zoom is centered around the current position of the mouse, which can be turned off by configuring.
Fix: 1.Fixed an issue where the default value of the zoom center point was not updated after changing the canvas size.
## 0.6.4
New: 1.The default is to scale at the center point of the canvas. 2.Optimize the scaling of both fingers on the mobile end, with the center position of the two fingers as the center point for scaling.
## 0.6.3
Fix: 1.Fix the issue where the summary node will respond to inserting node shortcuts.
New: 1.Support custom node content.
## 0.6.2
Fix: 1.Fixed the problem that the new node does not change with the theme in rich Text mode.

View File

@@ -1,6 +1,17 @@
<template>
<div>
<h1>Changelog</h1>
<h2>0.6.5</h2>
<p>Fix: 1.Fix the issue of xmind file import errors.</p>
<p>New: 1.The packaged library supports obtaining built-in constants, themes, and other data. 2.Supports configuring the zoom behavior corresponding to the direction of the mouse wheel. 3.Node images support dragging and resizing.</p>
<h2>0.6.4-fix.1</h2>
<p>New: 1.When zooming with the mouse wheel, the default zoom is centered around the current position of the mouse, which can be turned off by configuring.</p>
<p>Fix: 1.Fixed an issue where the default value of the zoom center point was not updated after changing the canvas size.</p>
<h2>0.6.4</h2>
<p>New: 1.The default is to scale at the center point of the canvas. 2.Optimize the scaling of both fingers on the mobile end, with the center position of the two fingers as the center point for scaling.</p>
<h2>0.6.3</h2>
<p>Fix: 1.Fix the issue where the summary node will respond to inserting node shortcuts.</p>
<p>New: 1.Support custom node content.</p>
<h2>0.6.2</h2>
<p>Fix: 1.Fixed the problem that the new node does not change with the theme in rich Text mode.</p>
<h2>0.6.1</h2>

View File

@@ -45,6 +45,7 @@ const mindMap = new MindMap({
| customHandleMousewheelv0.4.3+ | Function | null | User-defined mouse wheel event processing can pass a function, and the callback parameter is the event object | |
| mousewheelActionv0.4.3+ | String | zoom | The behavior of the mouse wheel, `zoom`(Zoom in and out)、`move`(Move up and down). If `customHandleMousewheel` passes a custom function, this property will not take effect | |
| mousewheelMoveStepv0.4.3+ | Number | 100 | When the `mousewheelAction` is set to `move`, you can use this attribute to control the step length of the view movement when the mouse scrolls. The unit is `px` | |
| mousewheelZoomActionReversev0.6.5+ | Boolean | false | When `mousewheelAction` is set to `zoom`, the default scrolling forward is to zoom out, and scrolling backward is to zoom in. If this property is set to true, it will be reversed | |
| defaultInsertSecondLevelNodeTextv0.4.7+ | String | 二级节点 | Text of the default inserted secondary node | |
| defaultInsertBelowSecondLevelNodeTextv0.4.7+ | String | 分支主题 | Text for nodes below the second level inserted by default | |
| expandBtnStylev0.5.0+ | Object | { color: '#808080', fill: '#fff' } | Expand the color of the stow button | |
@@ -67,6 +68,9 @@ const mindMap = new MindMap({
| enableCtrlKeyNodeSelectionv0.6.0+ | Boolean | true | Whether to enable the function of holding down the Ctrl key to select multiple nodes | |
| useLeftKeySelectionRightKeyDragv0.6.0+ | Boolean | false | Setting to left click to select multiple nodes and right click to drag the canvas. | |
| beforeTextEditv0.6.0+ | Function/null | null | The callback method before the node is about to enter editing. If the method returns a value other than true, the editing will be canceled. The function can return a value or a promise, and the callback parameter is the node instance | |
| isUseCustomNodeContentv0.6.3+ | Boolean | false | Whether to customize node content | |
| customCreateNodeContentv0.6.3+ | Function/null | null | If `isUseCustomNodeContent` is set to `true`, then this option needs to be used to pass in a method that receives the node instance `node` as a parameter (if you want to obtain data for that node, you can use `node.nodeData.data`). You need to return the custom node content element, which is the DOM node. If a node does not require customization, you can return `null` | |
| mouseScaleCenterUseMousePositionv0.6.4-fix.1+ | Boolean | true | Is the mouse zoom centered around the current position of the mouse, otherwise centered around the canvas | |
### Watermark config
@@ -231,6 +235,9 @@ Listen to an event. Event list:
| hide_text_edit | Node text edit box close event | textEditNode (text edit box DOM node), activeNodeList (current list of active nodes) |
| scale | Zoom event | scale (zoom ratio) |
| node_img_dblclickv0.2.15+ | Node image double-click event | this (node instance), e (event object) |
| node_img_mouseenterv0.6.5+ | Node image mouseenter event | thisnode instance、imgNodeimg node、eevent object |
| node_img_mouseleavev0.6.5+ | Node image mouseleave event | thisnode instance、imgNodeimg node、eevent object |
| node_img_mousemovev0.6.5+ | Node image mousemove event | thisnode instance、imgNodeimg node、eevent object |
| node_tree_render_endv0.2.16+ | Node tree render end event | |
| rich_text_selection_changev0.4.0+ | Available when the `RichText` plugin is registered. Triggered when the text selection area changes when the node is edited | hasRangeWhether there is a selection、rectInfoSize and location information of the selected area、formatInfoText formatting information of the selected area |
| transforming-dom-to-imagesv0.4.0+ | Available when the `RichText` plugin is registered. When there is a `DOM` node in `svg`, the `DOM` node will be converted to an image when exporting to an image. This event will be triggered during the conversion process. You can use this event to prompt the user about the node to which you are currently converting | indexIndex of the node currently converted to、lenTotal number of nodes to be converted |

View File

@@ -176,6 +176,13 @@
<td></td>
</tr>
<tr>
<td>mousewheelZoomActionReversev0.6.5+</td>
<td>Boolean</td>
<td>false</td>
<td>When <code>mousewheelAction</code> is set to <code>zoom</code>, the default scrolling forward is to zoom out, and scrolling backward is to zoom in. If this property is set to true, it will be reversed</td>
<td></td>
</tr>
<tr>
<td>defaultInsertSecondLevelNodeTextv0.4.7+</td>
<td>String</td>
<td>二级节点</td>
@@ -329,6 +336,27 @@
<td>The callback method before the node is about to enter editing. If the method returns a value other than true, the editing will be canceled. The function can return a value or a promise, and the callback parameter is the node instance</td>
<td></td>
</tr>
<tr>
<td>isUseCustomNodeContentv0.6.3+</td>
<td>Boolean</td>
<td>false</td>
<td>Whether to customize node content</td>
<td></td>
</tr>
<tr>
<td>customCreateNodeContentv0.6.3+</td>
<td>Function/null</td>
<td>null</td>
<td>If <code>isUseCustomNodeContent</code> is set to <code>true</code>, then this option needs to be used to pass in a method that receives the node instance <code>node</code> as a parameter (if you want to obtain data for that node, you can use <code>node.nodeData.data</code>). You need to return the custom node content element, which is the DOM node. If a node does not require customization, you can return <code>null</code></td>
<td></td>
</tr>
<tr>
<td>mouseScaleCenterUseMousePositionv0.6.4-fix.1+</td>
<td>Boolean</td>
<td>true</td>
<td>Is the mouse zoom centered around the current position of the mouse, otherwise centered around the canvas</td>
<td></td>
</tr>
</tbody>
</table>
<h3>Watermark config</h3>
@@ -632,6 +660,21 @@ poor performance and should be used sparingly.</p>
<td>this (node instance), e (event object)</td>
</tr>
<tr>
<td>node_img_mouseenterv0.6.5+</td>
<td>Node image mouseenter event</td>
<td>thisnode instanceimgNodeimg nodeeevent object</td>
</tr>
<tr>
<td>node_img_mouseleavev0.6.5+</td>
<td>Node image mouseleave event</td>
<td>thisnode instanceimgNodeimg nodeeevent object</td>
</tr>
<tr>
<td>node_img_mousemovev0.6.5+</td>
<td>Node image mousemove event</td>
<td>thisnode instanceimgNodeimg nodeeevent object</td>
</tr>
<tr>
<td>node_tree_render_endv0.2.16+</td>
<td>Node tree render end event</td>
<td></td>

View File

@@ -0,0 +1,16 @@
# NodeImgAdjust plugin
> v0.6.5+
This plugin provides the function of dragging and adjusting the size of images within nodes.
## Register
```js
import MindMap from 'simple-mind-map'
import NodeImgAdjust from 'simple-mind-map/src/plugins/NodeImgAdjust.js'
MindMap.usePlugin(NodeImgAdjust)
```
After registration and instantiation of `MindMap`, the instance can be obtained through `mindMap.nodeImgAdjust`.

View File

@@ -0,0 +1,27 @@
<template>
<div>
<h1>NodeImgAdjust plugin</h1>
<blockquote>
<p>v0.6.5+</p>
</blockquote>
<p>This plugin provides the function of dragging and adjusting the size of images within nodes.</p>
<h2>Register</h2>
<pre class="hljs"><code><span class="hljs-keyword">import</span> MindMap <span class="hljs-keyword">from</span> <span class="hljs-string">&#x27;simple-mind-map&#x27;</span>
<span class="hljs-keyword">import</span> NodeImgAdjust <span class="hljs-keyword">from</span> <span class="hljs-string">&#x27;simple-mind-map/src/plugins/NodeImgAdjust.js&#x27;</span>
MindMap.usePlugin(NodeImgAdjust)
</code></pre>
<p>After registration and instantiation of <code>MindMap</code>, the instance can be obtained through <code>mindMap.nodeImgAdjust</code>.</p>
</div>
</template>
<script>
export default {
}
</script>
<style>
</style>

View File

@@ -77,7 +77,7 @@ If you need a file in the format of `umd` module, such as `CDN` in the browser,
<script scr="simpleMindMap.umd.min.js"></script>
```
A global variable `window.simpleMindMap` will be created.
A global variable `window.simpleMindMap` will be created. you can get `MindMap` constructor by `window.simpleMindMap.default`, for more detail info you can log `window.simpleMindMap`.
The disadvantage of this method is that it will contain all the content, including the plugins you have not registered, so the overall volume will be relatively large.
@@ -112,6 +112,8 @@ cd web
npm run buildLibrary
```
The packaging entry is `simple-mind-map/full.js`, which will introduce all plugins by default. If you don't need all plugins, you can modify the file to only introduce the plugins you need, which can reduce the size of the packaged file.
The `package.json` file in the `simple-mind-map` library provides two export
fields:

View File

@@ -57,7 +57,7 @@ compile this dependency:</p>
<pre class="hljs"><code><span class="hljs-tag">&lt;<span class="hljs-name">link</span> <span class="hljs-attr">rel</span>=<span class="hljs-string">&quot;stylesheet&quot;</span> <span class="hljs-attr">href</span>=<span class="hljs-string">&quot;simpleMindMap.css&quot;</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">scr</span>=<span class="hljs-string">&quot;simpleMindMap.umd.min.js&quot;</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>
</code></pre>
<p>A global variable <code>window.simpleMindMap</code> will be created.</p>
<p>A global variable <code>window.simpleMindMap</code> will be created. you can get <code>MindMap</code> constructor by <code>window.simpleMindMap.default</code>, for more detail info you can log <code>window.simpleMindMap</code>.</p>
<p>The disadvantage of this method is that it will contain all the content, including the plugins you have not registered, so the overall volume will be relatively large.</p>
<p>v0.5.4+If you want to use the <code>ES</code> module directly on the browser side, you can find the <code>simpleMindMap.esm.js</code> and <code>simpleMindMap.esm.css</code> files in the <code>/simple-mind-map/dist/</code> directory.</p>
<h2>Development</h2>
@@ -80,6 +80,7 @@ simple-mind-map. This uses the same packaging tool as the sample project web.</p
<pre class="hljs"><code><span class="hljs-built_in">cd</span> web
npm run buildLibrary
</code></pre>
<p>The packaging entry is <code>simple-mind-map/full.js</code>, which will introduce all plugins by default. If you don't need all plugins, you can modify the file to only introduce the plugins you need, which can reduce the size of the packaged file.</p>
<p>The <code>package.json</code> file in the <code>simple-mind-map</code> library provides two export
fields:</p>
<pre class="hljs"><code>{

View File

@@ -10,6 +10,20 @@ import {walk, ...} from 'simple-mind-map/src/utils'
### Methods
#### resizeImgSizeByOriginRatio(width, height, newWidth, newHeight)
> v0.6.5+
`width`: The original width of the image
`height`The original height of the image
`newWidth`Width to zoom in to
`newHeight`Height to zoom in to
Scale the image proportionally. Zoom to the specified size of `newWidth` and `newHeight` while maintaining the original aspect ratio of the image.
#### walk(root, parent, beforeCallback, afterCallback, isRoot, layerIndex = 0, index = 0)
Depth-first traversal of a tree

View File

@@ -6,6 +6,15 @@
<pre class="hljs"><code><span class="hljs-keyword">import</span> {walk, ...} <span class="hljs-keyword">from</span> <span class="hljs-string">&#x27;simple-mind-map/src/utils&#x27;</span>
</code></pre>
<h3>Methods</h3>
<h4>resizeImgSizeByOriginRatio(width, height, newWidth, newHeight)</h4>
<blockquote>
<p>v0.6.5+</p>
</blockquote>
<p><code>width</code>: The original width of the image</p>
<p><code>height</code>The original height of the image</p>
<p><code>newWidth</code>Width to zoom in to</p>
<p><code>newHeight</code>Height to zoom in to</p>
<p>Scale the image proportionally. Zoom to the specified size of <code>newWidth</code> and <code>newHeight</code> while maintaining the original aspect ratio of the image.</p>
<h4>walk(root, parent, beforeCallback, afterCallback, isRoot, layerIndex = 0, index = 0)</h4>
<p>Depth-first traversal of a tree</p>
<p><code>root</code>: the root node of the tree to be traversed</p>

View File

@@ -35,11 +35,19 @@ Translate the `y` direction to a specific position
Revert to the default transformation
### narrow()
### narrow(cx, cy)
- `cx`:v0.6.4+Zoom to the specified position on the canvas, default to the center point of the canvas
- `cy`:v0.6.4+Zoom to the specified position on the canvas, default to the center point of the canvas
Zoom out
### enlarge()
### enlarge(cx, cy)
- `cx`:v0.6.4+Zoom to the specified position on the canvas, default to the center point of the canvas
- `cy`:v0.6.4+Zoom to the specified position on the canvas, default to the center point of the canvas
Zoom in
@@ -56,8 +64,12 @@ Get the current transform data, can be used for display
Dynamically set transform data, transform data can be obtained through the
getTransformData method"
### setScale(scale)
### setScale(scale, cx, cy)
> v0.2.17+
- `cx`:v0.6.4+Zoom to the specified position on the canvas, default to the center point of the canvas
- `cy`:v0.6.4+Zoom to the specified position on the canvas, default to the center point of the canvas
Setting Zoom

View File

@@ -25,9 +25,25 @@ through <code>mindMap.view</code></p>
<p>Translate the <code>y</code> direction to a specific position</p>
<h3>reset()</h3>
<p>Revert to the default transformation</p>
<h3>narrow()</h3>
<h3>narrow(cx, cy)</h3>
<ul>
<li>
<p><code>cx</code>:v0.6.4+Zoom to the specified position on the canvas, default to the center point of the canvas</p>
</li>
<li>
<p><code>cy</code>:v0.6.4+Zoom to the specified position on the canvas, default to the center point of the canvas</p>
</li>
</ul>
<p>Zoom out</p>
<h3>enlarge()</h3>
<h3>enlarge(cx, cy)</h3>
<ul>
<li>
<p><code>cx</code>:v0.6.4+Zoom to the specified position on the canvas, default to the center point of the canvas</p>
</li>
<li>
<p><code>cy</code>:v0.6.4+Zoom to the specified position on the canvas, default to the center point of the canvas</p>
</li>
</ul>
<p>Zoom in</p>
<h3>getTransformData()</h3>
<blockquote>
@@ -40,10 +56,18 @@ through <code>mindMap.view</code></p>
</blockquote>
<p>Dynamically set transform data, transform data can be obtained through the
getTransformData method&quot;</p>
<h3>setScale(scale)</h3>
<h3>setScale(scale, cx, cy)</h3>
<blockquote>
<p>v0.2.17+</p>
</blockquote>
<ul>
<li>
<p><code>cx</code>:v0.6.4+Zoom to the specified position on the canvas, default to the center point of the canvas</p>
</li>
<li>
<p><code>cy</code>:v0.6.4+Zoom to the specified position on the canvas, default to the center point of the canvas</p>
</li>
</ul>
<p>Setting Zoom</p>
</div>

View File

@@ -26,6 +26,7 @@ export default [
{ path: 'course17', title: '导入和导出' },
{ path: 'course18', title: '如何持久化数据' },
{ path: 'course19', title: '插入和扩展节点图标' },
{ path: 'course20', title: '如何自定义节点内容' },
{ path: 'doExport', title: 'Export 插件' },
{ path: 'drag', title: 'Drag插件' },
{ path: 'introduction', title: '简介' },
@@ -45,7 +46,8 @@ export default [
{ path: 'xmind', title: 'XMind解析' },
{ path: 'deploy', title: '部署' },
{ path: 'client', title: '客户端' },
{ path: 'touchEvent', title: 'TouchEvent插件' }
{ path: 'touchEvent', title: 'TouchEvent插件' },
{ path: 'nodeImgAdjust', title: 'NodeImgAdjust插件' }
]
},
{
@@ -74,7 +76,8 @@ export default [
{ path: 'watermark', title: 'Watermark plugin' },
{ path: 'xmind', title: 'XMind parse' },
{ path: 'deploy', title: 'Deploy' },
{ path: 'touchEvent', title: 'TouchEvent plugin' }
{ path: 'touchEvent', title: 'TouchEvent plugin' },
{ path: 'nodeImgAdjust', title: 'NodeImgAdjust plugin' }
]
}
]

View File

@@ -1,5 +1,27 @@
# Changelog
## 0.6.5
修复1.修复xmind文件导入报错的问题。 2.修复极少数情况下当节点文本的宽度为小数时显示发生换行的问题。
新增1.打包后的库支持获取内置常量、主题等数据。 2.支持配置鼠标滚轮方向对应的缩放行为。 3.节点图片支持拖拽调整大小。
## 0.6.4-fix.1
新增1.鼠标滚轮缩放时默认以鼠标当前位置为中心进行缩放,可以通过配置关闭该特性。
修复1.修复改变了画布大小后缩放中心点默认值不随之更新的问题。
## 0.6.4
新增1.默认以画布中心点进行缩放。 2.优化移动端双指缩放,以双指中心位置为中心点进行缩放。
## 0.6.3
修复1.修复概要节点会响应插入节点快捷键的问题。
新增1.支持自定义节点内容。
## 0.6.2
修复1.修复富文本模式下,新建节点不随主题变化而变化的问题。

View File

@@ -1,6 +1,17 @@
<template>
<div>
<h1>Changelog</h1>
<h2>0.6.5</h2>
<p>修复1.修复xmind文件导入报错的问题</p>
<p>新增1.打包后的库支持获取内置常量主题等数据 2.支持配置鼠标滚轮方向对应的缩放行为 3.节点图片支持拖拽调整大小</p>
<h2>0.6.4-fix.1</h2>
<p>新增1.鼠标滚轮缩放时默认以鼠标当前位置为中心进行缩放可以通过配置关闭该特性</p>
<p>修复1.修复改变了画布大小后缩放中心点默认值不随之更新的问题</p>
<h2>0.6.4</h2>
<p>新增1.默认以画布中心点进行缩放 2.优化移动端双指缩放以双指中心位置为中心点进行缩放</p>
<h2>0.6.3</h2>
<p>修复1.修复概要节点会响应插入节点快捷键的问题</p>
<p>新增1.支持自定义节点内容</p>
<h2>0.6.2</h2>
<p>修复1.修复富文本模式下新建节点不随主题变化而变化的问题</p>
<h2>0.6.1</h2>

View File

@@ -45,6 +45,7 @@ const mindMap = new MindMap({
| customHandleMousewheelv0.4.3+ | Function | null | 自定义鼠标滚轮事件处理,可以传一个函数,回调参数为事件对象 | |
| mousewheelActionv0.4.3+ | String | zoom | 鼠标滚轮的行为,`zoom`(放大缩小)、`move`(上下移动)。如果`customHandleMousewheel`传了自定义函数,这个属性不生效 | |
| mousewheelMoveStepv0.4.3+ | Number | 100 | 当`mousewheelAction`设为`move`时,可以通过该属性控制鼠标滚动一下视图移动的步长,单位`px` | |
| mousewheelZoomActionReversev0.6.5+ | Boolean | false | 当mousewheelAction设为zoom时默认向前滚动是缩小向后滚动是放大如果该属性设为true那么会反过来 | |
| defaultInsertSecondLevelNodeTextv0.4.7+ | String | 二级节点 | 默认插入的二级节点的文字 | |
| defaultInsertBelowSecondLevelNodeTextv0.4.7+ | String | 分支主题 | 默认插入的二级以下节点的文字 | |
| expandBtnStylev0.5.0+ | Object | { color: '#808080', fill: '#fff' } | 展开收起按钮的颜色 | |
@@ -67,6 +68,9 @@ const mindMap = new MindMap({
| enableCtrlKeyNodeSelectionv0.6.0+ | Boolean | true | 是否开启按住ctrl键多选节点的功能 | |
| useLeftKeySelectionRightKeyDragv0.6.0+ | Boolean | false | 设置为左键多选节点,右键拖动画布 | |
| beforeTextEditv0.6.0+ | Function/null | null | 节点即将进入编辑前的回调方法如果该方法返回true以外的值那么将取消编辑函数可以返回一个值或一个Promise回调参数为节点实例 | |
| isUseCustomNodeContentv0.6.3+ | Boolean | false | 是否自定义节点内容 | |
| customCreateNodeContentv0.6.3+ | Function/null | null | 如果`isUseCustomNodeContent`设为`true`,那么需要使用该选项传入一个方法,接收节点实例`node`为参数(如果要获取该节点的数据,可以通过`node.nodeData.data`需要返回自定义节点内容元素也就是DOM节点如果某个节点不需要自定义那么返回`null`即可 | |
| mouseScaleCenterUseMousePositionv0.6.4-fix.1+ | Boolean | true | 鼠标缩放是否以鼠标当前位置为中心点,否则以画布中心点 | |
### 水印配置
@@ -226,6 +230,9 @@ mindMap.setTheme('主题名称')
| hide_text_edit | 节点文本编辑框关闭事件 | textEditNode文本编辑框DOM节点、activeNodeList当前激活的所有节点列表 |
| scale | 放大缩小事件 | scale缩放比例 |
| node_img_dblclickv0.2.15+ | 节点内图片的双击事件 | this节点实例、e事件对象 |
| node_img_mouseenterv0.6.5+ | 节点内图片的鼠标移入事件 | this节点实例、imgNode图片节点、e事件对象 |
| node_img_mouseleavev0.6.5+ | 节点内图片的鼠标移出事件 | this节点实例、imgNode图片节点、e事件对象 |
| node_img_mousemovev0.6.5+ | 节点内图片的鼠标移动事件 | this节点实例、imgNode图片节点、e事件对象 |
| node_tree_render_endv0.2.16+ | 节点树渲染完毕事件 | |
| rich_text_selection_changev0.4.0+ | 当注册了`RichText`插件时可用。当节点编辑时,文本选区发生改变时触发 | hasRange是否存在选区、rectInfo选区的尺寸和位置信息、formatInfo选区的文本格式化信息 |
| transforming-dom-to-imagesv0.4.0+ | 当注册了`RichText`插件时可用。当`svg`中存在`DOM`节点时,导出为图片时会将`DOM`节点转换为图片,转换过程中会触发该事件,可用通过该事件给用户提示,告知目前转换到的节点 | index当前转换到的节点索引、len一共需要转换的节点数量 |

View File

@@ -176,6 +176,13 @@
<td></td>
</tr>
<tr>
<td>mousewheelZoomActionReversev0.6.5+</td>
<td>Boolean</td>
<td>false</td>
<td>当mousewheelAction设为zoom时默认向前滚动是缩小向后滚动是放大如果该属性设为true那么会反过来</td>
<td></td>
</tr>
<tr>
<td>defaultInsertSecondLevelNodeTextv0.4.7+</td>
<td>String</td>
<td>二级节点</td>
@@ -329,6 +336,27 @@
<td>节点即将进入编辑前的回调方法如果该方法返回true以外的值那么将取消编辑函数可以返回一个值或一个Promise回调参数为节点实例</td>
<td></td>
</tr>
<tr>
<td>isUseCustomNodeContentv0.6.3+</td>
<td>Boolean</td>
<td>false</td>
<td>是否自定义节点内容</td>
<td></td>
</tr>
<tr>
<td>customCreateNodeContentv0.6.3+</td>
<td>Function/null</td>
<td>null</td>
<td>如果<code>isUseCustomNodeContent</code>设为<code>true</code>那么需要使用该选项传入一个方法接收节点实例<code>node</code>为参数如果要获取该节点的数据可以通过<code>node.nodeData.data</code>需要返回自定义节点内容元素也就是DOM节点如果某个节点不需要自定义那么返回<code>null</code>即可</td>
<td></td>
</tr>
<tr>
<td>mouseScaleCenterUseMousePositionv0.6.4-fix.1+</td>
<td>Boolean</td>
<td>true</td>
<td>鼠标缩放是否以鼠标当前位置为中心点否则以画布中心点</td>
<td></td>
</tr>
</tbody>
</table>
<h3>水印配置</h3>
@@ -625,6 +653,21 @@ mindMap.setTheme(<span class="hljs-string">&#x27;主题名称&#x27;</span>)
<td>this节点实例e事件对象</td>
</tr>
<tr>
<td>node_img_mouseenterv0.6.5+</td>
<td>节点内图片的鼠标移入事件</td>
<td>this节点实例imgNode图片节点e事件对象</td>
</tr>
<tr>
<td>node_img_mouseleavev0.6.5+</td>
<td>节点内图片的鼠标移出事件</td>
<td>this节点实例imgNode图片节点e事件对象</td>
</tr>
<tr>
<td>node_img_mousemovev0.6.5+</td>
<td>节点内图片的鼠标移动事件</td>
<td>this节点实例imgNode图片节点e事件对象</td>
</tr>
<tr>
<td>node_tree_render_endv0.2.16+</td>
<td>节点树渲染完毕事件</td>
<td></td>

View File

@@ -0,0 +1,119 @@
# 如何自定义节点内容
> 该特性v0.6.3+版本支持
如果你想自定义节点的内容,那么可以在实例化`simple-mind-map`时传入以下选项:
```js
new MindMap({
isUseCustomNodeContent: true,
customCreateNodeContent: (node) => {
// return你的自定义DOM节点
}
})
```
`customCreateNodeContent`方法会接收当前遍历到的节点实例作为参数,一般而言你会需要该节点的数据,这可以通过如下方式获取:
```js
node.nodeData.data
```
其他节点实例属性你可以自行打印出来看看。
`customCreateNodeContent`方法需要返回`DOM`节点,如果某个节点你不想自定义,那么可以返回`null`,那么还是会走内置的节点渲染逻辑。
返回的`DOM`节点的宽高需要是确定的,如果是动态的那么会导致宽高获取错误,最终导致节点定位错误和发生重叠等问题。
如果使用了自定义节点内容,那么内置的插入节点内容的相关方法你都不应该再使用,因为相当于整个节点内容都由你自己控制,另外,节点样式设置也不会再生效,切换主题也只会切换非节点内容的样式,最后,双击节点也不会再进入编辑,所以这个功能一般用于展示性的需求。
## 示例1渲染自定义DOM节点
```js
{
customCreateNodeContent: (node) => {
let div = document.createElement('div')
div.className = 'xxx'
div.style.cssText = `xxx`
div.innerHTML = `
<h1>我是自定义节点</h1>
${ node.nodeData.text }
`
return div
}
}
```
## 示例2渲染Vue2组件
如果想要使用一个相对简单的`Vue`组件,那么可以通过如下方式:
```js
import CustomNodeContent from 'CustomNodeContent.vue'
import Vue from 'vue'
{
customCreateNodeContent: (node) => {
let el = document.createElement('div')
let Comp = Vue.extend(CustomNodeContent)
let comp = new Comp({
// props
propsData: {
html: node.nodeData.data.text
}
})
comp.$mount(el)
return comp.$el
}
}
```
如果你的`Vue`组件比较复杂,里面用到了`vueRouter``vuex``i18n`等,那么要和你项目的入口组件一样,在实例化时要把这些内容也加载到组件内,不然会报错。
```js
import CustomNodeContent from 'CustomNodeContent.vue'
import Vue from 'vue'
import router from './router'
import store from './store'
import i18n from './i18n'
{
customCreateNodeContent: (node) => {
let el = document.createElement('div')
let Comp = Vue.extend(CustomNodeContent)
let comp = new Comp({
// props
propsData: {
html: node.nodeData.data.text
},
router,
store,
i18n
})
comp.$mount(el)
return comp.$el
}
}
```
## 示例3渲染Vue3组件
```js
import { createApp } from "vue"
import CustomNodeContent from './CustomNodeContent.vue'
{
customCreateNodeContent: (node) => {
let el = document.createElement('div')
const app = createApp(CustomNodeContent, {// props
html: node.nodeData.data.text
})
app.mount(el)
return el
}
}
```
## 示例4渲染react组件
如果你成功渲染了`react`组件,欢迎[提交](https://github.com/wanglin2/mind-map/issues/new)示例代码给我~

View File

@@ -0,0 +1,110 @@
<template>
<div>
<h1>如何自定义节点内容</h1>
<blockquote>
<p>该特性v0.6.3+版本支持</p>
</blockquote>
<p>如果你想自定义节点的内容那么可以在实例化<code>simple-mind-map</code>时传入以下选项</p>
<pre class="hljs"><code><span class="hljs-keyword">new</span> MindMap({
<span class="hljs-attr">isUseCustomNodeContent</span>: <span class="hljs-literal">true</span>,
<span class="hljs-attr">customCreateNodeContent</span>: <span class="hljs-function">(<span class="hljs-params">node</span>) =&gt;</span> {
<span class="hljs-comment">// return你的自定义DOM节点</span>
}
})
</code></pre>
<p><code>customCreateNodeContent</code>方法会接收当前遍历到的节点实例作为参数一般而言你会需要该节点的数据这可以通过如下方式获取</p>
<pre class="hljs"><code>node.nodeData.data
</code></pre>
<p>其他节点实例属性你可以自行打印出来看看</p>
<p><code>customCreateNodeContent</code>方法需要返回<code>DOM</code>节点如果某个节点你不想自定义那么可以返回<code>null</code>那么还是会走内置的节点渲染逻辑</p>
<p>返回的<code>DOM</code>节点的宽高需要是确定的如果是动态的那么会导致宽高获取错误最终导致节点定位错误和发生重叠等问题</p>
<p>如果使用了自定义节点内容那么内置的插入节点内容的相关方法你都不应该再使用因为相当于整个节点内容都由你自己控制另外节点样式设置也不会再生效切换主题也只会切换非节点内容的样式最后双击节点也不会再进入编辑所以这个功能一般用于展示性的需求</p>
<h2>示例1渲染自定义DOM节点</h2>
<pre class="hljs"><code>{
<span class="hljs-attr">customCreateNodeContent</span>: <span class="hljs-function">(<span class="hljs-params">node</span>) =&gt;</span> {
<span class="hljs-keyword">let</span> div = <span class="hljs-built_in">document</span>.createElement(<span class="hljs-string">&#x27;div&#x27;</span>)
div.className = <span class="hljs-string">&#x27;xxx&#x27;</span>
div.style.cssText = <span class="hljs-string">`xxx`</span>
div.innerHTML = <span class="hljs-string">`
&lt;h1&gt;我是自定义节点&lt;/h1&gt;
<span class="hljs-subst">${ node.nodeData.text }</span>
`</span>
<span class="hljs-keyword">return</span> div
}
}
</code></pre>
<h2>示例2渲染Vue2组件</h2>
<p>如果想要使用一个相对简单的<code>Vue</code>组件那么可以通过如下方式</p>
<pre class="hljs"><code><span class="hljs-keyword">import</span> CustomNodeContent <span class="hljs-keyword">from</span> <span class="hljs-string">&#x27;CustomNodeContent.vue&#x27;</span>
<span class="hljs-keyword">import</span> Vue <span class="hljs-keyword">from</span> <span class="hljs-string">&#x27;vue&#x27;</span>
{
<span class="hljs-attr">customCreateNodeContent</span>: <span class="hljs-function">(<span class="hljs-params">node</span>) =&gt;</span> {
<span class="hljs-keyword">let</span> el = <span class="hljs-built_in">document</span>.createElement(<span class="hljs-string">&#x27;div&#x27;</span>)
<span class="hljs-keyword">let</span> Comp = Vue.extend(CustomNodeContent)
<span class="hljs-keyword">let</span> comp = <span class="hljs-keyword">new</span> Comp({
<span class="hljs-comment">// props</span>
<span class="hljs-attr">propsData</span>: {
<span class="hljs-attr">html</span>: node.nodeData.data.text
}
})
comp.$mount(el)
<span class="hljs-keyword">return</span> comp.$el
}
}
</code></pre>
<p>如果你的<code>Vue</code>组件比较复杂里面用到了<code>vueRouter</code><code>vuex</code><code>i18n</code>那么要和你项目的入口组件一样在实例化时要把这些内容也加载到组件内不然会报错</p>
<pre class="hljs"><code><span class="hljs-keyword">import</span> CustomNodeContent <span class="hljs-keyword">from</span> <span class="hljs-string">&#x27;CustomNodeContent.vue&#x27;</span>
<span class="hljs-keyword">import</span> Vue <span class="hljs-keyword">from</span> <span class="hljs-string">&#x27;vue&#x27;</span>
<span class="hljs-keyword">import</span> router <span class="hljs-keyword">from</span> <span class="hljs-string">&#x27;./router&#x27;</span>
<span class="hljs-keyword">import</span> store <span class="hljs-keyword">from</span> <span class="hljs-string">&#x27;./store&#x27;</span>
<span class="hljs-keyword">import</span> i18n <span class="hljs-keyword">from</span> <span class="hljs-string">&#x27;./i18n&#x27;</span>
{
<span class="hljs-attr">customCreateNodeContent</span>: <span class="hljs-function">(<span class="hljs-params">node</span>) =&gt;</span> {
<span class="hljs-keyword">let</span> el = <span class="hljs-built_in">document</span>.createElement(<span class="hljs-string">&#x27;div&#x27;</span>)
<span class="hljs-keyword">let</span> Comp = Vue.extend(CustomNodeContent)
<span class="hljs-keyword">let</span> comp = <span class="hljs-keyword">new</span> Comp({
<span class="hljs-comment">// props</span>
<span class="hljs-attr">propsData</span>: {
<span class="hljs-attr">html</span>: node.nodeData.data.text
},
router,
store,
i18n
})
comp.$mount(el)
<span class="hljs-keyword">return</span> comp.$el
}
}
</code></pre>
<h2>示例3渲染Vue3组件</h2>
<pre class="hljs"><code><span class="hljs-keyword">import</span> { createApp } <span class="hljs-keyword">from</span> <span class="hljs-string">&quot;vue&quot;</span>
<span class="hljs-keyword">import</span> CustomNodeContent <span class="hljs-keyword">from</span> <span class="hljs-string">&#x27;./CustomNodeContent.vue&#x27;</span>
{
<span class="hljs-attr">customCreateNodeContent</span>: <span class="hljs-function">(<span class="hljs-params">node</span>) =&gt;</span> {
<span class="hljs-keyword">let</span> el = <span class="hljs-built_in">document</span>.createElement(<span class="hljs-string">&#x27;div&#x27;</span>)
<span class="hljs-keyword">const</span> app = createApp(CustomNodeContent, {<span class="hljs-comment">// props</span>
<span class="hljs-attr">html</span>: node.nodeData.data.text
})
app.mount(el)
<span class="hljs-keyword">return</span> el
}
}
</code></pre>
<h2>示例4渲染react组件</h2>
<p>如果你成功渲染了<code>react</code>组件欢迎<a href="https://github.com/wanglin2/mind-map/issues/new">提交</a>示例代码给我~</p>
</div>
</template>
<script>
export default {
}
</script>
<style>
</style>

View File

@@ -125,4 +125,8 @@
<img src="../../../../assets/avatar/志斌.jpg" style="width: 50px;height: 50px;object-fit: cover;border-radius: 50%;" />
<p>志斌</p>
</div>
<div style="display: flex; flex-direction: column; align-items: center; width: fit-content; margin: 5px;">
<img src="../../../../assets/avatar/小土渣的宇宙.jpeg" style="width: 50px;height: 50px;object-fit: cover;border-radius: 50%;" />
<p>小土渣的宇宙</p>
</div>
</div>

View File

@@ -8,19 +8,19 @@
</blockquote>
<h2>特性</h2>
<ul>
<li><input type="checkbox" id="checkbox36" checked="true" /><label for="checkbox36">插件化架构除核心功能外其他功能作为插件提供按需使用减小整体体积</label></li>
<li><input type="checkbox" id="checkbox37" checked="true" /><label for="checkbox37">支持逻辑结构图思维导图组织结构图目录组织图时间轴鱼骨图六种结构</label></li>
<li><input type="checkbox" id="checkbox38" checked="true" /><label for="checkbox38">内置多种主题允许高度自定义样式支持注册新主题</label></li>
<li><input type="checkbox" id="checkbox39" checked="true" /><label for="checkbox39">支持快捷键</label></li>
<li><input type="checkbox" id="checkbox40" checked="true" /><label for="checkbox40">节点内容支持图片图标超链接备注标签概要</label></li>
<li><input type="checkbox" id="checkbox41" checked="true" /><label for="checkbox41">支持前进后退</label></li>
<li><input type="checkbox" id="checkbox42" checked="true" /><label for="checkbox42">支持拖动缩放</label></li>
<li><input type="checkbox" id="checkbox43" checked="true" /><label for="checkbox43">支持右键和Ctrl+左键两种多选方式</label></li>
<li><input type="checkbox" id="checkbox44" checked="true" /><label for="checkbox44">支持节点自由拖拽拖拽调整</label></li>
<li><input type="checkbox" id="checkbox45" checked="true" /><label for="checkbox45">支持多种节点形状</label></li>
<li><input type="checkbox" id="checkbox46" checked="true" /><label for="checkbox46">支持导出为</label><code>json</code><code>png</code><code>svg</code><code>pdf</code><code>markdown</code>支持从<code>json</code><code>xmind</code><code>markdown</code>导入</li>
<li><input type="checkbox" id="checkbox47" checked="true" /><label for="checkbox47">支持小地图支持水印</label></li>
<li><input type="checkbox" id="checkbox48" checked="true" /><label for="checkbox48">支持关联线</label></li>
<li><input type="checkbox" id="checkbox18" checked="true" /><label for="checkbox18">插件化架构除核心功能外其他功能作为插件提供按需使用减小整体体积</label></li>
<li><input type="checkbox" id="checkbox19" checked="true" /><label for="checkbox19">支持逻辑结构图思维导图组织结构图目录组织图时间轴鱼骨图六种结构</label></li>
<li><input type="checkbox" id="checkbox20" checked="true" /><label for="checkbox20">内置多种主题允许高度自定义样式支持注册新主题</label></li>
<li><input type="checkbox" id="checkbox21" checked="true" /><label for="checkbox21">支持快捷键</label></li>
<li><input type="checkbox" id="checkbox22" checked="true" /><label for="checkbox22">节点内容支持图片图标超链接备注标签概要</label></li>
<li><input type="checkbox" id="checkbox23" checked="true" /><label for="checkbox23">支持前进后退</label></li>
<li><input type="checkbox" id="checkbox24" checked="true" /><label for="checkbox24">支持拖动缩放</label></li>
<li><input type="checkbox" id="checkbox25" checked="true" /><label for="checkbox25">支持右键和Ctrl+左键两种多选方式</label></li>
<li><input type="checkbox" id="checkbox26" checked="true" /><label for="checkbox26">支持节点自由拖拽拖拽调整</label></li>
<li><input type="checkbox" id="checkbox27" checked="true" /><label for="checkbox27">支持多种节点形状</label></li>
<li><input type="checkbox" id="checkbox28" checked="true" /><label for="checkbox28">支持导出为</label><code>json</code><code>png</code><code>svg</code><code>pdf</code><code>markdown</code>支持从<code>json</code><code>xmind</code><code>markdown</code>导入</li>
<li><input type="checkbox" id="checkbox29" checked="true" /><label for="checkbox29">支持小地图支持水印</label></li>
<li><input type="checkbox" id="checkbox30" checked="true" /><label for="checkbox30">支持关联线</label></li>
</ul>
<h2>仓库目录介绍</h2>
<p>1.<code>simple-mind-map</code></p>
@@ -28,11 +28,11 @@
<p>2.<code>web</code></p>
<p>使用<code>simple-mind-map</code>基于<code>vue2.x</code><code>ElementUI</code>搭建的在线思维导图特性</p>
<ul>
<li><input type="checkbox" id="checkbox49" checked="true" /><label for="checkbox49">工具栏支持插入节点删除节点编辑节点图片图标超链接备注标签概要</label></li>
<li><input type="checkbox" id="checkbox50" checked="true" /><label for="checkbox50">侧边栏基础样式设置面板节点样式设置面板大纲面板主题选择面板结构选择面板</label></li>
<li><input type="checkbox" id="checkbox51" checked="true" /><label for="checkbox51">导入导出功能数据默认保存在浏览器本地存储也支持直接创建打开编辑电脑本地文件</label></li>
<li><input type="checkbox" id="checkbox52" checked="true" /><label for="checkbox52">右键菜单支持展开收起整理布局等操作</label></li>
<li><input type="checkbox" id="checkbox53" checked="true" /><label for="checkbox53">底部栏支持节点数量字数统计支持切换编辑和只读模式支持放大缩小支持全屏切换支持小地图</label></li>
<li><input type="checkbox" id="checkbox31" checked="true" /><label for="checkbox31">工具栏支持插入节点删除节点编辑节点图片图标超链接备注标签概要</label></li>
<li><input type="checkbox" id="checkbox32" checked="true" /><label for="checkbox32">侧边栏基础样式设置面板节点样式设置面板大纲面板主题选择面板结构选择面板</label></li>
<li><input type="checkbox" id="checkbox33" checked="true" /><label for="checkbox33">导入导出功能数据默认保存在浏览器本地存储也支持直接创建打开编辑电脑本地文件</label></li>
<li><input type="checkbox" id="checkbox34" checked="true" /><label for="checkbox34">右键菜单支持展开收起整理布局等操作</label></li>
<li><input type="checkbox" id="checkbox35" checked="true" /><label for="checkbox35">底部栏支持节点数量字数统计支持切换编辑和只读模式支持放大缩小支持全屏切换支持小地图</label></li>
</ul>
<p>提供文档页面服务</p>
<p>3.<code>dist</code></p>
@@ -84,6 +84,10 @@
<img src="../../../../assets/avatar/志斌.jpg" style="width: 50px;height: 50px;object-fit: cover;border-radius: 50%;" />
<p>志斌</p>
</div>
<div style="display: flex; flex-direction: column; align-items: center; width: fit-content; margin: 5px;">
<img src="../../../../assets/avatar/小土渣的宇宙.jpeg" style="width: 50px;height: 50px;object-fit: cover;border-radius: 50%;" />
<p>小土渣的宇宙</p>
</div>
</div>
</div>
</template>

View File

@@ -0,0 +1,16 @@
# NodeImgAdjust插件
> v0.6.5+
该插件提供拖拽调整节点内图片大小的功能。
## 注册
```js
import MindMap from 'simple-mind-map'
import NodeImgAdjust from 'simple-mind-map/src/plugins/NodeImgAdjust.js'
MindMap.usePlugin(NodeImgAdjust)
```
注册完且实例化`MindMap`后可通过`mindMap.nodeImgAdjust`获取到该实例。

View File

@@ -0,0 +1,27 @@
<template>
<div>
<h1>NodeImgAdjust插件</h1>
<blockquote>
<p>v0.6.5+</p>
</blockquote>
<p>该插件提供拖拽调整节点内图片大小的功能</p>
<h2>注册</h2>
<pre class="hljs"><code><span class="hljs-keyword">import</span> MindMap <span class="hljs-keyword">from</span> <span class="hljs-string">&#x27;simple-mind-map&#x27;</span>
<span class="hljs-keyword">import</span> NodeImgAdjust <span class="hljs-keyword">from</span> <span class="hljs-string">&#x27;simple-mind-map/src/plugins/NodeImgAdjust.js&#x27;</span>
MindMap.usePlugin(NodeImgAdjust)
</code></pre>
<p>注册完且实例化<code>MindMap</code>后可通过<code>mindMap.nodeImgAdjust</code>获取到该实例</p>
</div>
</template>
<script>
export default {
}
</script>
<style>
</style>

View File

@@ -72,7 +72,7 @@ const mindMap = new MindMap({
<script scr="simpleMindMap.umd.min.js"></script>
```
会创建一个全局变量`window.simpleMindMap`
会创建一个全局变量`window.simpleMindMap`,可以通过`window.simpleMindMap.default`获取到`MindMap`构造函数,详细信息可以把`window.simpleMindMap`打印出来看一下
这种方式的缺点是会包含所有的内容,包括你没有注册的插件,所以整体体积会比较大。
@@ -106,6 +106,8 @@ cd web
npm run buildLibrary
```
打包入口为`simple-mind-map/full.js`,默认会引入所有插件,如果你不需要所有插件的话,那么可以修改该文件,只引入你需要的插件,这样可以减少打包后的文件体积。
`simple-mind-map`库的`package.json`文件提供了两个导出字段:
```json

View File

@@ -51,7 +51,7 @@
<pre class="hljs"><code><span class="hljs-tag">&lt;<span class="hljs-name">link</span> <span class="hljs-attr">rel</span>=<span class="hljs-string">&quot;stylesheet&quot;</span> <span class="hljs-attr">href</span>=<span class="hljs-string">&quot;simpleMindMap.css&quot;</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">scr</span>=<span class="hljs-string">&quot;simpleMindMap.umd.min.js&quot;</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>
</code></pre>
<p>会创建一个全局变量<code>window.simpleMindMap</code></p>
<p>会创建一个全局变量<code>window.simpleMindMap</code>可以通过<code>window.simpleMindMap.default</code>获取到<code>MindMap</code>构造函数详细信息可以把<code>window.simpleMindMap</code>打印出来看一下</p>
<p>这种方式的缺点是会包含所有的内容包括你没有注册的插件所以整体体积会比较大</p>
<p>v0.5.4+如果你想直接在浏览器端通过<code>ES</code>模块的方式来使用你可以在<code>/simple-mind-map/dist/</code>目录中找到<code>simpleMindMap.esm.js</code><code>simpleMindMap.esm.css</code>文件</p>
<h2>开发</h2>
@@ -73,6 +73,7 @@ npm run serve
<pre class="hljs"><code><span class="hljs-built_in">cd</span> web
npm run buildLibrary
</code></pre>
<p>打包入口为<code>simple-mind-map/full.js</code>默认会引入所有插件如果你不需要所有插件的话那么可以修改该文件只引入你需要的插件这样可以减少打包后的文件体积</p>
<p><code>simple-mind-map</code>库的<code>package.json</code>文件提供了两个导出字段</p>
<pre class="hljs"><code>{
<span class="hljs-attr">&quot;module&quot;</span>: <span class="hljs-string">&quot;index.js&quot;</span>,

View File

@@ -10,6 +10,20 @@ import {walk, ...} from 'simple-mind-map/src/utils'
### 方法
#### resizeImgSizeByOriginRatio(width, height, newWidth, newHeight)
> v0.6.5+
`width`: 图片原始的宽度
`height`:图片原始的高度
`newWidth`:要缩放到的宽度
`newHeight`:要缩放到的高度
按比例缩放图片。在保持图片原始宽高比的情况下缩放到指定的`newWidth``newHeight`大小。
#### walk(root, parent, beforeCallback, afterCallback, isRoot, layerIndex = 0, index = 0)
深度优先遍历树

View File

@@ -6,6 +6,15 @@
<pre class="hljs"><code><span class="hljs-keyword">import</span> {walk, ...} <span class="hljs-keyword">from</span> <span class="hljs-string">&#x27;simple-mind-map/src/utils&#x27;</span>
</code></pre>
<h3>方法</h3>
<h4>resizeImgSizeByOriginRatio(width, height, newWidth, newHeight)</h4>
<blockquote>
<p>v0.6.5+</p>
</blockquote>
<p><code>width</code>: 图片原始的宽度</p>
<p><code>height</code>图片原始的高度</p>
<p><code>newWidth</code>要缩放到的宽度</p>
<p><code>newHeight</code>要缩放到的高度</p>
<p>按比例缩放图片在保持图片原始宽高比的情况下缩放到指定的<code>newWidth</code><code>newHeight</code>大小</p>
<h4>walk(root, parent, beforeCallback, afterCallback, isRoot, layerIndex = 0, index = 0)</h4>
<p>深度优先遍历树</p>
<p><code>root</code>要遍历的树的根节点</p>

View File

@@ -34,11 +34,19 @@
恢复到默认的变换
### narrow()
### narrow(cx, cy)
- `cx`v0.6.4+)以画布指定位置进行缩放,默认为画布中心点
- `cy`v0.6.4+)以画布指定位置进行缩放,默认为画布中心点
缩小
### enlarge()
### enlarge(cx, cy)
- `cx`v0.6.4+)以画布指定位置进行缩放,默认为画布中心点
- `cy`v0.6.4+)以画布指定位置进行缩放,默认为画布中心点
放大
@@ -54,8 +62,13 @@
动态设置变换数据可以通过getTransformData方法获取变换数据
### setScale(scale)
### setScale(scale, cx, cy)
> v0.2.17+
设置缩放
- `cx`v0.6.4+)以画布指定位置进行缩放,默认为画布中心点
- `cy`v0.6.4+)以画布指定位置进行缩放,默认为画布中心点
设置缩放

View File

@@ -24,9 +24,25 @@
<p>平移<code>y</code>方向到指定位置</p>
<h3>reset()</h3>
<p>恢复到默认的变换</p>
<h3>narrow()</h3>
<h3>narrow(cx, cy)</h3>
<ul>
<li>
<p><code>cx</code>v0.6.4+以画布指定位置进行缩放默认为画布中心点</p>
</li>
<li>
<p><code>cy</code>v0.6.4+以画布指定位置进行缩放默认为画布中心点</p>
</li>
</ul>
<p>缩小</p>
<h3>enlarge()</h3>
<h3>enlarge(cx, cy)</h3>
<ul>
<li>
<p><code>cx</code>v0.6.4+以画布指定位置进行缩放默认为画布中心点</p>
</li>
<li>
<p><code>cy</code>v0.6.4+以画布指定位置进行缩放默认为画布中心点</p>
</li>
</ul>
<p>放大</p>
<h3>getTransformData()</h3>
<blockquote>
@@ -38,10 +54,18 @@
<p>v0.1.1+</p>
</blockquote>
<p>动态设置变换数据可以通过getTransformData方法获取变换数据</p>
<h3>setScale(scale)</h3>
<h3>setScale(scale, cx, cy)</h3>
<blockquote>
<p>v0.2.17+</p>
</blockquote>
<ul>
<li>
<p><code>cx</code>v0.6.4+以画布指定位置进行缩放默认为画布中心点</p>
</li>
<li>
<p><code>cy</code>v0.6.4+以画布指定位置进行缩放默认为画布中心点</p>
</li>
</ul>
<p>设置缩放</p>
</div>

View File

@@ -567,6 +567,7 @@
</template>
<!-- 其他配置 -->
<div class="title noTop">{{ $t('baseStyle.otherConfig') }}</div>
<!-- 配置开启自由拖拽 -->
<div class="row">
<div class="rowItem">
<el-checkbox v-model="config.enableFreeDrag" @change="
@@ -576,11 +577,13 @@
">{{ $t('baseStyle.enableFreeDrag') }}</el-checkbox>
</div>
</div>
<!-- 配置是否启用富文本编辑 -->
<div class="row">
<div class="rowItem">
<el-checkbox v-model="enableNodeRichText" @change="enableNodeRichTextChange">{{ $t('baseStyle.isEnableNodeRichText') }}</el-checkbox>
</div>
</div>
<!-- 配置鼠标滚轮行为 -->
<div class="row">
<div class="rowItem">
<span class="name">{{ $t('baseStyle.mousewheelAction') }}</span>
@@ -600,6 +603,26 @@
</el-select>
</div>
</div>
<!-- 配置鼠标缩放行为 -->
<div class="row" v-if="config.mousewheelAction === 'zoom'">
<div class="rowItem">
<span class="name">{{ $t('baseStyle.mousewheelZoomActionReverse') }}</span>
<el-select
size="mini"
style="width: 120px"
v-model="config.mousewheelZoomActionReverse"
placeholder=""
@change="
value => {
updateOtherConfig('mousewheelZoomActionReverse', value)
}
"
>
<el-option :label="$t('baseStyle.mousewheelZoomActionReverse1') " :value="false"></el-option>
<el-option :label="$t('baseStyle.mousewheelZoomActionReverse2') " :value="true"></el-option>
</el-select>
</div>
</div>
</div>
</Sidebar>
</template>
@@ -668,7 +691,8 @@ export default {
},
config: {
enableFreeDrag: false,
mousewheelAction: 'zoom'
mousewheelAction: 'zoom',
mousewheelZoomActionReverse: false
},
watermarkConfig: {
show: false,
@@ -720,6 +744,7 @@ export default {
created () {
this.enableNodeRichText = this.localConfig.openNodeRichText
this.mousewheelAction = this.localConfig.mousewheelAction
this.mousewheelZoomActionReverse = this.localConfig.mousewheelZoomActionReverse
},
methods: {
...mapMutations(['setLocalConfig']),
@@ -765,7 +790,7 @@ export default {
// 初始化其他配置
initConfig() {
;['enableFreeDrag', 'mousewheelAction'].forEach(key => {
;['enableFreeDrag', 'mousewheelAction', 'mousewheelZoomActionReverse'].forEach(key => {
this.config[key] = this.mindMap.getConfig(key)
})
},

View File

@@ -0,0 +1,36 @@
<template>
<div class="customNodeContent">
<p>{{ title }}</p>
<p v-html="html"></p>
<p :style="{ backgroundColor: color }" @click="onClick">点击我会变色</p>
</div>
</template>
<script>
export default {
props: {
html: {
type: String,
default: ''
}
},
data() {
return {
title: '我是自定义节点',
color: ''
}
},
methods: {
onClick() {
this.color = 'red'
}
}
}
</script>
<style lang="less" scoped>
.customNodeContent {
padding: 10px;
cursor: pointer;
}
</style>

View File

@@ -32,6 +32,8 @@ import Drag from 'simple-mind-map/src/plugins/Drag.js'
import Select from 'simple-mind-map/src/plugins/Select.js'
import RichText from 'simple-mind-map/src/plugins/RichText.js'
import AssociativeLine from 'simple-mind-map/src/plugins/AssociativeLine.js'
import TouchEvent from 'simple-mind-map/src/plugins/TouchEvent.js'
import NodeImgAdjust from 'simple-mind-map/src/plugins/NodeImgAdjust.js'
import Outline from './Outline'
import Style from './Style'
import BaseStyle from './BaseStyle'
@@ -48,8 +50,14 @@ import Navigator from './Navigator.vue'
import NodeImgPreview from './NodeImgPreview.vue'
import SidebarTrigger from './SidebarTrigger.vue'
import { mapState } from 'vuex'
import customThemeList from '@/customThemes'
import icon from '@/config/icon'
import customThemeList from '@/customThemes'
import CustomNodeContent from './CustomNodeContent.vue'
import Color from './Color.vue'
import Vue from 'vue'
import router from '../../../router'
import store from '../../../store'
import i18n from '../../../i18n'
// 注册插件
MindMap
@@ -61,6 +69,8 @@ MindMap
.usePlugin(Export)
.usePlugin(Select)
.usePlugin(AssociativeLine)
.usePlugin(NodeImgAdjust)
.usePlugin(TouchEvent)
// 注册自定义主题
// customThemeList.forEach((item) => {
@@ -293,7 +303,32 @@ export default {
},
...(config || {}),
iconList: icon,
useLeftKeySelectionRightKeyDrag: this.useLeftKeySelectionRightKeyDrag
useLeftKeySelectionRightKeyDrag: this.useLeftKeySelectionRightKeyDrag,
// isUseCustomNodeContent: true,
// 示例1组件里用到了router、store、i18n等实例化vue组件时需要用到的东西
// customCreateNodeContent: (node) => {
// let el = document.createElement('div')
// let Comp = Vue.extend(Color)
// let comp = new Comp({
// router,
// store,
// i18n
// })
// comp.$mount(el)
// return comp.$el
// },
// 示例2组件里没有用到示例1的东西
// customCreateNodeContent: (node) => {
// let el = document.createElement('div')
// let Comp = Vue.extend(CustomNodeContent)
// let comp = new Comp({
// propsData: {
// html: node.nodeData.data.text
// }
// })
// comp.$mount(el)
// return comp.$el
// }
})
if (this.openNodeRichText) this.addRichTextPlugin()
this.mindMap.keyCommand.addShortcut('Control+s', () => {