Feat:新增开启节点文本编辑实时更新节点大小和位置的实例化选项
This commit is contained in:
parent
4e327c3a48
commit
570bbb1b16
@ -238,6 +238,10 @@ export const defaultOpt = {
|
|||||||
padding: 100, // 超出画布四周指定范围内依旧渲染节点
|
padding: 100, // 超出画布四周指定范围内依旧渲染节点
|
||||||
removeNodeWhenOutCanvas: true // 节点移除画布可视区域后从画布删除
|
removeNodeWhenOutCanvas: true // 节点移除画布可视区域后从画布删除
|
||||||
},
|
},
|
||||||
|
// 如果节点文本为空,那么为了避免空白节点高度塌陷,会用该字段指定的文本测量一个高度
|
||||||
|
emptyTextMeasureHeightText: 'abc123我和你',
|
||||||
|
// 是否在进行节点文本编辑时实时更新节点大小和节点位置,开启后当节点数量比较多时可能会造成卡顿
|
||||||
|
openRealtimeRenderOnNodeTextEdit: false,
|
||||||
|
|
||||||
// 【Select插件】
|
// 【Select插件】
|
||||||
// 多选节点时鼠标移动到边缘时的画布移动偏移量
|
// 多选节点时鼠标移动到边缘时的画布移动偏移量
|
||||||
|
|||||||
@ -147,6 +147,19 @@ class Render {
|
|||||||
})
|
})
|
||||||
// 性能模式
|
// 性能模式
|
||||||
this.performanceMode()
|
this.performanceMode()
|
||||||
|
// 实时渲染当节点文本编辑时
|
||||||
|
if (this.mindMap.opt.openRealtimeRenderOnNodeTextEdit) {
|
||||||
|
this.mindMap.on('node_text_edit_change', ({ node, text }) => {
|
||||||
|
node._textData = node.createTextNode(text)
|
||||||
|
const { width, height } = node.getNodeRect()
|
||||||
|
node.width = width
|
||||||
|
node.height = height
|
||||||
|
node.layout()
|
||||||
|
this.mindMap.render(() => {
|
||||||
|
this.textEdit.updateTextEditNode()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 性能模式,懒加载节点
|
// 性能模式,懒加载节点
|
||||||
|
|||||||
@ -25,6 +25,8 @@ export default class TextEdit {
|
|||||||
// 如果编辑过程中缩放画布了,那么缓存当前编辑的内容
|
// 如果编辑过程中缩放画布了,那么缓存当前编辑的内容
|
||||||
this.cacheEditingText = ''
|
this.cacheEditingText = ''
|
||||||
this.hasBodyMousedown = false
|
this.hasBodyMousedown = false
|
||||||
|
this.textNodePaddingX = 5
|
||||||
|
this.textNodePaddingY = 3
|
||||||
this.bindEvent()
|
this.bindEvent()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -214,7 +216,7 @@ export default class TextEdit {
|
|||||||
this.registerTmpShortcut()
|
this.registerTmpShortcut()
|
||||||
if (!this.textEditNode) {
|
if (!this.textEditNode) {
|
||||||
this.textEditNode = document.createElement('div')
|
this.textEditNode = document.createElement('div')
|
||||||
this.textEditNode.style.cssText = `position:fixed;box-sizing: border-box;background-color:#fff;box-shadow: 0 0 20px rgba(0,0,0,.5);padding: 3px 5px;margin-left: -5px;margin-top: -3px;outline: none; word-break: break-all;`
|
this.textEditNode.style.cssText = `position:fixed;box-sizing: border-box;background-color:#fff;box-shadow: 0 0 20px rgba(0,0,0,.5);padding: ${this.textNodePaddingY}px ${this.textNodePaddingX}px;margin-left: -5px;margin-top: -3px;outline: none; word-break: break-all;`
|
||||||
this.textEditNode.setAttribute('contenteditable', true)
|
this.textEditNode.setAttribute('contenteditable', true)
|
||||||
this.textEditNode.addEventListener('keyup', e => {
|
this.textEditNode.addEventListener('keyup', e => {
|
||||||
e.stopPropagation()
|
e.stopPropagation()
|
||||||
@ -240,6 +242,13 @@ export default class TextEdit {
|
|||||||
handleInputPasteText(e)
|
handleInputPasteText(e)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
this.textEditNode.addEventListener('input', () => {
|
||||||
|
this.mindMap.emit('node_text_edit_change', {
|
||||||
|
node: this.currentNode,
|
||||||
|
text: this.getEditText(),
|
||||||
|
richText: false
|
||||||
|
})
|
||||||
|
})
|
||||||
const targetNode =
|
const targetNode =
|
||||||
this.mindMap.opt.customInnerElsAppendTo || document.body
|
this.mindMap.opt.customInnerElsAppendTo || document.body
|
||||||
targetNode.appendChild(this.textEditNode)
|
targetNode.appendChild(this.textEditNode)
|
||||||
@ -256,8 +265,10 @@ export default class TextEdit {
|
|||||||
node.style.domText(this.textEditNode, scale, isMultiLine)
|
node.style.domText(this.textEditNode, scale, isMultiLine)
|
||||||
this.textEditNode.style.zIndex = nodeTextEditZIndex
|
this.textEditNode.style.zIndex = nodeTextEditZIndex
|
||||||
this.textEditNode.innerHTML = textLines.join('<br>')
|
this.textEditNode.innerHTML = textLines.join('<br>')
|
||||||
this.textEditNode.style.minWidth = rect.width + 10 + 'px'
|
this.textEditNode.style.minWidth =
|
||||||
this.textEditNode.style.minHeight = rect.height + 6 + 'px'
|
rect.width + this.textNodePaddingX * 2 + 'px'
|
||||||
|
this.textEditNode.style.minHeight =
|
||||||
|
rect.height + this.textNodePaddingY * 2 + 'px'
|
||||||
this.textEditNode.style.left = rect.left + 'px'
|
this.textEditNode.style.left = rect.left + 'px'
|
||||||
this.textEditNode.style.top = rect.top + 'px'
|
this.textEditNode.style.top = rect.top + 'px'
|
||||||
this.textEditNode.style.display = 'block'
|
this.textEditNode.style.display = 'block'
|
||||||
@ -280,6 +291,24 @@ export default class TextEdit {
|
|||||||
this.cacheEditingText = ''
|
this.cacheEditingText = ''
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 更新文本编辑框的大小和位置
|
||||||
|
updateTextEditNode() {
|
||||||
|
if (this.mindMap.richText) {
|
||||||
|
this.mindMap.richText.updateTextEditNode()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (!this.showTextEdit || !this.currentNode) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const rect = this.currentNode._textData.node.node.getBoundingClientRect()
|
||||||
|
this.textEditNode.style.minWidth =
|
||||||
|
rect.width + this.textNodePaddingX * 2 + 'px'
|
||||||
|
this.textEditNode.style.minHeight =
|
||||||
|
rect.height + this.textNodePaddingY * 2 + 'px'
|
||||||
|
this.textEditNode.style.left = rect.left + 'px'
|
||||||
|
this.textEditNode.style.top = rect.top + 'px'
|
||||||
|
}
|
||||||
|
|
||||||
// 删除文本编辑元素
|
// 删除文本编辑元素
|
||||||
removeTextEditEl() {
|
removeTextEditEl() {
|
||||||
if (this.mindMap.richText) {
|
if (this.mindMap.richText) {
|
||||||
|
|||||||
@ -114,8 +114,10 @@ function createIconNode() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 创建富文本节点
|
// 创建富文本节点
|
||||||
function createRichTextNode() {
|
function createRichTextNode(specifyText) {
|
||||||
const { textAutoWrapWidth } = this.mindMap.opt
|
let text =
|
||||||
|
typeof specifyText === 'string' ? specifyText : this.getData('text')
|
||||||
|
const { textAutoWrapWidth, emptyTextMeasureHeightText } = this.mindMap.opt
|
||||||
let g = new G()
|
let g = new G()
|
||||||
// 重新设置富文本节点内容
|
// 重新设置富文本节点内容
|
||||||
let recoverText = false
|
let recoverText = false
|
||||||
@ -129,7 +131,6 @@ function createRichTextNode() {
|
|||||||
recoverText = true
|
recoverText = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let text = this.getData('text')
|
|
||||||
if (recoverText && !isUndef(text)) {
|
if (recoverText && !isUndef(text)) {
|
||||||
// 判断节点内容是否是富文本
|
// 判断节点内容是否是富文本
|
||||||
let isRichText = checkIsRichText(text)
|
let isRichText = checkIsRichText(text)
|
||||||
@ -153,7 +154,7 @@ function createRichTextNode() {
|
|||||||
text: text
|
text: text
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
let html = `<div>${this.getData('text')}</div>`
|
let html = `<div>${text}</div>`
|
||||||
if (!this.mindMap.commonCaches.measureRichtextNodeTextSizeEl) {
|
if (!this.mindMap.commonCaches.measureRichtextNodeTextSizeEl) {
|
||||||
this.mindMap.commonCaches.measureRichtextNodeTextSizeEl =
|
this.mindMap.commonCaches.measureRichtextNodeTextSizeEl =
|
||||||
document.createElement('div')
|
document.createElement('div')
|
||||||
@ -174,7 +175,7 @@ function createRichTextNode() {
|
|||||||
let { width, height } = el.getBoundingClientRect()
|
let { width, height } = el.getBoundingClientRect()
|
||||||
// 如果文本为空,那么需要计算一个默认高度
|
// 如果文本为空,那么需要计算一个默认高度
|
||||||
if (height <= 0) {
|
if (height <= 0) {
|
||||||
div.innerHTML = '<p>abc123我和你</p>'
|
div.innerHTML = `<p>${emptyTextMeasureHeightText}</p>`
|
||||||
let elTmp = div.children[0]
|
let elTmp = div.children[0]
|
||||||
elTmp.classList.add('smm-richtext-node-wrap')
|
elTmp.classList.add('smm-richtext-node-wrap')
|
||||||
height = elTmp.getBoundingClientRect().height
|
height = elTmp.getBoundingClientRect().height
|
||||||
@ -199,10 +200,12 @@ function createRichTextNode() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 创建文本节点
|
// 创建文本节点
|
||||||
function createTextNode() {
|
function createTextNode(specifyText) {
|
||||||
if (this.getData('richText')) {
|
if (this.getData('richText')) {
|
||||||
return this.createRichTextNode()
|
return this.createRichTextNode(specifyText)
|
||||||
}
|
}
|
||||||
|
const text =
|
||||||
|
typeof specifyText === 'string' ? specifyText : this.getData('text')
|
||||||
if (this.getData('resetRichText')) {
|
if (this.getData('resetRichText')) {
|
||||||
delete this.nodeData.data.resetRichText
|
delete this.nodeData.data.resetRichText
|
||||||
}
|
}
|
||||||
@ -212,10 +215,11 @@ function createTextNode() {
|
|||||||
// 文本超长自动换行
|
// 文本超长自动换行
|
||||||
let textStyle = this.style.getTextFontStyle()
|
let textStyle = this.style.getTextFontStyle()
|
||||||
let textArr = []
|
let textArr = []
|
||||||
if (!isUndef(this.getData('text'))) {
|
if (!isUndef(text)) {
|
||||||
textArr = String(this.getData('text')).split(/\n/gim)
|
textArr = String(text).split(/\n/gim)
|
||||||
}
|
}
|
||||||
let maxWidth = this.mindMap.opt.textAutoWrapWidth
|
const { textAutoWrapWidth: maxWidth, emptyTextMeasureHeightText } =
|
||||||
|
this.mindMap.opt
|
||||||
let isMultiLine = false
|
let isMultiLine = false
|
||||||
textArr.forEach((item, index) => {
|
textArr.forEach((item, index) => {
|
||||||
let arr = item.split('')
|
let arr = item.split('')
|
||||||
@ -247,6 +251,13 @@ function createTextNode() {
|
|||||||
g.add(node)
|
g.add(node)
|
||||||
})
|
})
|
||||||
let { width, height } = g.bbox()
|
let { width, height } = g.bbox()
|
||||||
|
// 如果文本为空,那么需要计算一个默认高度
|
||||||
|
if (height <= 0) {
|
||||||
|
const tmpNode = new Text().text(emptyTextMeasureHeightText)
|
||||||
|
this.style.text(tmpNode)
|
||||||
|
const tmpBbox = tmpNode.bbox()
|
||||||
|
height = tmpBbox.height
|
||||||
|
}
|
||||||
width = Math.min(Math.ceil(width), maxWidth)
|
width = Math.min(Math.ceil(width), maxWidth)
|
||||||
height = Math.ceil(height)
|
height = Math.ceil(height)
|
||||||
g.attr('data-width', width)
|
g.attr('data-width', width)
|
||||||
|
|||||||
@ -57,6 +57,8 @@ class RichText {
|
|||||||
this.cacheEditingText = ''
|
this.cacheEditingText = ''
|
||||||
this.lostStyle = false
|
this.lostStyle = false
|
||||||
this.isCompositing = false
|
this.isCompositing = false
|
||||||
|
this.textNodePaddingX = 6
|
||||||
|
this.textNodePaddingY = 4
|
||||||
this.initOpt()
|
this.initOpt()
|
||||||
this.extendQuill()
|
this.extendQuill()
|
||||||
this.appendCss()
|
this.appendCss()
|
||||||
@ -71,14 +73,17 @@ class RichText {
|
|||||||
// 绑定事件
|
// 绑定事件
|
||||||
bindEvent() {
|
bindEvent() {
|
||||||
this.onCompositionStart = this.onCompositionStart.bind(this)
|
this.onCompositionStart = this.onCompositionStart.bind(this)
|
||||||
|
this.onCompositionUpdate = this.onCompositionUpdate.bind(this)
|
||||||
this.onCompositionEnd = this.onCompositionEnd.bind(this)
|
this.onCompositionEnd = this.onCompositionEnd.bind(this)
|
||||||
window.addEventListener('compositionstart', this.onCompositionStart)
|
window.addEventListener('compositionstart', this.onCompositionStart)
|
||||||
|
window.addEventListener('compositionupdate', this.onCompositionUpdate)
|
||||||
window.addEventListener('compositionend', this.onCompositionEnd)
|
window.addEventListener('compositionend', this.onCompositionEnd)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 解绑事件
|
// 解绑事件
|
||||||
unbindEvent() {
|
unbindEvent() {
|
||||||
window.removeEventListener('compositionstart', this.onCompositionStart)
|
window.removeEventListener('compositionstart', this.onCompositionStart)
|
||||||
|
window.removeEventListener('compositionupdate', this.onCompositionUpdate)
|
||||||
window.removeEventListener('compositionend', this.onCompositionEnd)
|
window.removeEventListener('compositionend', this.onCompositionEnd)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -198,8 +203,8 @@ class RichText {
|
|||||||
let scaleX = rect.width / originWidth
|
let scaleX = rect.width / originWidth
|
||||||
let scaleY = rect.height / originHeight
|
let scaleY = rect.height / originHeight
|
||||||
// 内边距
|
// 内边距
|
||||||
let paddingX = 6
|
let paddingX = this.textNodePaddingX
|
||||||
let paddingY = 4
|
let paddingY = this.textNodePaddingY
|
||||||
if (richTextEditFakeInPlace) {
|
if (richTextEditFakeInPlace) {
|
||||||
let paddingValue = node.getPaddingVale()
|
let paddingValue = node.getPaddingVale()
|
||||||
paddingX = paddingValue.paddingX
|
paddingX = paddingValue.paddingX
|
||||||
@ -287,6 +292,20 @@ class RichText {
|
|||||||
this.cacheEditingText = ''
|
this.cacheEditingText = ''
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 更新文本编辑框的大小和位置
|
||||||
|
updateTextEditNode() {
|
||||||
|
if (!this.node) return
|
||||||
|
const rect = this.node._textData.node.node.getBoundingClientRect()
|
||||||
|
const g = this.node._textData.node
|
||||||
|
const originWidth = g.attr('data-width')
|
||||||
|
const originHeight = g.attr('data-height')
|
||||||
|
this.textEditNode.style.minWidth =
|
||||||
|
originWidth + this.textNodePaddingX * 2 + 'px'
|
||||||
|
this.textEditNode.style.minHeight = originHeight + 'px'
|
||||||
|
this.textEditNode.style.left = rect.left + 'px'
|
||||||
|
this.textEditNode.style.top = rect.top + 'px'
|
||||||
|
}
|
||||||
|
|
||||||
// 删除文本编辑框元素
|
// 删除文本编辑框元素
|
||||||
removeTextEditEl() {
|
removeTextEditEl() {
|
||||||
if (!this.textEditNode) return
|
if (!this.textEditNode) return
|
||||||
@ -491,6 +510,11 @@ class RichText {
|
|||||||
this.setTextStyleIfNotRichText(this.node)
|
this.setTextStyleIfNotRichText(this.node)
|
||||||
this.lostStyle = false
|
this.lostStyle = false
|
||||||
}
|
}
|
||||||
|
this.mindMap.emit('node_text_edit_change', {
|
||||||
|
node: this.node,
|
||||||
|
text: this.getEditText(),
|
||||||
|
richText: true
|
||||||
|
})
|
||||||
})
|
})
|
||||||
// 拦截粘贴,只允许粘贴纯文本
|
// 拦截粘贴,只允许粘贴纯文本
|
||||||
// this.quill.clipboard.addMatcher(Node.TEXT_NODE, node => {
|
// this.quill.clipboard.addMatcher(Node.TEXT_NODE, node => {
|
||||||
@ -545,6 +569,16 @@ class RichText {
|
|||||||
this.isCompositing = true
|
this.isCompositing = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 中文输入中
|
||||||
|
onCompositionUpdate() {
|
||||||
|
if (!this.showTextEdit || !this.node) return
|
||||||
|
this.mindMap.emit('node_text_edit_change', {
|
||||||
|
node: this.node,
|
||||||
|
text: this.getEditText(),
|
||||||
|
richText: true
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// 中文输入结束
|
// 中文输入结束
|
||||||
onCompositionEnd() {
|
onCompositionEnd() {
|
||||||
if (!this.showTextEdit) {
|
if (!this.showTextEdit) {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user