增加快捷键和全屏功能

This commit is contained in:
wanglin 2021-07-11 22:21:40 +08:00
parent 5c6fef71bd
commit f38b92a2e2
22 changed files with 608 additions and 53 deletions

View File

@ -24,14 +24,14 @@ export default {
"data": {
"text": "根节点"
},
"childrens": [
"children": [
{
"data": {
"text": "二级节点1"
}
}
],
"children": [
"childrens": [
{
"data": {
"text": "二级节点1",

View File

@ -26,7 +26,15 @@ const defaultOpt = {
// 设置鼠标左键还是右键按下拖动1左键、2右键
dragButton: 1,
// 最多显示几个标签
maxTag: 5
maxTag: 5,
// 导出图片时的内边距
exportPadding: 20,
// 展开收缩按钮尺寸
expandBtnSize: 20,
// 节点里图片和文字的间距
imgTextMargin: 5,
// 节点里各种文字信息的间距,如图标和文字的间距
textContentMargin: 2
}
/**
@ -155,6 +163,18 @@ class MindMap {
})
}
/**
* @Author: 王林
* @Date: 2021-07-11 21:16:52
* @Desc: 容器尺寸变化调整尺寸
*/
resize() {
this.elRect = this.el.getBoundingClientRect()
this.width = this.elRect.width
this.height = this.elRect.height
this.svg.size(this.width, this.height)
}
/**
* @Author: 王林
* @Date: 2021-04-24 13:25:50

View File

@ -14,6 +14,7 @@ class Export {
*/
constructor(opt) {
this.mindMap = opt.mindMap
this.exportPadding = this.mindMap.opt.exportPadding
}
/**
@ -85,13 +86,13 @@ class Export {
img.onload = async () => {
try {
let canvas = document.createElement('canvas')
canvas.width = img.width
canvas.height = img.height
canvas.width = img.width + this.exportPadding * 2
canvas.height = img.height + this.exportPadding * 2
let ctx = canvas.getContext('2d')
// 绘制背景
await this.drawBackgroundToCanvas(ctx, img.width, img.height)
await this.drawBackgroundToCanvas(ctx, canvas.width, canvas.height)
// 图片绘制到canvas里
ctx.drawImage(img, 0, 0, img.width, img.height)
ctx.drawImage(img, 0, 0, img.width, img.height, this.exportPadding, this.exportPadding, img.width, img.height)
resolve(canvas.toDataURL())
} catch (error) {
reject(error)

View File

@ -76,12 +76,12 @@ class Node {
textContentHeight: 0,
textContentHeight: 0
}
// icon间距
this._textContentItemMargin = 2
// 各种文字信息的间距
this._textContentItemMargin = this.mindMap.opt.textContentMargin
// 图片和文字节点的间距
this._blockContentMargin = 5
this._blockContentMargin = this.mindMap.opt.imgTextMargin
// 展开收缩按钮尺寸
this._expandBtnSize = 20
this._expandBtnSize = this.mindMap.opt.expandBtnSize
// 初始渲染
this._initRender = true
// 初始化
@ -574,7 +574,6 @@ class Node {
this.removeAllNode()
this.createNodeData()
this.layout()
this.renderExpandBtn()
this.draw.add(this.group)
}
@ -591,6 +590,7 @@ class Node {
if (this._expandBtn && this.nodeData.children.length <= 0) {
this.removeExpandBtn()
} else if (!this._expandBtn && this.nodeData.children.length > 0) {// 需要添加展开收缩按钮
this.renderExpandBtn()
}
let t = this.group.transform()
@ -749,9 +749,7 @@ class Node {
removeExpandBtn() {
if (this._expandBtn) {
this._expandBtn.off(['mouseover', 'mouseout', 'click'])
}
// 展开收缩按钮
if (this._expandBtn) {
this._expandBtn.clear()
this._expandBtn.remove()
this._expandBtn = null
}

View File

@ -48,6 +48,8 @@ class Render {
this.bindEvent()
// 注册命令
this.registerCommands()
// 注册快捷键
this.registerShortcutKeys()
}
/**
@ -116,6 +118,35 @@ class Render {
this.mindMap.command.add('SET_NODE_TAG', this.setNodeTag)
}
/**
* @Author: 王林
* @Date: 2021-07-11 16:55:44
* @Desc: 注册快捷键
*/
registerShortcutKeys() {
// 插入下级节点
this.mindMap.keyCommand.addShortcut('Tab', () => {
this.insertChildNode()
})
// 插入同级节点
this.mindMap.keyCommand.addShortcut('Enter', () => {
this.insertNode()
})
// 展开/收起节点
this.mindMap.keyCommand.addShortcut('/', () => {
this.activeNodeList.forEach((node) => {
if (node.nodeData.children.length <= 0) {
return
}
this.toggleNodeExpand(node)
})
})
// 删除节点
this.mindMap.keyCommand.addShortcut('Del|Backspace', () => {
this.removeNode()
})
}
/**
* javascript comment
* @Author: 王林25
@ -326,6 +357,15 @@ class Render {
this.mindMap.render()
}
/**
* @Author: 王林
* @Date: 2021-07-11 17:15:33
* @Desc: 切换节点展开状态
*/
toggleNodeExpand(node) {
this.mindMap.execCommand('SET_NODE_EXPAND', node, !node.nodeData.data.expand)
}
/**
* @Author: 王林
* @Date: 2021-07-09 22:04:19

View File

@ -49,7 +49,8 @@ class Select {
[this.mouseMoveX, this.mouseMoveY],
[this.mouseDownX, this.mouseMoveY]
])
this.mindMap.batchExecution.push('checkInNodes', this.checkInNodes)
this.checkInNodes()
// this.mindMap.batchExecution.push('checkInNodes', this.checkInNodes)
})
this.mindMap.on('mouseup', (e) => {
if (!this.isMousedown) {
@ -110,11 +111,15 @@ class Select {
top >= miny &&
bottom <= maxy
) {
this.mindMap.execCommand('SET_NODE_ACTIVE', node, true)
this.mindMap.renderer.addActiveNode(node)
this.mindMap.batchExecution.push('activeNode' + node.uid, () => {
this.mindMap.execCommand('SET_NODE_ACTIVE', node, true)
this.mindMap.renderer.addActiveNode(node)
})
} else if (node.nodeData.data.isActive) {
this.mindMap.execCommand('SET_NODE_ACTIVE', node, false)
this.mindMap.renderer.removeActiveNode(node)
this.mindMap.batchExecution.push('activeNode' + node.uid, () => {
this.mindMap.execCommand('SET_NODE_ACTIVE', node, false)
this.mindMap.renderer.removeActiveNode(node)
})
}
})
}

View File

@ -51,6 +51,10 @@ export default class TextEdit {
this.mindMap.keyCommand.addShortcut('Enter', () => {
this.hideEditTextBox()
})
// 注册编辑快捷键
this.mindMap.keyCommand.addShortcut('F2', () => {
this.show()
})
}
/**

View File

@ -29,6 +29,16 @@ class View {
* @Desc: 绑定
*/
bind() {
// 快捷键
this.mindMap.keyCommand.addShortcut('Control+=', () => {
this.enlarge()
})
this.mindMap.keyCommand.addShortcut('Control+-', () => {
this.narrow()
})
this.mindMap.keyCommand.addShortcut('Control+Enter', () => {
this.reset()
})
// 拖动视图
this.mindMap.event.on('mousedown', () => {
this.sx = this.x
@ -63,6 +73,23 @@ class View {
})
}
/**
* @Author: 王林
* @Date: 2021-07-11 17:41:35
* @Desc: 恢复
*/
reset() {
let t = this.mindMap.draw.transform()
this.scale = 1
this.x = 0
this.y = 0
this.mindMap.draw.transform({
scale: this.scale,
origin: 'left center',
translate: [this.x, this.y],
})
}
/**
* @Author: 王林
* @Date: 2021-07-04 17:10:34

View File

@ -54,6 +54,42 @@
<div class="content unicode" style="display: block;">
<ul class="icon_lists dib-box">
<li class="dib">
<span class="icon iconfont">&#xe60e;</span>
<div class="name">恢复默认</div>
<div class="code-name">&amp;#xe60e;</div>
</li>
<li class="dib">
<span class="icon iconfont">&#xe61e;</span>
<div class="name">换行</div>
<div class="code-name">&amp;#xe61e;</div>
</li>
<li class="dib">
<span class="icon iconfont">&#xec13;</span>
<div class="name">缩小</div>
<div class="code-name">&amp;#xec13;</div>
</li>
<li class="dib">
<span class="icon iconfont">&#xe626;</span>
<div class="name">编辑</div>
<div class="code-name">&amp;#xe626;</div>
</li>
<li class="dib">
<span class="icon iconfont">&#xe663;</span>
<div class="name">放大</div>
<div class="code-name">&amp;#xe663;</div>
</li>
<li class="dib">
<span class="icon iconfont">&#xe664;</span>
<div class="name">全屏</div>
<div class="code-name">&amp;#xe664;</div>
</li>
<li class="dib">
<span class="icon iconfont">&#xe616;</span>
<div class="name">定位</div>
@ -186,9 +222,9 @@
<pre><code class="language-css"
>@font-face {
font-family: 'iconfont';
src: url('iconfont.woff2?t=1625399244498') format('woff2'),
url('iconfont.woff?t=1625399244498') format('woff'),
url('iconfont.ttf?t=1625399244498') format('truetype');
src: url('iconfont.woff2?t=1626000433132') format('woff2'),
url('iconfont.woff?t=1626000433132') format('woff'),
url('iconfont.ttf?t=1626000433132') format('truetype');
}
</code></pre>
<h3 id="-iconfont-">第二步:定义使用 iconfont 的样式</h3>
@ -214,6 +250,60 @@
<div class="content font-class">
<ul class="icon_lists dib-box">
<li class="dib">
<span class="icon iconfont iconhuifumoren"></span>
<div class="name">
恢复默认
</div>
<div class="code-name">.iconhuifumoren
</div>
</li>
<li class="dib">
<span class="icon iconfont iconhuanhang"></span>
<div class="name">
换行
</div>
<div class="code-name">.iconhuanhang
</div>
</li>
<li class="dib">
<span class="icon iconfont iconsuoxiao"></span>
<div class="name">
缩小
</div>
<div class="code-name">.iconsuoxiao
</div>
</li>
<li class="dib">
<span class="icon iconfont iconbianji"></span>
<div class="name">
编辑
</div>
<div class="code-name">.iconbianji
</div>
</li>
<li class="dib">
<span class="icon iconfont iconfangda"></span>
<div class="name">
放大
</div>
<div class="code-name">.iconfangda
</div>
</li>
<li class="dib">
<span class="icon iconfont iconquanping1"></span>
<div class="name">
全屏
</div>
<div class="code-name">.iconquanping1
</div>
</li>
<li class="dib">
<span class="icon iconfont icondingwei"></span>
<div class="name">
@ -412,6 +502,54 @@
<div class="content symbol">
<ul class="icon_lists dib-box">
<li class="dib">
<svg class="icon svg-icon" aria-hidden="true">
<use xlink:href="#iconhuifumoren"></use>
</svg>
<div class="name">恢复默认</div>
<div class="code-name">#iconhuifumoren</div>
</li>
<li class="dib">
<svg class="icon svg-icon" aria-hidden="true">
<use xlink:href="#iconhuanhang"></use>
</svg>
<div class="name">换行</div>
<div class="code-name">#iconhuanhang</div>
</li>
<li class="dib">
<svg class="icon svg-icon" aria-hidden="true">
<use xlink:href="#iconsuoxiao"></use>
</svg>
<div class="name">缩小</div>
<div class="code-name">#iconsuoxiao</div>
</li>
<li class="dib">
<svg class="icon svg-icon" aria-hidden="true">
<use xlink:href="#iconbianji"></use>
</svg>
<div class="name">编辑</div>
<div class="code-name">#iconbianji</div>
</li>
<li class="dib">
<svg class="icon svg-icon" aria-hidden="true">
<use xlink:href="#iconfangda"></use>
</svg>
<div class="name">放大</div>
<div class="code-name">#iconfangda</div>
</li>
<li class="dib">
<svg class="icon svg-icon" aria-hidden="true">
<use xlink:href="#iconquanping1"></use>
</svg>
<div class="name">全屏</div>
<div class="code-name">#iconquanping1</div>
</li>
<li class="dib">
<svg class="icon svg-icon" aria-hidden="true">
<use xlink:href="#icondingwei"></use>

View File

@ -1,8 +1,8 @@
@font-face {
font-family: "iconfont"; /* Project id 2479351 */
src: url('iconfont.woff2?t=1625399244498') format('woff2'),
url('iconfont.woff?t=1625399244498') format('woff'),
url('iconfont.ttf?t=1625399244498') format('truetype');
src: url('iconfont.woff2?t=1626000433132') format('woff2'),
url('iconfont.woff?t=1626000433132') format('woff'),
url('iconfont.ttf?t=1626000433132') format('truetype');
}
.iconfont {
@ -13,6 +13,30 @@
-moz-osx-font-smoothing: grayscale;
}
.iconhuifumoren:before {
content: "\e60e";
}
.iconhuanhang:before {
content: "\e61e";
}
.iconsuoxiao:before {
content: "\ec13";
}
.iconbianji:before {
content: "\e626";
}
.iconfangda:before {
content: "\e663";
}
.iconquanping1:before {
content: "\e664";
}
.icondingwei:before {
content: "\e616";
}

File diff suppressed because one or more lines are too long

View File

@ -5,6 +5,48 @@
"css_prefix_text": "icon",
"description": "思维导图",
"glyphs": [
{
"icon_id": "19980541",
"name": "恢复默认",
"font_class": "huifumoren",
"unicode": "e60e",
"unicode_decimal": 58894
},
{
"icon_id": "1616783",
"name": "换行",
"font_class": "huanhang",
"unicode": "e61e",
"unicode_decimal": 58910
},
{
"icon_id": "4777227",
"name": "缩小",
"font_class": "suoxiao",
"unicode": "ec13",
"unicode_decimal": 60435
},
{
"icon_id": "18811980",
"name": "编辑",
"font_class": "bianji",
"unicode": "e626",
"unicode_decimal": 58918
},
{
"icon_id": "21188137",
"name": "放大",
"font_class": "fangda",
"unicode": "e663",
"unicode_decimal": 58979
},
{
"icon_id": "21189639",
"name": "全屏",
"font_class": "quanping1",
"unicode": "e664",
"unicode_decimal": 58980
},
{
"icon_id": "397753",
"name": "定位",

View File

@ -197,4 +197,63 @@ export const backgroundPositionList = [
// 数据存储
export const store = {
sidebarZIndex: 1//侧边栏zIndex
}
}
// 快捷键列表
export const shortcutKeyList = [
{
type: '节点操作',
list: [
{
icon: 'icontianjiazijiedian',
name: '插入下级节点',
value: 'Tab'
},
{
icon: 'iconjiedian',
name: '插入同级节点',
value: 'Enter'
},
{
icon: 'iconzhankai',
name: '展开/收起节点',
value: '/'
},
{
icon: 'iconshanchu',
name: '删除节点',
value: 'Delete | Backspace'
},
{
icon: 'iconbianji',
name: '编辑节点',
value: 'F2'
},
{
icon: 'iconhuanhang',
name: '文本换行',
value: 'Shift + Enter'
}
]
},
{
type: '画布操作',
list: [
{
icon: 'iconfangda',
name: '放大',
value: 'Ctrl + +'
},
{
icon: 'iconsuoxiao',
name: '缩小',
value: 'Ctrl + -'
},
{
icon: 'icondingwei',
name: '恢复默认',
value: 'Ctrl + Enter'
}
]
}
]

View File

@ -8,6 +8,7 @@
<BaseStyle :data="mindMapData" :mindMap="mindMap"></BaseStyle>
<Theme :mindMap="mindMap"></Theme>
<Structure :mindMap="mindMap"></Structure>
<ShortcutKey></ShortcutKey>
</div>
</template>
@ -20,7 +21,8 @@ import exampleData from "simple-mind-map/example/exampleData";
import Theme from "./Theme";
import Structure from "./Structure";
import Count from "./Count";
import NavigatorToolbar from './NavigatorToolbar';
import NavigatorToolbar from "./NavigatorToolbar";
import ShortcutKey from "./ShortcutKey";
/**
* @Author: 王林
@ -36,7 +38,8 @@ export default {
Theme,
Structure,
Count,
NavigatorToolbar
NavigatorToolbar,
ShortcutKey,
},
data() {
return {

View File

@ -0,0 +1,62 @@
<template>
<div class="fullscreenContainer">
<el-tooltip class="item" effect="dark" content="全屏" placement="top">
<div class="btn iconfont iconquanping" @click="toFullscreen"></div>
</el-tooltip>
</div>
</template>
<script>
import { fullscrrenEvent, fullScreen } from "@/utils";
/**
* @Author: 王林
* @Date: 2021-06-24 22:53:10
* @Desc: 全屏
*/
export default {
name: "Fullscreen",
props: {
mindMap: {
type: Object,
},
},
data() {
return {};
},
watch: {
mindMap(val, oldVal) {
if (val && !oldVal) {
}
},
},
created() {
document[fullscrrenEvent] = (e) => {
setTimeout(() => {
this.mindMap.resize();
}, 1000);
};
},
methods: {
/**
* @Author: 王林
* @Date: 2021-07-11 21:14:30
* @Desc: 准备全屏
*/
toFullscreen() {
fullScreen(this.mindMap.el);
},
},
};
</script>
<style lang="less" scoped>
.fullscreenContainer {
display: flex;
align-items: center;
.btn {
cursor: pointer;
}
}
</style>

View File

@ -1,13 +1,17 @@
<template>
<div class="navigatorContainer">
<div class="item">
<Scale :mindMap="mindMap"></Scale>
</div>
</div>
<div class="item">
<Scale :mindMap="mindMap"></Scale>
</div>
<div class="item">
<Fullscreen :mindMap="mindMap"></Fullscreen>
</div>
</div>
</template>
<script>
import Scale from './Scale';
import Scale from "./Scale";
import Fullscreen from "./Fullscreen";
/**
* @Author: 王林
@ -16,19 +20,15 @@ import Scale from './Scale';
*/
export default {
name: "NavigatorToolbar",
components: {
Scale
},
components: {
Scale,
Fullscreen,
},
props: {
mindMap: {
mindMap: {
type: Object,
},
},
data() {
return {};
},
created() {},
methods: {},
}
};
</script>
@ -44,6 +44,14 @@ export default {
height: 44px;
font-size: 12px;
display: flex;
align-items: center;
align-items: center;
.item {
margin-right: 20px;
&:last-of-type {
margin-right: 0;
}
}
}
</style>

View File

@ -0,0 +1,87 @@
<template>
<Sidebar ref="sidebar" title="快捷键">
<div class="box">
<div v-for="item in shortcutKeyList" :key="item.type">
<div class="title">{{ item.type }}</div>
<div class="list" v-for="item2 in item.list" :key="item2.value">
<div class="item">
<span
v-if="item2.icon"
class="icon iconfont"
:class="[item2.icon]"
></span>
<span class="name">{{ item2.name }}</span>
<div class="value">{{ item2.value }}</div>
</div>
</div>
</div>
</div>
</Sidebar>
</template>
<script>
import Sidebar from "./Sidebar";
import { shortcutKeyList } from "@/config";
/**
* @Author: 王林
* @Date: 2021-06-24 22:54:14
* @Desc: 快捷键
*/
export default {
name: "ShortcutKey",
components: {
Sidebar,
},
data() {
return {
shortcutKeyList,
};
},
created() {
this.$bus.$on("showShortcutKey", () => {
this.$refs.sidebar.show = false;
this.$nextTick(() => {
this.$refs.sidebar.show = true;
});
});
},
};
</script>
<style lang="less" scoped>
.box {
padding: 20px;
.title {
font-size: 16px;
font-weight: 500;
color: #333;
margin: 26px 0 20px;
}
.list {
font-size: 14px;
.item {
display: flex;
align-items: center;
margin-bottom: 15px;
.icon {
font-size: 16px;
margin-right: 16px;
}
.name {
color: #333;
}
.value {
color: #909090;
margin-left: auto;
}
}
}
}
</style>

View File

@ -109,6 +109,10 @@
<span class="icon iconfont icondaochu"></span>
<span class="text">导出</span>
</div>
<div class="toolbarBtn" @click="$bus.$emit('showShortcutKey')">
<span class="icon iconfont iconjianpan"></span>
<span class="text">快捷键</span>
</div>
</div>
</div>
<NodeImage></NodeImage>
@ -126,12 +130,12 @@ import NodeHyperlink from "./NodeHyperlink";
import NodeIcon from "./NodeIcon";
import NodeNote from "./NodeNote";
import NodeTag from "./NodeTag";
import Export from './Export';
import Export from "./Export";
/**
* @Author: 王林
* @Date: 2021-06-24 22:54:58
* @Desc: 工具栏
/**
* @Author: 王林
* @Date: 2021-06-24 22:54:58
* @Desc: 工具栏
*/
export default {
name: "Toolbar",
@ -141,11 +145,11 @@ export default {
NodeIcon,
NodeNote,
NodeTag,
Export
Export,
},
data() {
return {
activeNodes: []
activeNodes: [],
};
},
computed: {

View File

@ -0,0 +1,33 @@
/**
* @Author: 王林
* @Date: 2021-07-11 21:38:09
* @Desc: 全屏事件检测
*/
const getOnfullscreEnevt = () => {
if (document.documentElement.requestFullScreen) {
return 'onfullscreenchange';
} else if (document.documentElement.webkitRequestFullScreen) {
return 'onwebkitfullscreenchange';
} else if (document.documentElement.mozRequestFullScreen) {
return 'onmozfullscreenchange';
} else if (document.documentElement.msRequestFullscreen) {
return 'onmsfullscreenchange';
}
}
export const fullscrrenEvent = getOnfullscreEnevt()
/**
* @Author: 王林
* @Date: 2021-07-11 21:45:06
* @Desc: 全屏
*/
export const fullScreen = (element) => {
if (element.requestFullScreen) {
element.requestFullScreen();
} else if (element.webkitRequestFullScreen) {
element.webkitRequestFullScreen();
} else if (element.mozRequestFullScreen) {
element.mozRequestFullScreen();
}
}