mirror of
https://github.com/wanglin2/mind-map.git
synced 2026-02-21 10:27:44 +08:00
Feat:1.支持定义标签样式;2.新增标签显示位置的实例化选项;
This commit is contained in:
parent
ba9a6e501a
commit
f79918ec6f
@ -235,6 +235,10 @@ export const CONSTANTS = {
|
||||
DEFAULT: 'default',
|
||||
NOT_ACTIVE: 'notActive',
|
||||
ACTIVE_ONLY: 'activeOnly'
|
||||
},
|
||||
TAG_POSITION: {
|
||||
RIGHT: 'right',
|
||||
BOTTOM: 'bottom'
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -23,6 +23,8 @@ export const defaultOpt = {
|
||||
mouseScaleCenterUseMousePosition: true,
|
||||
// 最多显示几个标签
|
||||
maxTag: 5,
|
||||
// 标签显示的位置,相对于节点文本,bottom(下方)、right(右侧)
|
||||
tagPosition: CONSTANTS.TAG_POSITION.RIGHT,
|
||||
// 展开收缩按钮尺寸
|
||||
expandBtnSize: 20,
|
||||
// 节点里图片和文字的间距
|
||||
|
||||
@ -253,11 +253,15 @@ class Node {
|
||||
height: rect.height
|
||||
}
|
||||
}
|
||||
const { tagPosition } = this.mindMap.opt
|
||||
const tagIsBottom = tagPosition === CONSTANTS.TAG_POSITION.BOTTOM
|
||||
// 宽高
|
||||
let imgContentWidth = 0
|
||||
let imgContentHeight = 0
|
||||
let textContentWidth = 0
|
||||
let textContentHeight = 0
|
||||
let tagContentWidth = 0
|
||||
let tagContentHeight = 0
|
||||
// 存在图片
|
||||
if (this._imgData) {
|
||||
this._rectInfo.imgContentWidth = imgContentWidth = this._imgData.width
|
||||
@ -290,10 +294,20 @@ class Node {
|
||||
}
|
||||
// 标签
|
||||
if (this._tagData.length > 0) {
|
||||
textContentWidth += this._tagData.reduce((sum, cur) => {
|
||||
textContentHeight = Math.max(textContentHeight, cur.height)
|
||||
let maxTagHeight = 0
|
||||
const totalTagWidth = this._tagData.reduce((sum, cur) => {
|
||||
maxTagHeight = Math.max(maxTagHeight, cur.height)
|
||||
return (sum += cur.width + this.textContentItemMargin)
|
||||
}, 0)
|
||||
if (tagIsBottom) {
|
||||
// 文字下方
|
||||
tagContentWidth = totalTagWidth
|
||||
tagContentHeight = maxTagHeight
|
||||
} else {
|
||||
// 否则在右侧
|
||||
textContentWidth += totalTagWidth
|
||||
textContentHeight = Math.max(textContentHeight, maxTagHeight)
|
||||
}
|
||||
}
|
||||
// 备注
|
||||
if (this._noteData) {
|
||||
@ -325,6 +339,15 @@ class Node {
|
||||
// 纯内容宽高
|
||||
let _width = Math.max(imgContentWidth, textContentWidth)
|
||||
let _height = imgContentHeight + textContentHeight
|
||||
// 如果标签在文字下方
|
||||
if (tagIsBottom && tagContentHeight > 0 && textContentHeight > 0) {
|
||||
// 那么文字和标签之间也需要间距
|
||||
margin += this.blockContentMargin
|
||||
// 整体高度要考虑标签宽度
|
||||
_width = Math.max(_width, tagContentWidth)
|
||||
// 整体高度要加上标签的高度
|
||||
_height += tagContentHeight
|
||||
}
|
||||
// 计算节点形状需要的附加内边距
|
||||
let { paddingX: shapePaddingX, paddingY: shapePaddingY } =
|
||||
this.shapeInstance.getShapePadding(_width, _height, paddingX, paddingY)
|
||||
@ -342,7 +365,7 @@ class Node {
|
||||
layout() {
|
||||
// 清除之前的内容
|
||||
this.group.clear()
|
||||
const { hoverRectPadding } = this.mindMap.opt
|
||||
const { hoverRectPadding, tagPosition } = this.mindMap.opt
|
||||
let { width, height, textContentItemMargin } = this
|
||||
let { paddingY } = this.getPaddingVale()
|
||||
const halfBorderWidth = this.getBorderWidth() / 2
|
||||
@ -382,6 +405,8 @@ class Node {
|
||||
addHoverNode()
|
||||
return
|
||||
}
|
||||
const tagIsBottom = tagPosition === CONSTANTS.TAG_POSITION.BOTTOM
|
||||
const { textContentHeight } = this._rectInfo
|
||||
// 图片节点
|
||||
let imgHeight = 0
|
||||
if (this._imgData) {
|
||||
@ -401,7 +426,7 @@ class Node {
|
||||
})
|
||||
foreignObject
|
||||
.x(textContentOffsetX)
|
||||
.y((this._rectInfo.textContentHeight - this._prefixData.height) / 2)
|
||||
.y((textContentHeight - this._prefixData.height) / 2)
|
||||
textContentNested.add(foreignObject)
|
||||
textContentOffsetX += this._prefixData.width + textContentItemMargin
|
||||
}
|
||||
@ -412,7 +437,7 @@ class Node {
|
||||
this._iconData.forEach(item => {
|
||||
item.node
|
||||
.x(textContentOffsetX + iconLeft)
|
||||
.y((this._rectInfo.textContentHeight - item.height) / 2)
|
||||
.y((textContentHeight - item.height) / 2)
|
||||
iconNested.add(item.node)
|
||||
iconLeft += item.width + textContentItemMargin
|
||||
})
|
||||
@ -427,7 +452,7 @@ class Node {
|
||||
;(this._textData.nodeContent || this._textData.node)
|
||||
.x(-oldX) // 修复非富文本模式下同时存在图标和换行的文本时,被收起和展开时图标与文字距离会逐渐拉大的问题
|
||||
.x(textContentOffsetX)
|
||||
.y((this._rectInfo.textContentHeight - this._textData.height) / 2)
|
||||
.y((textContentHeight - this._textData.height) / 2)
|
||||
textContentNested.add(this._textData.node)
|
||||
textContentOffsetX += this._textData.width + textContentItemMargin
|
||||
}
|
||||
@ -435,29 +460,50 @@ class Node {
|
||||
if (this._hyperlinkData) {
|
||||
this._hyperlinkData.node
|
||||
.x(textContentOffsetX)
|
||||
.y((this._rectInfo.textContentHeight - this._hyperlinkData.height) / 2)
|
||||
.y((textContentHeight - this._hyperlinkData.height) / 2)
|
||||
textContentNested.add(this._hyperlinkData.node)
|
||||
textContentOffsetX += this._hyperlinkData.width + textContentItemMargin
|
||||
}
|
||||
// 标签
|
||||
let tagNested = new G()
|
||||
if (this._tagData && this._tagData.length > 0) {
|
||||
let tagLeft = 0
|
||||
this._tagData.forEach(item => {
|
||||
item.node
|
||||
.x(textContentOffsetX + tagLeft)
|
||||
.y((this._rectInfo.textContentHeight - item.height) / 2)
|
||||
tagNested.add(item.node)
|
||||
tagLeft += item.width + textContentItemMargin
|
||||
})
|
||||
textContentNested.add(tagNested)
|
||||
textContentOffsetX += tagLeft
|
||||
if (tagIsBottom) {
|
||||
// 标签显示在文字下方
|
||||
let tagLeft = 0
|
||||
this._tagData.forEach(item => {
|
||||
item.node.x(tagLeft).y(0)
|
||||
tagNested.add(item.node)
|
||||
tagLeft += item.width + textContentItemMargin
|
||||
})
|
||||
tagNested.cx(width / 2).y(
|
||||
paddingY + // 内边距
|
||||
imgHeight + // 图片高度
|
||||
textContentHeight + // 文本区域高度
|
||||
(imgHeight > 0 && textContentHeight > 0
|
||||
? this.blockContentMargin
|
||||
: 0) + // 图片和文本之间的间距
|
||||
this.blockContentMargin // 标签和文本之间的间距
|
||||
)
|
||||
this.group.add(tagNested)
|
||||
} else {
|
||||
// 标签显示在文字右侧
|
||||
let tagLeft = 0
|
||||
this._tagData.forEach(item => {
|
||||
item.node
|
||||
.x(textContentOffsetX + tagLeft)
|
||||
.y((textContentHeight - item.height) / 2)
|
||||
tagNested.add(item.node)
|
||||
tagLeft += item.width + textContentItemMargin
|
||||
})
|
||||
textContentNested.add(tagNested)
|
||||
textContentOffsetX += tagLeft
|
||||
}
|
||||
}
|
||||
// 备注
|
||||
if (this._noteData) {
|
||||
this._noteData.node
|
||||
.x(textContentOffsetX)
|
||||
.y((this._rectInfo.textContentHeight - this._noteData.height) / 2)
|
||||
.y((textContentHeight - this._noteData.height) / 2)
|
||||
textContentNested.add(this._noteData.node)
|
||||
textContentOffsetX += this._noteData.width
|
||||
}
|
||||
@ -465,7 +511,7 @@ class Node {
|
||||
if (this._attachmentData) {
|
||||
this._attachmentData.node
|
||||
.x(textContentOffsetX)
|
||||
.y((this._rectInfo.textContentHeight - this._attachmentData.height) / 2)
|
||||
.y((textContentHeight - this._attachmentData.height) / 2)
|
||||
textContentNested.add(this._attachmentData.node)
|
||||
textContentOffsetX += this._attachmentData.width
|
||||
}
|
||||
@ -478,18 +524,16 @@ class Node {
|
||||
})
|
||||
foreignObject
|
||||
.x(textContentOffsetX)
|
||||
.y((this._rectInfo.textContentHeight - this._postfixData.height) / 2)
|
||||
.y((textContentHeight - this._postfixData.height) / 2)
|
||||
textContentNested.add(foreignObject)
|
||||
textContentOffsetX += this._postfixData.width
|
||||
}
|
||||
// 文字内容整体
|
||||
textContentNested.translate(
|
||||
width / 2 - textContentNested.bbox().width / 2,
|
||||
imgHeight +
|
||||
paddingY +
|
||||
(imgHeight > 0 && this._rectInfo.textContentHeight > 0
|
||||
? this.blockContentMargin
|
||||
: 0)
|
||||
paddingY + // 内边距
|
||||
imgHeight + // 图片高度
|
||||
(imgHeight > 0 && textContentHeight > 0 ? this.blockContentMargin : 0) // 和图片的间距
|
||||
)
|
||||
this.group.add(textContentNested)
|
||||
addHoverNode()
|
||||
|
||||
@ -1,8 +1,4 @@
|
||||
import {
|
||||
checkIsNodeStyleDataKey,
|
||||
generateColorByContent
|
||||
} from '../../../utils/index'
|
||||
import { Gradient } from '@svgdotjs/svg.js'
|
||||
import { checkIsNodeStyleDataKey } from '../../../utils/index'
|
||||
|
||||
const rootProp = ['paddingX', 'paddingY']
|
||||
const backgroundStyleProps = [
|
||||
@ -182,21 +178,24 @@ class Style {
|
||||
}
|
||||
|
||||
// 标签文字
|
||||
tagText(node) {
|
||||
tagText(node, style) {
|
||||
node
|
||||
.fill({
|
||||
color: '#fff'
|
||||
})
|
||||
.css({
|
||||
'font-size': '12px'
|
||||
'font-size': style.fontSize + 'px'
|
||||
})
|
||||
}
|
||||
|
||||
// 标签矩形
|
||||
tagRect(node, text, color) {
|
||||
tagRect(node, style) {
|
||||
node.fill({
|
||||
color: color || generateColorByContent(text.node.textContent)
|
||||
color: style.fill
|
||||
})
|
||||
if (style.radius) {
|
||||
node.radius(style.radius)
|
||||
}
|
||||
}
|
||||
|
||||
// 内置图标
|
||||
|
||||
@ -6,12 +6,23 @@ import {
|
||||
checkIsRichText,
|
||||
isUndef,
|
||||
createForeignObjectNode,
|
||||
addXmlns
|
||||
addXmlns,
|
||||
generateColorByContent
|
||||
} from '../../../utils'
|
||||
import { Image as SVGImage, SVG, A, G, Rect, Text } from '@svgdotjs/svg.js'
|
||||
import iconsSvg from '../../../svg/icons'
|
||||
import { CONSTANTS } from '../../../constants/constant'
|
||||
|
||||
// 标签默认的样式
|
||||
const defaultTagStyle = {
|
||||
radius: 3, // 标签矩形的圆角大小
|
||||
fontSize: 12, // 字号,建议文字高度不要大于height
|
||||
fill: '', // 标签矩形的背景颜色
|
||||
height: 20, // 标签矩形的高度
|
||||
paddingX: 8 // 水平内边距,如果设置了width,将忽略该配置
|
||||
//width: 30 // 标签矩形的宽度,如果不设置,默认以文字的宽度+paddingX*2为宽度
|
||||
}
|
||||
|
||||
// 创建图片节点
|
||||
function createImgNode() {
|
||||
const img = this.getData('image')
|
||||
@ -284,31 +295,69 @@ function createHyperlinkNode() {
|
||||
|
||||
// 创建标签节点
|
||||
function createTagNode() {
|
||||
let tagData = this.getData('tag')
|
||||
const tagData = this.getData('tag')
|
||||
if (!tagData || tagData.length <= 0) {
|
||||
return []
|
||||
}
|
||||
let nodes = []
|
||||
tagData.slice(0, this.mindMap.opt.maxTag).forEach((item, index) => {
|
||||
let tag = new G()
|
||||
let { maxTag, tagsColorMap } = this.mindMap.opt
|
||||
tagsColorMap = tagsColorMap || {}
|
||||
const nodes = []
|
||||
tagData.slice(0, maxTag).forEach(item => {
|
||||
let str = ''
|
||||
let style = {
|
||||
...defaultTagStyle
|
||||
}
|
||||
// 旧版只支持字符串类型
|
||||
if (typeof item === 'string') {
|
||||
str = item
|
||||
} else {
|
||||
// v0.10.3+版本支持对象类型
|
||||
str = item.text
|
||||
style = { ...defaultTagStyle, ...item.style }
|
||||
}
|
||||
// 是否手动设置了标签宽度
|
||||
const hasCustomWidth = typeof style.width !== 'undefined'
|
||||
// 创建容器节点
|
||||
const tag = new G()
|
||||
tag.on('click', () => {
|
||||
this.mindMap.emit('node_tag_click', this, item)
|
||||
})
|
||||
// 标签文本
|
||||
let text = new Text().text(item).x(8).cy(8)
|
||||
this.style.tagText(text, index)
|
||||
let { width } = text.bbox()
|
||||
const text = new Text().text(str)
|
||||
this.style.tagText(text, style)
|
||||
// 获取文本宽高
|
||||
const { width: textWidth, height: textHeight } = text.bbox()
|
||||
// 矩形宽度
|
||||
const rectWidth = hasCustomWidth
|
||||
? style.width
|
||||
: textWidth + style.paddingX * 2
|
||||
// 取文本和矩形最大宽高作为标签宽高
|
||||
const maxWidth = hasCustomWidth ? Math.max(rectWidth, textWidth) : rectWidth
|
||||
const maxHeight = Math.max(style.height, textHeight)
|
||||
// 文本居中
|
||||
if (hasCustomWidth) {
|
||||
text.x((maxWidth - textWidth) / 2)
|
||||
} else {
|
||||
text.x(hasCustomWidth ? 0 : style.paddingX)
|
||||
}
|
||||
text.cy(-maxHeight / 2)
|
||||
// 标签矩形
|
||||
let rect = new Rect().size(width + 16, 20)
|
||||
// 先从自定义的颜色中获取颜色,没有的话就按照内容生成
|
||||
const tagsColorList = this.mindMap.opt.tagsColorMap || {}
|
||||
const color = tagsColorList[text.node.textContent]
|
||||
this.style.tagRect(rect, text, color)
|
||||
const rect = new Rect().size(rectWidth, style.height).cy(-maxHeight / 2)
|
||||
if (hasCustomWidth) {
|
||||
rect.x((maxWidth - rectWidth) / 2)
|
||||
}
|
||||
this.style.tagRect(rect, {
|
||||
...style,
|
||||
fill:
|
||||
style.fill || // 优先节点自身配置
|
||||
tagsColorMap[text.node.textContent] || // 否则尝试从实例化选项tagsColorMap映射中获取颜色
|
||||
generateColorByContent(text.node.textContent) // 否则按照标签内容生成
|
||||
})
|
||||
tag.add(rect).add(text)
|
||||
nodes.push({
|
||||
node: tag,
|
||||
width: width + 16,
|
||||
height: 20
|
||||
width: maxWidth,
|
||||
height: maxHeight
|
||||
})
|
||||
})
|
||||
return nodes
|
||||
|
||||
Loading…
Reference in New Issue
Block a user