Feat:1.演示模式中禁止画布的所有内容响应鼠标事件;2.节点的超链接和备注图标支持响应鼠标事件;3.支持填空模式

This commit is contained in:
街角小林
2024-05-11 10:05:46 +08:00
parent d60f30d97e
commit bb2502501e
4 changed files with 108 additions and 20 deletions

View File

@@ -128,13 +128,18 @@ class MindMap {
// 创建容器元素
initContainer() {
const { associativeLineIsAlwaysAboveNode } = this.opt
// 给容器元素添加一个类名
this.el.classList.add('smm-mind-map-container')
// 节点关联线容器
const createAssociativeLineDraw = () => {
this.associativeLineDraw = this.draw.group()
this.associativeLineDraw.addClass('smm-associative-line-container')
}
// 画布
this.svg = SVG().addTo(this.el).size(this.width, this.height)
this.svg = SVG()
.addTo(this.el)
.size(this.width, this.height)
// 容器
this.draw = this.svg.group()
this.draw.addClass('smm-container')
@@ -432,7 +437,13 @@ class MindMap {
// 需要裁减的区域
let clipData = null
if (node) {
clipData = getNodeTreeBoundingRect(node, rect.x, rect.y, paddingX, paddingY)
clipData = getNodeTreeBoundingRect(
node,
rect.x,
rect.y,
paddingX,
paddingY
)
}
// 内边距
const fixHeight = 0
@@ -581,6 +592,8 @@ class MindMap {
this.svg.remove()
// 去除给容器元素设置的背景样式
Style.removeBackgroundStyle(this.el)
// 移除给容器元素添加的类名
this.el.classList.remove('smm-mind-map-container')
this.el.innerHTML = ''
this.el = null
this.removeCss()

View File

@@ -316,7 +316,10 @@ function createNoteNode() {
return null
}
let iconSize = this.mindMap.themeConfig.iconSize
let node = new SVG().attr('cursor', 'pointer').size(iconSize, iconSize)
let node = new SVG()
.attr('cursor', 'pointer')
.addClass('smm-node-note')
.size(iconSize, iconSize)
// 透明的层,用来作为鼠标区域
node.add(new Rect().size(iconSize, iconSize).fill({ color: 'transparent' }))
// 备注图标

View File

@@ -14,16 +14,25 @@ const defaultConfig = {
transition: 'all 0.3s ease-out', // 高亮框动画的过渡
zIndex: 9999, // 高亮框元素的层级
padding: 20, // 高亮框的内边距
margin: 50 // 高亮框的外边距
margin: 50, // 高亮框的外边距
openBlankMode: true // 是否开启填空模式,即带下划线的文本默认不显示,按回车键才依次显示
}
// 演示插件
class Demonstrate {
constructor(opt) {
this.mindMap = opt.mindMap
// 演示的步骤列表
this.stepList = []
// 当前所在步骤
this.currentStepIndex = 0
this.maskEl = null
// 当前所在步骤对应的节点实例
this.currentStepNode = null
// 当前所在步骤节点的下划线文本数据
this.currentUnderlineTextData = null
// 临时的样式剩余
this.tmpStyleEl = null
// 高亮样式元素
this.highlightEl = null
this.transformState = null
this.renderTree = null
@@ -47,6 +56,8 @@ class Demonstrate {
}
_enter() {
// 添加演示用的临时的样式
this.addTmpStyles()
// 记录演示前的画布状态
this.transformState = this.mindMap.view.getTransformData()
// 记录演示前的画布数据
@@ -79,27 +90,53 @@ class Demonstrate {
this.transformState = null
this.stepList = []
this.currentStepIndex = 0
this.currentStepNode = null
this.currentUnderlineTextData = null
this.unBindEvent()
this.removeTmpStyles()
this.removeHighlightEl()
this.mindMap.command.recovery()
this.mindMap.keyCommand.recovery()
this.mindMap.emit('exit_demonstrate')
}
// 添加临时的样式
addTmpStyles() {
this.tmpStyleEl = document.createElement('style')
let cssText = `
/* 画布所有元素禁止响应鼠标事件 */
.smm-mind-map-container {
pointer-events: none;
}
/* 超链接图标允许响应鼠标事件 */
.smm-node a {
pointer-events: all;
}
/* 备注图标允许响应鼠标事件 */
.smm-node .smm-node-note {
pointer-events: all;
}
`
if (this.config.openBlankMode) {
cssText += `
/* 带下划线的文本内容全部隐藏 */
.smm-richtext-node-wrap u {
opacity: 0;
}
`
}
this.tmpStyleEl.innerText = cssText
document.head.appendChild(this.tmpStyleEl)
}
// 移除临时的样式
removeTmpStyles() {
if (this.tmpStyleEl) document.head.removeChild(this.tmpStyleEl)
}
// 创建高亮元素
createHighlightEl() {
if (!this.highlightEl) {
// 遮罩元素
this.maskEl = document.createElement('div')
this.maskEl.style.cssText = `
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 100%;
z-index: ${this.config.zIndex};
`
this.mindMap.el.appendChild(this.maskEl)
// 高亮元素
this.highlightEl = document.createElement('div')
this.highlightEl.style.cssText = `
@@ -108,6 +145,7 @@ class Demonstrate {
border-radius: ${this.config.borderRadius};
transition: ${this.config.transition};
z-index: ${this.config.zIndex + 1};
pointer-events: none;
`
this.mindMap.el.appendChild(this.highlightEl)
}
@@ -119,10 +157,6 @@ class Demonstrate {
this.mindMap.el.removeChild(this.highlightEl)
this.highlightEl = null
}
if (this.maskEl) {
this.mindMap.el.removeChild(this.maskEl)
this.maskEl = null
}
}
// 更新高亮元素的位置和大小
@@ -180,6 +214,9 @@ class Demonstrate {
} else if (e.keyCode === keyMap.Esc) {
// 退出演示
this.exit()
} else if (e.keyCode === keyMap.Enter) {
// 回车键显示隐藏的下划线文本
this.showNextUnderlineText()
}
}
@@ -198,8 +235,31 @@ class Demonstrate {
}
}
// 显示隐藏的下划线文本
showNextUnderlineText() {
if (
!this.config.openBlankMode ||
!this.currentStepNode ||
!this.currentUnderlineTextData
)
return
const { index, list, length } = this.currentUnderlineTextData
if (index >= length) return
const node = list[index]
this.currentUnderlineTextData.index++
node.node.style.opacity = 1
}
// 跳转到某一张
jump(index) {
// 移除该当前下划线元素设置的样式
if (this.currentUnderlineTextData) {
this.currentUnderlineTextData.list.forEach(item => {
item.node.style.opacity = ''
})
this.currentUnderlineTextData = null
}
this.currentStepNode = null
this.currentStepIndex = index
this.mindMap.emit(
'demonstrate_jump',
@@ -226,6 +286,16 @@ class Demonstrate {
}
// 1.聚焦到某个节点
if (step.type === 'node') {
this.currentStepNode = node
// 当前节点存在带下划线的文本内容
const uNodeList = this.config.openBlankMode ? node.group.find('u') : null
if (uNodeList && uNodeList.length > 0) {
this.currentUnderlineTextData = {
index: 0,
list: uNodeList,
length: uNodeList.length
}
}
// 适应画布大小
this.mindMap.view.fit(
() => {

View File

@@ -139,6 +139,7 @@ export default {
top: 20px;
cursor: pointer;
z-index: 10001;
pointer-events: all;
.icon {
font-size: 28px;
@@ -150,6 +151,7 @@ export default {
position: absolute;
right: 40px;
bottom: 20px;
pointer-events: all;
z-index: 10001;
display: flex;