g6绘制鱼骨图,求求求?

g6绘制鱼骨图,求求求?有没有哪位大神有这种图绘制的代码啊,求求了,太难了


回答:

G6? 这么个图用什么G6? 下面的代码直接创建个Html粘贴进去,就可以看到成品。上才艺!(给个点赞关注吧!)
2023-5-20:40%,雏形已建立...
2023-5-21:80%了,明天再搞...
2023-5-21:90%了,下午再搞...
算了直接通宵:实现了90%了。下面是已经实现的效果图。
2023-5-24:已完成99%了,心血来潮,突然想起,已经没有布局上的bug了。如图所示:
g6绘制鱼骨图,求求求?
已无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

回到顶部