鱼骨结构开发中:完成上方鱼骨图

This commit is contained in:
wanglin2 2023-04-13 09:35:06 +08:00
parent 965ab8151e
commit 940c60f23d
2 changed files with 97 additions and 119 deletions

View File

@ -157,6 +157,19 @@ class Base {
}) })
} }
// 更新子节点多个属性
updateChildrenPro(children, props) {
children.forEach(item => {
Object.keys(props).forEach((prop) => {
item[prop] += props[prop]
})
if (item.children && item.children.length && !item.hasCustomPosition()) {
// 适配自定义位置
this.updateChildrenPro(item.children, props)
}
})
}
// 二次贝塞尔曲线 // 二次贝塞尔曲线
quadraticCurvePath(x1, y1, x2, y2) { quadraticCurvePath(x1, y1, x2, y2) {
let cx = x1 + (x2 - x1) * 0.2 let cx = x1 + (x2 - x1) * 0.2

View File

@ -2,8 +2,8 @@ import Base from './Base'
import { walk, asyncRun } from '../utils' import { walk, asyncRun } from '../utils'
import { CONSTANTS } from '../utils/constant' import { CONSTANTS } from '../utils/constant'
const degToRad = (deg) => { const degToRad = deg => {
return Math.PI / 180 * deg return (Math.PI / 180) * deg
} }
// 鱼骨图 // 鱼骨图
@ -37,33 +37,30 @@ class Fishbone extends Base {
walk( walk(
this.renderer.renderTree, this.renderer.renderTree,
null, null,
(cur, parent, isRoot, layerIndex, index) => { (node, parent, isRoot, layerIndex, index) => {
let newNode = this.createNode(cur, parent, isRoot, layerIndex) // 创建节点
let newNode = this.createNode(node, parent, isRoot, layerIndex)
// 根节点定位在画布中心位置 // 根节点定位在画布中心位置
if (isRoot) { if (isRoot) {
this.setNodeCenter(newNode) this.setNodeCenter(newNode)
} else { } else {
// 非根节点 // 非根节点
// 三级及以下节点以上级为准 // 三级及以下节点以上级方向为准
if (parent._node.dir) { if (parent._node.dir) {
newNode.dir = parent._node.dir newNode.dir = parent._node.dir
} else { } else {
// 节点生长方向 // 节点生长方向
newNode.dir = newNode.dir =
index % 2 === 0 index % 2 === 0
? CONSTANTS.TIMELINE_DIR.BOTTOM ? CONSTANTS.TIMELINE_DIR.TOP
: CONSTANTS.TIMELINE_DIR.TOP : CONSTANTS.TIMELINE_DIR.BOTTOM
} }
// 计算二级节点的top值 // 计算二级节点的top值
if (parent._node.isRoot) { if (parent._node.isRoot) {
newNode.top = newNode.top = parent._node.top - newNode.height
parent._node.top +
(cur._node.height > parent._node.height
? -(cur._node.height - parent._node.height) / 2
: (parent._node.height - cur._node.height) / 2)
} }
} }
if (!cur.data.expand) { if (!node.data.expand) {
return true return true
} }
}, },
@ -79,50 +76,27 @@ class Fishbone extends Base {
this.root, this.root,
null, null,
(node, parent, isRoot, layerIndex, index) => { (node, parent, isRoot, layerIndex, index) => {
if ( if (node.isRoot) {
node.nodeData.data.expand && let totalLeft = node.left + node.width
node.children && node.children.forEach(item => {
node.children.length item.left = totalLeft
) { totalLeft += item.width
if (isRoot) { })
let left = node.left + node.width }
let topTotalLeft = left if (layerIndex >= 1 && node.children) {
let bottomTotalLeft = left // 遍历三级及以下节点的子节点
node.children.forEach((cur) => { let startLeft = node.left + node.width * 0.5
if (cur.dir === 'top') { let totalTop =
cur.left = topTotalLeft node.top +
topTotalLeft += cur.width node.height +
} else { (this.getNodeActChildrenLength(node) > 0 ? node.expandBtnSize : 0)
cur.left = bottomTotalLeft node.children.forEach(item => {
bottomTotalLeft += cur.width item.left = startLeft
} item.top += totalTop
}) totalTop +=
} else { item.height +
if (node.dir === 'top' || node.layerIndex < 2) { (this.getNodeActChildrenLength(item) > 0 ? item.expandBtnSize : 0)
let totalTop = })
node.top +
node.height +
(this.getNodeActChildrenLength(node) > 0 ? node.expandBtnSize : 0)
node.children.forEach(cur => {
cur.left = node.left + node.width * 0.5
cur.top = totalTop
totalTop +=
cur.height +
(this.getNodeActChildrenLength(cur) > 0 ? cur.expandBtnSize : 0)
})
} else {
let totalTop =
node.top -
(this.getNodeActChildrenLength(node) > 0 ? node.expandBtnSize : 0)
node.children.forEach(cur => {
cur.left = node.left + node.width * 0.5
cur.top = totalTop - cur.height
totalTop -=
cur.height +
(this.getNodeActChildrenLength(cur) > 0 ? cur.expandBtnSize : 0)
})
}
}
} }
}, },
null, null,
@ -139,79 +113,56 @@ class Fishbone extends Base {
if (!node.nodeData.data.expand) { if (!node.nodeData.data.expand) {
return return
} }
// 调整left
if (node.isRoot) {
this.updateBrothersLeft(node)
}
// 调整top // 调整top
let len = node.children.length let len = node.children.length
// 调整三级及以下节点的top
if (parent && !parent.isRoot && len > 0) { if (parent && !parent.isRoot && len > 0) {
let totalHeight = let totalHeight = node.children.reduce((h, item) => {
node.children.reduce((h, item) => { return (
return ( h +
h + item.height +
item.height + (this.getNodeActChildrenLength(item) > 0 ? item.expandBtnSize : 0)
(this.getNodeActChildrenLength(item) > 0 )
? item.expandBtnSize }, 0)
: 0) this.updateBrothersTop(node, totalHeight)
)
}, 0)
this.updateBrothersTop(node, node.dir !== 'top' && node.layerIndex > 2 ? -totalHeight : totalHeight)
} }
}, },
(node, parent) => { (node, parent) => {
if ( // 将二级节点的子节点移到上方
parent && if (parent && parent.isRoot) {
parent.isRoot &&
node.dir === CONSTANTS.TIMELINE_DIR.TOP
) {
// 遍历二级节点的子节点 // 遍历二级节点的子节点
let totalHeight = 0
node.children.forEach(item => { node.children.forEach(item => {
let totalHeight = this.getNodeAreaHeight(item) // 调整top
let nodeTotalHeight = this.getNodeAreaHeight(item)
let _top = item.top let _top = item.top
item.top = item.top =
node.top - (item.top - node.top) - totalHeight + node.height node.top - (item.top - node.top) - nodeTotalHeight + node.height
this.updateChildren(item.children, 'top', item.top - _top) // 调整left
let offsetLeft =
(nodeTotalHeight + totalHeight) / Math.tan(degToRad(45))
item.left += offsetLeft
totalHeight += nodeTotalHeight
// 同步更新后代节点
this.updateChildrenPro(item.children, {
top: item.top - _top,
left: offsetLeft
})
}) })
} }
if ( // 调整二级节点的子节点的left值
parent && if (node.isRoot) {
parent.isRoot && let totalLeft = 0
node.dir === CONSTANTS.TIMELINE_DIR.BOTTOM
) {
// 遍历二级节点的子节点
node.children.forEach(item => { node.children.forEach(item => {
let totalHeight = this.getNodeAreaHeight(item) item.left += totalLeft
let _top = item.top this.updateChildren(item.children, 'left', totalLeft)
item.top += totalHeight let { left, right } = this.getNodeBoundaries(item, 'h')
this.updateChildren(item.children, 'top', item.top - _top) totalLeft += right - left
}) })
} }
}, },
true true
) )
walk(
this.root,
null,
(node, parent, isRoot, layerIndex) => {
if (!node.nodeData.data.expand) {
return
}
// 调整left
if (node.layerIndex === 1) {
let totalHeight = 0
node.children.forEach((item) => {
let h = this.getNodeAreaHeight(item)
let x = (h + totalHeight) / Math.tan(degToRad(45))
totalHeight += h
item.left += x
this.updateChildren(item.children, 'left', x)
})
}
},
null,
true
)
} }
// 递归计算节点的宽度 // 递归计算节点的宽度
@ -320,8 +271,12 @@ class Fishbone extends Base {
// 当前节点为非根节点 // 当前节点为非根节点
let maxy = -Infinity let maxy = -Infinity
let miny = Infinity let miny = Infinity
let maxx = -Infinity
let x = node.left + node.width * 0.3 let x = node.left + node.width * 0.3
node.children.forEach((item, index) => { node.children.forEach((item, index) => {
if (item.left > maxx) {
maxx = item.left
}
let y = item.top + item.height / 2 let y = item.top + item.height / 2
if (y > maxy) { if (y > maxy) {
maxy = y maxy = y
@ -340,15 +295,29 @@ class Fishbone extends Base {
if (len > 0) { if (len > 0) {
let line = this.draw.path() let line = this.draw.path()
expandBtnSize = len > 0 ? expandBtnSize : 0 expandBtnSize = len > 0 ? expandBtnSize : 0
let lineLength = maxx - node.left - node.width * 0.3
if ( if (
node.parent && node.parent &&
node.parent.isRoot && node.parent.isRoot &&
node.dir === CONSTANTS.TIMELINE_DIR.TOP node.dir === CONSTANTS.TIMELINE_DIR.TOP
) { ) {
line.plot(`M ${x},${top} L ${x + 1000},${top - Math.tan(degToRad(45)) * 1000}`) line.plot(
`M ${x},${top} L ${x + lineLength},${
top - Math.tan(degToRad(45)) * lineLength
}`
)
} else { } else {
if (node.parent && node.parent.isRoot) { if (node.parent && node.parent.isRoot) {
line.plot(`M ${x},${top + height + expandBtnSize} L ${x + 1000},${top + Math.tan(degToRad(45)) * 1000}`) line.plot(
`M ${x},${top} L ${x + lineLength},${
top - Math.tan(degToRad(45)) * lineLength
}`
)
// line.plot(
// `M ${x},${top + height + expandBtnSize} L ${x + 1000},${
// top + Math.tan(degToRad(45)) * 1000
// }`
// )
} else { } else {
line.plot(`M ${x},${top + height + expandBtnSize} L ${x},${maxy}`) line.plot(`M ${x},${top + height + expandBtnSize} L ${x},${maxy}`)
} }
@ -365,11 +334,7 @@ class Fishbone extends Base {
let { width, height, expandBtnSize, isRoot } = node let { width, height, expandBtnSize, isRoot } = node
if (!isRoot) { if (!isRoot) {
let { translateX, translateY } = btn.transform() let { translateX, translateY } = btn.transform()
if ( if (node.parent && node.parent.isRoot) {
node.parent &&
node.parent.isRoot &&
node.dir === CONSTANTS.TIMELINE_DIR.TOP
) {
btn.translate( btn.translate(
width * 0.3 - expandBtnSize / 2 - translateX, width * 0.3 - expandBtnSize / 2 - translateX,
-expandBtnSize / 2 - translateY -expandBtnSize / 2 - translateY