g6/demos/custom-node-chart.html
2020-02-14 11:30:12 +08:00

924 lines
28 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!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,
duration: 300,
},
);
});
fan.on('mouseleave', function(evt) {
fan.animate(
{
re: re,
},
{
repeat: false,
duration: 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,
duration: 200,
},
);
});
littleCircle.on('mouseleave', function(evt) {
littleCircle.animate(
{
r: 2,
},
{
repeat: false,
duration: 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的xy必须是相对于画布的坐标
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>