feat: add demo about why do cats (#6259)

* test: add demo of why do cats

* docs: add site demo

* docs: add demo ref

---------

Co-authored-by: antv <antv@antfin.com>
This commit is contained in:
Aaron 2024-09-02 10:02:43 +08:00 committed by GitHub
parent 6317115603
commit b00ec104f8
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 381 additions and 0 deletions

View File

@ -30,6 +30,8 @@
]
},
"devDependencies": {
"@antv/g-canvas": "^2.0.10",
"@antv/g-plugin-rough-canvas-renderer": "^2.0.12",
"@babel/core": "^7.25.2",
"@babel/plugin-transform-typescript": "^7.25.2",
"@changesets/cli": "^2.27.7",
@ -42,12 +44,14 @@
"@rollup/plugin-typescript": "^11.1.6",
"@swc/core": "^1.7.6",
"@swc/jest": "^0.2.36",
"@types/d3-hierarchy": "^3.1.7",
"@types/jest": "^29.5.12",
"@types/jsdom": "^21.1.7",
"@types/node": "^20.14.14",
"@typescript-eslint/eslint-plugin": "^6.21.0",
"@typescript-eslint/parser": "^6.21.0",
"chalk": "^4.1.2",
"d3-hierarchy": "^3.1.2",
"eslint": "^8.57.0",
"eslint-plugin-jsdoc": "^46.10.1",
"husky": "^8.0.3",

View File

@ -0,0 +1,189 @@
import { Renderer as CanvasRenderer } from '@antv/g-canvas';
import { Plugin as PluginRoughCanvasRenderer } from '@antv/g-plugin-rough-canvas-renderer';
import type { ComboData, GraphData, NodeData } from '@antv/g6';
import { BaseLayout, ExtensionCategory, Graph, register } from '@antv/g6';
import { hierarchy, pack } from 'd3-hierarchy';
export const caseWhyDoCats: TestCase = async (context) => {
const style = document.createElement('style');
style.innerHTML = `
@font-face {
font-family: 'handwriting';
src: url('https://mass-office.alipay.com/huamei_koqzbu/afts/file/sgUeRbI3d-IAAAAAAAAAABAADnV5AQBr/font.woff2')
format('woff2');
}`;
document.head.appendChild(style);
function getColor(id: string) {
const colors = [
'#8dd3c7',
'#bebada',
'#fb8072',
'#80b1d3',
'#fdb462',
'#b3de69',
'#fccde5',
'#d9d9d9',
'#bc80bd',
'#ccebc5',
'#ffed6f',
];
const index = parseInt(id);
return colors[index % colors.length];
}
type RowDatum = {
animal: string;
id: string;
id_num: string;
index_value: string;
leaf: string;
parentId: string;
remainder: string;
start_sentence: string;
sum_index_value: string;
text: string;
};
const rawData: RowDatum[] = await fetch('https://assets.antv.antgroup.com/g6/cat-hierarchy.json').then((res) =>
res.json(),
);
const topics = [
'cat.like',
'cat.hate',
'cat.love',
'cat.not.like',
'cat.afraid_of',
'cat.want.to',
'cat.scared.of',
'cat.not.want_to',
];
const graphData = rawData.reduce(
(acc, row) => {
const { id } = row;
topics.forEach((topic) => {
if (id.startsWith(topic)) {
if (id === topic) {
acc.nodes.push({ ...row, depth: 1 });
} else {
acc.nodes.push({ ...row, depth: 2, actualParentId: topic });
}
}
});
return acc;
},
{ nodes: [], edges: [], combos: [] } as Required<GraphData>,
);
class BubbleLayout extends BaseLayout {
id = 'bubble-layout';
public async execute(model: GraphData, options?: any): Promise<GraphData> {
const { nodes = [] } = model;
const { width = 0, height = 0 } = { ...this.options, ...options };
const root = hierarchy<NodeData | ComboData>({ id: 'root' }, (datum) => {
const { id } = datum;
if (id === 'root') return nodes.filter((node) => node.depth === 1);
else if (datum.depth === 2) return [];
else return nodes.filter((node) => node.actualParentId === id);
});
root.sum((d: any) => (+d.index_value || 0.01) ** 0.5 * 100);
pack<NodeData | ComboData>()
.size([width, height])
.padding((node) => {
return node.depth === 0 ? 20 : 2;
})(root);
const result: Required<GraphData> = { nodes: [], edges: [], combos: [] };
root.descendants().forEach((node) => {
const {
data: { id },
x,
y,
// @ts-expect-error r is exist
r,
} = node;
if (node.depth >= 1) result.nodes.push({ id, style: { x, y, size: r * 2 } });
});
return result;
}
}
register(ExtensionCategory.LAYOUT, 'bubble-layout', BubbleLayout);
const graph = new Graph({
...context,
animation: false,
data: graphData,
renderer: (layer) => {
const renderer = new CanvasRenderer();
if (layer === 'main') {
renderer.registerPlugin(new PluginRoughCanvasRenderer());
}
return renderer;
},
node: {
style: (d) => {
const id_num = d.id_num as string;
const color = getColor(id_num);
if (d.depth === 1) {
return {
fill: 'none',
stroke: color,
labelFontFamily: 'handwriting',
labelFontSize: 20,
labelText: d.id.replace('cat.', '').replace(/\.|_/g, ' '),
labelTextTransform: 'capitalize',
lineWidth: 1,
zIndex: -1,
};
}
const text = d.text as string;
const diameter = d.style!.size as number;
return {
fill: color,
fillOpacity: 0.7,
stroke: color,
fillStyle: 'cross-hatch',
hachureGap: 1.5,
iconFontFamily: 'handwriting',
iconFontSize: (diameter / text.length) * 2,
iconText: diameter > 20 ? d.text : '',
iconFontWeight: 'bold',
iconStroke: color,
iconLineWidth: 2,
lineWidth: (diameter || 20) ** 0.5 / 5,
};
},
},
layout: {
type: 'bubble-layout',
},
plugins: [
{
type: 'tooltip',
getContent: (event: any, items: NodeData[]) => {
return `<span style="text-transform: capitalize; font-family: handwriting; font-size: 20px;">${items[0].id.replace(/\.|_/g, ' ')}</span>`;
},
},
],
behaviors: [{ type: 'drag-canvas', enable: true }, 'zoom-canvas'],
});
await graph.render();
return graph;
};

View File

@ -25,6 +25,7 @@ export { caseDecisionTree } from './case-decision-tree';
export { caseIndentedTree } from './case-indented-tree';
export { caseMindmap } from './case-mindmap';
export { caseOrgChart } from './case-org-chart';
export { caseWhyDoCats } from './case-why-do-cats';
export { commonGraph } from './common-graph';
export { controllerViewport } from './controller-viewport';
export { demoAutosizeElementLabel } from './demo-autosize-element-label';

View File

@ -1019,6 +1019,7 @@ export class Graph extends EventEmitter {
if (container instanceof Canvas) {
this.context.canvas = container;
if (cursor) container.setCursor(cursor);
if (renderer) container.setRenderer(renderer);
await container.ready;
} else {
const $container = isString(container) ? document.getElementById(container!) : container;

View File

@ -1,5 +1,9 @@
// @ts-nocheck
if (window) {
window.d3Hierarchy = require('d3-hierarchy');
window.gCanvas = require('@antv/g-canvas');
window.gPluginRoughCanvasRenderer = require('@antv/g-plugin-rough-canvas-renderer');
window.g6 = require('@antv/g6');
window.g6Extension3d = require('@antv/g6-extension-3d');
window.g6ExtensionReact = require('@antv/g6-extension-react');

View File

@ -59,6 +59,14 @@
"en": "Sub Graph"
},
"screenshot": "https://mdn.alipayobjects.com/huamei_qa8qxu/afts/img/A*2HzDTrQZ910AAAAAAAAAAAAADmJ7AQ/original"
},
{
"filename": "why-do-cats.js",
"title": {
"zh": "猫咪喜好",
"en": "Why Do Cats?"
},
"screenshot": "https://mdn.alipayobjects.com/huamei_qa8qxu/afts/img/A*ug4vTJA7QbMAAAAAAAAAAAAADmJ7AQ/original"
}
]
}

