构建个性化脑图工具:PHP和Vue的结合应用

1. 简介

个性化脑图工具是一款具有图形化界面的工具,用户可以通过拖拽节点的形式,创建自己的思维导图并进行存储、编辑、分享等操作。本文将介绍一个利用PHP和Vue结合开发的个性化脑图工具,在实现基本功能的基础上,还包含了一些自定义和高级功能。

2. 技术选型

2.1 PHP后端

本文使用PHP作为后端语言,主要原因是PHP易于学习上手,具有良好的扩展性和跨平台性。

/**

* 获取节点信息

* @param int $id 节点id

* @access public

* @return array 节点信息

*/

public function getNode(int $id): array

{

//todo

}

上述代码为PHP中获取节点信息的函数,使用了type hinting增强了类型约束,增加了代码的健壮性。

2.2 Vue前端

本文使用Vue.js作为前端框架,主要原因是Vue.js轻量且易于学习,采用了组件化的思想,方便维护和扩展。

// 定义节点组件

Vue.component('node', {

props: {

title: {

type: String,

required: true,

default: 'New Node'

},

x: {

type: Number,

required: true,

default: 0

},

y: {

type: Number,

required: true,

default: 0

}

},

template: `

{{ title }}

`

})

上述代码为Vue中定义节点组件的代码,使用了props接收父组件传递的数据,在template中使用了绑定语法将组件绑定到节点上。

3. 基本功能

3.1 创建节点

用户可以通过鼠标左键点击空白处来创建新节点,节点会自动定位到鼠标位置,并且可以自定义节点的名称。

用户添加新节点的方法为单击空白处——>输入名称——>按下回车键,下面是实现此功能的代码:

// 添加鼠标左键点击事件

container.addEventListener('click', event => {

if (event.button !== 0) return // 只处理鼠标左键事件

const node = new Node({

x: event.clientX,

y: event.clientY,

title: prompt('Please input node title')

})

nodes.push(node)

})

3.2 连接节点

用户可以通过拖拽鼠标左键连接两个节点,并且可以自定义连线的类型和样式。

用户连接两个节点的方法为单击起点节点——>拖拽到终点节点——>松开鼠标左键,下面是实现此功能的代码:

// 添加起点节点选中事件

container.addEventListener('mousedown', event => {

if (event.button !== 0) return // 只处理鼠标左键事件

const node = getClickedNode(event)

if (node) {

startLinkNode = node

isLinking = true

event.preventDefault() // 防止浏览器默认选中

}

})

// 添加鼠标移动事件

container.addEventListener('mousemove', event => {

if (isLinking) {

context.beginPath()

context.moveTo(startLinkNode.x, startLinkNode.y)

context.lineTo(event.clientX, event.clientY)

context.stroke()

}

})

// 添加终点节点选中事件

container.addEventListener('mouseup', event => {

if (event.button !== 0) return // 只处理鼠标左键事件

if (!isLinking || event.target.tagName.toUpperCase() !== 'CANVAS') return

const endNode = getClickedNode(event)

if (endNode) {

const promptResult = prompt('Please input the link type (default: link)', 'link') || 'link'

const link = new Link({

start: startLinkNode,

end: endNode,

type: promptResult,

style: {

strokeStyle: 'blue',

lineWidth: 2

}

})

}

})

上述代码中主要使用了Canvas画布来绘制连线的效果。

3.3 存储图谱

用户可以在本地保存已经创建好的图谱,下次打开后可以直接加载到已经保存的图谱。

用户存储图谱的方法为单击顶部工具栏中的保存按钮,下面是实现此功能的代码:

// 添加保存按钮事件

const saveButton = document.getElementById('saveButton')

saveButton.addEventListener('click', event => {

const data = {

nodes: nodes,

links: links

}

localStorage.setItem('mindmap', JSON.stringify(data))

})

上述代码中,使用了localStorage将图谱信息存储到本地,下次打开时可以在localStorage中查找是否有对应信息,如果有则加载。

4. 自定义功能

4.1 鼠标滚轮缩放

用户可以通过鼠标滚轮来缩放图谱,方便查看和编辑。同时,用户可以通过顶部工具栏中的放大、缩小按钮来实现同样效果。

实现鼠标滚轮缩放的代码如下:

// 添加鼠标滚轮事件

container.addEventListener('wheel', event => {

event.preventDefault() // 防止页面滚动

const scaleDelta = event.wheelDelta > 0 ? zoomScale : 1 / zoomScale

scale *= scaleDelta

})

上述代码中,使用了scale变量记录当前缩放比例,zoomScale表示存储的缩放倍数,防止继续缩放或放大超过常规使用范围。

4.2 节点隐藏/显示

用户可以选择特定的节点或节点分支,隐藏其下方的节点;在需要时再次显示。

实现节点隐藏/显示的代码如下:

// 添加节点选中事件

container.addEventListener('click', event => {

if (event.button !== 0) return // 只处理鼠标左键事件

const node = getClickedNode(event)

if (node) {

node.toggle()

}

})

// 定义节点类的toggle函数

class Node {

// ...

toggle() {

this.children.forEach(child => child.toggle())

this.isHidden = !this.isHidden

}

}

上述代码中,toggle函数可以递归隐藏/显示节点,isHidden是存储该节点是否隐藏的标记。

5. 高级功能

5.1 打印图谱

用户可以通过顶部工具栏中的“打印”按钮将图谱内容输出为纸质版。

实现打印图谱功能的代码如下:

// 添加打印按钮事件

const printButton = document.getElementById('printButton')

printButton.addEventListener('click', event => {

const dataUrl = canvas.toDataURL()

const printWindow = window.open('', '_blank')

})

