diff --git a/examples/case/australiaFire/index.en.md b/examples/case/australiaFire/index.en.md index b71006fd50..05cb1bc941 100644 --- a/examples/case/australiaFire/index.en.md +++ b/examples/case/australiaFire/index.en.md @@ -1,6 +1,6 @@ --- title: Australia Fire -order: 6 +order: 7 --- This graph demonstrates the fire affectness and population on different cities of Australia. The data comes from NASA, which includes the fire points of Australia detected by the satellites. [Link of the data](https://firms.modaps.eosdis.nasa.gov/active_fire/#firms-shapefile). diff --git a/examples/case/australiaFire/index.zh.md b/examples/case/australiaFire/index.zh.md index e8d2af8702..6b9875cd11 100644 --- a/examples/case/australiaFire/index.zh.md +++ b/examples/case/australiaFire/index.zh.md @@ -1,6 +1,6 @@ --- title: 澳大利亚大火 -order: 6 +order: 7 --- 本图展示了 2020 年 1 月 11 日到 18 日的火灾情况,数据来源于 NASA 公开数据,由卫星拍摄到的起火点。[数据链接](https://firms.modaps.eosdis.nasa.gov/active_fire/#firms-shapefile)。 diff --git a/examples/case/christmasBubbles/index.en.md b/examples/case/christmasBubbles/index.en.md index 5bb1c4eba5..b88b1af49a 100644 --- a/examples/case/christmasBubbles/index.en.md +++ b/examples/case/christmasBubbles/index.en.md @@ -1,6 +1,6 @@ --- title: Top 100 Words at Christmas -order: 1 +order: 3 --- This demo shows the top 100 hot words on Tweeter at Christmas eve and Christmas day. You can drag and click the nodes to explore the context of the top words. diff --git a/examples/case/christmasBubbles/index.zh.md b/examples/case/christmasBubbles/index.zh.md index 075c41c9e4..bdb139cc23 100644 --- a/examples/case/christmasBubbles/index.zh.md +++ b/examples/case/christmasBubbles/index.zh.md @@ -1,6 +1,6 @@ --- title: 圣诞推文可视化 -order: 1 +order: 3 --- 该案例展示了圣诞夜与圣诞节网友们在 Tweeter 上发送的推文中,出现频率 top 100 的单词。可以通过拖拽、点击等交互,查看单词的上下文。 diff --git a/examples/case/customFlow/index.en.md b/examples/case/customFlow/index.en.md index 3bcd764b30..08ed12b926 100644 --- a/examples/case/customFlow/index.en.md +++ b/examples/case/customFlow/index.en.md @@ -1,6 +1,6 @@ --- title: Custom Fow Graph -order: 5 +order: 1 --- Custom flow graph. diff --git a/examples/case/customFlow/index.zh.md b/examples/case/customFlow/index.zh.md index aeed9defbb..c8f0d73658 100644 --- a/examples/case/customFlow/index.zh.md +++ b/examples/case/customFlow/index.zh.md @@ -1,6 +1,6 @@ --- title: 自定义流向图 -order: 5 +order: 1 --- 自定义流向图。 diff --git a/examples/case/decisionTree/demo/index.ts b/examples/case/decisionTree/demo/index.ts index 73753b0f7d..f09a53e9ed 100644 --- a/examples/case/decisionTree/demo/index.ts +++ b/examples/case/decisionTree/demo/index.ts @@ -1,165 +1,226 @@ import G6 from '@antv/g6'; +import { TreeGraphData } from '../../../../src/types'; +import insertCss from 'insert-css'; -type STATUS = 'I' | 'C' | 'S'; -interface ListItem { - id: string; - name: string; - count: number; - label: string; - rate: number; - status: STATUS; // 状态 - childList?: ListItem[]; -} +insertCss(` + .g6-component-tooltip { + background-color: rgba(0,0,0, 0.65); + padding: 10px; + box-shadow: rgb(174, 174, 174) 0px 0px 10px; + width: fit-content; + color: #fff; + border-radius = 4px; + } +`); export interface IProps { - data?: ListItem[]; + data?: TreeGraphData; config?: any; onInit?: (instance: any) => void; - nodeClick?: (nodeItem: ListItem) => void; } -interface GraphData { - nodes: object[]; - edges: object[]; -} +// mocked data +const mockData: TreeGraphData = { + id: 'g1', + name: 'Name1', + count: 123456, + label: '538.90', + currency: 'Yuan', + rate: 1.00, + status: 'B', + variableName: 'V1', + variableValue: 0.341, + variableUp: false, + children: [ + { + id: 'g12', + name: 'Deal with LONG label LONG label LONG label LONG label', + count: 123456, + label: '338.00', + rate: 0.627, + status: 'R', + currency: 'Yuan', + variableName: 'V2', + variableValue: 0.179, + variableUp: true, + children: [ + { + id: 'g121', + name: 'Name3', + collapsed: true, + count: 123456, + label: '138.00', + rate: 0.123, + status: 'B', + currency: 'Yuan', + variableName: 'V2', + variableValue: 0.27, + variableUp: true, + children: [ + { + id: 'g1211', + name: 'Name4', + count: 123456, + label: '138.00', + rate: 1.000, + status: 'B', + currency: 'Yuan', + variableName: 'V1', + variableValue: 0.164, + variableUp: false, + children: [], + }, + ], + }, + { + id: 'g122', + name: 'Name5', + collapsed: true, + count: 123456, + label: '100.00', + rate: 0.296, + status: 'G', + currency: 'Yuan', + variableName: 'V1', + variableValue: 0.259, + variableUp: true, + children: [ + { + id: 'g1221', + name: 'Name6', + count: 123456, + label: '40.00', + rate: 0.400, + status: 'G', + currency: 'Yuan', + variableName: 'V1', + variableValue: 0.135, + variableUp: true, + children: [ + { + id: 'g12211', + name: 'Name6-1', + count: 123456, + label: '40.00', + rate: 1.000, + status: 'R', + currency: 'Yuan', + variableName: 'V1', + variableValue: 0.181, + variableUp: true, + children: [], + }, + ], + }, + { + id: 'g1222', + name: 'Name7', + count: 123456, + label: '60.00', + rate: 0.600, + status: 'G', + currency: 'Yuan', + variableName: 'V1', + variableValue: 0.239, + variableUp: false, + children: [], + }, + ], + }, + { + id: 'g123', + name: 'Name8', + collapsed: true, + count: 123456, + label: '100.00', + rate: 0.296, + status: 'DI', + currency: 'Yuan', + variableName: 'V2', + variableValue: 0.131, + variableUp: false, + children: [ + { + id: 'g1231', + name: 'Name8-1', + count: 123456, + label: '100.00', + rate: 1.000, + status: 'DI', + currency: 'Yuan', + variableName: 'V2', + variableValue: 0.131, + variableUp: false, + children: [], + }, + ], + }, + ], + }, + { + id: 'g13', + name: 'Name9', + count: 123456, + label: '100.90', + rate: 0.187, + status: 'B', + currency: 'Yuan', + variableName: 'V2', + variableValue: 0.221, + variableUp: true, + children: [ + { + id: 'g131', + name: 'Name10', + count: 123456, + label: '33.90', + rate: 0.336, + status: 'R', + currency: 'Yuan', + variableName: 'V1', + variableValue: 0.120, + variableUp: true, + children: [], + }, + { + id: 'g132', + name: 'Name11', + count: 123456, + label: '67.00', + rate: 0.664, + status: 'G', + currency: 'Yuan', + variableName: 'V1', + variableValue: 0.241, + variableUp: false, + children: [], + }, + ], + }, + { + id: 'g14', + name: 'Name12', + count: 123456, + label: '100.00', + rate: 0.186, + status: 'G', + currency: 'Yuan', + variableName: 'V2', + variableValue: 0.531, + variableUp: true, + children: [], + }, + ], +}; -// 模拟数据 -const mockData: ListItem[] = [ - { - id: 'g1', - name: 'Name1', - count: 123456, - label: 'xx Yuan', - rate: 0.97, - status: 'S', - childList: [ - { - id: 'g12', - name: 'Deal with Long label', - count: 123456, - label: 'xx Yuan', - rate: 0.123, - status: 'S', - childList: [ - { - id: 'g121', - name: 'Name3', - count: 123456, - label: 'xx Yuan', - rate: 0.123, - status: 'S', - childList: [ - { - id: 'g1211', - name: 'Name4', - count: 123456, - label: 'xx Yuan', - rate: 0.123, - status: 'I', - childList: [], - }, - ], - }, - { - id: 'g122', - name: 'Name5', - count: 123456, - label: 'xx Yuan', - rate: 0.96, - status: 'S', - childList: [ - { - id: 'g1221', - name: 'Name6', - count: 123456, - label: 'xx Yuan', - rate: 0.123, - status: 'S', - childList: [ - { - id: 'g12211', - name: 'Name6-1', - count: 123456, - label: 'xx Yuan', - rate: 0.123, - status: 'I', - childList: [], - }, - ], - }, - { - id: 'g1222', - name: 'Name7', - count: 123456, - label: 'xx Yuan', - rate: 0.123, - status: 'S', - childList: [], - }, - ], - }, - { - id: 'g123', - name: 'Name8', - count: 123456, - label: 'xx Yuan', - rate: 0.23, - status: 'S', - childList: [ - { - id: 'g1231', - name: 'Name8-1', - count: 123456, - label: 'xx Yuan', - rate: 0.123, - status: 'I', - childList: [], - }, - ], - }, - ], - }, - { - id: 'g13', - name: 'Name9', - count: 123456, - label: 'xx Yuan', - rate: 0.123, - status: 'S', - childList: [ - { - id: 'g131', - name: 'Name10', - count: 123456, - label: 'xx Yuan', - rate: 0.123, - status: 'I', - childList: [], - }, - { - id: 'g132', - name: 'Name11', - count: 123456, - label: 'xx Yuan', - rate: 0.123, - status: 'I', - childList: [], - }, - ], - }, - { - id: 'g14', - name: 'Name12', - count: 123456, - label: 'xx Yuan', - rate: 0.123, - status: 'I', - childList: [], - }, - ], - }, -]; +const colors = { + B: '#5B8FF9', + R: '#F46649', + Y: '#EEBC20', + G: '#5BD8A6', + DI: '#A7A7A7' +} // 组件props const props: IProps = { @@ -170,50 +231,39 @@ const props: IProps = { defaultZoom: 0.8, modes: { default: ['zoom-canvas', 'drag-canvas'] }, }, - nodeClick: (item: any) => { - console.log(item); - }, }; +const container = document.getElementById('container'); +const width = container.scrollWidth; +const height = container.scrollHeight || 500; + // 默认配置 const defaultConfig = { - width: 1600, - height: 800, + width, + height, modes: { default: ['zoom-canvas', 'drag-canvas'], }, - fitView: false, + fitView: true, animate: true, + defaultNode: { + type: 'flow-rect' + }, defaultEdge: { + type: 'cubic-horizontal', style: { - stroke: '#1890FF', + stroke: '#CED4D9', }, }, -}; - -/** - * 简易get,项目中请使用lodash的get - * @param {Object} object 查找对象 - * @param {string} path 查找路径 - * @param {*} defaultValue 默认值 - */ -const get = (object: object, path: string, defaultValue?: any) => { - return object?.[path] || defaultValue; -}; - -// number to string -const toString = (id: number) => id + ''; - -/** - * sleep - * @param {duration} number unit ms - */ -const sleep = (duration = 500) => { - return new Promise((resolve) => { - setTimeout(() => { - resolve('done'); - }, duration); - }); + layout: { + type: 'indented', + direction: 'LR', + dropCap: false, + indent: 300, + getHeight: () => { + return 60; + }, + } }; // 自定义节点、边 @@ -226,123 +276,175 @@ const registerFn = () => { { shapeType: 'flow-rect', draw(cfg: any, group: any) { - const { name = '', lightColor, hasChildren, label, rate, collapsed } = cfg; + const { name = '', variableName, variableValue, variableUp, label, collapsed, currency, status, rate } = cfg; + const grey = '#CED4D9'; // 逻辑不应该在这里判断 const rectConfig = { - width: 184, - height: 74, + width: 202, + height: 60, lineWidth: 1, fontSize: 12, fill: '#fff', radius: 4, - stroke: lightColor, + stroke: grey, opacity: 1, }; + const nodeOrigin = { + x: -rectConfig.width / 2, + y: -rectConfig.height / 2, + } + const textConfig = { textAlign: 'left', - textBaseline: 'top', + textBaseline: 'bottom', }; const rect = group.addShape('rect', { attrs: { - x: 0, - y: 0, + x: nodeOrigin.x, + y: nodeOrigin.y, ...rectConfig, }, }); + const rectBBox = rect.getBBox(); + // label title group.addShape('text', { attrs: { ...textConfig, - x: 12, - y: 8, - text: name.length > 10 ? name.substr(0, 10) + '...' : name, - fontSize: 14, + x: 12 + nodeOrigin.x, + y: 20 + nodeOrigin.y, + text: name.length > 28 ? name.substr(0, 28) + '...' : name, + fontSize: 12, + opacity: 0.85, fill: '#000', - cursor: 'pointer', - isTitleShape: true, + cursor: 'pointer' }, + name: 'name-shape' }); - // label count - group.addShape('text', { + // price + const price = group.addShape('text', { attrs: { ...textConfig, - x: 12, - y: 34, + x: 12 + nodeOrigin.x, + y: rectBBox.maxY - 12, text: label, - fontSize: 20, + fontSize: 16, fill: '#000', + opacity: 0.85, }, }); - // label percentage + // label currency group.addShape('text', { attrs: { ...textConfig, - x: 178, - y: 37, - text: `${((rate || 0) * 100).toFixed(2)}%`, - fontSize: 14, - textAlign: 'right', - fill: lightColor, + x: price.getBBox().maxX + 5, + y: rectBBox.maxY - 12, + text: currency, + fontSize: 12, + fill: '#000', + opacity: 0.75, }, }); - // bottom line - group.addShape('rect', { + // percentage + const percentText = group.addShape('text', { attrs: { - x: 0, - y: 70, + ...textConfig, + x: rectBBox.maxX - 8, + y: rectBBox.maxY - 12, + text: `${((variableValue || 0) * 100).toFixed(2)}%`, + fontSize: 12, + textAlign: 'right', + fill: colors[status], + }, + }); + + // percentage triangle + const symbol = variableUp ? 'triangle' : 'triangle-down'; + const triangle = group.addShape('marker', { + attrs: { + ...textConfig, + x: percentText.getBBox().minX - 10, + y: rectBBox.maxY - 12 - 6, + symbol, + r: 6, + fill: colors[status], + }, + }); + + // variable name + group.addShape('text', { + attrs: { + ...textConfig, + x: triangle.getBBox().minX - 4, + y: rectBBox.maxY - 12, + text: variableName, + fontSize: 12, + textAlign: 'right', + fill: '#000', + opacity: 0.45, + }, + }); + + // bottom line background + const bottomBackRect = group.addShape('rect', { + attrs: { + x: nodeOrigin.x, + y: rectBBox.maxY - 4, width: rectConfig.width, height: 4, radius: [0, 0, rectConfig.radius, rectConfig.radius], - fill: '#DCDFE5', + fill: '#E0DFE3', }, }); // bottom percent - group.addShape('rect', { + const bottomRect = group.addShape('rect', { attrs: { - x: 0, - y: 70, - width: 100, + x: nodeOrigin.x, + y: rectBBox.maxY - 4, + width: rate * rectBBox.width, height: 4, radius: [0, 0, 0, rectConfig.radius], - fill: lightColor, + fill: colors[status], }, }); - if (hasChildren) { - // collapse circle - group.addShape('circle', { + // collapse rect + if (cfg.children && cfg.children.length) { + group.addShape('rect', { attrs: { - x: rectConfig.width, - y: rectConfig.height / 2, - r: 8, - stroke: lightColor, - fill: collapsed ? lightColor : '#fff', - isCollapseShape: true, + x: rectConfig.width / 2 - 8, + y: -8, + width: 16, + height: 16, + stroke: 'rgba(0, 0, 0, 0.25)', + cursor: 'pointer', + fill: '#fff' }, + name: 'collapse-back', + modelId: cfg.id }); // collpase text group.addShape('text', { attrs: { - x: rectConfig.width, - y: rectConfig.height / 2, - width: 16, - height: 16, + x: rectConfig.width / 2, + y: -1, textAlign: 'center', textBaseline: 'middle', text: collapsed ? '+' : '-', fontSize: 16, - fill: collapsed ? '#fff' : lightColor, cursor: 'pointer', - isCollapseShape: true, + fill: 'rgba(0, 0, 0, 0.25)', }, + name: 'collapse-text', + modelId: cfg.id }); } @@ -354,29 +456,17 @@ const registerFn = () => { this.updateLinkPoints(cfg, group); }, setState(name: any, value: any, item: any) { - if (name === 'click' && value) { + if (name === 'collapse') { const group = item.getContainer(); - const { collapsed } = item.getModel(); - const [, , , , , , CircleShape, TextShape] = group.get('children'); - if (TextShape) { - const { - attrs: { stroke }, - } = CircleShape; - if (!collapsed) { - TextShape.attr({ + const collapseText = group.find(e => e.get('name') === 'collapse-text'); + if (collapseText) { + if (!value) { + collapseText.attr({ text: '-', - fill: stroke, - }); - CircleShape.attr({ - fill: '#fff', }); } else { - TextShape.attr({ + collapseText.attr({ text: '+', - fill: '#fff', - }); - CircleShape.attr({ - fill: stroke, }); } } @@ -389,7 +479,6 @@ const registerFn = () => { ]; }, }, - // 注意这里继承了 'single-shape' 'rect', ); @@ -437,492 +526,68 @@ const registerFn = () => { registerFn(); const { data } = props; -let backUpData: any; -let maxMatrixY = 0; -let isAnimating = false; let graph = null; -const initGraph = (data?: ListItem[]) => { - if (!data?.length) { +const initGraph = (data?: TreeGraphData) => { + if (!data) { return; } - transformData(data); const { onInit, config } = props; - graph = new G6.Graph({ + const tooltip = new G6.Tooltip({ + // offsetX and offsetY include the padding of the parent container + // offsetX 与 offsetY 需要加上父容器的 padding + offsetX: 140 + 10, + offsetY: 100 + 10, + // the types of items that allow the tooltip show up + // 允许出现 tooltip 的 item 类型 + itemTypes: ['node'], + // custom the tooltip's content + // 自定义 tooltip 内容 + getContent: (e) => { + const outDiv = document.createElement('div'); + //outDiv.style.padding = '0px 0px 20px 0px'; + const nodeName = e.item.getModel().name; + let formatedNodeName = ''; + for (let i = 0; i < nodeName.length; i++) { + formatedNodeName = `${formatedNodeName}${nodeName[i]}`; + if (i !== 0 && i % 20 === 0) formatedNodeName = `${formatedNodeName}
`; + } + outDiv.innerHTML = `${formatedNodeName}`; + return outDiv; + }, + shouldBegin: (e) => { + if (e.target.get('name') === 'name-shape') return true; + return false; + } + }); + graph = new G6.TreeGraph({ container: 'container', ...defaultConfig, ...config, + plugins: [tooltip] }); - initEvent(); if (typeof onInit === 'function') { onInit(graph); } - backUpData = JSON.parse(JSON.stringify(data)); - graph.data(getPosition(data, true)); + graph.data(data); graph.render(); graph.zoom(config.defaultZoom || 1); - if (data?.length) { - graph.changeData(getPosition(backUpData)); - } -}; -// 事件绑定 -const initEvent = () => { - graph.on('node:click', async (evt: any) => { - if (isAnimating) { - return; - } - const { item, target } = evt; - const { - attrs: { isCollapseShape }, - } = target; - if (isCollapseShape) { - isAnimating = true; - const model = item.getModel(); - graph.setItemState(item, 'click', true); - const { childrenKeys, id, collapsed, recordIndex } = model; - // 更新状态 - if (collapsed) { - updateCollapseStatus(id, recordIndex, collapsed, 'expand'); - graph.changeData(getExpandPosition(backUpData)); - graph.stopAnimate(); - childrenKeys.forEach(async (key: string) => { - const childrenItem = graph.findById(key); - if (childrenItem) { - childrenItem.toBack(); - } - }); - updateCollapseStatus(id, recordIndex, collapsed); - graph.changeData(getPosition(backUpData)); - await sleep(500); - graph.setItemState(item, 'click', true); - isAnimating = false; - } else { - updateCollapseStatus(id, recordIndex, collapsed, 'collapsed'); - graph.changeData(getPosition(backUpData)); - childrenKeys.forEach(async (key: string) => { - const childrenItem = graph.findById(key); - if (childrenItem) { - childrenItem.toBack(); - } - }); - await sleep(500); - updateCollapseStatus(id, recordIndex, collapsed); - childrenKeys.forEach(async (key: string) => { - const childrenItem = graph.findById(key); - if (childrenItem) { - graph.remove(childrenItem); - } - }); - graph.setItemState(item, 'click', true); - isAnimating = false; - } - } else { - const { nodeClick } = props; - if (typeof nodeClick === 'function') { - nodeClick(item.getModel()); - } - } - }); - - graph.on('node:mouseenter', (evt: any) => { - const node = evt.item; - graph.setItemState(node, 'hover', true); - graph.updateItem(node, { - style: { - ...node._cfg.originStyle, - shadowColor: '#bbb', - shadowBlur: 6, - }, - }); - }); - - graph.on('node:mousemove', (evt: any) => { - if (isAnimating) { - return; - } - const { item, target, x, y } = evt; - const { - attrs: { isTitleShape }, - } = target; - const model = item.getModel(); - const { name, id } = model; - if (isTitleShape) { - const postion = graph.getClientByPoint(x, y); - createTooltip(postion, name, id); - } else { - removeTooltip(id); - } - }); - - graph.on('node:mouseout', (evt: any) => { - if (isAnimating) { - return; - } - const { item, target } = evt; - const { - attrs: { isTitleShape }, - } = target; - const model = item.getModel(); - const { id } = model; - if (isTitleShape) { - removeTooltip(id); - } - }); - - graph.on('node:mouseleave', (evt: any) => { - const node = evt.item; - graph.setItemState(node, 'hover', false); - graph.updateItem(node, { - style: { - ...node._cfg.originStyle, - shadowColor: 'transparent', - shadowBlur: 0, - }, - }); - }); -}; - -/** - * 创建提示 - * @param {postion} 鼠标点击的位置 - * @param {name} string 节点Name - * @param {id} string 节点id - */ -const createTooltip = (postion: { x: number; y: number }, name: string, id: string) => { - const offsetTop = -60; - const existTooltip = document.getElementById(id); - const x = postion.x + 'px'; - const y = postion.y + offsetTop + 'px'; - if (existTooltip) { - existTooltip.style.left = x; - existTooltip.style.top = y; - } else { - // content - const tooltip = document.createElement('div'); - const span = document.createElement('span'); - span.textContent = name; - tooltip.style.padding = '10px'; - tooltip.style.background = 'rgba(0,0,0, 0.65)'; - tooltip.style.color = '#fff'; - tooltip.style.borderRadius = '4px'; - tooltip.appendChild(span); - // box - const div = document.createElement('div'); - div.style.position = 'absolute'; - div.style.zIndex = '99'; - div.id = id; - div.style.left = x; - div.style.top = y; - div.appendChild(tooltip); - document.body.appendChild(div); - } -}; -/** - * 删除提示 - * @param {id} string - */ -const removeTooltip = (id: string) => { - const removeNode = document.getElementById(id); - if (removeNode) { - document.body.removeChild(removeNode); - } -}; - -/** - * 计算位置 - * @param {data} Array - * @param {flag} string[] | string - * @param {postion} object - */ -const getPosition = (data: ListItem[] | undefined, init?: boolean) => { - maxMatrixY = 0; - const graphData = { - nodes: [], - edges: [], + const handleCollapse = (e) => { + const target = e.target; + const id = target.get('modelId'); + const item = graph.findById(id); + const nodeModel = item.getModel(); + nodeModel.collapsed = !nodeModel.collapsed; + graph.layout() + graph.setItemState(item, 'collapse', nodeModel.collapsed) }; - - if (!data) { - return graphData; - } - - if (init) { - initAnimateData(data, graphData); - } else { - recursion(data, 0, graphData); - } - - return graphData; -}; - -/** - * 计算位置 - * @param {data} Array - * @param {flag} string[] | string - * @param {postion} object - */ -const getExpandPosition = (data: ListItem[] | undefined) => { - maxMatrixY = 0; - const graphData = { - nodes: [], - edges: [], - }; - - if (!data) { - return graphData; - } - - recursionExpand(data, 0, graphData); - - return graphData; -}; - -/** - * 展开时的特殊处理 - */ -const recursionExpand = ( - data: any[], - parentMatrixX: number, - graphData: GraphData, - parentX?: number, - parentY?: number, - parentAnimate?: string, -): void => { - if (!data || !data.length) { - return; - } - data.forEach((item, index) => { - const matrixX = parentMatrixX || 0; - const children = get(item, 'childList', []); - const animate = get(item, 'animate', false); - const afterDrawHidden = get(item, 'afterDrawHidden', false); - const collapsed = get(item, 'collapsed'); - const currentX = parentAnimate === 'expand' ? parentX : item.x; - const currentY = parentAnimate === 'expand' ? parentY : item.y; - - item = { - ...item, - id: toString(item.id), - x: currentX, - y: currentY, - hasChildren: children.length, - }; - data[index] = item; - const { childList, ...model } = item; - graphData.nodes.push(model); - - if ((children.length && animate) || (children.length && !collapsed)) { - recursionExpand( - children, - afterDrawHidden ? matrixX : matrixX + 1, - graphData, - currentX, - currentY, - animate, - ); - } - }); -}; - -/** - * 数据转换,生成图表数据 - */ -const transformData = (data: any[], parentIndex?: string): void => { - if (!data || !data.length) { - return; - } - const { - config: { defaultLevel = 10, padding = [20, 20] }, - } = props; - data.forEach((item, index) => { - const { status, rate } = item; - const children = get(item, 'childList', []); - const recordIndex = parentIndex !== undefined ? parentIndex + '-' + index : index + ''; - maxMatrixY = index === 0 ? maxMatrixY : maxMatrixY + 1; - const recordLength = recordIndex.split('-').length; - const childrenKeys: string[] = []; - if (children.length) { - getKeys(children, childrenKeys); - } - let lightColor: string; - if (status === 'I') { - lightColor = '#DCDFE5'; - } else { - lightColor = rate >= 0.95 ? '#1890FF' : '#EB2F96'; - } - item = { - ...item, - lightColor, - id: toString(item.id), - x: padding[0], - y: padding[1], - recordIndex, - collapsed: recordLength >= defaultLevel, - hasChildren: children.length, - childrenKeys, - }; - data[index] = item; - if (children.length) { - transformData(get(item, 'childList', []), recordIndex); - } - }); -}; - -/** - * 生成初始化数据,为了动画而动画 - */ -const initAnimateData = (data: any[], graphData: GraphData): void => { - if (!data || !data.length) { - return; - } - data.forEach((item) => { - const children = get(item, 'childList', []); - const collapsed = get(item, 'collapsed'); - const { childList, ...model } = item; - graphData.nodes.push(model); - if (children.length && !collapsed) { - initAnimateData(get(item, 'childList', []), graphData); - } - }); -}; - -/** - * 递归 - */ -const recursion = ( - data: any[], - parentMatrixX: number, - graphData: GraphData, - parentId?: string, - parentX?: number, - parentY?: number, - parentAnimate?: string, -): void => { - if (!data || !data.length) { - return; - } - const { - config: { padding = [20, 20], nodesMargin = [250, 100], coefficient = [0.2, -0.1] }, - } = props; - data.forEach((item, index) => { - const matrixX = parentMatrixX || 0; - const children = get(item, 'childList', []); - const animate = get(item, 'animate', false); - const afterDrawHidden = get(item, 'afterDrawHidden', false); - const collapsed = get(item, 'collapsed'); - maxMatrixY = index === 0 || afterDrawHidden ? maxMatrixY : maxMatrixY + 1; - - const currentX = - afterDrawHidden || parentAnimate === 'expand' - ? parentX - : matrixX * nodesMargin[0] + padding[0]; - const currentY = - afterDrawHidden || parentAnimate === 'expand' - ? parentY - : maxMatrixY * nodesMargin[1] + padding[1]; - - item = { - ...item, - id: toString(item.id), - matrixX, - matrixY: maxMatrixY, - x: currentX, - y: currentY, - type: 'flow-rect', - coefficientX: coefficient[0], - coefficientY: coefficient[1], - hasChildren: children.length, - collapsed: item.collapsed || false, - }; - data[index] = item; - const { childList, ...model } = item; - graphData.nodes.push(model); - - if (parentId) { - graphData.edges.push({ - source: parentId, - target: toString(item.id), - targetAnchor: 0, - sourceAnchor: 1, - type: index === 0 ? 'line' : 'flow-cubic', - }); - } - - if ((children.length && animate) || (children.length && !collapsed)) { - recursion( - children, - afterDrawHidden ? matrixX : matrixX + 1, - graphData, - toString(item.id), - currentX, - currentY, - animate, - ); - } - }); -}; - -/** - * 获取keys, 折叠、展开时直接使用 - * @param {data} ListItem - * @param {keys} string[] - */ -const getKeys = (data: ListItem[], keys: string[]): void => { - if (!data || !data.length) { - return; - } - data.forEach((item) => { - const { id } = item; - const children = get(item, 'childList', []); - keys.push(id); - if (children.length) { - getKeys(children, keys); - } - }); -}; - -/** - * 更新当前数据的collapse状态以及子节点的afterDrawHidden状态 - * @param {id} string - * @param {recordIndex} string 节点索引 - * @param {collapsed} boolean - */ -const updateCollapseStatus = ( - id: string, - recordIndex: string, - collapsed: boolean, - animate?: string, -): void => { - let currentList: any = backUpData; - try { - let currentRecord: any; - const indexs = recordIndex.split('-'); - for (let i = 0; i < indexs.length; i += 1) { - currentRecord = currentList[indexs[i]]; - currentList = currentList[indexs[i]].childList; - } - currentRecord.collapsed = !collapsed; - currentRecord.animate = animate; - - const setHidden = (data: any[]) => { - if (!data || !data.length) { - return; - } - data.forEach((item, index) => { - const children = get(item, 'childList', []); - data[index] = { - ...item, - afterDrawHidden: !collapsed, - }; - if (children.length && !item.collapsed) { - setHidden(children); - } - }); - }; - setHidden(currentList); - } catch (err) { - console.error(err, id, currentList); - } + graph.on('collapse-text:click', e => { + handleCollapse(e) + }) + graph.on('collapse-back:click', e => { + handleCollapse(e) + }) }; initGraph(data); diff --git a/examples/case/metroLines/index.en.md b/examples/case/metroLines/index.en.md index a766d2bbf7..33124f8cc6 100644 --- a/examples/case/metroLines/index.en.md +++ b/examples/case/metroLines/index.en.md @@ -1,6 +1,6 @@ --- title: Animated Metro Map -order: 4 +order: 5 --- Metro map with animated edges. diff --git a/examples/case/metroLines/index.zh.md b/examples/case/metroLines/index.zh.md index 38394ea1e2..9102cf7190 100644 --- a/examples/case/metroLines/index.zh.md +++ b/examples/case/metroLines/index.zh.md @@ -1,6 +1,6 @@ --- title: 地铁线路图 -order: 4 +order: 5 --- 基于 G6 实现的地铁线路图。 diff --git a/examples/case/simplifyCluster/index.en.md b/examples/case/simplifyCluster/index.en.md index 9d2e540abd..47d50dc431 100644 --- a/examples/case/simplifyCluster/index.en.md +++ b/examples/case/simplifyCluster/index.en.md @@ -1,6 +1,6 @@ --- title: Collapse/Expand Cluster -order: 3 +order: 4 --- This demo shows interactively collapse and expand clusters with Fruchterman layout. diff --git a/examples/case/simplifyCluster/index.zh.md b/examples/case/simplifyCluster/index.zh.md index 8dfddcbff7..a0fc00c4c3 100644 --- a/examples/case/simplifyCluster/index.zh.md +++ b/examples/case/simplifyCluster/index.zh.md @@ -1,6 +1,6 @@ --- title: 聚类的折叠/扩展 -order: 3 +order: 4 --- 使用 Fruchterman 布局实现交互式折叠/扩展聚类。 diff --git a/examples/tool/tooltip/demo/tooltip.js b/examples/tool/tooltip/demo/tooltip.js index b80a2947ee..123f0cb6a6 100644 --- a/examples/tool/tooltip/demo/tooltip.js +++ b/examples/tool/tooltip/demo/tooltip.js @@ -110,17 +110,20 @@ const graph = new G6.Graph({ defaultNode: { size: [80, 40], type: 'rect', - style: { - fill: '#DEE9FF', - stroke: '#5B8FF9', - }, - }, - defaultEdge: { - style: { - stroke: '#b5b5b5', - lineAppendWidth: 3, - }, }, }); graph.data(data); graph.render(); + +graph.on('node:mouseenter', e => { + graph.setItemState(e.item, 'active', true) +}); +graph.on('node:mouseleave', e => { + graph.setItemState(e.item, 'active', false) +}); +graph.on('edge:mouseenter', e => { + graph.setItemState(e.item, 'active', true) +}); +graph.on('edge:mouseleave', e => { + graph.setItemState(e.item, 'active', false) +}); diff --git a/examples/tool/tooltip/demo/tooltipLocal.js b/examples/tool/tooltip/demo/tooltipLocal.js index e1aac1fd6f..c06708e1c0 100644 --- a/examples/tool/tooltip/demo/tooltipLocal.js +++ b/examples/tool/tooltip/demo/tooltipLocal.js @@ -70,16 +70,8 @@ const graph = new G6.Graph({ defaultNode: { size: [150, 50], type: 'rect', - style: { - fill: '#DEE9FF', - stroke: '#5B8FF9', - }, }, defaultEdge: { - style: { - stroke: '#b5b5b5', - lineAppendWidth: 3, - }, labelCfg: { autoRotate: true, @@ -117,3 +109,16 @@ const graph = new G6.Graph({ }); graph.data(data); graph.render(); + +graph.on('node:mouseenter', e => { + graph.setItemState(e.item, 'active', true) +}); +graph.on('node:mouseleave', e => { + graph.setItemState(e.item, 'active', false) +}); +graph.on('edge:mouseenter', e => { + graph.setItemState(e.item, 'active', true) +}); +graph.on('edge:mouseleave', e => { + graph.setItemState(e.item, 'active', false) +}); diff --git a/examples/tool/tooltip/demo/tooltipLocalCustom.js b/examples/tool/tooltip/demo/tooltipLocalCustom.js index 31dd30f829..af253741da 100644 --- a/examples/tool/tooltip/demo/tooltipLocalCustom.js +++ b/examples/tool/tooltip/demo/tooltipLocalCustom.js @@ -151,3 +151,16 @@ const graph = new G6.Graph({ }); graph.data(data); graph.render(); + +graph.on('node:mouseenter', e => { + graph.setItemState(e.item, 'active', true) +}); +graph.on('node:mouseleave', e => { + graph.setItemState(e.item, 'active', false) +}); +graph.on('edge:mouseenter', e => { + graph.setItemState(e.item, 'active', true) +}); +graph.on('edge:mouseleave', e => { + graph.setItemState(e.item, 'active', false) +}); diff --git a/examples/tool/tooltip/demo/tooltipPlugin.js b/examples/tool/tooltip/demo/tooltipPlugin.js index 68f1595798..a769996144 100644 --- a/examples/tool/tooltip/demo/tooltipPlugin.js +++ b/examples/tool/tooltip/demo/tooltipPlugin.js @@ -1,6 +1,9 @@ import G6 from '@antv/g6'; import insertCss from 'insert-css'; +// 我们用 insert-css 演示引入自定义样式 +// 推荐将样式添加到自己的样式文件中 +// 若拷贝官方代码,别忘了 npm install insert-css // 我们用 insert-css 演示引入自定义样式 // 推荐将样式添加到自己的样式文件中 // 若拷贝官方代码,别忘了 npm install insert-css @@ -71,8 +74,8 @@ const data = { const tooltip = new G6.Tooltip({ // offsetX and offsetY include the padding of the parent container // offsetX 与 offsetY 需要加上父容器的 padding - offsetX: 16 + 10, - offsetY: 24 + 10, + offsetX: 140 + 10, + offsetY: 100 + 10, // the types of items that allow the tooltip show up // 允许出现 tooltip 的 item 类型 itemTypes: ['node', 'edge'], @@ -107,10 +110,6 @@ const graph = new G6.Graph({ defaultNode: { size: [80, 40], type: 'rect', - style: { - fill: '#DEE9FF', - stroke: '#5B8FF9', - }, }, defaultEdge: { style: { @@ -121,3 +120,16 @@ const graph = new G6.Graph({ }); graph.data(data); graph.render(); + +graph.on('node:mouseenter', e => { + graph.setItemState(e.item, 'active', true) +}); +graph.on('node:mouseleave', e => { + graph.setItemState(e.item, 'active', false) +}); +graph.on('edge:mouseenter', e => { + graph.setItemState(e.item, 'active', true) +}); +graph.on('edge:mouseleave', e => { + graph.setItemState(e.item, 'active', false) +}); diff --git a/examples/tool/tooltip/demo/tooltipPluginLocal.js b/examples/tool/tooltip/demo/tooltipPluginLocal.js index 3cc5d4d9de..dd0d815d8f 100644 --- a/examples/tool/tooltip/demo/tooltipPluginLocal.js +++ b/examples/tool/tooltip/demo/tooltipPluginLocal.js @@ -131,19 +131,20 @@ const graph = new G6.Graph({ defaultNode: { size: [80, 40], type: 'rect', - style: { - fill: '#DEE9FF', - stroke: '#5B8FF9', - }, - }, - defaultEdge: { - style: { - stroke: '#b5b5b5', - lineAppendWidth: 3, - }, }, }); graph.data(data); graph.render(); -console.log(graph.getNodes()[0]) +graph.on('node:mouseenter', e => { + graph.setItemState(e.item, 'active', true) +}); +graph.on('node:mouseleave', e => { + graph.setItemState(e.item, 'active', false) +}); +graph.on('edge:mouseenter', e => { + graph.setItemState(e.item, 'active', true) +}); +graph.on('edge:mouseleave', e => { + graph.setItemState(e.item, 'active', false) +}); diff --git a/gatsby-config.js b/gatsby-config.js index 9ff2660e6b..73a8e2dac2 100644 --- a/gatsby-config.js +++ b/gatsby-config.js @@ -237,6 +237,22 @@ module.exports = { } ], examples: [ + { + slug: 'case', + icon: 'gallery', + title: { + zh: '场景案例', + en: 'Case', + }, + }, + { + slug: 'gallery', + icon: 'gallery', + title: { + zh: '所有图表', + en: 'All Demos', + }, + }, { slug: 'tree', icon: 'tree', // 图标名可以去 https://antv.alipay.com/zh-cn/g2/3.x/demo/index.html 打开控制台查看图标类名 @@ -301,14 +317,6 @@ module.exports = { en: 'Algorithm', }, }, - { - slug: 'case', - icon: 'case', - title: { - zh: '复杂案例', - en: 'Case', - }, - }, { slug: 'performance', icon: 'net', diff --git a/package.json b/package.json index 5cd9fb8324..9b3f2e9cca 100644 --- a/package.json +++ b/package.json @@ -90,7 +90,7 @@ }, "devDependencies": { "@antv/chart-node-g6": "^0.0.3", - "@antv/gatsby-theme-antv": "^1.0.0-beta.11", + "@antv/gatsby-theme-antv": "^1.0.0-beta.15", "@babel/core": "^7.7.7", "@babel/plugin-proposal-class-properties": "^7.1.0", "@babel/preset-react": "^7.7.4",