mirror of
https://gitee.com/antv/g6.git
synced 2024-11-29 18:28:19 +08:00
fix: drag group node
This commit is contained in:
parent
a367e01878
commit
862357583c
194
demos/area-chart-node.html
Normal file
194
demos/area-chart-node.html
Normal file
@ -0,0 +1,194 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta http-equiv="X-UA-Compatible" content="ie=edge">
|
||||
<title>面积图节点</title>
|
||||
<style>
|
||||
#mountNode {
|
||||
background:#001528;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="mountNode"></div>
|
||||
<script src="../build/g6.js"></script>
|
||||
<script>
|
||||
/**
|
||||
* 该案例演示如何使用G6自定义面积图节点
|
||||
* by 镜曦
|
||||
*
|
||||
*/
|
||||
|
||||
// 自定义面积图节点
|
||||
G6.registerNode('area', {
|
||||
draw(cfg, group) {
|
||||
const size = cfg.size || [40, 40]; // 如果没有 size 时的默认大小
|
||||
const width = size[0];
|
||||
const height = size[1];
|
||||
|
||||
const baseR = 30;
|
||||
let nowAngle = 0;
|
||||
|
||||
// Ref line
|
||||
let refR = baseR;
|
||||
const refInc = 10;
|
||||
for(let i = 0; i< 6; i++){
|
||||
group.addShape('circle', {
|
||||
// attrs: style
|
||||
attrs: {
|
||||
x: 0, // 居中
|
||||
y: 0,
|
||||
r: refR += refInc,
|
||||
stroke:'rgba(255,255,255,0.4)',
|
||||
lineDash:[4, 4],
|
||||
|
||||
}
|
||||
});
|
||||
}
|
||||
const everyIncAngle = 2 * Math.PI * (360 / 5 ) / 360;
|
||||
const tempIncValues = [baseR, baseR, baseR, baseR, baseR];
|
||||
const allRs = [];
|
||||
cfg.details.forEach(cat =>{
|
||||
|
||||
const oneRs = [];
|
||||
cat.values.forEach((v, i) =>{
|
||||
const R = tempIncValues[i] + v * 0.4;
|
||||
oneRs.push(R);
|
||||
tempIncValues[i] = R;
|
||||
});
|
||||
allRs.push(oneRs);
|
||||
|
||||
});
|
||||
const strokeColors = [
|
||||
'rgba(37,203,253,1)',
|
||||
'rgba(254,255,123,1)',
|
||||
'rgba(254,171,58,1)',
|
||||
'rgba(254,87,102,1)',
|
||||
'rgba(22,193,118,1)',
|
||||
];
|
||||
const fillColors = [
|
||||
'rgba(37,203,253,0.5)',
|
||||
'rgba(254,255,123,0.5)',
|
||||
'rgba(254,171,58,0.5)',
|
||||
'rgba(254,87,102,0.5)',
|
||||
'rgba(22,193,118,0.5)',
|
||||
];
|
||||
|
||||
|
||||
allRs.reverse().forEach((Rs, index) =>{
|
||||
let curAngle = 0;
|
||||
const poss = [];
|
||||
Rs.forEach(r=>{
|
||||
const xPos = r * Math.cos(curAngle);
|
||||
const yPos = r * Math.sin(curAngle);
|
||||
curAngle += everyIncAngle;
|
||||
poss.push([xPos, yPos]);
|
||||
});
|
||||
const Ls = poss.map((p, i)=>{
|
||||
if( i === 0 ){
|
||||
return ["M", ...p]
|
||||
}
|
||||
return ["L", ...p]
|
||||
});
|
||||
console.log('Ls', ...Ls);
|
||||
const shape = group.addShape('path', {
|
||||
attrs: {
|
||||
path: [
|
||||
...Ls,
|
||||
['Z'] // 封闭
|
||||
],
|
||||
stroke:strokeColors[index] ,
|
||||
fill:fillColors[index],
|
||||
}
|
||||
});
|
||||
|
||||
});
|
||||
let nowAngle2 = 0;
|
||||
const everyIncAngleCat = 2 * Math.PI * (360 / 5 ) / 360;
|
||||
for(let i = 0; i < 5; i++){
|
||||
const r = 30 + 60;
|
||||
const xPos = r * Math.cos(nowAngle2);
|
||||
const yPos = r * Math.sin(nowAngle2);
|
||||
|
||||
const shape = group.addShape('path', {
|
||||
attrs: {
|
||||
path: [
|
||||
['M', 0, 0 ],
|
||||
['L', xPos, yPos],
|
||||
|
||||
],
|
||||
lineDash:[4, 4],
|
||||
|
||||
stroke: 'darkgray' // 颜色应用到边上,如果应用到填充,则使用 fill: cfg.color
|
||||
}
|
||||
});
|
||||
nowAngle2 += everyIncAngleCat;
|
||||
}
|
||||
|
||||
// 添加一个和背景色相同的圆形
|
||||
group.addShape('circle', {
|
||||
// attrs: style
|
||||
attrs: {
|
||||
x: 0, // 居中
|
||||
y: 0,
|
||||
r: baseR,
|
||||
fill: cfg.centerColor,
|
||||
stroke:'darkgray',
|
||||
}
|
||||
});
|
||||
|
||||
if(cfg.label) {
|
||||
group.addShape('text', {
|
||||
// attrs: style
|
||||
attrs: {
|
||||
x: 0, // 居中
|
||||
y: 0,
|
||||
textAlign: 'center',
|
||||
textBaseline: 'middle',
|
||||
text: cfg.label,
|
||||
fill: 'white',
|
||||
fontStyle:'bold'
|
||||
}
|
||||
});
|
||||
}
|
||||
return group;
|
||||
}
|
||||
});
|
||||
|
||||
const graph = new G6.Graph({
|
||||
container: 'mountNode',
|
||||
width: 500,
|
||||
height: 500
|
||||
})
|
||||
|
||||
const data = {
|
||||
nodes: [
|
||||
{
|
||||
id: 'nodeD',
|
||||
x: 150,
|
||||
y: 200,
|
||||
label: 'Area',
|
||||
shape:'area',
|
||||
anchorPoints: [
|
||||
[0, 0.5], [1, 0.5]
|
||||
],
|
||||
details:[
|
||||
{cat:'pv', values:[20,30,40,30,30], color:"#25cbfd"},
|
||||
{cat:'dal', values:[40,30,20,30,50], color:"#feff7b"},
|
||||
{cat:'uv', values:[40,30,30,40,40], color:"#feab3a"},
|
||||
{cat:'sal', values:[20,30,50,20,20], color:"#fe5766"},
|
||||
{cat:'cal', values:[10,10,20,20,20], color:"#16c176"},
|
||||
],
|
||||
centerColor:'#0066FF',
|
||||
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
graph.data(data)
|
||||
graph.render()
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
141
demos/bar-chart-node.html
Normal file
141
demos/bar-chart-node.html
Normal file
@ -0,0 +1,141 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta http-equiv="X-UA-Compatible" content="ie=edge">
|
||||
<title>环形柱状图节点</title>
|
||||
<style>
|
||||
#mountNode {
|
||||
background:#001528;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="mountNode"></div>
|
||||
<script src="../build/g6.js"></script>
|
||||
<script>
|
||||
/**
|
||||
* 该案例演示如何自定义一个类似南丁格尔玫瑰一样的节点
|
||||
* by 镜曦
|
||||
*/
|
||||
|
||||
/**
|
||||
* 注册一个类似南丁格尔玫瑰一样的节点
|
||||
*/
|
||||
G6.registerNode('circleBar', {
|
||||
draw(cfg, group) {
|
||||
const size = cfg.size || [40, 40]; // 如果没有 size 时的默认大小
|
||||
const width = size[0];
|
||||
const height = size[1];
|
||||
/*
|
||||
G:
|
||||
Fan
|
||||
x: 扇形圆心的 x 坐标
|
||||
y: 扇形圆心的 y 坐标
|
||||
rs: 内圈半径
|
||||
re: 外圈半径
|
||||
startAngle: 起点弧度
|
||||
endAngle: 终点弧度
|
||||
clockwise: 为true时顺时针渲染,为false时逆时针渲染
|
||||
*/
|
||||
const baseR = 30;
|
||||
let nowAngle = 0;
|
||||
const everyIncAngle = 2 * Math.PI * (360 / 5 / 5) / 360;
|
||||
cfg.details.forEach(cat =>{
|
||||
cat.values.forEach(item =>{
|
||||
const re = item+baseR;
|
||||
const fan = group.addShape('fan', {
|
||||
attrs:{
|
||||
x:0,
|
||||
y:0,
|
||||
rs:baseR,
|
||||
re:item+baseR,
|
||||
startAngle:nowAngle,
|
||||
endAngle: nowAngle += everyIncAngle,
|
||||
clockwise:false,
|
||||
stroke: 'darkgray',
|
||||
fill:cat.color,
|
||||
}
|
||||
});
|
||||
// 加上交互动画
|
||||
fan.on('mouseenter', function(evt) {
|
||||
fan.animate({
|
||||
re: re + 8,
|
||||
repeat: false
|
||||
}, 300);
|
||||
});
|
||||
fan.on('mouseleave', function(evt) {
|
||||
fan.animate({
|
||||
re:re,
|
||||
repeat: false
|
||||
}, 300);
|
||||
});
|
||||
|
||||
// 设置class
|
||||
fan.set("className", 'littleCircle');
|
||||
|
||||
});
|
||||
});
|
||||
group.addShape('circle', {
|
||||
// attrs: style
|
||||
attrs: {
|
||||
x: 0, // 居中
|
||||
y: 0,
|
||||
r: baseR,
|
||||
fill: cfg.centerColor,
|
||||
stroke:'darkgray',
|
||||
}
|
||||
});
|
||||
if(cfg.label) {
|
||||
group.addShape('text', {
|
||||
// attrs: style
|
||||
attrs: {
|
||||
x: 0, // 居中
|
||||
y: 0,
|
||||
textAlign: 'center',
|
||||
textBaseline: 'middle',
|
||||
text: cfg.label,
|
||||
fill: 'white',
|
||||
fontStyle:'bold',
|
||||
}
|
||||
});
|
||||
}
|
||||
return group;
|
||||
}
|
||||
});
|
||||
|
||||
const graph = new G6.Graph({
|
||||
container: 'mountNode',
|
||||
width: 500,
|
||||
height: 500
|
||||
})
|
||||
|
||||
const data = {
|
||||
nodes: [
|
||||
{
|
||||
id: 'nodeA',
|
||||
x: 150,
|
||||
y: 150,
|
||||
label: 'Bar',
|
||||
shape:'circleBar',
|
||||
anchorPoints: [
|
||||
[0, 0.5], [1, 0.5]
|
||||
],
|
||||
details:[
|
||||
{cat:'pv', values:[20,30,40,30,30], color:"#25cbfd"},
|
||||
{cat:'dal', values:[40,30,20,30,50], color:"#feff7b"},
|
||||
{cat:'uv', values:[40,30,30,40,40], color:"#feab3a"},
|
||||
{cat:'sal', values:[20,30,50,20,20], color:"#fe5766"},
|
||||
{cat:'cal', values:[10,10,20,20,20], color:"#16c176"},
|
||||
],
|
||||
centerColor:'#0066FF',
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
graph.data(data)
|
||||
graph.render()
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
936
demos/chart-node.html
Normal file
936
demos/chart-node.html
Normal file
@ -0,0 +1,936 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta http-equiv="X-UA-Compatible" content="ie=edge">
|
||||
<title>统计图表节点</title>
|
||||
<style>
|
||||
#mountNode {
|
||||
background:#001528;
|
||||
}
|
||||
.graph-tooltip {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
border: 1px solid #e2e2e2;
|
||||
border-radius: 4px;
|
||||
font-size: 12px;
|
||||
color: #545454;
|
||||
background-color: rgba(255, 255, 255, 0.9);
|
||||
padding: 10px 8px;
|
||||
box-shadow: rgb(174, 174, 174) 0px 0px 10px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="mountNode"></div>
|
||||
<script src="../build/g6.js"></script>
|
||||
<script>
|
||||
/**
|
||||
* 该案例演示如何使用G6自定义面积图节点
|
||||
* by 镜曦
|
||||
*
|
||||
*/
|
||||
/**
|
||||
* 注册一个类似南丁格尔玫瑰一样的节点
|
||||
*/
|
||||
G6.registerNode('circleBar', {
|
||||
draw(cfg, group) {
|
||||
const size = cfg.size || [40, 40]; // 如果没有 size 时的默认大小
|
||||
const width = size[0];
|
||||
const height = size[1];
|
||||
/*
|
||||
G:
|
||||
Fan
|
||||
x: 扇形圆心的 x 坐标
|
||||
y: 扇形圆心的 y 坐标
|
||||
rs: 内圈半径
|
||||
re: 外圈半径
|
||||
startAngle: 起点弧度
|
||||
endAngle: 终点弧度
|
||||
clockwise: 为true时顺时针渲染,为false时逆时针渲染
|
||||
*/
|
||||
const baseR = 30;
|
||||
let nowAngle = 0;
|
||||
const everyIncAngle = 2 * Math.PI * (360 / 5 / 5) / 360;
|
||||
cfg.details.forEach(cat =>{
|
||||
cat.values.forEach(item =>{
|
||||
const re = item+baseR;
|
||||
const fan = group.addShape('fan', {
|
||||
attrs:{
|
||||
x:0,
|
||||
y:0,
|
||||
rs:baseR,
|
||||
re:item+baseR,
|
||||
startAngle:nowAngle,
|
||||
endAngle: nowAngle += everyIncAngle,
|
||||
clockwise:false,
|
||||
stroke: 'darkgray',
|
||||
fill:cat.color,
|
||||
}
|
||||
});
|
||||
// 加上交互动画
|
||||
fan.on('mouseenter', function(evt) {
|
||||
fan.animate({
|
||||
re: re + 8,
|
||||
repeat: false
|
||||
}, 300);
|
||||
});
|
||||
fan.on('mouseleave', function(evt) {
|
||||
fan.animate({
|
||||
re:re,
|
||||
repeat: false
|
||||
}, 300);
|
||||
});
|
||||
|
||||
// 设置class
|
||||
fan.set("className", 'littleCircle');
|
||||
|
||||
});
|
||||
});
|
||||
group.addShape('circle', {
|
||||
// attrs: style
|
||||
attrs: {
|
||||
x: 0, // 居中
|
||||
y: 0,
|
||||
r: baseR,
|
||||
fill: cfg.centerColor,
|
||||
stroke:'darkgray',
|
||||
}
|
||||
});
|
||||
if(cfg.label) {
|
||||
group.addShape('text', {
|
||||
// attrs: style
|
||||
attrs: {
|
||||
x: 0, // 居中
|
||||
y: 0,
|
||||
textAlign: 'center',
|
||||
textBaseline: 'middle',
|
||||
text: cfg.label,
|
||||
fill: 'white',
|
||||
fontStyle:'bold',
|
||||
}
|
||||
});
|
||||
}
|
||||
return group;
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
/**
|
||||
* 注册一个 分布在圆周上的折线图
|
||||
*/
|
||||
G6.registerNode('circleLine', {
|
||||
draw(cfg, group) {
|
||||
const size = cfg.size || [40, 40]; // 如果没有 size 时的默认大小
|
||||
const width = size[0];
|
||||
const height = size[1];
|
||||
|
||||
const baseR = 30;
|
||||
let nowAngle = 0;
|
||||
|
||||
// Ref line
|
||||
let refR = baseR;
|
||||
const refInc = 10;
|
||||
for(let i = 0; i< 5; i++){
|
||||
group.addShape('circle', {
|
||||
// attrs: style
|
||||
attrs: {
|
||||
x: 0, // 居中
|
||||
y: 0,
|
||||
r: refR += refInc,
|
||||
stroke:'rgba(255,255,255,0.4)',
|
||||
lineDash:[4, 4],
|
||||
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
const everyIncAngle = 2 * Math.PI * (360 / 5 / 5) / 360;
|
||||
cfg.details.forEach(cat =>{
|
||||
// 计算一系列点的位置
|
||||
const postions = [];
|
||||
cat.values.forEach((item, index) =>{
|
||||
const r = baseR + item;
|
||||
const xPos = r * Math.cos(nowAngle);
|
||||
const yPos = r * Math.sin(nowAngle);
|
||||
nowAngle += everyIncAngle;
|
||||
postions.push([xPos, yPos]);
|
||||
if(index === 4){
|
||||
const r = baseR + item;
|
||||
const xPos = r * Math.cos(nowAngle );
|
||||
const yPos = r * Math.sin(nowAngle );
|
||||
postions.push([xPos, yPos]);
|
||||
}
|
||||
});
|
||||
const pathArrayL = postions.map(item =>(["L", ...item]));
|
||||
// 添加连线
|
||||
const shape = group.addShape('path', {
|
||||
attrs: {
|
||||
path: [
|
||||
['M', 0, 0 ], // 上部顶点
|
||||
...pathArrayL,
|
||||
['Z'] // 封闭
|
||||
],
|
||||
stroke: cat.color // 颜色应用到边上,如果应用到填充,则使用 fill: cfg.color
|
||||
}
|
||||
});
|
||||
// 添加标注点
|
||||
postions.forEach(( pos, index )=>{
|
||||
if(index !== 5){
|
||||
const littleCircle = group.addShape('circle', {
|
||||
// attrs: style
|
||||
attrs: {
|
||||
x: pos[0], // 居中
|
||||
y: pos[1],
|
||||
r: 2,
|
||||
fill: 'black',
|
||||
stroke:cat.color,
|
||||
cursor: "pointer",
|
||||
}
|
||||
});
|
||||
// 加上交互动画
|
||||
littleCircle.on('mouseenter', function(evt) {
|
||||
littleCircle.animate({
|
||||
r: 5,
|
||||
repeat: false
|
||||
}, 200);
|
||||
});
|
||||
littleCircle.on('mouseleave', function(evt) {
|
||||
littleCircle.animate({
|
||||
r: 2,
|
||||
repeat: false
|
||||
}, 200);
|
||||
});
|
||||
// 设置class
|
||||
littleCircle.set("className", 'littleCircle');
|
||||
}
|
||||
|
||||
})
|
||||
|
||||
/*
|
||||
const shape = group.addShape('path', {
|
||||
attrs: {
|
||||
path: [
|
||||
['M', 0, 0 ], // 上部顶点
|
||||
['L', width / 2, 0], // 右侧点
|
||||
['L', 0, height / 2], // 下部
|
||||
['L', - width / 2, 0], // 左侧
|
||||
['Z'] // 封闭
|
||||
],
|
||||
stroke: cfg.color // 颜色应用到边上,如果应用到填充,则使用 fill: cfg.color
|
||||
}
|
||||
});
|
||||
|
||||
*/
|
||||
|
||||
});
|
||||
|
||||
// 添加一个和背景色相同的圆形
|
||||
group.addShape('circle', {
|
||||
// attrs: style
|
||||
attrs: {
|
||||
x: 0, // 居中
|
||||
y: 0,
|
||||
r: baseR,
|
||||
fill: cfg.centerColor,
|
||||
stroke:'darkgray',
|
||||
}
|
||||
});
|
||||
if(cfg.label) {
|
||||
group.addShape('text', {
|
||||
// attrs: style
|
||||
attrs: {
|
||||
x: 0, // 居中
|
||||
y: 0,
|
||||
textAlign: 'center',
|
||||
textBaseline: 'middle',
|
||||
text: cfg.label,
|
||||
fill: 'white',
|
||||
fontStyle:'bold',
|
||||
}
|
||||
});
|
||||
}
|
||||
return group;
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
/**
|
||||
* 注册一个 只有标注点
|
||||
*/
|
||||
G6.registerNode('justPoints', {
|
||||
draw(cfg, group) {
|
||||
const size = cfg.size || [40, 40]; // 如果没有 size 时的默认大小
|
||||
const width = size[0];
|
||||
const height = size[1];
|
||||
|
||||
const baseR = 30;
|
||||
let nowAngle = 0;
|
||||
|
||||
// Ref line
|
||||
let refR = baseR;
|
||||
const refInc = 10;
|
||||
for(let i = 0; i< 5; i++){
|
||||
group.addShape('circle', {
|
||||
// attrs: style
|
||||
attrs: {
|
||||
x: 0, // 居中
|
||||
y: 0,
|
||||
r: refR += refInc,
|
||||
stroke:'rgba(255,255,255,0.4)',
|
||||
lineDash:[4, 4],
|
||||
|
||||
}
|
||||
});
|
||||
}
|
||||
const everyIncAngle = 2 * Math.PI * (360 / 5 / 5) / 360;
|
||||
nowAngle = nowAngle + everyIncAngle / 2;
|
||||
cfg.details.forEach(cat =>{
|
||||
// 计算一系列点的位置
|
||||
const postions = [];
|
||||
cat.values.forEach((item, index) =>{
|
||||
const r = baseR + item;
|
||||
const xPos = r * Math.cos(nowAngle);
|
||||
const yPos = r * Math.sin(nowAngle);
|
||||
nowAngle += everyIncAngle;
|
||||
postions.push([xPos, yPos]);
|
||||
if(index === 4){
|
||||
const r = baseR + item;
|
||||
const xPos = r * Math.cos(nowAngle );
|
||||
const yPos = r * Math.sin(nowAngle );
|
||||
postions.push([xPos, yPos]);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
// 添加标注点
|
||||
postions.forEach(( pos, index )=>{
|
||||
if(index !== 5){
|
||||
group.addShape('circle', {
|
||||
// attrs: style
|
||||
attrs: {
|
||||
x: pos[0], // 居中
|
||||
y: pos[1],
|
||||
r: 2,
|
||||
fill: 'black',
|
||||
stroke:cat.color,
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
})
|
||||
|
||||
/*
|
||||
const shape = group.addShape('path', {
|
||||
attrs: {
|
||||
path: [
|
||||
['M', 0, 0 ], // 上部顶点
|
||||
['L', width / 2, 0], // 右侧点
|
||||
['L', 0, height / 2], // 下部
|
||||
['L', - width / 2, 0], // 左侧
|
||||
['Z'] // 封闭
|
||||
],
|
||||
stroke: cfg.color // 颜色应用到边上,如果应用到填充,则使用 fill: cfg.color
|
||||
}
|
||||
});
|
||||
|
||||
*/
|
||||
|
||||
});
|
||||
|
||||
let nowAngle2 = 0;
|
||||
const everyIncAngleCat = 2 * Math.PI * (360 / 5 ) / 360;
|
||||
for(let i = 0; i < 5; i++){
|
||||
const r = 30 + 50;
|
||||
const xPos = r * Math.cos(nowAngle2);
|
||||
const yPos = r * Math.sin(nowAngle2);
|
||||
|
||||
const shape = group.addShape('path', {
|
||||
attrs: {
|
||||
path: [
|
||||
['M', 0, 0 ],
|
||||
['L', xPos, yPos],
|
||||
|
||||
],
|
||||
lineDash:[4, 4],
|
||||
|
||||
stroke: 'darkgray' // 颜色应用到边上,如果应用到填充,则使用 fill: cfg.color
|
||||
}
|
||||
});
|
||||
nowAngle2 += everyIncAngleCat;
|
||||
}
|
||||
// 添加一个和背景色相同的圆形
|
||||
group.addShape('circle', {
|
||||
// attrs: style
|
||||
attrs: {
|
||||
x: 0, // 居中
|
||||
y: 0,
|
||||
r: baseR,
|
||||
fill: cfg.centerColor,
|
||||
stroke:'darkgray',
|
||||
}
|
||||
});
|
||||
|
||||
if(cfg.label) {
|
||||
group.addShape('text', {
|
||||
// attrs: style
|
||||
attrs: {
|
||||
x: 0, // 居中
|
||||
y: 0,
|
||||
textAlign: 'center',
|
||||
textBaseline: 'middle',
|
||||
text: cfg.label,
|
||||
fill: 'white',
|
||||
fontStyle:'bold',
|
||||
}
|
||||
});
|
||||
}
|
||||
return group;
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* 注册一个 面积图节点
|
||||
*/
|
||||
G6.registerNode('area', {
|
||||
draw(cfg, group) {
|
||||
const size = cfg.size || [40, 40]; // 如果没有 size 时的默认大小
|
||||
const width = size[0];
|
||||
const height = size[1];
|
||||
|
||||
const baseR = 30;
|
||||
let nowAngle = 0;
|
||||
|
||||
// Ref line
|
||||
let refR = baseR;
|
||||
const refInc = 10;
|
||||
for(let i = 0; i< 6; i++){
|
||||
group.addShape('circle', {
|
||||
// attrs: style
|
||||
attrs: {
|
||||
x: 0, // 居中
|
||||
y: 0,
|
||||
r: refR += refInc,
|
||||
stroke:'rgba(255,255,255,0.4)',
|
||||
lineDash:[4, 4],
|
||||
|
||||
}
|
||||
});
|
||||
}
|
||||
const everyIncAngle = 2 * Math.PI * (360 / 5 ) / 360;
|
||||
const tempIncValues = [baseR, baseR, baseR, baseR, baseR];
|
||||
const allRs = [];
|
||||
cfg.details.forEach(cat =>{
|
||||
|
||||
const oneRs = [];
|
||||
cat.values.forEach((v, i) =>{
|
||||
const R = tempIncValues[i] + v * 0.4;
|
||||
oneRs.push(R);
|
||||
tempIncValues[i] = R;
|
||||
});
|
||||
allRs.push(oneRs);
|
||||
|
||||
});
|
||||
const strokeColors = [
|
||||
'rgba(37,203,253,1)',
|
||||
'rgba(254,255,123,1)',
|
||||
'rgba(254,171,58,1)',
|
||||
'rgba(254,87,102,1)',
|
||||
'rgba(22,193,118,1)',
|
||||
];
|
||||
const fillColors = [
|
||||
'rgba(37,203,253,0.5)',
|
||||
'rgba(254,255,123,0.5)',
|
||||
'rgba(254,171,58,0.5)',
|
||||
'rgba(254,87,102,0.5)',
|
||||
'rgba(22,193,118,0.5)',
|
||||
];
|
||||
|
||||
|
||||
allRs.reverse().forEach((Rs, index) =>{
|
||||
let curAngle = 0;
|
||||
const poss = [];
|
||||
Rs.forEach(r=>{
|
||||
const xPos = r * Math.cos(curAngle);
|
||||
const yPos = r * Math.sin(curAngle);
|
||||
curAngle += everyIncAngle;
|
||||
poss.push([xPos, yPos]);
|
||||
});
|
||||
const Ls = poss.map((p, i)=>{
|
||||
if( i === 0 ){
|
||||
return ["M", ...p]
|
||||
}
|
||||
return ["L", ...p]
|
||||
});
|
||||
console.log('Ls', ...Ls);
|
||||
const shape = group.addShape('path', {
|
||||
attrs: {
|
||||
path: [
|
||||
...Ls,
|
||||
['Z'] // 封闭
|
||||
],
|
||||
stroke:strokeColors[index] ,
|
||||
fill:fillColors[index],
|
||||
}
|
||||
});
|
||||
|
||||
});
|
||||
let nowAngle2 = 0;
|
||||
const everyIncAngleCat = 2 * Math.PI * (360 / 5 ) / 360;
|
||||
for(let i = 0; i < 5; i++){
|
||||
const r = 30 + 60;
|
||||
const xPos = r * Math.cos(nowAngle2);
|
||||
const yPos = r * Math.sin(nowAngle2);
|
||||
|
||||
const shape = group.addShape('path', {
|
||||
attrs: {
|
||||
path: [
|
||||
['M', 0, 0 ],
|
||||
['L', xPos, yPos],
|
||||
|
||||
],
|
||||
lineDash:[4, 4],
|
||||
|
||||
stroke: 'darkgray' // 颜色应用到边上,如果应用到填充,则使用 fill: cfg.color
|
||||
}
|
||||
});
|
||||
nowAngle2 += everyIncAngleCat;
|
||||
}
|
||||
|
||||
// 添加一个和背景色相同的圆形
|
||||
group.addShape('circle', {
|
||||
// attrs: style
|
||||
attrs: {
|
||||
x: 0, // 居中
|
||||
y: 0,
|
||||
r: baseR,
|
||||
fill: cfg.centerColor,
|
||||
stroke:'darkgray',
|
||||
}
|
||||
});
|
||||
|
||||
if(cfg.label) {
|
||||
group.addShape('text', {
|
||||
// attrs: style
|
||||
attrs: {
|
||||
x: 0, // 居中
|
||||
y: 0,
|
||||
textAlign: 'center',
|
||||
textBaseline: 'middle',
|
||||
text: cfg.label,
|
||||
fill: 'white',
|
||||
fontStyle:'bold'
|
||||
}
|
||||
});
|
||||
}
|
||||
return group;
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
环 1
|
||||
*/
|
||||
G6.registerNode('rings1', {
|
||||
draw(cfg, group) {
|
||||
const size = cfg.size || [40, 40]; // 如果没有 size 时的默认大小
|
||||
const width = size[0];
|
||||
const height = size[1];
|
||||
/*
|
||||
G:
|
||||
Fan
|
||||
x: 扇形圆心的 x 坐标
|
||||
y: 扇形圆心的 y 坐标
|
||||
rs: 内圈半径
|
||||
re: 外圈半径
|
||||
startAngle: 起点弧度
|
||||
endAngle: 终点弧度
|
||||
clockwise: 为true时顺时针渲染,为false时逆时针渲染
|
||||
*/
|
||||
const baseR = 30;
|
||||
let nowAngle = 0;
|
||||
const everyIncAngle = 2 * Math.PI * (360 / 5 / 5) / 360;
|
||||
cfg.details.forEach(cat =>{
|
||||
cat.values.forEach(item =>{
|
||||
const baseNbr = Math.ceil(item / 10);
|
||||
const baseIncR = 7;
|
||||
let nowStartR = baseR;
|
||||
const last = item % 10;
|
||||
const endAngle = nowAngle + everyIncAngle;
|
||||
for (let i = 0; i < baseNbr ; i ++ ) {
|
||||
const fan = group.addShape('fan', {
|
||||
attrs:{
|
||||
x:0,
|
||||
y:0,
|
||||
rs:nowStartR,
|
||||
re:nowStartR + baseIncR,
|
||||
startAngle:nowAngle,
|
||||
endAngle:endAngle,
|
||||
clockwise:false,
|
||||
stroke: 'darkgray',
|
||||
fill:cat.color,
|
||||
}
|
||||
});
|
||||
nowStartR = nowStartR + baseIncR + 2
|
||||
if(i === baseNbr -1 && last !== 0){
|
||||
const fan = group.addShape('fan', {
|
||||
attrs:{
|
||||
x:0,
|
||||
y:0,
|
||||
rs:nowStartR,
|
||||
re:nowStartR + baseIncR * last / 10,
|
||||
startAngle:nowAngle,
|
||||
endAngle:endAngle,
|
||||
clockwise:false,
|
||||
stroke: 'darkgray',
|
||||
fill:cat.color,
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
nowAngle = endAngle
|
||||
});
|
||||
});
|
||||
|
||||
group.addShape('circle', {
|
||||
// attrs: style
|
||||
attrs: {
|
||||
x: 0, // 居中
|
||||
y: 0,
|
||||
r: baseR,
|
||||
fill: cfg.centerColor,
|
||||
stroke:'darkgray',
|
||||
}
|
||||
});
|
||||
if(cfg.label) {
|
||||
group.addShape('text', {
|
||||
// attrs: style
|
||||
attrs: {
|
||||
x: 0, // 居中
|
||||
y: 0,
|
||||
textAlign: 'center',
|
||||
textBaseline: 'middle',
|
||||
text: cfg.label,
|
||||
fill: 'white',
|
||||
fontStyle:'bold',
|
||||
}
|
||||
});
|
||||
}
|
||||
return group;
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 注册一个 面积图节点
|
||||
*/
|
||||
G6.registerNode('rings2', {
|
||||
draw(cfg, group) {
|
||||
const size = cfg.size || [40, 40]; // 如果没有 size 时的默认大小
|
||||
const width = size[0];
|
||||
const height = size[1];
|
||||
|
||||
const baseR = 30;
|
||||
let nowAngle = 0;
|
||||
|
||||
// Ref line
|
||||
let refR = baseR;
|
||||
const refInc = 10;
|
||||
for(let i = 0; i< 6; i++){
|
||||
group.addShape('circle', {
|
||||
// attrs: style
|
||||
attrs: {
|
||||
x: 0, // 居中
|
||||
y: 0,
|
||||
r: refR += refInc,
|
||||
stroke:'rgba(255,255,255,0.4)',
|
||||
lineDash:[4, 4],
|
||||
|
||||
}
|
||||
});
|
||||
}
|
||||
const everyIncAngle = 2 * Math.PI * (360 / 5 ) / 360;
|
||||
const tempIncValues = [baseR, baseR, baseR, baseR, baseR];
|
||||
const allRs = [];
|
||||
cfg.details.forEach(cat =>{
|
||||
|
||||
const oneRs = [];
|
||||
cat.values.forEach((v, i) =>{
|
||||
const R = tempIncValues[i] + v * 0.4;
|
||||
oneRs.push(R);
|
||||
tempIncValues[i] = R;
|
||||
});
|
||||
allRs.push(oneRs);
|
||||
|
||||
});
|
||||
const strokeColors = [
|
||||
'rgba(37,203,253,1)',
|
||||
'rgba(254,255,123,1)',
|
||||
'rgba(254,171,58,1)',
|
||||
'rgba(254,87,102,1)',
|
||||
'rgba(22,193,118,1)',
|
||||
];
|
||||
const fillColors = [
|
||||
'rgba(37,203,253,0.5)',
|
||||
'rgba(254,255,123,0.5)',
|
||||
'rgba(254,171,58,0.5)',
|
||||
'rgba(254,87,102,0.5)',
|
||||
'rgba(22,193,118,0.5)',
|
||||
];
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
allRs.reverse().forEach((Rs, index) =>{
|
||||
let curAngle = 0;
|
||||
const poss = [];
|
||||
Rs.forEach(r=>{
|
||||
|
||||
const baseNbr = Math.ceil(r / 10);
|
||||
const baseIncR = 7;
|
||||
let nowStartR = baseR;
|
||||
const last = r % 10;
|
||||
|
||||
for(let i = 0; i < baseNbr; i++){
|
||||
|
||||
const endAngle = nowAngle + everyIncAngle;
|
||||
|
||||
const fan = group.addShape('fan', {
|
||||
attrs:{
|
||||
x:0,
|
||||
y:0,
|
||||
rs:nowStartR,
|
||||
re:nowStartR + baseIncR,
|
||||
startAngle:nowAngle,
|
||||
endAngle:endAngle,
|
||||
clockwise:false,
|
||||
stroke: 'darkgray',
|
||||
fill:strokeColors[index],
|
||||
}
|
||||
});
|
||||
nowStartR = nowStartR + baseIncR + 2
|
||||
if(i === baseNbr -1 && last !== 0){
|
||||
const fan = group.addShape('fan', {
|
||||
attrs:{
|
||||
x:0,
|
||||
y:0,
|
||||
rs:nowStartR,
|
||||
re:nowStartR + baseIncR * last / 10,
|
||||
startAngle:nowAngle,
|
||||
endAngle:endAngle,
|
||||
clockwise:false,
|
||||
stroke: 'darkgray',
|
||||
fill:strokeColors[index],
|
||||
}
|
||||
});
|
||||
}
|
||||
nowAngle = endAngle
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// 添加一个和背景色相同的圆形
|
||||
group.addShape('circle', {
|
||||
// attrs: style
|
||||
attrs: {
|
||||
x: 0, // 居中
|
||||
y: 0,
|
||||
r: baseR,
|
||||
fill: cfg.centerColor,
|
||||
stroke:'darkgray',
|
||||
}
|
||||
});
|
||||
|
||||
if(cfg.label) {
|
||||
group.addShape('text', {
|
||||
// attrs: style
|
||||
attrs: {
|
||||
x: 0, // 居中
|
||||
y: 0,
|
||||
textAlign: 'center',
|
||||
textBaseline: 'middle',
|
||||
text: cfg.label,
|
||||
fill: 'white',
|
||||
fontStyle:'bold'
|
||||
}
|
||||
});
|
||||
}
|
||||
return group;
|
||||
}
|
||||
});
|
||||
|
||||
/** 数据 */
|
||||
const data = {
|
||||
nodes: [{
|
||||
id: 'nodeA',
|
||||
x: 150,
|
||||
y: 150,
|
||||
label: 'Bar',
|
||||
shape:'circleBar',
|
||||
anchorPoints: [
|
||||
[0, 0.5], [1, 0.5]
|
||||
],
|
||||
details:[
|
||||
{cat:'pv', values:[20,30,40,30,30], color:"#25cbfd"},
|
||||
{cat:'dal', values:[40,30,20,30,50], color:"#feff7b"},
|
||||
{cat:'uv', values:[40,30,30,40,40], color:"#feab3a"},
|
||||
{cat:'sal', values:[20,30,50,20,20], color:"#fe5766"},
|
||||
{cat:'cal', values:[10,10,20,20,20], color:"#16c176"},
|
||||
],
|
||||
centerColor:'#0066FF',
|
||||
},
|
||||
{
|
||||
id: 'nodeB',
|
||||
x: 400,
|
||||
y: 150,
|
||||
label: 'Line',
|
||||
shape:'circleLine',
|
||||
anchorPoints: [
|
||||
[0, 0.5], [1, 0.5]
|
||||
],
|
||||
details:[
|
||||
{cat:'pv', values:[20,30,40,30,30], color:"#25cbfd"},
|
||||
{cat:'dal', values:[40,30,20,30,50], color:"#feff7b"},
|
||||
{cat:'uv', values:[40,30,30,40,40], color:"#feab3a"},
|
||||
{cat:'sal', values:[20,30,50,20,20], color:"#fe5766"},
|
||||
{cat:'cal', values:[10,10,20,20,20], color:"#16c176"},
|
||||
],
|
||||
centerColor:'#0066FF',
|
||||
|
||||
},
|
||||
|
||||
{
|
||||
id: 'nodeC',
|
||||
x: 650,
|
||||
y: 150,
|
||||
label: 'Point',
|
||||
shape:'justPoints',
|
||||
anchorPoints: [
|
||||
[0, 0.5], [1, 0.5]
|
||||
],
|
||||
details:[
|
||||
{cat:'pv', values:[20,30,40,30,30], color:"#25cbfd"},
|
||||
{cat:'dal', values:[40,30,20,30,50], color:"#feff7b"},
|
||||
{cat:'uv', values:[40,30,30,40,40], color:"#feab3a"},
|
||||
{cat:'sal', values:[20,30,50,20,20], color:"#fe5766"},
|
||||
{cat:'cal', values:[10,10,20,20,20], color:"#16c176"},
|
||||
],
|
||||
centerColor:'#0066FF',
|
||||
|
||||
},
|
||||
|
||||
{
|
||||
id: 'nodeD',
|
||||
x: 150,
|
||||
y: 400,
|
||||
label: 'Area',
|
||||
shape:'area',
|
||||
anchorPoints: [
|
||||
[0, 0.5], [1, 0.5]
|
||||
],
|
||||
details:[
|
||||
{cat:'pv', values:[20,30,40,30,30], color:"#25cbfd"},
|
||||
{cat:'dal', values:[40,30,20,30,50], color:"#feff7b"},
|
||||
{cat:'uv', values:[40,30,30,40,40], color:"#feab3a"},
|
||||
{cat:'sal', values:[20,30,50,20,20], color:"#fe5766"},
|
||||
{cat:'cal', values:[10,10,20,20,20], color:"#16c176"},
|
||||
],
|
||||
centerColor:'#0066FF',
|
||||
|
||||
},
|
||||
|
||||
{
|
||||
id: 'nodeF',
|
||||
x: 400,
|
||||
y: 400,
|
||||
label: 'Rings1',
|
||||
shape:'rings1',
|
||||
anchorPoints: [
|
||||
[0, 0.5], [1, 0.5]
|
||||
],
|
||||
details:[
|
||||
{cat:'pv', values:[20,30,48,30,30], color:"#25cbfd"},
|
||||
{cat:'dal', values:[40,30,20,30,50], color:"#feff7b"},
|
||||
{cat:'uv', values:[40,30,30,4,40], color:"#feab3a"},
|
||||
{cat:'sal', values:[20,30,50,20,20], color:"#fe5766"},
|
||||
{cat:'cal', values:[10,10,25,20,20], color:"#16c176"},
|
||||
],
|
||||
centerColor:'#0066FF',
|
||||
|
||||
},
|
||||
|
||||
|
||||
],
|
||||
edges: [
|
||||
//{source:'nodeA', target:'nodeB', color:"rgba(0, 255, 255, 0.5)"},
|
||||
//{source:'nodeB', target:'nodeC', color:"rgba(0, 255, 255, 0.5)"},
|
||||
|
||||
]
|
||||
};
|
||||
|
||||
const graph = new G6.Graph({
|
||||
container: 'mountNode',
|
||||
width: 1000,
|
||||
height: 600
|
||||
});
|
||||
|
||||
graph.on("node:mouseenter", function(event) {
|
||||
var node = event.item;
|
||||
var nodeId = node.get("model").id;
|
||||
var shape = event.target;
|
||||
|
||||
if (shape.get("className") === "littleCircle") {
|
||||
// 如果点击是发生在节点里面的小圆上,显示tooltip
|
||||
console.log('x', event);
|
||||
console.log('Y', event);
|
||||
|
||||
showTooltip("tooltip for " + nodeId, {
|
||||
x: event.x,
|
||||
y: event.y
|
||||
});
|
||||
} else {
|
||||
// 否则隐藏tooltip
|
||||
hideTooltip();
|
||||
}
|
||||
});
|
||||
graph.on("node:mouseleave", function(event) {
|
||||
hideTooltip();
|
||||
});
|
||||
|
||||
|
||||
graph.data(data);
|
||||
graph.render();
|
||||
|
||||
var tooltipEl = null;
|
||||
// 在指定的位置显示tooltip
|
||||
function showTooltip(message, position) {
|
||||
const offSetX = 50;
|
||||
if (!tooltipEl) {
|
||||
var container = document.getElementById("mountNode");
|
||||
tooltipEl = document.createElement("div");
|
||||
tooltipEl.setAttribute("class", "graph-tooltip");
|
||||
container.appendChild(tooltipEl);
|
||||
}
|
||||
tooltipEl.textContent = message;
|
||||
// tooltip是相对于画布canvas element绝对定位,所以position的x,y必须是相对于画布的坐标
|
||||
tooltipEl.style.left = position.x + offSetX+ "px";
|
||||
tooltipEl.style.top = position.y + "px";
|
||||
tooltipEl.style.display = "block";
|
||||
}
|
||||
|
||||
// 隐藏tooltip
|
||||
function hideTooltip() {
|
||||
if (!tooltipEl) {
|
||||
return;
|
||||
}
|
||||
tooltipEl.style.display = "none";
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
@ -40,6 +40,33 @@
|
||||
},
|
||||
modes: {
|
||||
default: [ 'drag-canvas', 'zoom-canvas', 'collapse-expand-group' ]
|
||||
},
|
||||
groupStyle: {
|
||||
default: {
|
||||
lineWidth: 2,
|
||||
stroke: '#A3B1BF',
|
||||
radius: 10,
|
||||
lineDash: [ 5, 5 ],
|
||||
strokeOpacity: 0.9,
|
||||
fill: '#F3F9FF',
|
||||
fillOpacity: 0.8,
|
||||
opacity: 0.8
|
||||
},
|
||||
hover: {
|
||||
stroke: '#faad14',
|
||||
fill: '#ffe58f',
|
||||
fillOpacity: 0.3,
|
||||
opacity: 0.3,
|
||||
lineWidth: 3
|
||||
},
|
||||
// 收起状态样式
|
||||
collapseStyle: {
|
||||
r: 50,
|
||||
// lineDash: [ 5, 5 ],
|
||||
stroke: '#ffa39e',
|
||||
lineWidth: 3,
|
||||
fill: '#ffccc7'
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
587
demos/custom-tree-interactive.html
Normal file
587
demos/custom-tree-interactive.html
Normal file
@ -0,0 +1,587 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta http-equiv="X-UA-Compatible" content="ie=edge">
|
||||
<title>Document</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="mountNode"></div>
|
||||
<script src="../build/g6.js"></script>
|
||||
<script>
|
||||
/**
|
||||
* 该案例演示如何交互复杂的列表组件
|
||||
* 内网可以参考:https://riddle.alibaba-inc.com/riddles/3acae792
|
||||
*
|
||||
*/
|
||||
const COLLAPSE_ICON = (x, y, r) => {
|
||||
return [
|
||||
['M', x, y],
|
||||
['a', r, r, 0, 1, 0, r * 2, 0],
|
||||
['a', r, r, 0, 1, 0, -r * 2, 0],
|
||||
['M', x + 2, y],
|
||||
['L', x + 2 * r - 2, y],
|
||||
];
|
||||
};
|
||||
|
||||
const EXPAND_ICON = (x, y, r) => {
|
||||
return [
|
||||
['M', x, y],
|
||||
['a', r, r, 0, 1, 0, r * 2, 0],
|
||||
['a', r, r, 0, 1, 0, -r * 2, 0],
|
||||
['M', x + 2, y],
|
||||
['L', x + 2 * r - 2, y],
|
||||
['M', x + r, y - r + 2],
|
||||
['L', x + r, y + r - 2],
|
||||
];
|
||||
};
|
||||
|
||||
//注册边
|
||||
G6.registerEdge('hvh', {
|
||||
draw(cfg, group) {
|
||||
const startPoint = cfg.startPoint;
|
||||
const endPoint = cfg.endPoint;
|
||||
const shape = group.addShape('path', {
|
||||
attrs: {
|
||||
endArrow: true,
|
||||
endArrow: {
|
||||
path: 'M 10,0 L -10,-10 L -10,10 Z',
|
||||
d: 10,
|
||||
},
|
||||
stroke: '#A3B1BF',
|
||||
path: [
|
||||
['M', startPoint.x, startPoint.y],
|
||||
['L', endPoint.x / 3 + (2 / 3) * startPoint.x, startPoint.y],
|
||||
['L', endPoint.x / 3 + (2 / 3) * startPoint.x, endPoint.y],
|
||||
['L', endPoint.x, endPoint.y],
|
||||
],
|
||||
},
|
||||
});
|
||||
return shape;
|
||||
},
|
||||
});
|
||||
|
||||
//root节点
|
||||
G6.registerNode(
|
||||
'tree-node',
|
||||
{
|
||||
drawShape: (cfg, group) => {
|
||||
const rect = group.addShape('rect', {
|
||||
attrs: {
|
||||
fill: '#0096e0',
|
||||
},
|
||||
});
|
||||
const content = cfg.name;
|
||||
const text = group.addShape('text', {
|
||||
attrs: {
|
||||
text: content,
|
||||
x: 0,
|
||||
y: 0,
|
||||
textAlign: 'left',
|
||||
textBaseline: 'middle',
|
||||
fill: '#fff',
|
||||
},
|
||||
});
|
||||
const bbox = text.getBBox();
|
||||
const hasChildren = cfg.children && cfg.children.length > 0;
|
||||
if (hasChildren) {
|
||||
group.addShape('marker', {
|
||||
attrs: {
|
||||
x: bbox.maxX + 6,
|
||||
y: bbox.minX + bbox.height / 2 - 6,
|
||||
r: 6,
|
||||
symbol: COLLAPSE_ICON,
|
||||
stroke: '#fff',
|
||||
lineWidth: 2,
|
||||
},
|
||||
className: 'collapse-icon',
|
||||
});
|
||||
}
|
||||
|
||||
//节点高度(getVGap值保持一致)
|
||||
let height = 50;
|
||||
//根节点高度:子节点高度+节点间距
|
||||
let rootHeight = height * count + height * (count - 1);
|
||||
//节点Y坐标
|
||||
let nodeY = bbox.minY - 6;
|
||||
//根节点Y坐标:节点Y坐标上移节点高度*count
|
||||
let rootY = nodeY - height * (count - 1) + 12.5;
|
||||
|
||||
//console.log('rootHeight');
|
||||
//console.log(rootHeight);
|
||||
//console.log('rootY');
|
||||
//console.log(rootY);
|
||||
|
||||
rect.attr({
|
||||
x: bbox.minX - 4,
|
||||
y: hasChildren ? rootY : nodeY,
|
||||
width: bbox.width + (hasChildren ? 26 : 8),
|
||||
height: hasChildren ? rootHeight : height,
|
||||
});
|
||||
return rect;
|
||||
},
|
||||
},
|
||||
'single-shape',
|
||||
);
|
||||
|
||||
//子节点
|
||||
G6.registerNode('expandNode', {
|
||||
draw: function draw(cfg, group) {
|
||||
var mainGroup = group.addGroup({
|
||||
id: 'main-group',
|
||||
});
|
||||
var keyShape = mainGroup.addShape('rect', {
|
||||
attrs: {
|
||||
x: 0,
|
||||
y: 0,
|
||||
width: 100 + 60 * cfg.values.length,
|
||||
height: 50,
|
||||
fill: '#f5f5f5',
|
||||
},
|
||||
});
|
||||
|
||||
// name text
|
||||
mainGroup.addShape('text', {
|
||||
attrs: {
|
||||
text: cfg.name,
|
||||
fill: '#000',
|
||||
width: 130,
|
||||
x: 10,
|
||||
y: 32,
|
||||
},
|
||||
});
|
||||
|
||||
var subGroup = group.addGroup({
|
||||
id: 'sub-group',
|
||||
});
|
||||
cfg.values.forEach(function(data, index) {
|
||||
subGroup.addShape('rect', {
|
||||
attrs: {
|
||||
x: 110 + index * 60,
|
||||
y: 0,
|
||||
width: 50,
|
||||
height: 50,
|
||||
},
|
||||
});
|
||||
|
||||
subGroup.addShape('text', {
|
||||
attrs: {
|
||||
text: data.key,
|
||||
fill: '#000',
|
||||
x: 130 + index * 60,
|
||||
y: 20,
|
||||
fontSize: 10,
|
||||
textBaseline: 'middle',
|
||||
className: 'sub-group-text',
|
||||
},
|
||||
});
|
||||
|
||||
subGroup.addShape('text', {
|
||||
attrs: {
|
||||
text: data.value,
|
||||
fill: '#000',
|
||||
x: 130 + index * 60,
|
||||
y: 30,
|
||||
fontSize: 10,
|
||||
textBaseline: 'middle',
|
||||
textAlign: 'left',
|
||||
className: 'sub-group-text',
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
var listGroup = group.addGroup({
|
||||
id: 'detail-list-group',
|
||||
});
|
||||
|
||||
listGroup.addShape('rect', {
|
||||
attrs: {
|
||||
width: 100 + 60 * cfg.values.length - 70,
|
||||
height: 30 * cfg.properties.length + 20,
|
||||
fill: '#fff',
|
||||
x: 70,
|
||||
y: 30,
|
||||
},
|
||||
});
|
||||
|
||||
var rectWidth = 100 + 60 * cfg.values.length - 80;
|
||||
cfg.properties.forEach(function(property, index) {
|
||||
listGroup.addShape('rect', {
|
||||
attrs: {
|
||||
width: rectWidth,
|
||||
height: 30,
|
||||
fill: '#e8e8e8',
|
||||
x: 80,
|
||||
y: 40 * index + 40,
|
||||
},
|
||||
});
|
||||
var count = 0;
|
||||
for (var p in property) {
|
||||
// 每个rect中添加5个文本
|
||||
listGroup.addShape('text', {
|
||||
attrs: {
|
||||
text: property[p],
|
||||
fill: '#000',
|
||||
x: 85 + count * (rectWidth / cfg.values.length) - count * 10,
|
||||
y: 40 * index + 40 + 15,
|
||||
fontSize: 10,
|
||||
textBaseline: 'middle',
|
||||
textAlign: 'left',
|
||||
},
|
||||
});
|
||||
count++;
|
||||
}
|
||||
});
|
||||
listGroup.hide();
|
||||
return keyShape;
|
||||
},
|
||||
});
|
||||
|
||||
//graph
|
||||
const graph = new G6.TreeGraph({
|
||||
container: 'mountNode',
|
||||
width: window.innerWidth - 100,
|
||||
height: window.innerHeight - 100,
|
||||
modes: {
|
||||
default: [
|
||||
{
|
||||
type: 'collapse-expand',
|
||||
onChange: (item, collapsed) => {
|
||||
const data = item.get('model').data;
|
||||
const icon = item.get('group').findByClassName('collapse-icon');
|
||||
if (collapsed) {
|
||||
icon.attr('symbol', EXPAND_ICON);
|
||||
} else {
|
||||
icon.attr('symbol', COLLAPSE_ICON);
|
||||
}
|
||||
data.collapsed = collapsed;
|
||||
return true;
|
||||
},
|
||||
},
|
||||
'drag-canvas',
|
||||
'zoom-canvas',
|
||||
],
|
||||
},
|
||||
defaultNode: {
|
||||
shape: 'tree-node',
|
||||
anchorPoints: [[0, 0.5], [1, 0.5]],
|
||||
},
|
||||
defaultEdge: {
|
||||
shape: 'hvh',
|
||||
},
|
||||
edgeStyle: {
|
||||
default: {
|
||||
stroke: '#A3B1BF',
|
||||
},
|
||||
},
|
||||
layout: {
|
||||
type: 'compactBox',
|
||||
direction: 'LR',
|
||||
getId: d => {
|
||||
return d.id;
|
||||
},
|
||||
getHeight: () => {
|
||||
return 0;
|
||||
},
|
||||
getWidth: () => {
|
||||
return 16;
|
||||
},
|
||||
getVGap: d => {
|
||||
return 50
|
||||
},
|
||||
getHGap: () => {
|
||||
return 80;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const data = {
|
||||
id: 'root',
|
||||
name: 'root',
|
||||
children: [
|
||||
{
|
||||
id: 'shape2',
|
||||
//x: 0,
|
||||
//y: 50,
|
||||
shape: 'expandNode',
|
||||
name: '网站引流1',
|
||||
values: [
|
||||
{
|
||||
key: '曝光率',
|
||||
value: '1938.33w',
|
||||
},
|
||||
{
|
||||
key: '流入UV',
|
||||
value: '1938.33w',
|
||||
},
|
||||
{
|
||||
key: '点击率',
|
||||
value: '99.9%',
|
||||
},
|
||||
{
|
||||
key: '占比',
|
||||
value: '99.9%',
|
||||
},
|
||||
],
|
||||
properties: [
|
||||
{
|
||||
name: '宫格',
|
||||
value1: '1938.33w',
|
||||
value2: '1938.33w',
|
||||
value3: '99.9%',
|
||||
value4: '99.9%',
|
||||
},
|
||||
{
|
||||
name: '更多应用',
|
||||
value1: '1938.33w',
|
||||
value2: '1938.33w',
|
||||
value3: '99.9%',
|
||||
value4: '99.9%',
|
||||
},
|
||||
{
|
||||
name: '搜索',
|
||||
value1: '1938.33w',
|
||||
value2: '1938.33w',
|
||||
value3: '99.9%',
|
||||
value4: '99.9%',
|
||||
},
|
||||
{
|
||||
name: '扫一扫',
|
||||
value1: '1938.33w',
|
||||
value2: '1938.33w',
|
||||
value3: '99.9%',
|
||||
value4: '99.9%',
|
||||
},
|
||||
{
|
||||
name: '我的Tab',
|
||||
value1: '1938.33w',
|
||||
value2: '1938.33w',
|
||||
value3: '99.9%',
|
||||
value4: '99.9%',
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
{
|
||||
id: 'shape30',
|
||||
//x: 0,
|
||||
//y: 50,
|
||||
shape: 'expandNode',
|
||||
name: '网站引流',
|
||||
values: [
|
||||
{
|
||||
key: '曝光率',
|
||||
value: '19.09',
|
||||
},
|
||||
{
|
||||
key: '流入UV',
|
||||
value: '910',
|
||||
},
|
||||
{
|
||||
key: '点击率',
|
||||
value: '90',
|
||||
},
|
||||
{
|
||||
key: '占比',
|
||||
value: '90',
|
||||
},
|
||||
],
|
||||
properties: [
|
||||
{
|
||||
name: '宫格',
|
||||
value1: '1938.33w',
|
||||
value2: '1938.33w',
|
||||
value3: '99.9%',
|
||||
value4: '99.9%',
|
||||
},
|
||||
{
|
||||
name: '更多应用',
|
||||
value1: '1938.33w',
|
||||
value2: '1938.33w',
|
||||
value3: '99.9%',
|
||||
value4: '99.9%',
|
||||
},
|
||||
{
|
||||
name: '搜索',
|
||||
value1: '1938.33w',
|
||||
value2: '1938.33w',
|
||||
value3: '99.9%',
|
||||
value4: '99.9%',
|
||||
},
|
||||
{
|
||||
name: '扫一扫',
|
||||
value1: '1938.33w',
|
||||
value2: '1938.33w',
|
||||
value3: '99.9%',
|
||||
value4: '99.9%',
|
||||
},
|
||||
{
|
||||
name: '我的Tab',
|
||||
value1: '1938.33w',
|
||||
value2: '1938.33w',
|
||||
value3: '99.9%',
|
||||
value4: '99.9%',
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
{
|
||||
id: 'shape3',
|
||||
//x: 0,
|
||||
//y: 50,
|
||||
shape: 'expandNode',
|
||||
name: '网站引流',
|
||||
values: [
|
||||
{
|
||||
key: '曝光率',
|
||||
value: '19.09',
|
||||
},
|
||||
{
|
||||
key: '流入UV',
|
||||
value: '910',
|
||||
},
|
||||
{
|
||||
key: '点击率',
|
||||
value: '90',
|
||||
},
|
||||
{
|
||||
key: '占比',
|
||||
value: '90',
|
||||
},
|
||||
],
|
||||
properties: [
|
||||
{
|
||||
name: '宫格',
|
||||
value1: '1938.33w',
|
||||
value2: '1938.33w',
|
||||
value3: '99.9%',
|
||||
value4: '99.9%',
|
||||
},
|
||||
{
|
||||
name: '更多应用',
|
||||
value1: '1938.33w',
|
||||
value2: '1938.33w',
|
||||
value3: '99.9%',
|
||||
value4: '99.9%',
|
||||
},
|
||||
{
|
||||
name: '搜索',
|
||||
value1: '1938.33w',
|
||||
value2: '1938.33w',
|
||||
value3: '99.9%',
|
||||
value4: '99.9%',
|
||||
},
|
||||
{
|
||||
name: '扫一扫',
|
||||
value1: '1938.33w',
|
||||
value2: '1938.33w',
|
||||
value3: '99.9%',
|
||||
value4: '99.9%',
|
||||
},
|
||||
{
|
||||
name: '我的Tab',
|
||||
value1: '1938.33w',
|
||||
value2: '1938.33w',
|
||||
value3: '99.9%',
|
||||
value4: '99.9%',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
//统计子节点个数
|
||||
let count = -1;
|
||||
|
||||
G6.Util.traverseTree(data, e => {
|
||||
count++;
|
||||
});
|
||||
|
||||
graph.data(data);
|
||||
graph.render();
|
||||
graph.fitView();
|
||||
|
||||
// 点击node,展开详情
|
||||
graph.on('node:click', function(evt) {
|
||||
console.log(graph);
|
||||
|
||||
var target = evt.target;
|
||||
var parentGroup = target.get('parent').get('parent');
|
||||
var detailGroup = parentGroup.findById('detail-list-group');
|
||||
// 将sub-group中的内容网上移动一段距离
|
||||
var subGroup = parentGroup.findById('sub-group');
|
||||
var keyTexts = subGroup.findAll(function(item) {
|
||||
return item.attr('className') === 'sub-group-text';
|
||||
});
|
||||
var isVisible = detailGroup.get('visible');
|
||||
if (isVisible) {
|
||||
detailGroup.hide();
|
||||
keyTexts.forEach(function(text) {
|
||||
var top = text.attr('y');
|
||||
text.attr('y', top + 10);
|
||||
});
|
||||
|
||||
const layout = {
|
||||
type: 'compactBox',
|
||||
direction: 'LR',
|
||||
getId: d => {
|
||||
return d.id;
|
||||
},
|
||||
getHeight: () => {
|
||||
return 0;
|
||||
},
|
||||
getWidth: () => {
|
||||
return 16;
|
||||
},
|
||||
getVGap: d => {
|
||||
return 50
|
||||
},
|
||||
getHGap: () => {
|
||||
return 80;
|
||||
},
|
||||
}
|
||||
|
||||
graph.changeLayout(layout)
|
||||
} else {
|
||||
keyTexts.forEach(function(text) {
|
||||
var top = text.attr('y');
|
||||
text.attr('y', top - 10);
|
||||
});
|
||||
|
||||
detailGroup.show();
|
||||
|
||||
const layout = {
|
||||
type: 'compactBox',
|
||||
direction: 'LR',
|
||||
getId: d => {
|
||||
return d.id;
|
||||
},
|
||||
getHeight: () => {
|
||||
return 0;
|
||||
},
|
||||
getWidth: () => {
|
||||
return 16;
|
||||
},
|
||||
getVGap: d => {
|
||||
console.log('ok', d, evt);
|
||||
const id = evt.item.get('id');
|
||||
if (d.id === id) {
|
||||
return 120;
|
||||
} else {
|
||||
return 50;
|
||||
}
|
||||
},
|
||||
getHGap: () => {
|
||||
return 80;
|
||||
},
|
||||
}
|
||||
|
||||
graph.changeLayout(layout)
|
||||
|
||||
}
|
||||
//graph.paint();
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
@ -60,7 +60,6 @@
|
||||
document.getElementById('changeView').addEventListener('click', (evt) => {
|
||||
const edge=graph.findById('edge1')
|
||||
const nodeGroup = graph.get('nodeGroup')
|
||||
const edgeGroup = graph.get('edgeGroup')
|
||||
const edge1G = edge.get('group')
|
||||
edge1G.toFront()
|
||||
nodeGroup.toBack();
|
||||
|
194
demos/line-chart-node.html
Normal file
194
demos/line-chart-node.html
Normal file
@ -0,0 +1,194 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta http-equiv="X-UA-Compatible" content="ie=edge">
|
||||
<title>折线图节点</title>
|
||||
<style>
|
||||
#mountNode {
|
||||
background:#001528;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="mountNode"></div>
|
||||
<script src="../build/g6.js"></script>
|
||||
<script>
|
||||
/**
|
||||
* 该案例演示如何自定义一个折线图节点
|
||||
* by 镜曦
|
||||
*
|
||||
*/
|
||||
|
||||
// 自定义折线图节点
|
||||
G6.registerNode('circleLine', {
|
||||
draw(cfg, group) {
|
||||
const size = cfg.size || [40, 40]; // 如果没有 size 时的默认大小
|
||||
const width = size[0];
|
||||
const height = size[1];
|
||||
|
||||
const baseR = 30;
|
||||
let nowAngle = 0;
|
||||
|
||||
// Ref line
|
||||
let refR = baseR;
|
||||
const refInc = 10;
|
||||
for(let i = 0; i< 5; i++){
|
||||
group.addShape('circle', {
|
||||
// attrs: style
|
||||
attrs: {
|
||||
x: 0, // 居中
|
||||
y: 0,
|
||||
r: refR += refInc,
|
||||
stroke:'rgba(255,255,255,0.4)',
|
||||
lineDash:[4, 4],
|
||||
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
const everyIncAngle = 2 * Math.PI * (360 / 5 / 5) / 360;
|
||||
cfg.details.forEach(cat =>{
|
||||
// 计算一系列点的位置
|
||||
const postions = [];
|
||||
cat.values.forEach((item, index) =>{
|
||||
const r = baseR + item;
|
||||
const xPos = r * Math.cos(nowAngle);
|
||||
const yPos = r * Math.sin(nowAngle);
|
||||
nowAngle += everyIncAngle;
|
||||
postions.push([xPos, yPos]);
|
||||
if(index === 4){
|
||||
const r = baseR + item;
|
||||
const xPos = r * Math.cos(nowAngle );
|
||||
const yPos = r * Math.sin(nowAngle );
|
||||
postions.push([xPos, yPos]);
|
||||
}
|
||||
});
|
||||
const pathArrayL = postions.map(item =>(["L", ...item]));
|
||||
// 添加连线
|
||||
const shape = group.addShape('path', {
|
||||
attrs: {
|
||||
path: [
|
||||
['M', 0, 0 ], // 上部顶点
|
||||
...pathArrayL,
|
||||
['Z'] // 封闭
|
||||
],
|
||||
stroke: cat.color // 颜色应用到边上,如果应用到填充,则使用 fill: cfg.color
|
||||
}
|
||||
});
|
||||
// 添加标注点
|
||||
postions.forEach(( pos, index )=>{
|
||||
if(index !== 5){
|
||||
const littleCircle = group.addShape('circle', {
|
||||
// attrs: style
|
||||
attrs: {
|
||||
x: pos[0], // 居中
|
||||
y: pos[1],
|
||||
r: 2,
|
||||
fill: 'black',
|
||||
stroke:cat.color,
|
||||
cursor: "pointer",
|
||||
}
|
||||
});
|
||||
// 加上交互动画
|
||||
littleCircle.on('mouseenter', function(evt) {
|
||||
littleCircle.animate({
|
||||
r: 5,
|
||||
repeat: false
|
||||
}, 200);
|
||||
});
|
||||
littleCircle.on('mouseleave', function(evt) {
|
||||
littleCircle.animate({
|
||||
r: 2,
|
||||
repeat: false
|
||||
}, 200);
|
||||
});
|
||||
// 设置class
|
||||
littleCircle.set("className", 'littleCircle');
|
||||
}
|
||||
|
||||
})
|
||||
|
||||
/*
|
||||
const shape = group.addShape('path', {
|
||||
attrs: {
|
||||
path: [
|
||||
['M', 0, 0 ], // 上部顶点
|
||||
['L', width / 2, 0], // 右侧点
|
||||
['L', 0, height / 2], // 下部
|
||||
['L', - width / 2, 0], // 左侧
|
||||
['Z'] // 封闭
|
||||
],
|
||||
stroke: cfg.color // 颜色应用到边上,如果应用到填充,则使用 fill: cfg.color
|
||||
}
|
||||
});
|
||||
|
||||
*/
|
||||
|
||||
});
|
||||
|
||||
// 添加一个和背景色相同的圆形
|
||||
group.addShape('circle', {
|
||||
// attrs: style
|
||||
attrs: {
|
||||
x: 0, // 居中
|
||||
y: 0,
|
||||
r: baseR,
|
||||
fill: cfg.centerColor,
|
||||
stroke:'darkgray',
|
||||
}
|
||||
});
|
||||
if(cfg.label) {
|
||||
group.addShape('text', {
|
||||
// attrs: style
|
||||
attrs: {
|
||||
x: 0, // 居中
|
||||
y: 0,
|
||||
textAlign: 'center',
|
||||
textBaseline: 'middle',
|
||||
text: cfg.label,
|
||||
fill: 'white',
|
||||
fontStyle:'bold',
|
||||
}
|
||||
});
|
||||
}
|
||||
return group;
|
||||
}
|
||||
});
|
||||
|
||||
const graph = new G6.Graph({
|
||||
container: 'mountNode',
|
||||
width: 500,
|
||||
height: 500
|
||||
})
|
||||
|
||||
const data = {
|
||||
nodes: [
|
||||
{
|
||||
id: 'nodeB',
|
||||
x: 400,
|
||||
y: 150,
|
||||
label: 'Line',
|
||||
shape:'circleLine',
|
||||
anchorPoints: [
|
||||
[0, 0.5], [1, 0.5]
|
||||
],
|
||||
details:[
|
||||
{cat:'pv', values:[20,30,40,30,30], color:"#25cbfd"},
|
||||
{cat:'dal', values:[40,30,20,30,50], color:"#feff7b"},
|
||||
{cat:'uv', values:[40,30,30,40,40], color:"#feab3a"},
|
||||
{cat:'sal', values:[20,30,50,20,20], color:"#fe5766"},
|
||||
{cat:'cal', values:[10,10,20,20,20], color:"#16c176"},
|
||||
],
|
||||
centerColor:'#0066FF',
|
||||
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
graph.data(data)
|
||||
graph.render()
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
173
demos/point-chart-node.html
Normal file
173
demos/point-chart-node.html
Normal file
@ -0,0 +1,173 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta http-equiv="X-UA-Compatible" content="ie=edge">
|
||||
<title>标注图节点</title>
|
||||
<style>
|
||||
#mountNode {
|
||||
background:#001528;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="mountNode"></div>
|
||||
<script src="../build/g6.js"></script>
|
||||
<script>
|
||||
/**
|
||||
* 该案例演示如何自定义一个标注点节点
|
||||
* by 镜曦
|
||||
*
|
||||
*/
|
||||
|
||||
// 自定义标注点节点
|
||||
G6.registerNode('justPoints', {
|
||||
draw(cfg, group) {
|
||||
const size = cfg.size || [40, 40]; // 如果没有 size 时的默认大小
|
||||
const width = size[0];
|
||||
const height = size[1];
|
||||
|
||||
const baseR = 30;
|
||||
let nowAngle = 0;
|
||||
|
||||
// Ref line
|
||||
let refR = baseR;
|
||||
const refInc = 10;
|
||||
for(let i = 0; i< 5; i++){
|
||||
group.addShape('circle', {
|
||||
// attrs: style
|
||||
attrs: {
|
||||
x: 0, // 居中
|
||||
y: 0,
|
||||
r: refR += refInc,
|
||||
stroke:'rgba(255,255,255,0.4)',
|
||||
lineDash:[4, 4],
|
||||
|
||||
}
|
||||
});
|
||||
}
|
||||
const everyIncAngle = 2 * Math.PI * (360 / 5 / 5) / 360;
|
||||
nowAngle = nowAngle + everyIncAngle / 2;
|
||||
cfg.details.forEach(cat =>{
|
||||
// 计算一系列点的位置
|
||||
const postions = [];
|
||||
cat.values.forEach((item, index) =>{
|
||||
const r = baseR + item;
|
||||
const xPos = r * Math.cos(nowAngle);
|
||||
const yPos = r * Math.sin(nowAngle);
|
||||
nowAngle += everyIncAngle;
|
||||
postions.push([xPos, yPos]);
|
||||
if(index === 4){
|
||||
const r = baseR + item;
|
||||
const xPos = r * Math.cos(nowAngle );
|
||||
const yPos = r * Math.sin(nowAngle );
|
||||
postions.push([xPos, yPos]);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
// 添加标注点
|
||||
postions.forEach(( pos, index )=>{
|
||||
if(index !== 5){
|
||||
group.addShape('circle', {
|
||||
// attrs: style
|
||||
attrs: {
|
||||
x: pos[0], // 居中
|
||||
y: pos[1],
|
||||
r: 2,
|
||||
fill: 'black',
|
||||
stroke:cat.color,
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
})
|
||||
|
||||
});
|
||||
|
||||
let nowAngle2 = 0;
|
||||
const everyIncAngleCat = 2 * Math.PI * (360 / 5 ) / 360;
|
||||
for(let i = 0; i < 5; i++){
|
||||
const r = 30 + 50;
|
||||
const xPos = r * Math.cos(nowAngle2);
|
||||
const yPos = r * Math.sin(nowAngle2);
|
||||
|
||||
const shape = group.addShape('path', {
|
||||
attrs: {
|
||||
path: [
|
||||
['M', 0, 0 ],
|
||||
['L', xPos, yPos],
|
||||
|
||||
],
|
||||
lineDash:[4, 4],
|
||||
|
||||
stroke: 'darkgray' // 颜色应用到边上,如果应用到填充,则使用 fill: cfg.color
|
||||
}
|
||||
});
|
||||
nowAngle2 += everyIncAngleCat;
|
||||
}
|
||||
// 添加一个和背景色相同的圆形
|
||||
group.addShape('circle', {
|
||||
// attrs: style
|
||||
attrs: {
|
||||
x: 0, // 居中
|
||||
y: 0,
|
||||
r: baseR,
|
||||
fill: cfg.centerColor,
|
||||
stroke:'darkgray',
|
||||
}
|
||||
});
|
||||
|
||||
if(cfg.label) {
|
||||
group.addShape('text', {
|
||||
// attrs: style
|
||||
attrs: {
|
||||
x: 0, // 居中
|
||||
y: 0,
|
||||
textAlign: 'center',
|
||||
textBaseline: 'middle',
|
||||
text: cfg.label,
|
||||
fill: 'white',
|
||||
fontStyle:'bold',
|
||||
}
|
||||
});
|
||||
}
|
||||
return group;
|
||||
}
|
||||
});
|
||||
|
||||
const graph = new G6.Graph({
|
||||
container: 'mountNode',
|
||||
width: 500,
|
||||
height: 500
|
||||
})
|
||||
|
||||
const data = {
|
||||
nodes: [
|
||||
{
|
||||
id: 'nodeC',
|
||||
x: 250,
|
||||
y: 150,
|
||||
label: 'Point',
|
||||
shape:'justPoints',
|
||||
anchorPoints: [
|
||||
[0, 0.5], [1, 0.5]
|
||||
],
|
||||
details:[
|
||||
{cat:'pv', values:[20,30,40,30,30], color:"#25cbfd"},
|
||||
{cat:'dal', values:[40,30,20,30,50], color:"#feff7b"},
|
||||
{cat:'uv', values:[40,30,30,40,40], color:"#feab3a"},
|
||||
{cat:'sal', values:[20,30,50,20,20], color:"#fe5766"},
|
||||
{cat:'cal', values:[10,10,20,20,20], color:"#16c176"},
|
||||
],
|
||||
centerColor:'#0066FF',
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
graph.data(data)
|
||||
graph.render()
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
146
demos/stacked-bar-chart-node.html
Normal file
146
demos/stacked-bar-chart-node.html
Normal file
@ -0,0 +1,146 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta http-equiv="X-UA-Compatible" content="ie=edge">
|
||||
<title>堆叠柱状图节点</title>
|
||||
<style>
|
||||
#mountNode {
|
||||
background:#001528;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="mountNode"></div>
|
||||
<script src="../build/g6.js"></script>
|
||||
<script>
|
||||
/**
|
||||
* 该案例演示如何自定义一个堆叠柱状图节点
|
||||
* by 镜曦
|
||||
*
|
||||
*/
|
||||
|
||||
// 自定义标注点节点
|
||||
G6.registerNode('stacked-bar-node', {
|
||||
draw(cfg, group) {
|
||||
const size = cfg.size || [40, 40]; // 如果没有 size 时的默认大小
|
||||
const width = size[0];
|
||||
const height = size[1];
|
||||
/*
|
||||
G:
|
||||
Fan
|
||||
x: 扇形圆心的 x 坐标
|
||||
y: 扇形圆心的 y 坐标
|
||||
rs: 内圈半径
|
||||
re: 外圈半径
|
||||
startAngle: 起点弧度
|
||||
endAngle: 终点弧度
|
||||
clockwise: 为true时顺时针渲染,为false时逆时针渲染
|
||||
*/
|
||||
const baseR = 30;
|
||||
let nowAngle = 0;
|
||||
const everyIncAngle = 2 * Math.PI * (360 / 5 / 5) / 360;
|
||||
cfg.details.forEach(cat =>{
|
||||
cat.values.forEach(item =>{
|
||||
const baseNbr = Math.ceil(item / 10);
|
||||
const baseIncR = 7;
|
||||
let nowStartR = baseR;
|
||||
const last = item % 10;
|
||||
const endAngle = nowAngle + everyIncAngle;
|
||||
for (let i = 0; i < baseNbr ; i ++ ) {
|
||||
const fan = group.addShape('fan', {
|
||||
attrs:{
|
||||
x:0,
|
||||
y:0,
|
||||
rs:nowStartR,
|
||||
re:nowStartR + baseIncR,
|
||||
startAngle:nowAngle,
|
||||
endAngle:endAngle,
|
||||
clockwise:false,
|
||||
stroke: 'darkgray',
|
||||
fill:cat.color,
|
||||
}
|
||||
});
|
||||
nowStartR = nowStartR + baseIncR + 2
|
||||
if(i === baseNbr -1 && last !== 0){
|
||||
const fan = group.addShape('fan', {
|
||||
attrs:{
|
||||
x:0,
|
||||
y:0,
|
||||
rs:nowStartR,
|
||||
re:nowStartR + baseIncR * last / 10,
|
||||
startAngle:nowAngle,
|
||||
endAngle:endAngle,
|
||||
clockwise:false,
|
||||
stroke: 'darkgray',
|
||||
fill:cat.color,
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
nowAngle = endAngle
|
||||
});
|
||||
});
|
||||
|
||||
group.addShape('circle', {
|
||||
// attrs: style
|
||||
attrs: {
|
||||
x: 0, // 居中
|
||||
y: 0,
|
||||
r: baseR,
|
||||
fill: cfg.centerColor,
|
||||
stroke:'darkgray',
|
||||
}
|
||||
});
|
||||
if(cfg.label) {
|
||||
group.addShape('text', {
|
||||
// attrs: style
|
||||
attrs: {
|
||||
x: 0, // 居中
|
||||
y: 0,
|
||||
textAlign: 'center',
|
||||
textBaseline: 'middle',
|
||||
text: cfg.label,
|
||||
fill: 'white',
|
||||
fontStyle:'bold',
|
||||
}
|
||||
});
|
||||
}
|
||||
return group;
|
||||
}
|
||||
});
|
||||
|
||||
const graph = new G6.Graph({
|
||||
container: 'mountNode',
|
||||
width: 500,
|
||||
height: 500
|
||||
})
|
||||
|
||||
const data = {
|
||||
nodes: [
|
||||
{
|
||||
id: 'nodeF',
|
||||
x: 100,
|
||||
y: 100,
|
||||
label: 'StackedBar',
|
||||
shape:'stacked-bar-node',
|
||||
anchorPoints: [
|
||||
[0, 0.5], [1, 0.5]
|
||||
],
|
||||
details:[
|
||||
{cat:'pv', values:[20,30,48,30,30], color:"#25cbfd"},
|
||||
{cat:'dal', values:[40,30,20,30,50], color:"#feff7b"},
|
||||
{cat:'uv', values:[40,30,30,4,40], color:"#feab3a"},
|
||||
{cat:'sal', values:[20,30,50,20,20], color:"#fe5766"},
|
||||
{cat:'cal', values:[10,10,25,20,20], color:"#16c176"},
|
||||
],
|
||||
centerColor:'#0066FF'
|
||||
}]
|
||||
}
|
||||
|
||||
graph.data(data)
|
||||
graph.render()
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
@ -98,7 +98,7 @@
|
||||
"screenshot": "node ./bin/screenshot.js",
|
||||
"start": "npm run dev",
|
||||
"test": "torch --compile --renderer --opts test/mocha.opts --recursive ./test/unit",
|
||||
"test-live": "torch --compile --interactive --watch --opts test/mocha.opts --recursive ./test/unit/behavior/drag-group-spec.js",
|
||||
"test-live": "torch --compile --interactive --watch --opts test/mocha.opts --recursive ./test/unit/graph/controller/custom-group-spec.js",
|
||||
"test-live-tree": "torch --compile --interactive --watch --opts test/mocha.opts --recursive ./test/unit/graph/tree-graph-spec.js",
|
||||
"test-bugs": "torch --compile --renderer --recursive ./test/bugs",
|
||||
"test-bugs-live": "torch --compile --interactive --watch --recursive ./test/bugs",
|
||||
|
@ -5,7 +5,7 @@
|
||||
* @LastEditTime: 2019-08-23 11:13:43
|
||||
* @Description: 拖动群组
|
||||
*/
|
||||
const { merge } = require('lodash');
|
||||
const deepMix = require('@antv/util/lib/deep-mix');
|
||||
|
||||
const delegateStyle = {
|
||||
fill: '#F3F9FF',
|
||||
@ -275,7 +275,7 @@ module.exports = {
|
||||
height,
|
||||
x,
|
||||
y,
|
||||
...merge({}, delegateStyle, this.delegateStyle)
|
||||
...deepMix({}, delegateStyle, this.delegateStyle)
|
||||
};
|
||||
|
||||
// 如果delegate是circle
|
||||
@ -289,7 +289,7 @@ module.exports = {
|
||||
x: cx,
|
||||
y: cy,
|
||||
r,
|
||||
...merge({}, delegateStyle, this.delegateStyle)
|
||||
...deepMix({}, delegateStyle, this.delegateStyle)
|
||||
}
|
||||
});
|
||||
self.shapeOrigin = { x: cx, y: cy };
|
||||
|
@ -5,7 +5,7 @@
|
||||
* @LastEditTime: 2019-08-23 13:54:53
|
||||
* @Description: 有group的情况下,拖动节点的Behavior
|
||||
*/
|
||||
const { merge } = require('lodash');
|
||||
const deepMix = require('@antv/util/lib/deep-mix');
|
||||
const { delegateStyle } = require('../global');
|
||||
const body = document.body;
|
||||
|
||||
@ -15,8 +15,8 @@ module.exports = {
|
||||
updateEdge: true,
|
||||
delegate: true,
|
||||
delegateStyle: {},
|
||||
maxMultiple: 1.2,
|
||||
minMultiple: 0.8
|
||||
maxMultiple: 0.9,
|
||||
minMultiple: 1
|
||||
};
|
||||
},
|
||||
getEvents() {
|
||||
@ -26,12 +26,16 @@ module.exports = {
|
||||
'node:dragend': 'onDragEnd',
|
||||
'canvas:mouseleave': 'onOutOfRange',
|
||||
mouseenter: 'onMouseEnter',
|
||||
mouseout: 'onMouseOut'
|
||||
mouseleave: 'onMouseLeave'
|
||||
};
|
||||
},
|
||||
onMouseEnter(evt) {
|
||||
const { target } = evt;
|
||||
const groupId = target.get('groupId');
|
||||
const type = target.get('type');
|
||||
if (type !== 'circle') {
|
||||
return;
|
||||
}
|
||||
if (groupId && this.origin) {
|
||||
const graph = this.graph;
|
||||
const customGroupControll = graph.get('customGroupControll');
|
||||
@ -47,7 +51,7 @@ module.exports = {
|
||||
* 拖动节点移除Group时的事件
|
||||
* @param {Event} evt 事件句柄
|
||||
*/
|
||||
onMouseOut(evt) {
|
||||
onMouseLeave(evt) {
|
||||
const { target } = evt;
|
||||
const groupId = target.get('groupId');
|
||||
if (groupId && this.origin) {
|
||||
@ -59,7 +63,10 @@ module.exports = {
|
||||
|
||||
customGroupControll.setGroupStyle(keyShape, 'default');
|
||||
}
|
||||
|
||||
if (!groupId) {
|
||||
this.inGroupId = null;
|
||||
}
|
||||
},
|
||||
onDragStart(e) {
|
||||
if (!this.shouldBegin.call(this, e)) {
|
||||
@ -93,6 +100,9 @@ module.exports = {
|
||||
const customGroup = customGroupControll.customGroup;
|
||||
const currentGroup = customGroup[groupId].nodeGroup;
|
||||
customGroupControll.setGroupStyle(currentGroup.get('keyShape'), 'hover');
|
||||
|
||||
// 初始拖动时候,如果是在当前群组中拖动,则赋值为当前groupId
|
||||
this.inGroupId = groupId;
|
||||
}
|
||||
} else {
|
||||
// 拖动多个节点
|
||||
@ -137,13 +147,8 @@ module.exports = {
|
||||
const currentGroup = customGroup[groupId].nodeGroup;
|
||||
const keyShape = currentGroup.get('keyShape');
|
||||
|
||||
const currentGroupBBox = keyShape.getBBox();
|
||||
|
||||
const delegateShape = this.target.get('delegateShape');
|
||||
const { x, y } = delegateShape.getBBox();
|
||||
const { minX, minY, maxX, maxY } = currentGroupBBox;
|
||||
|
||||
if (x > maxX || x < minX || y > maxY || y < minY) {
|
||||
// 当前
|
||||
if (this.inGroupId !== groupId) {
|
||||
customGroupControll.setGroupStyle(keyShape, 'default');
|
||||
} else {
|
||||
customGroupControll.setGroupStyle(keyShape, 'hover');
|
||||
@ -224,11 +229,12 @@ module.exports = {
|
||||
// 检测操作的群组中是否包括子群组
|
||||
const groups = graph.get('groups');
|
||||
const hasSubGroup = !!groups.filter(g => g.parentId === groupId).length > 0;
|
||||
const r = width > height ? width / 2 : height / 2 + (hasSubGroup ? 20 : 0);
|
||||
const addR = hasSubGroup ? 20 : 10;
|
||||
const r = width > height ? width / 2 : height / 2;
|
||||
const cx = (width + 2 * x) / 2;
|
||||
const cy = (height + 2 * y) / 2;
|
||||
keyShape.attr({
|
||||
r: r + groupNodes[groupId].length * 10,
|
||||
r: r + groupNodes[groupId].length * 10 + addR,
|
||||
x: cx,
|
||||
y: cy
|
||||
});
|
||||
@ -247,7 +253,6 @@ module.exports = {
|
||||
// 节点所在的GroupId
|
||||
const { groupId, id } = model;
|
||||
|
||||
// console.log(groupId, this.inGroupId)
|
||||
const customGroupControll = graph.get('customGroupControll');
|
||||
const customGroup = customGroupControll.customGroup;
|
||||
const groupNodes = graph.get('groupNodes');
|
||||
@ -262,7 +267,11 @@ module.exports = {
|
||||
const { minX, minY, maxX, maxY } = currentGroupBBox;
|
||||
|
||||
// 在自己的group中拖动,判断是否拖出了自己的group
|
||||
if (!(x < maxX * this.maxMultiple && x > minX * this.minMultiple && y < maxY * this.maxMultiple && y > minY * this.minMultiple)) {
|
||||
// this.inGroupId !== groupId,则说明拖出了原来的group,拖到了其他group上面,
|
||||
// 则删除item中的groupId字段,同时删除group中的nodeID
|
||||
if (
|
||||
!(x < maxX * this.maxMultiple && x > minX * this.minMultiple && y < maxY * this.maxMultiple && y > minY * this.minMultiple)
|
||||
|| this.inGroupId !== groupId) {
|
||||
// 拖出了group,则删除item中的groupId字段,同时删除group中的nodeID
|
||||
const currentGroupNodes = groupNodes[groupId];
|
||||
groupNodes[groupId] = currentGroupNodes.filter(node => node !== id);
|
||||
@ -274,8 +283,10 @@ module.exports = {
|
||||
}
|
||||
// 拖动到其他的group上面
|
||||
if (this.inGroupId !== groupId) {
|
||||
|
||||
// 拖动新的group后,更新groupNodes及model中的groupId
|
||||
const nodeInGroup = customGroup[this.inGroupId].nodeGroup;
|
||||
const keyShape = nodeInGroup.get('keyShape');
|
||||
const targetKeyShape = nodeInGroup.get('keyShape');
|
||||
// 将该节点添加到inGroupId中
|
||||
if (groupNodes[this.inGroupId].indexOf(id) === -1) {
|
||||
groupNodes[this.inGroupId].push(id);
|
||||
@ -284,7 +295,7 @@ module.exports = {
|
||||
model.groupId = this.inGroupId;
|
||||
|
||||
// 拖入节点后,根据最新的节点数量,重新计算群组大小
|
||||
this.dynamicChangeGroupSize(evt, nodeInGroup, keyShape);
|
||||
this.dynamicChangeGroupSize(evt, nodeInGroup, targetKeyShape);
|
||||
}
|
||||
customGroupControll.setGroupStyle(keyShape, 'default');
|
||||
} else if (this.inGroupId && !groupId) {
|
||||
@ -372,7 +383,7 @@ module.exports = {
|
||||
if (!this.shape) {
|
||||
// 拖动多个
|
||||
const parent = graph.get('group');
|
||||
const attrs = merge({}, delegateStyle, this.delegateStyle);
|
||||
const attrs = deepMix({}, delegateStyle, this.delegateStyle);
|
||||
if (this.targets.length > 0) {
|
||||
const nodes = graph.findAllByState('node', 'selected');
|
||||
if (nodes.length === 0) {
|
||||
|
@ -5,7 +5,8 @@
|
||||
* @LastEditTime: 2019-08-22 18:41:45
|
||||
* @Description: 拖动节点的Behavior
|
||||
*/
|
||||
const { merge, isString } = require('lodash');
|
||||
const isString = require('@antv/util/lib/type/is-string');
|
||||
const deepMix = require('@antv/util/lib/deep-mix');
|
||||
const { delegateStyle } = require('../global');
|
||||
const body = document.body;
|
||||
|
||||
@ -174,7 +175,7 @@ module.exports = {
|
||||
if (!this.shape) {
|
||||
// 拖动多个
|
||||
const parent = this.graph.get('group');
|
||||
const attrs = merge({}, delegateStyle, this.delegateStyle);
|
||||
const attrs = deepMix({}, delegateStyle, this.delegateStyle);
|
||||
if (this.targets.length > 0) {
|
||||
const { x, y, width, height, minX, minY } = this.calculationGroupPosition();
|
||||
this.originPoint = { x, y, width, height, minX, minY };
|
||||
|
@ -5,8 +5,8 @@
|
||||
* @LastEditTime: 2019-08-23 11:44:32
|
||||
* @Description: Group Controller
|
||||
*/
|
||||
const { merge, isString } = require('lodash');
|
||||
|
||||
const isString = require('@antv/util/lib/type/is-string');
|
||||
const deepMix = require('@antv/util/lib/deep-mix');
|
||||
class CustomGroup {
|
||||
getDefaultCfg() {
|
||||
return {
|
||||
@ -60,7 +60,8 @@ class CustomGroup {
|
||||
// const { cfg = {} } = options;
|
||||
this.graph = graph;
|
||||
window.graph = graph;
|
||||
this.styles = this.getDefaultCfg();
|
||||
const groupStyle = graph.get('groupStyle');
|
||||
this.styles = deepMix({}, this.getDefaultCfg(), groupStyle);
|
||||
// 创建的群组集合
|
||||
this.customGroup = {};
|
||||
// 群组初始位置集合
|
||||
@ -154,12 +155,12 @@ class CustomGroup {
|
||||
const { hover: hoverStyle, default: defaultStyle } = this.styles;
|
||||
if (isString(style)) {
|
||||
if (style === 'default') {
|
||||
styles = merge({}, defaultStyle);
|
||||
styles = deepMix({}, defaultStyle);
|
||||
} else if (style === 'hover') {
|
||||
styles = merge({}, hoverStyle);
|
||||
styles = deepMix({}, hoverStyle);
|
||||
}
|
||||
} else {
|
||||
styles = merge({}, defaultStyle, style);
|
||||
styles = deepMix({}, defaultStyle, style);
|
||||
}
|
||||
for (const s in styles) {
|
||||
keyShape.attr(s, styles[s]);
|
||||
@ -328,7 +329,7 @@ class CustomGroup {
|
||||
} else {
|
||||
// 更新时候merge配置项
|
||||
const { groupStyle } = customGroupStyle;
|
||||
const styles = merge({}, groupStyle, property);
|
||||
const styles = deepMix({}, groupStyle, property);
|
||||
this.customGroup[groupId] = {
|
||||
nodeGroup: deletage,
|
||||
groupStyle: styles
|
||||
@ -536,7 +537,7 @@ class CustomGroup {
|
||||
});
|
||||
|
||||
// 缓存群组groupId下的edge和临时生成的node节点
|
||||
this.delegateInGroup[groupId] = merge({
|
||||
this.delegateInGroup[groupId] = deepMix({
|
||||
sourceOutTargetInEdges,
|
||||
sourceInTargetOutEdges,
|
||||
edgesOuts,
|
||||
@ -571,7 +572,7 @@ class CustomGroup {
|
||||
|
||||
const { default: defaultStyle } = this.styles;
|
||||
|
||||
// const styles = merge({}, defaultStyle, { x: cx, y: cy });
|
||||
// const styles = deepMix({}, defaultStyle, { x: cx, y: cy });
|
||||
for (const style in defaultStyle) {
|
||||
keyShape.attr(style, defaultStyle[style]);
|
||||
}
|
||||
|
@ -3,11 +3,7 @@
|
||||
* @Date: 2019-06-27 18:12:06
|
||||
* @LastEditors: moyee
|
||||
* @LastEditTime: 2019-08-22 11:22:16
|
||||
* @Description: file content
|
||||
*/
|
||||
/**
|
||||
* @fileOverview graph
|
||||
* @author huangtonger@aliyun.com
|
||||
* @Description: Graph
|
||||
*/
|
||||
const { groupBy } = require('lodash');
|
||||
const G = require('@antv/g/lib');
|
||||
@ -200,7 +196,8 @@ class Graph extends EventEmitter {
|
||||
/**
|
||||
* 群组的原始数据
|
||||
*/
|
||||
groups: []
|
||||
groups: [],
|
||||
groupStyle: {}
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -594,7 +594,7 @@ describe.only('signle layer group', () => {
|
||||
expect(isVisible).to.be.true;
|
||||
}
|
||||
|
||||
expect(keyShape.attr('r')).eql(groupStyle.r);
|
||||
expect(keyShape.attr('r')).eql(30);
|
||||
expect(keyShape.attr('x')).eql(groupStyle.x);
|
||||
expect(keyShape.attr('y')).eql(groupStyle.y);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user