diff --git a/simple-mind-map/src/constants/defaultOptions.js b/simple-mind-map/src/constants/defaultOptions.js index cf47a42d..0d339e82 100644 --- a/simple-mind-map/src/constants/defaultOptions.js +++ b/simple-mind-map/src/constants/defaultOptions.js @@ -265,5 +265,15 @@ export const defaultOpt = { to: '' // 关联线目标节点上端点的位置 }, // 是否允许调整关联线两个端点的位置 - enableAdjustAssociativeLinePoints: true + enableAdjustAssociativeLinePoints: true, + // 自定义创建节点形状的方法,可以传一个函数,均接收一个参数 + // 矩形、圆角矩形、椭圆、圆等形状会调用该方法 + // 接收svg path字符串,返回svg节点 + customCreateNodePath: null, + // 菱形、平行四边形、八角矩形、外三角矩形、内三角矩形等形状会调用该方法 + // 接收points数组点位,返回svg节点 + customCreateNodePolygon: null, + // 自定义转换节点连线路径的方法 + // 接收svg path字符串,返回转换后的svg path字符串 + customTransformNodeLinePath: null } diff --git a/simple-mind-map/src/core/render/node/Shape.js b/simple-mind-map/src/core/render/node/Shape.js index 380a8447..85e52517 100644 --- a/simple-mind-map/src/core/render/node/Shape.js +++ b/simple-mind-map/src/core/render/node/Shape.js @@ -1,10 +1,11 @@ -import { Rect, Polygon, Path } from '@svgdotjs/svg.js' +import { Polygon, Path, SVG } from '@svgdotjs/svg.js' import { CONSTANTS } from '../../../constants/constant' // 节点形状类 export default class Shape { constructor(node) { this.node = node + this.mindMap = node.mindMap } // 形状需要的padding @@ -106,11 +107,29 @@ export default class Shape { } } + // 创建路径节点 + createPath(pathStr) { + const { customCreateNodePath } = this.mindMap.opt + if (customCreateNodePath) { + return SVG(customCreateNodePath(pathStr)) + } + return new Path().plot(pathStr) + } + + // 创建多边形节点 + createPolygon(points) { + const { customCreateNodePolygon } = this.mindMap.opt + if (customCreateNodePolygon) { + return SVG(customCreateNodePolygon(points)) + } + return new Polygon().plot(points) + } + // 创建矩形 createRect() { let { width, height } = this.getNodeSize() let borderRadius = this.node.style.merge('borderRadius') - return new Path().plot(` + const pathStr = ` M${borderRadius},0 L${width - borderRadius},0 C${width - borderRadius},0 ${width},${0} ${width},${borderRadius} @@ -123,7 +142,8 @@ export default class Shape { L${0},${borderRadius} C${0},${borderRadius} ${0},${0} ${borderRadius},${0} Z - `) + ` + return this.createPath(pathStr) } // 创建菱形 @@ -139,12 +159,13 @@ export default class Shape { let bottomY = height let leftX = 0 let leftY = halfHeight - return new Polygon().plot([ + const points = [ [topX, topY], [rightX, rightY], [bottomX, bottomY], [leftX, leftY] - ]) + ] + return this.createPolygon(points) } // 创建平行四边形 @@ -152,32 +173,34 @@ export default class Shape { let { paddingX } = this.node.getPaddingVale() paddingX = paddingX || this.node.shapePadding.paddingX let { width, height } = this.getNodeSize() - return new Polygon().plot([ + const points = [ [paddingX, 0], [width, 0], [width - paddingX, height], [0, height] - ]) + ] + return this.createPolygon(points) } // 创建圆角矩形 createRoundedRectangle() { let { width, height } = this.getNodeSize() let halfHeight = height / 2 - return new Path().plot(` + const pathStr = ` M${halfHeight},0 L${width - halfHeight},0 A${height / 2},${height / 2} 0 0,1 ${width - halfHeight},${height} L${halfHeight},${height} A${height / 2},${height / 2} 0 0,1 ${halfHeight},${0} - `) + ` + return this.createPath(pathStr) } // 创建八角矩形 createOctagonalRectangle() { let w = 5 let { width, height } = this.getNodeSize() - return new Polygon().plot([ + const points = [ [0, w], [w, 0], [width - w, 0], @@ -186,7 +209,8 @@ export default class Shape { [width - w, height], [w, height], [0, height - w] - ]) + ] + return this.createPolygon(points) } // 创建外三角矩形 @@ -194,14 +218,15 @@ export default class Shape { let { paddingX } = this.node.getPaddingVale() paddingX = paddingX || this.node.shapePadding.paddingX let { width, height } = this.getNodeSize() - return new Polygon().plot([ + const points = [ [paddingX, 0], [width - paddingX, 0], [width, height / 2], [width - paddingX, height], [paddingX, height], [0, height / 2] - ]) + ] + return this.createPolygon(points) } // 创建内三角矩形 @@ -209,14 +234,15 @@ export default class Shape { let { paddingX } = this.node.getPaddingVale() paddingX = paddingX || this.node.shapePadding.paddingX let { width, height } = this.getNodeSize() - return new Polygon().plot([ + const points = [ [0, 0], [width, 0], [width - paddingX / 2, height / 2], [width, height], [0, height], [paddingX / 2, height / 2] - ]) + ] + return this.createPolygon(points) } // 创建椭圆 @@ -224,12 +250,13 @@ export default class Shape { let { width, height } = this.getNodeSize() let halfWidth = width / 2 let halfHeight = height / 2 - return new Path().plot(` + const pathStr = ` M${halfWidth},0 A${halfWidth},${halfHeight} 0 0,1 ${halfWidth},${height} M${halfWidth},${height} A${halfWidth},${halfHeight} 0 0,1 ${halfWidth},${0} - `) + ` + return this.createPath(pathStr) } // 创建圆 @@ -237,12 +264,13 @@ export default class Shape { let { width, height } = this.getNodeSize() let halfWidth = width / 2 let halfHeight = height / 2 - return new Path().plot(` + const pathStr = ` M${halfWidth},0 A${halfWidth},${halfHeight} 0 0,1 ${halfWidth},${height} M${halfWidth},${height} A${halfWidth},${halfHeight} 0 0,1 ${halfWidth},${0} - `) + ` + return this.createPath(pathStr) } } diff --git a/simple-mind-map/src/layouts/Base.js b/simple-mind-map/src/layouts/Base.js index 93d07fef..7bdfe90d 100644 --- a/simple-mind-map/src/layouts/Base.js +++ b/simple-mind-map/src/layouts/Base.js @@ -505,9 +505,19 @@ class Base { // 设置连线样式 setLineStyle(style, line, path, childNode) { - line.plot(path) + line.plot(this.transformPath(path)) style && style(line, childNode, true) } + + // 转换路径,可以转换成特殊风格的线条样式 + transformPath(path) { + const { customTransformNodeLinePath } = this.mindMap.opt + if (customTransformNodeLinePath) { + return customTransformNodeLinePath(path) + } else { + return path + } + } } export default Base diff --git a/simple-mind-map/src/layouts/CatalogOrganization.js b/simple-mind-map/src/layouts/CatalogOrganization.js index 2459a8c3..a5150232 100644 --- a/simple-mind-map/src/layouts/CatalogOrganization.js +++ b/simple-mind-map/src/layouts/CatalogOrganization.js @@ -240,14 +240,14 @@ class CatalogOrganization extends Base { // 父节点的竖线 let line1 = this.lineDraw.path() node.style.line(line1) - line1.plot(`M ${x1},${y1} L ${x1},${y1 + s1}`) + line1.plot(this.transformPath(`M ${x1},${y1} L ${x1},${y1 + s1}`)) node._lines.push(line1) style && style(line1, node) // 水平线 if (len > 0) { let lin2 = this.lineDraw.path() node.style.line(lin2) - lin2.plot(`M ${minx},${y1 + s1} L ${maxx},${y1 + s1}`) + lin2.plot(this.transformPath(`M ${minx},${y1 + s1} L ${maxx},${y1 + s1}`)) node._lines.push(lin2) style && style(lin2, node) } @@ -311,7 +311,9 @@ class CatalogOrganization extends Base { if (maxy < y1 + expandBtnSize) { lin2.hide() } else { - lin2.plot(`M ${x2},${y1 + expandBtnSize} L ${x2},${maxy}`) + lin2.plot( + this.transformPath(`M ${x2},${y1 + expandBtnSize} L ${x2},${maxy}`) + ) lin2.show() } node._lines.push(lin2) @@ -349,7 +351,7 @@ class CatalogOrganization extends Base { let cx = x1 + 20 let cy = y1 + (y2 - y1) / 2 let path = `M ${x1},${y1} Q ${cx},${cy} ${x2},${y2}` - item.generalizationLine.plot(path) + item.generalizationLine.plot(this.transformPath(path)) item.generalizationNode.left = right + generalizationNodeMargin item.generalizationNode.top = top + (bottom - top - item.generalizationNode.height) / 2 diff --git a/simple-mind-map/src/layouts/Fishbone.js b/simple-mind-map/src/layouts/Fishbone.js index 35505e13..3eb38c97 100644 --- a/simple-mind-map/src/layouts/Fishbone.js +++ b/simple-mind-map/src/layouts/Fishbone.js @@ -253,15 +253,19 @@ class Fishbone extends Base { let line = this.lineDraw.path() if (this.checkIsTop(item)) { line.plot( - `M ${nodeLineX - offsetX},${item.top + item.height + offset} L ${ - item.left - },${item.top + item.height}` + this.transformPath( + `M ${nodeLineX - offsetX},${item.top + item.height + offset} L ${ + item.left + },${item.top + item.height}` + ) ) } else { line.plot( - `M ${nodeLineX - offsetX},${item.top - offset} L ${nodeLineX},${ - item.top - }` + this.transformPath( + `M ${nodeLineX - offsetX},${item.top - offset} L ${nodeLineX},${ + item.top + }` + ) ) } node.style.line(line) @@ -273,9 +277,11 @@ class Fishbone extends Base { let offset = node.height / 2 + this.getMarginY(node.layerIndex + 1) let line = this.lineDraw.path() line.plot( - `M ${node.left + node.width},${nodeHalfTop} L ${ - maxx - offset / Math.tan(degToRad(this.mindMap.opt.fishboneDeg)) - },${nodeHalfTop}` + this.transformPath( + `M ${node.left + node.width},${nodeHalfTop} L ${ + maxx - offset / Math.tan(degToRad(this.mindMap.opt.fishboneDeg)) + },${nodeHalfTop}` + ) ) node.style.line(line) node._lines.push(line) @@ -372,7 +378,7 @@ class Fishbone extends Base { let cx = x1 + 20 let cy = y1 + (y2 - y1) / 2 let path = `M ${x1},${y1} Q ${cx},${cy} ${x2},${y2}` - item.generalizationLine.plot(path) + item.generalizationLine.plot(this.transformPath(path)) item.generalizationNode.left = right + generalizationNodeMargin item.generalizationNode.top = top + (bottom - top - item.generalizationNode.height) / 2 diff --git a/simple-mind-map/src/layouts/OrganizationStructure.js b/simple-mind-map/src/layouts/OrganizationStructure.js index 74c4d1ce..f30ef57a 100644 --- a/simple-mind-map/src/layouts/OrganizationStructure.js +++ b/simple-mind-map/src/layouts/OrganizationStructure.js @@ -213,14 +213,16 @@ class OrganizationStructure extends Base { let line1 = this.lineDraw.path() node.style.line(line1) expandBtnSize = len > 0 && !isRoot ? expandBtnSize : 0 - line1.plot(`M ${x1},${y1 + expandBtnSize} L ${x1},${y1 + s1}`) + line1.plot( + this.transformPath(`M ${x1},${y1 + expandBtnSize} L ${x1},${y1 + s1}`) + ) node._lines.push(line1) style && style(line1, node) // 水平线 if (len > 0) { let lin2 = this.lineDraw.path() node.style.line(lin2) - lin2.plot(`M ${minx},${y1 + s1} L ${maxx},${y1 + s1}`) + lin2.plot(this.transformPath(`M ${minx},${y1 + s1} L ${maxx},${y1 + s1}`)) node._lines.push(lin2) style && style(lin2, node) } @@ -253,7 +255,7 @@ class OrganizationStructure extends Base { let cx = x1 + (x2 - x1) / 2 let cy = y1 + 20 let path = `M ${x1},${y1} Q ${cx},${cy} ${x2},${y2}` - item.generalizationLine.plot(path) + item.generalizationLine.plot(this.transformPath(path)) item.generalizationNode.top = bottom + generalizationNodeMargin item.generalizationNode.left = left + (right - left - item.generalizationNode.width) / 2 diff --git a/simple-mind-map/src/layouts/Timeline.js b/simple-mind-map/src/layouts/Timeline.js index 030ef2dd..c0626a71 100644 --- a/simple-mind-map/src/layouts/Timeline.js +++ b/simple-mind-map/src/layouts/Timeline.js @@ -274,9 +274,13 @@ class Timeline extends Base { node.parent.isRoot && node.dir === CONSTANTS.LAYOUT_GROW_DIR.TOP ) { - line.plot(`M ${x},${top} L ${x},${miny}`) + line.plot(this.transformPath(`M ${x},${top} L ${x},${miny}`)) } else { - line.plot(`M ${x},${top + height + expandBtnSize} L ${x},${maxy}`) + line.plot( + this.transformPath( + `M ${x},${top + height + expandBtnSize} L ${x},${maxy}` + ) + ) } node.style.line(line) node._lines.push(line) @@ -325,9 +329,10 @@ class Timeline extends Base { let cx = x1 + 20 let cy = y1 + (y2 - y1) / 2 let path = `M ${x1},${y1} Q ${cx},${cy} ${x2},${y2}` - item.generalizationLine.plot(path) + item.generalizationLine.plot(this.transformPath(path)) item.generalizationNode.left = right + generalizationNodeMargin - item.generalizationNode.top = top + (bottom - top - item.generalizationNode.height) / 2 + item.generalizationNode.top = + top + (bottom - top - item.generalizationNode.height) / 2 }) } diff --git a/simple-mind-map/src/layouts/VerticalTimeline.js b/simple-mind-map/src/layouts/VerticalTimeline.js index c8262de6..ac10d581 100644 --- a/simple-mind-map/src/layouts/VerticalTimeline.js +++ b/simple-mind-map/src/layouts/VerticalTimeline.js @@ -398,7 +398,7 @@ class VerticalTimeline extends Base { let cx = x1 + (isLeft ? -20 : 20) let cy = y1 + (y2 - y1) / 2 let path = `M ${x1},${y1} Q ${cx},${cy} ${x2},${y2}` - item.generalizationLine.plot(path) + item.generalizationLine.plot(this.transformPath(path)) item.generalizationNode.left = x + (isLeft ? -generalizationNodeMargin : generalizationNodeMargin) - diff --git a/simple-mind-map/src/layouts/fishboneUtils.js b/simple-mind-map/src/layouts/fishboneUtils.js index f4fbf137..1c371be2 100644 --- a/simple-mind-map/src/layouts/fishboneUtils.js +++ b/simple-mind-map/src/layouts/fishboneUtils.js @@ -36,12 +36,18 @@ export default { }) { if (node.parent && node.parent.isRoot) { line.plot( - `M ${x},${top} L ${x + lineLength},${ - top - Math.tan(degToRad(ctx.mindMap.opt.fishboneDeg)) * lineLength - }` + ctx.transformPath( + `M ${x},${top} L ${x + lineLength},${ + top - Math.tan(degToRad(ctx.mindMap.opt.fishboneDeg)) * lineLength + }` + ) ) } else { - line.plot(`M ${x},${top + height + expandBtnSize} L ${x},${maxy}`) + line.plot( + ctx.transformPath( + `M ${x},${top + height + expandBtnSize} L ${x},${maxy}` + ) + ) } }, computedLeftTopValue({ layerIndex, node, ctx }) { @@ -135,14 +141,16 @@ export default { renderLine({ node, line, top, x, lineLength, height, miny, ctx }) { if (node.parent && node.parent.isRoot) { line.plot( - `M ${x},${top + height} L ${x + lineLength},${ - top + - height + - Math.tan(degToRad(ctx.mindMap.opt.fishboneDeg)) * lineLength - }` + ctx.transformPath( + `M ${x},${top + height} L ${x + lineLength},${ + top + + height + + Math.tan(degToRad(ctx.mindMap.opt.fishboneDeg)) * lineLength + }` + ) ) } else { - line.plot(`M ${x},${top} L ${x},${miny}`) + line.plot(ctx.transformPath(`M ${x},${top} L ${x},${miny}`)) } }, computedLeftTopValue({ layerIndex, node, ctx }) {