diff --git a/simple-mind-map/src/plugins/RichText.js b/simple-mind-map/src/plugins/RichText.js index 4e38c231..a4b846d3 100644 --- a/simple-mind-map/src/plugins/RichText.js +++ b/simple-mind-map/src/plugins/RichText.js @@ -60,6 +60,7 @@ class RichText { padding: 0; height: auto; line-height: normal; + -webkit-user-select: text; } .ql-container { diff --git a/simple-mind-map/src/plugins/TouchEvent.js b/simple-mind-map/src/plugins/TouchEvent.js new file mode 100644 index 00000000..135d6ece --- /dev/null +++ b/simple-mind-map/src/plugins/TouchEvent.js @@ -0,0 +1,122 @@ +// 手势事件支持类 + +class TouchEvent { + // 构造函数 + constructor({ mindMap }) { + this.mindMap = mindMap + this.touchesNum = 0 + this.singleTouchstartEvent = null + this.clickNum = 0 + this.doubleTouchmoveDistance = 0 + this.bindEvent() + } + + // 绑定事件 + bindEvent() { + this.onTouchstart = this.onTouchstart.bind(this) + this.onTouchmove = this.onTouchmove.bind(this) + this.onTouchcancel = this.onTouchcancel.bind(this) + this.onTouchend = this.onTouchend.bind(this) + window.addEventListener('touchstart', this.onTouchstart) + window.addEventListener('touchmove', this.onTouchmove) + window.addEventListener('touchcancel', this.onTouchcancel) + window.addEventListener('touchend', this.onTouchend) + } + + // 解绑事件 + unBindEvent() { + window.removeEventListener('touchstart', this.onTouchstart) + window.removeEventListener('touchmove', this.onTouchmove) + window.removeEventListener('touchcancel', this.onTouchcancel) + window.removeEventListener('touchend', this.onTouchend) + } + + // 手指按下事件 + onTouchstart(e) { + this.touchesNum = e.touches.length + if (this.touchesNum === 1) { + let touch = e.touches[0] + this.singleTouchstartEvent = touch + this.dispatchMouseEvent('mousedown', touch.target, touch) + } + } + + // 手指移动事件 + onTouchmove(e) { + let len = e.touches.length + if (len === 1) { + let touch = e.touches[0] + this.dispatchMouseEvent('mousemove', touch.target, touch) + } 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) + ) + if (distance > this.doubleTouchmoveDistance) { + // 放大 + this.mindMap.view.enlarge() + } else { + // 缩小 + this.mindMap.view.narrow() + } + this.doubleTouchmoveDistance = distance + } + } + + // 手指取消事件 + onTouchcancel(e) {} + + // 手指松开事件 + onTouchend(e) { + this.dispatchMouseEvent('mouseup', e.target) + if (this.touchesNum === 1) { + // 模拟双击事件 + this.clickNum++ + setTimeout(() => { + this.clickNum = 0 + }, 300) + let ev = this.singleTouchstartEvent + if (this.clickNum > 1) { + this.clickNum = 0 + this.dispatchMouseEvent('dblclick', ev.target, ev) + } else { + this.dispatchMouseEvent('click', ev.target, ev) + } + } + this.touchesNum = 0 + this.singleTouchstartEvent = null + this.doubleTouchmoveDistance = 0 + } + + // 发送鼠标事件 + dispatchMouseEvent(eventName, target, e) { + let opt = {} + if (e) { + opt = { + screenX: e.screenX, + screenY: e.screenY, + clientX: e.clientX, + clientY: e.clientY, + which: 1 + } + } + let event = new MouseEvent(eventName, { + view: window, + bubbles: true, + cancelable: true, + ...opt + }) + target.dispatchEvent(event) + } + + // 插件被移除前做的事情 + beforePluginRemove() { + this.unBindEvent() + } +} + +TouchEvent.instanceName = 'touchEvent' + +export default TouchEvent diff --git a/web/package-lock.json b/web/package-lock.json index d89d26ed..6b3fc039 100644 --- a/web/package-lock.json +++ b/web/package-lock.json @@ -33,6 +33,7 @@ "markdown-it": "^13.0.1", "markdown-it-checkbox": "^1.1.0", "prettier": "^1.19.1", + "vconsole": "^3.15.1", "vue-template-compiler": "^2.6.11", "webpack": "^4.44.2" } @@ -5168,6 +5169,18 @@ "node": ">=0.10.0" } }, + "node_modules/copy-text-to-clipboard": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/copy-text-to-clipboard/-/copy-text-to-clipboard-3.1.0.tgz", + "integrity": "sha512-PFM6BnjLnOON/lB3ta/Jg7Ywsv+l9kQGD4TWDCSlRBGmqnnTM5MrDkhAFgw+8HZt0wW6Q2BBE4cmy9sq+s9Qng==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/copy-webpack-plugin": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/copy-webpack-plugin/-/copy-webpack-plugin-5.1.2.tgz", @@ -10351,6 +10364,12 @@ "integrity": "sha512-cnAsSVxIDsYt0v7HmC0hWZFwwXSh+E6PgCrREDuN/EsjgLwA5XRmlMHhSiDPrt6HxY1gTivEa/Zh7GtODoLevQ==", "dev": true }, + "node_modules/mutation-observer": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/mutation-observer/-/mutation-observer-1.0.3.tgz", + "integrity": "sha512-M/O/4rF2h776hV7qGMZUH3utZLO/jK7p8rnNgGkjKUw8zCGjRQPxB8z6+5l8+VjRUQ3dNYu4vjqXYLr+U8ZVNA==", + "dev": true + }, "node_modules/mute-stream": { "version": "0.0.8", "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", @@ -15185,6 +15204,18 @@ "node": ">= 0.8" } }, + "node_modules/vconsole": { + "version": "3.15.1", + "resolved": "https://registry.npmjs.org/vconsole/-/vconsole-3.15.1.tgz", + "integrity": "sha512-KH8XLdrq9T5YHJO/ixrjivHfmF2PC2CdVoK6RWZB4yftMykYIaXY1mxZYAic70vADM54kpMQF+dYmvl5NRNy1g==", + "dev": true, + "dependencies": { + "@babel/runtime": "^7.17.2", + "copy-text-to-clipboard": "^3.0.1", + "core-js": "^3.11.0", + "mutation-observer": "^1.0.3" + } + }, "node_modules/vendors": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/vendors/-/vendors-1.0.4.tgz", @@ -20525,6 +20556,12 @@ "integrity": "sha512-XgZ0pFcakEUlbwQEVNg3+QAis1FyTL3Qel9FYy8pSkQqoG3PNoT0bOCQtOXcOkur21r2Eq2kI+IE+gsmAEVlYw==", "dev": true }, + "copy-text-to-clipboard": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/copy-text-to-clipboard/-/copy-text-to-clipboard-3.1.0.tgz", + "integrity": "sha512-PFM6BnjLnOON/lB3ta/Jg7Ywsv+l9kQGD4TWDCSlRBGmqnnTM5MrDkhAFgw+8HZt0wW6Q2BBE4cmy9sq+s9Qng==", + "dev": true + }, "copy-webpack-plugin": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/copy-webpack-plugin/-/copy-webpack-plugin-5.1.2.tgz", @@ -24567,6 +24604,12 @@ "integrity": "sha512-cnAsSVxIDsYt0v7HmC0hWZFwwXSh+E6PgCrREDuN/EsjgLwA5XRmlMHhSiDPrt6HxY1gTivEa/Zh7GtODoLevQ==", "dev": true }, + "mutation-observer": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/mutation-observer/-/mutation-observer-1.0.3.tgz", + "integrity": "sha512-M/O/4rF2h776hV7qGMZUH3utZLO/jK7p8rnNgGkjKUw8zCGjRQPxB8z6+5l8+VjRUQ3dNYu4vjqXYLr+U8ZVNA==", + "dev": true + }, "mute-stream": { "version": "0.0.8", "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", @@ -28566,6 +28609,18 @@ "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", "dev": true }, + "vconsole": { + "version": "3.15.1", + "resolved": "https://registry.npmjs.org/vconsole/-/vconsole-3.15.1.tgz", + "integrity": "sha512-KH8XLdrq9T5YHJO/ixrjivHfmF2PC2CdVoK6RWZB4yftMykYIaXY1mxZYAic70vADM54kpMQF+dYmvl5NRNy1g==", + "dev": true, + "requires": { + "@babel/runtime": "^7.17.2", + "copy-text-to-clipboard": "^3.0.1", + "core-js": "^3.11.0", + "mutation-observer": "^1.0.3" + } + }, "vendors": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/vendors/-/vendors-1.0.4.tgz", diff --git a/web/package.json b/web/package.json index cc9d1cbd..4e94f77e 100644 --- a/web/package.json +++ b/web/package.json @@ -37,6 +37,7 @@ "markdown-it": "^13.0.1", "markdown-it-checkbox": "^1.1.0", "prettier": "^1.19.1", + "vconsole": "^3.15.1", "vue-template-compiler": "^2.6.11", "webpack": "^4.44.2" }, diff --git a/web/public/index.html b/web/public/index.html index 10932fe8..294e768a 100644 --- a/web/public/index.html +++ b/web/public/index.html @@ -3,7 +3,7 @@ - + 一个简单的web思维导图实现 diff --git a/web/src/main.js b/web/src/main.js index 6729eda9..346c5f02 100644 --- a/web/src/main.js +++ b/web/src/main.js @@ -8,6 +8,8 @@ import '@/assets/icon-font/iconfont.css' import 'viewerjs/dist/viewer.css' import VueViewer from 'v-viewer' import i18n from './i18n' +// import VConsole from 'vconsole' +// const vConsole = new VConsole() Vue.config.productionTip = false Vue.prototype.$bus = new Vue() diff --git a/web/src/pages/Doc/catalogList.js b/web/src/pages/Doc/catalogList.js index 4282f0ab..cdc0ec6f 100644 --- a/web/src/pages/Doc/catalogList.js +++ b/web/src/pages/Doc/catalogList.js @@ -30,6 +30,7 @@ let APIList = [ 'miniMap', 'watermark', 'associativeLine', + 'touchEvent', 'xmind', 'markdown', 'utils' diff --git a/web/src/pages/Doc/en/changelog/index.md b/web/src/pages/Doc/en/changelog/index.md index 8edcc789..12e99159 100644 --- a/web/src/pages/Doc/en/changelog/index.md +++ b/web/src/pages/Doc/en/changelog/index.md @@ -4,7 +4,7 @@ Breaking change: Adjusted the directory structure of the simple-mind-map source code, Main impact: 1. The introduction path of the plugin needs to be modified. The constant file path needs to be modified. -New: 1.Supports one click zoom to fit the canvas function. 2.Press and hold the Ctrl key to activate the multi selection function on demand through configuration. 3.Support setting to left click to select multiple nodes and right click to drag the canvas. 4. Support controlling whether nodes are allowed to be edited. 5.Add a method for destroying mind maps. +New: 1.Supports one click zoom to fit the canvas function. 2.Press and hold the Ctrl key to activate the multi selection function on demand through configuration. 3.Support setting to left click to select multiple nodes and right click to drag the canvas. 4. Support controlling whether nodes are allowed to be edited. 5.Add a method for destroying mind maps. 6.Added touch event support plugin. Fix: 1.Fix the issue where holding down the Ctrl key to select multiple nodes does not trigger the click event for the node. 2.Fixed the issue of node style loss when clearing a node and then entering text. diff --git a/web/src/pages/Doc/en/changelog/index.vue b/web/src/pages/Doc/en/changelog/index.vue index 64c87c99..3274fcf9 100644 --- a/web/src/pages/Doc/en/changelog/index.vue +++ b/web/src/pages/Doc/en/changelog/index.vue @@ -3,7 +3,7 @@

Changelog

0.6.0

Breaking change: Adjusted the directory structure of the simple-mind-map source code, Main impact: 1. The introduction path of the plugin needs to be modified. The constant file path needs to be modified.

-

New: 1.Supports one click zoom to fit the canvas function. 2.Press and hold the Ctrl key to activate the multi selection function on demand through configuration. 3.Support setting to left click to select multiple nodes and right click to drag the canvas. 4. Support controlling whether nodes are allowed to be edited. 5.Add a method for destroying mind maps.

+

New: 1.Supports one click zoom to fit the canvas function. 2.Press and hold the Ctrl key to activate the multi selection function on demand through configuration. 3.Support setting to left click to select multiple nodes and right click to drag the canvas. 4. Support controlling whether nodes are allowed to be edited. 5.Add a method for destroying mind maps. 6.Added touch event support plugin.

Fix: 1.Fix the issue where holding down the Ctrl key to select multiple nodes does not trigger the click event for the node. 2.Fixed the issue of node style loss when clearing a node and then entering text.

0.5.11

New: Supports associative text editing.

diff --git a/web/src/pages/Doc/en/touchEvent/index.md b/web/src/pages/Doc/en/touchEvent/index.md new file mode 100644 index 00000000..7b65cd6b --- /dev/null +++ b/web/src/pages/Doc/en/touchEvent/index.md @@ -0,0 +1,18 @@ +# TouchEvent plugin + +> v0.6.0+ + +This plugin supports mobile touch events for users. The principle is to listen for 'touchstart', 'touchmove', and 'touchend' events on the mobile end, and then dispatch corresponding mouse events. + +Currently, it supports single finger touch to move the canvas, click to activate nodes, double finger zoom the canvas, single finger double-click to reset and edit nodes. + +## Register + +```js +import MindMap from 'simple-mind-map' +import TouchEvent from 'simple-mind-map/src/plugins/TouchEvent.js' + +MindMap.usePlugin(TouchEvent) +``` + +After registration and instantiation of `MindMap`, the instance can be obtained through `mindMap.touchEvent`. \ No newline at end of file diff --git a/web/src/pages/Doc/en/touchEvent/index.vue b/web/src/pages/Doc/en/touchEvent/index.vue new file mode 100644 index 00000000..e1fedd45 --- /dev/null +++ b/web/src/pages/Doc/en/touchEvent/index.vue @@ -0,0 +1,28 @@ + + + + + \ No newline at end of file diff --git a/web/src/pages/Doc/routerList.js b/web/src/pages/Doc/routerList.js index 5e7a4e5f..d024d5e5 100644 --- a/web/src/pages/Doc/routerList.js +++ b/web/src/pages/Doc/routerList.js @@ -44,7 +44,8 @@ export default [ { path: 'watermark', title: 'Watermark插件' }, { path: 'xmind', title: 'XMind解析' }, { path: 'deploy', title: '部署' }, - { path: 'client', title: '客户端' } + { path: 'client', title: '客户端' }, + { path: 'touchEvent', title: 'TouchEvent插件' } ] }, { @@ -72,7 +73,8 @@ export default [ { path: 'view', title: 'View instance' }, { path: 'watermark', title: 'Watermark plugin' }, { path: 'xmind', title: 'XMind parse' }, - { path: 'deploy', title: 'Deploy' } + { path: 'deploy', title: 'Deploy' }, + { path: 'touchEvent', title: 'TouchEvent plugin' } ] } ] diff --git a/web/src/pages/Doc/zh/changelog/index.md b/web/src/pages/Doc/zh/changelog/index.md index 8460412a..028d39bd 100644 --- a/web/src/pages/Doc/zh/changelog/index.md +++ b/web/src/pages/Doc/zh/changelog/index.md @@ -4,7 +4,7 @@ 破坏性更新:调整了simple-mind-map源码的目录结构,主要影响:1.插件的引入路径需要修改。2.constant文件路径需要修改。 -新增:1.支持一键缩放至适应画布功能。 2.按住Ctrl键多选功能可通过配置按需开启。 3.支持设置为左键多选节点,右键拖动画布。 4.支持控制节点是否允许编辑。 5.新增销毁思维导图的方法。 +新增:1.支持一键缩放至适应画布功能。 2.按住Ctrl键多选功能可通过配置按需开启。 3.支持设置为左键多选节点,右键拖动画布。 4.支持控制节点是否允许编辑。 5.新增销毁思维导图的方法。 6.新增触摸事件支持插件。 修复:1.修复按住ctrl键多选节点时不会触发节点的click事件的问题。 2.修复清空一个节点后再输入文字时节点样式丢失的问题。 diff --git a/web/src/pages/Doc/zh/changelog/index.vue b/web/src/pages/Doc/zh/changelog/index.vue index c0cd6daf..2a519d61 100644 --- a/web/src/pages/Doc/zh/changelog/index.vue +++ b/web/src/pages/Doc/zh/changelog/index.vue @@ -3,7 +3,7 @@

Changelog

0.6.0

破坏性更新:调整了simple-mind-map源码的目录结构,主要影响:1.插件的引入路径需要修改。2.constant文件路径需要修改。

-

新增:1.支持一键缩放至适应画布功能。 2.按住Ctrl键多选功能可通过配置按需开启。 3.支持设置为左键多选节点,右键拖动画布。 4.支持控制节点是否允许编辑。 5.新增销毁思维导图的方法。

+

新增:1.支持一键缩放至适应画布功能。 2.按住Ctrl键多选功能可通过配置按需开启。 3.支持设置为左键多选节点,右键拖动画布。 4.支持控制节点是否允许编辑。 5.新增销毁思维导图的方法。 6.新增触摸事件支持插件。

修复:1.修复按住ctrl键多选节点时不会触发节点的click事件的问题。 2.修复清空一个节点后再输入文字时节点样式丢失的问题。

0.5.11

新增:支持关联性文本编辑。

diff --git a/web/src/pages/Doc/zh/touchEvent/index.md b/web/src/pages/Doc/zh/touchEvent/index.md new file mode 100644 index 00000000..cd0dabb5 --- /dev/null +++ b/web/src/pages/Doc/zh/touchEvent/index.md @@ -0,0 +1,18 @@ +# TouchEvent插件 + +> v0.6.0+ + +该插件用户支持移动端触摸事件。原理是监听移动端的`touchstart`、`touchmove`、`touchend`事件,然后派发对应的鼠标事件。 + +目前支持单指触摸移动画布、点击激活节点,双指缩放画布,单指双击复位和编辑节点。 + +## 注册 + +```js +import MindMap from 'simple-mind-map' +import TouchEvent from 'simple-mind-map/src/plugins/TouchEvent.js' + +MindMap.usePlugin(TouchEvent) +``` + +注册完且实例化`MindMap`后可通过`mindMap.touchEvent`获取到该实例。 \ No newline at end of file diff --git a/web/src/pages/Doc/zh/touchEvent/index.vue b/web/src/pages/Doc/zh/touchEvent/index.vue new file mode 100644 index 00000000..02df092f --- /dev/null +++ b/web/src/pages/Doc/zh/touchEvent/index.vue @@ -0,0 +1,28 @@ + + + + + \ No newline at end of file