上述代码中,使用了Canvas的toDataURL方法将画布内容转为图片并输出到打印窗口中,同时通过window.print()方法实现打印机打印。使用了window.open()打开了一个新窗口,显示打印内容。

5.2 复制/剪切/粘贴节点、分支

用户可以将特定节点或节点分支进行复制、剪切、粘贴功能。

实现此功能的代码如下:

// 定义复制节点类

class ClipboardNode {

constructor(source) {

this.source = source

this.target = null

this.nodes = []

this.links = []

}

// 复制节点(禁止递归遍历)

prepare() {

const nodesIdMap = {}

// 收集子节点和父节点

const getNodes = node => {

nodesIdMap[node.id] = true

this.nodes.push(node.clone())

node.links.forEach(link => {

if (!nodesIdMap[link.end.id]) {

this.links.push(link.clone())

getNodes(link.end)

}

})

}

getNodes(this.source)

}

// 粘贴节点

paste(position) {

if (!this.target) {

// 新增节点

this.nodes.forEach(node => {

node.offset(position.x, position.y)

nodes.push(node)

})

this.links.forEach(link => {

link.relation = nodes

links.push(link)

})

} else {

// 替换节点

let idMap = {}

let newNodes = []

let newLinks = []

const getNodes = (node, parent) => {

const newNode = node.clone()

idMap[node.id] = newNode.id

newNode.offset(position.x, position.y)

newNode.parent = parent

newNodes.push(newNode)

node.links.forEach(link => {

if (!idMap[link.end.id]) {

newLinks.push(link.clone())

link.end.parent = newNode

link.end.links = []

getNodes(link.end, newNode)

} else {

const targetId = idMap[link.end.id]

const newLink = link.clone()

newLink.start = newNode

newLink.end = newNodes.find(node => node.id === targetId)

newLinks.push(newLink)

}

})

}

getNodes(this.source, this.target.parent)

const replaceData = {

nodes: newNodes,

links: newLinks

}

nodes.splice(nodes.findIndex(node => node.id === this.target.id), 1, ...newNodes)

links.splice(links.findIndex(link => link.end.id === this.target.id), 1, ...newLinks)

}

}

}

// 添加剪切、复制、粘贴事件

container.addEventListener('keydown', event => {

const isCtrlKey = event.ctrlKey || event.metaKey // Mac键盘是command

if (isCtrlKey && event.key === 'c') {

const nodesIdMap = {}

const getNodes = node => {

nodesIdMap[node.id] = true

node.links.forEach(link => {

if (!nodesIdMap[link.end.id]) {

getNodes(link.end)

}

})

}

const selectedNode = getSelectedNode()

if (selectedNode) {

const clipboard = new ClipboardNode(selectedNode)

getNodes(selectedNode)

clipboard.prepare()

localStorage.setItem('clipboard', JSON.stringify(clipboard))

}

} else if (isCtrlKey && event.key === 'x') {

const nodesIdMap = {}

const getNodes = node => {

nodesIdMap[node.id] = true

this.nodes.push(node.clone())

node.links.forEach(link => {

if (!nodesIdMap[link.end.id]) {

this.links.push(link.clone())

getNodes(link.end)

}

})

}

const selectedNode = getSelectedNode()

if (selectedNode) {

const clipboard = new ClipboardNode(selectedNode)

getNodes(selectedNode)

clipboard.prepare()

localStorage.setItem('clipboard', JSON.stringify(clipboard))

deleteNode(selectedNode)

}

} else if (isCtrlKey && event.key === 'v') {

const clipboard = JSON.parse(localStorage.getItem('clipboard'))

if (clipboard) {

clipboard.paste(getMousePos(event))

}

}

})

上述代码中,使用了一个剪贴板类,将需要复制、剪切和粘贴的节点相关信息保存起来,并使用localStorage将保存节点信息。

5.3 快捷键

用户可以快速响应常用功能,如节点复制、快速隐藏所有节点等快捷键功能。

实现快捷键功能的代码如下:

// 添加快捷键

document.body.addEventListener('keydown', event => {

const isCtrlKey = event.ctrlKey || event.metaKey // Mac键盘是command

if (isCtrlKey && event.key === 'c') {

// ...

} else if (isCtrlKey && event.key === 'x') {

// ...

} else if (isCtrlKey && event.key === 'v') {

// ...

} else if (event.key === 'h') {

nodes.forEach(node => {

node.hide()

})

} else if (event.key === 's') {

const data = {

nodes: nodes,

links: links

}

localStorage.setItem('mindmap', JSON.stringify(data))

} else if (event.key === '+') {

scale *= zoomScale

} else if (event.key === '-') {

scale *= 1 / zoomScale

}

})

上述代码中,使用了document.body作为事件监听对象,并判断了键盘事件类型和使用的快捷键。

6. 总结

该篇文章介绍了一个PHP和Vue.js结合的个性化脑图工具,其中实现了基本功能如节点创建、连接、存储、编辑和图谱缩放;同时增加了自定义功能如节点隐藏/显示、分支复制/剪切/粘贴和节点删除等;高级功能方面则增加了打印图谱和快捷键等便捷功能。该工具结合PHP后端和Vue.js前端技术,具有较好的可扩展性和跨平台性,适用于大多数用户创建和编辑自己的脑图工具。

免责声明:本文来自互联网,本站所有信息(包括但不限于文字、视频、音频、数据及图表),不保证该信息的准确性、真实性、完整性、有效性、及时性、原创性等,版权归属于原作者,如无意侵犯媒体或个人知识产权,请来电或致函告之,本站将在第一时间处理。猿码集站发布此文目的在于促进信息交流,此文观点与本站立场无关,不承担任何责任。

后端开发标签