g6绘制鱼骨图,求求求?
有没有哪位大神有这种图绘制的代码啊,求求了,太难了
回答:
G6? 这么个图用什么G6? 下面的代码直接创建个Html粘贴进去,就可以看到成品。上才艺!(给个点赞关注吧!)
2023-5-20:40%,雏形已建立...
2023-5-21:80%了,明天再搞...
2023-5-21:90%了,下午再搞...
算了直接通宵:实现了90%了。下面是已经实现的效果图。
2023-5-24:已完成99%了,心血来潮,突然想起,已经没有布局上的bug了。如图所示:
已无bug
已经实现:
每个月的宽度会自动根据子集最长项之和自动伸缩
文字显示和底部线段会自动根据文字长度自动伸缩
我已经给你完成99%了。
给你留了点,你得自己实现:
子集展开收缩、
线条颜色配置(我预留了,自己实现)、
整体收缩展开、
图自适应大小(svg是矢量图可以通过css3自由缩放或给个滚动条也行)
节点事件、(可以在arr数据中加入事件,然后在创建元素的时候绑定即可)
<!DOCTYPE html><html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<style>
#svgBox{
height: 100vh;
width: 100vw;
background-color: antiquewhite;
}
</style>
<body>
<div id="svgBox"></div>
</body>
<script>
const arr = [
{
label: '1月',
value: 1,
type: 'h'
},
{
label: '2月222',
value: 2,
type: 'h'
},
{
label: '3月',
value: 3,
type: 'h'
},
{
label: '4月',
value: 4,
type: 'h'
},
{
label: '5月',
value: 5,
type: 'h',
child: [
{
label: '1',
value: '项目工作研讨会1',
time: '2022-01-20',
type: 'h1',
child: [
{
label: '1.1',
value: '责任体系-1',
time: '2022-01-20',
type: 'h2',
child: [
{
label: '1.1.1',
value: '责任体系',
time: '2022-01-20',
type: 'h3',
},
{
label: '1.1.2',
value: '责任体系',
time: '2022-01-20',
type: 'h3',
},
{
label: '1.1.2',
value: '责任体系',
time: '2022-01-20',
type: 'h3',
},
{
label: '1.1.2',
value: '责任体系',
time: '2022-01-20',
type: 'h3',
},
{
label: '1.1.2',
value: '责任体系',
time: '2022-01-20',
type: 'h3',
}
]
},
{
label: '1.2',
value: '动员会议-1',
time: '2022-01-20',
type: 'h2',
child: [
{
label: '1.1.1',
value: '动员会议1qwer',
time: '2022-01-20',
type: 'h3',
},
{
label: '1.1.2',
value: '动员会议2',
time: '2022-01-20',
type: 'h3',
},
{
label: '1.1.3',
value: '动员会议3',
time: '2022-01-20',
type: 'h3',
},
{
label: '1.1.4',
value: '动员会议4',
time: '2022-01-20',
type: 'h3',
}
]
}
]
}
]
},
{
label: '6月',
value: 6,
type: 'h',
child: [
{
label: '2',
value: '项目工作研讨会2',
time: '2022-01-20',
type: 'h1',
child: [
{
label: '1.1',
value: '责任体系',
time: '2022-01-20',
type: 'h2',
},
{
label: '1.2',
value: '动员会议',
time: '2022-01-20',
type: 'h2',
}
]
}
]
},
{
label: '7月',
value: 7,
type: 'h'
},
{
label: '8月',
value: 8,
type: 'h'
},
{
label: '9月',
value: 9,
type: 'h',
child: [
{
label: '3',
value: '项目工作研讨会11',
time: '2022-01-20',
type: 'h1',
child: [
{
label: '1.1',
value: '责任体系',
time: '2022-01-20',
type: 'h2',
child: [
{
label: '1.1.1',
value: '动员会议1',
time: '2022-01-20',
type: 'h3',
}
]
},
{
label: '1.2',
value: '动员会议',
time: '2022-01-20',
type: 'h2',
child: [
{
label: '1.1.1',
value: '动员会议1',
time: '2022-01-20',
type: 'h3',
},
{
label: '1.1.2',
value: '动员会议2',
time: '2022-01-20',
type: 'h3',
}
]
}
]
}
]
},
{
label: '10月',
value: 10,
type: 'h'
},
{
label: '11月',
value: 11,
type: 'h'
},
{
label: '12月',
value: 12,
type: 'h'
}
]
/**
* text: string,
fontWeight: string = 'normal',
fontSize: string = '14px',
fontFamily: string = '黑体'
@return number
*
*/
let utilCanvas = null
const getTextWidth = (
text,
fontWeight = 'normal',
fontSize = '14px',
fontFamily = '黑体'
) => {
const canvas = utilCanvas || (utilCanvas = document.createElement('canvas'))
const context = canvas.getContext('2d')
context.font = `${fontWeight} ${fontSize} ${fontFamily}`
const metrics = context.measureText(text)
return Math.ceil(metrics.width)
}
class FishBone {
constructor(arr) {
this.data = arr
this.oParent = document.getElementById('svgBox');
this.svgNS = "http://www.w3.org/2000/svg";
this.xMap = new Map()
this.defaultBoxHeight = 30
this.defaultBoxWidth = 120
this.drawHeight = 1000
this.leafHeight = 40
this.kvArray = new Map()
// 创建画布
this.oSvg = this.createTag('svg',{'xmlns':this.svgNS,'width':'100%','height': this.drawHeight + 'px'});
this.tempArr = []
// 零食存放最长子集
this.tempLen = 0
this.defultLineWidth = 220
this.childMaxHeight = 0
this.down = false
this.main()
}
main() {
this.initKvArray()
this.rander()
}
rander() {
this.randerMonthBox()
this.randerFirstCircle()
this.oParent.appendChild(this.oSvg);
console.log(this.kvArray)
}
randerFirstCircle() {
this.data.forEach((item, index) => {
if (item.len != this.defaultBoxWidth) {
this.oSvg.appendChild(this.creatBigCircle(this.xMap.get(index + 1), this.drawHeight / 2, this.down, item.child[0]));
this.down = !this.down
}
})
}
initKvArray() {
this.data.forEach((item, index) => {
this.tempLen = 0
item.child && item.child.length > 0 && this.getMaxWidth(item.child, 1)
item.len = this.tempLen || this.defaultBoxWidth
this.kvArray.set(item.value, item.len)
});
}
getMaxWidth(arr, level) {
// 获取子集最长
arr.forEach((item, index) => {
const len = getTextWidth(item.value) + this.defultLineWidth * level
item.len = getTextWidth(item.value) + this.defultLineWidth
if (!index) {
this.tempLen = len
} else {
if (len > this.tempLen) {
this.tempLen += len + this.defultLineWidth * level
}
}
if (item.child && item.child.length > 0) this.getMaxWidth(item.child, level++)
})
}
randerMonthBox() {
for (let index = 1; index <= this.data.length; index++) {
let w = this.kvArray.get(index)
let count = index
let wcount = 0
while(count > 0) {
wcount += this.kvArray.get(count)
count--
}
// 把每月的x坐标存起来
this.xMap.set(index, wcount - w)
this.oSvg.appendChild(this.createMonth(w, this.defaultBoxHeight, 'dimgrey', this.data[index - 1].label, this.xMap.get(index), this.drawHeight / 2));
}
}
// 封装createTag函数
createTag (tag, objAttr) {
var oTag = document.createElementNS(this.svgNS,tag);
for( var attr in objAttr){
oTag.setAttribute(attr,objAttr[attr]);
}
return oTag;
}
/** 封装创建月份的方法 - 平行四边形和文字
* @param width { int } 宽
* @param height { int } 高
* @param stroke { string } 边线颜色
* @param font { string } 内部文字
* @param x { number } 绘制位置x坐标
* @param y { number } 绘制位置y坐标
*/
createMonth(width=120, height=30, stroke='dimgrey', font='', x, y) {
var oG = this.createTag('g', { transform: `translate(${x}, ${y})`});
var polygon = this.createTag('polygon',{'x':'0','y':'0',points: `10,0 ${width},0 ${width - 10},${height} 0, ${height}`,'fill':'#fff', stroke:`${stroke}`,'stroke-width':2});
var text = this.createTag('text', {'x':`${width / 2}`,'y':`${height / 2}`, 'font-size':'14px', 'font-weight': 'bold','fill':'#000', 'dominant-baseline': "central",'text-anchor':"middle"});
var tsapn = this.createTag('tspan');
tsapn.innerHTML=font;
text.appendChild(tsapn);
oG.appendChild(polygon);
oG.appendChild(text);
return oG
}
creatBigCircle(x, y, down, item) {
const r = 20
// var oG = createTag('g', { transform: `translate(${x}, ${y})`});
const halfR = r * 1.5
let yc = 0
let yl1 = 0
let yl2 = 0
let lx = r + 5
// 是否向下拓展
if (down) {
yc = y + this.defaultBoxHeight + halfR
yl1 = -r
yl2 = -halfR
} else {
yc = y - halfR
yl1 = r
yl2 = halfR
}
var g = this.createTag('g', { transform: `translate(${x + 2 * r}, ${yc})`})
var bigCircle = this.createTag('circle', { cx: 0, cy: 0, r: r, stroke: 'dimgrey', 'stroke-width': 2, fill:"white" })
var line = this.createTag('line', { x1: 0, y1: yl1, x2: 0, y2:yl2, stroke: 'dimgrey', 'stroke-width': 2 })
var text = this.createTag('text', {'x':0,'y':0, 'font-size':'16px', 'font-weight': 'bold','fill':'#000', 'dominant-baseline': "middle",'text-anchor':"middle"});
var tsapn = this.createTag('tspan');
tsapn.innerHTML=item.label;
text.appendChild(tsapn);
var titleline = this.createTag('line', { x1: lx, y1: 0, x2: lx + this.getLineWidth(item), y2:0, stroke: 'dimgrey', 'stroke-width': 2 })
var textTime = this.createTag('text', {'x':30,'y': 0, 'font-size':'12px', 'font-weight': 'normal','fill':'#000', 'dominant-baseline': "ideographic",'text-anchor':"left"});
var tsapnTime = this.createTag('tspan');
tsapnTime.innerHTML=item.time;
textTime.appendChild(tsapnTime);
var textTitle = this.createTag('text', {'x':110,'y': 0, 'font-size':'12px', 'font-weight': 'normal','fill':'#000', 'dominant-baseline': "ideographic",'text-anchor':"left"});
var tsapnTitle = this.createTag('tspan');
tsapnTitle.innerHTML=item.value;
textTitle.appendChild(tsapnTitle);
g.appendChild(bigCircle);
g.appendChild(line);
g.appendChild(text);
g.appendChild(titleline);
g.appendChild(textTime);
g.appendChild(textTitle);
let prev = 0 // 记录每组中上一个的高度,因为下一个高度=上一个的高度+当前高度
item.child.forEach((ele) => {
prev = this.renderParent(g,ele, down, r, prev)
})
return g
}
getLineWidth(item, leaf) {
var lineWidth = (getTextWidth(item.time) + 20 + getTextWidth(item.value))
if (leaf == 'leaf') {
lineWidth = (getTextWidth(item.time, 'normal','12px') + 40 + getTextWidth(item.value,'normal','12px'))
}
var curLineWidth = lineWidth < 220 ? 220 : lineWidth
return lineWidth
}
renderParent(pg,item, down, r, prev) {
// console.log(item, 'down');
this.childMaxHeight = 0
var haflTh = 0
if (item.child) {
// 获取所有子集最长项, 既数组最长的子集也是最长的数组. 然后遍历每一个子集, 计算其高度并根据当前高
this.getMaxChildHeight(item.child)
haflTh = this.childMaxHeight * this.leafHeight
} else {
// 没有子集就走这个
haflTh = this.childMaxHeight * this.leafHeight / 2 + 50 + prev
}
// 把最长项*子集高度的一半+初始高度+上一个的高度
var h = this.childMaxHeight * this.leafHeight / 2 + 50 + prev
var g = this.createTag('g')
var points = `0,-20 0,-${h} 10,-${h}`
var cx = 10+r
var cy = -h
if (down) {
points = `0,20 0,${h} 10,${h}`
cy = h
}
var xStart = cx + r + 5
var xEnd = xStart + this.getLineWidth(item)
var polyline = this.createTag('polyline',{points: points,'fill':'none', stroke:`dimgrey`,'stroke-width':2});
var bigCircle = this.createTag('circle', { cx: cx, cy: cy, r: r, stroke: 'dimgrey', 'stroke-width': 2, fill:"white" })
var line = this.createTag('line', { x1: xStart, y1: cy, x2: xEnd, y2: cy, stroke: 'dimgrey', 'stroke-width': 2 })
var text = this.createTag('text', {'x':cx,'y':cy, 'font-size':'14px', 'font-weight': 'bold','fill':'#000', 'dominant-baseline': "middle",'text-anchor':"middle"});
var tsapn = this.createTag('tspan');
tsapn.innerHTML=item.label;
var textTime = this.createTag('text', {'x':xStart,'y': cy, 'font-size':'12px', 'font-weight': 'normal','fill':'#000', 'dominant-baseline': "ideographic",'text-anchor':"left"});
var tsapnTime = this.createTag('tspan');
tsapnTime.innerHTML=item.time;
textTime.appendChild(tsapnTime);
var textTitle = this.createTag('text', {'x':xStart + 60 + 20,'y': cy, 'font-size':'12px', 'font-weight': 'normal','fill':'#000', 'dominant-baseline': "ideographic",'text-anchor':"left"});
var tsapnTitle = this.createTag('tspan');
tsapnTitle.innerHTML=item.value;
textTitle.appendChild(tsapnTitle);
text.appendChild(tsapn);
g.appendChild(polyline);
g.appendChild(bigCircle);
g.appendChild(text);
g.appendChild(line);
g.appendChild(textTime);
g.appendChild(textTitle);
pg.appendChild(g)
if (item.child && item.child.length > 0) {
var startY = this.leafHeight * (item.child.length - 1) / 2
console.log(startY, 'startY');
item.child && item.child.forEach((ele, index) => {
this.randerLeaf(g, xEnd, cy, ele, startY)
startY -= this.leafHeight
})
}
return haflTh
}
randerLeaf(pg, sx, sy, item, startY) {
var g = this.createTag('g', { transform: `translate(${sx}, ${sy})`})
var xEnd = 70 + 5 + this.getLineWidth(item, 'leaf')
var points = `0,0 0,${startY} 20,${startY}`
var polyline = this.createTag('polyline', { points: points,'fill':'none', stroke:`dimgrey`,'stroke-width':2 });
var polygon = this.createTag('polygon',{points: `25,${startY - 10} 80,${startY - 10} 70,${startY + 10} 15, ${startY + 10}`,'fill':'#fff', stroke:'dimgrey','stroke-width':2});
var line = this.createTag('line', { x1: 80, y1: startY, x2: xEnd, y2: startY, stroke: 'dimgrey', 'stroke-width': 2 })
var text = this.createTag('text', {'x': 45,'y': startY, 'font-size':'12px', 'font-weight': 'bold','fill':'#000', 'dominant-baseline': "central",'text-anchor':"middle"});
var tsapn = this.createTag('tspan');
tsapn.innerHTML=item.label;
text.appendChild(tsapn);
var textTime = this.createTag('text', {'x':85,'y': startY, 'font-size':'12px', 'font-weight': 'normal','fill':'#000', 'dominant-baseline': "ideographic",'text-anchor':"left"});
var tsapnTime = this.createTag('tspan');
tsapnTime.innerHTML=item.time;
textTime.appendChild(tsapnTime);
var textTitle = this.createTag('text', {'x':85 + 60 + 20,'y': startY, 'font-size':'12px', 'font-weight': 'normal','fill':'#000', 'dominant-baseline': "ideographic",'text-anchor':"left"});
var tsapnTitle = this.createTag('tspan');
tsapnTitle.innerHTML=item.value;
textTitle.appendChild(tsapnTitle);
g.appendChild(polyline);
g.appendChild(polygon);
g.appendChild(line);
g.appendChild(text);
g.appendChild(textTime);
g.appendChild(textTitle);
pg.appendChild(g)
}
getMaxChildHeight(arr) {
if (arr.length > this.childMaxHeight) {
this.childMaxHeight = arr.length
}
arr.forEach(item => {
if (item.child && item.child.length > 0) {
this.getMaxChildHeight(item.child)
}
})
}
}
new FishBone(arr)
</script>
</html>
回答:
定制性有点高,估计不好找现成的,不如自己画算了,也就平行四边形、圆形、直线三种,都不难
以上是 g6绘制鱼骨图,求求求? 的全部内容, 来源链接: utcz.com/p/934319.html