View File

@ -0,0 +1,174 @@
// ref: https://whydocatsanddogs.com/cats
import { Renderer as CanvasRenderer } from '@antv/g-canvas';
import { Plugin as PluginRoughCanvasRenderer } from '@antv/g-plugin-rough-canvas-renderer';
import { BaseLayout, ExtensionCategory, Graph, register } from '@antv/g6';
import { hierarchy, pack } from 'd3-hierarchy';
const style = document.createElement('style');
style.innerHTML = `
@font-face {
font-family: 'handwriting';
src: url('https://mass-office.alipay.com/huamei_koqzbu/afts/file/sgUeRbI3d-IAAAAAAAAAABAADnV5AQBr/font.woff2')
format('woff2');
}`;
document.head.appendChild(style);
function getColor(id) {
const colors = [
'#8dd3c7',
'#bebada',
'#fb8072',
'#80b1d3',
'#fdb462',
'#b3de69',
'#fccde5',
'#d9d9d9',
'#bc80bd',
'#ccebc5',
'#ffed6f',
];
const index = parseInt(id);
return colors[index % colors.length];
}
const topics = [
'cat.like',
'cat.hate',
'cat.love',
'cat.not.like',
'cat.afraid_of',
'cat.want.to',
'cat.scared.of',
'cat.not.want_to',
];
class BubbleLayout extends BaseLayout {
id = 'bubble-layout';
async execute(model, options) {
const { nodes = [] } = model;
const { width = 0, height = 0 } = { ...this.options, ...options };
const root = hierarchy({ id: 'root' }, (datum) => {
const { id } = datum;
if (id === 'root') return nodes.filter((node) => node.depth === 1);
else if (datum.depth === 2) return [];
else return nodes.filter((node) => node.actualParentId === id);
});
root.sum((d) => (+d.index_value || 0.01) ** 0.5 * 100);
pack()
.size([width, height])
.padding((node) => {
return node.depth === 0 ? 20 : 2;
})(root);
const result = { nodes: [] };
root.descendants().forEach((node) => {
const {
data: { id },
x,
y,
// @ts-expect-error r is exist
r,
} = node;
if (node.depth >= 1) result.nodes.push({ id, style: { x, y, size: r * 2 } });
});
return result;
}
}
register(ExtensionCategory.LAYOUT, 'bubble-layout', BubbleLayout);
fetch('https://assets.antv.antgroup.com/g6/cat-hierarchy.json')
.then((res) => res.json())
.then((rawData) => {
const graphData = rawData.reduce(
(acc, row) => {
const { id } = row;
topics.forEach((topic) => {
if (id.startsWith(topic)) {
if (id === topic) {
acc.nodes.push({ ...row, depth: 1 });
} else {
acc.nodes.push({ ...row, depth: 2, actualParentId: topic });
}
}
});
return acc;
},
{ nodes: [] },
);
const graph = new Graph({
container: 'container',
animation: false,
data: graphData,
renderer: (layer) => {
const renderer = new CanvasRenderer();
if (layer === 'main') {
renderer.registerPlugin(new PluginRoughCanvasRenderer());
}
return renderer;
},
node: {
style: (d) => {
const { id, depth, id_num } = d;
const color = getColor(id_num);
if (depth === 1) {
return {
fill: 'none',
stroke: color,
labelFontFamily: 'handwriting',
labelFontSize: 20,
labelText: id.replace('cat.', '').replace(/\.|_/g, ' '),
labelTextTransform: 'capitalize',
lineWidth: 1,
zIndex: -1,
};
}
const {
text,
style: { size: diameter },
} = d;
return {
fill: color,
fillOpacity: 0.7,
stroke: color,
fillStyle: 'cross-hatch',
hachureGap: 1.5,
iconFontFamily: 'handwriting',
iconFontSize: (diameter / text.length) * 2,
iconText: diameter > 20 ? text : '',
iconFontWeight: 'bold',
iconStroke: color,
iconLineWidth: 2,
lineWidth: (diameter || 20) ** 0.5 / 5,
};
},
},
layout: {
type: 'bubble-layout',
},
plugins: [
{
type: 'tooltip',
getContent: (event, items) => {
return `<span style="text-transform: capitalize; font-family: handwriting; font-size: 20px;">${items[0].id.replace(/\.|_/g, ' ')}</span>`;
},
},
],
behaviors: [{ type: 'drag-canvas', enable: true }, 'zoom-canvas'],
});
graph.render();
});