mirror of
https://gitee.com/antv/g6.git
synced 2024-12-02 03:38:20 +08:00
perf: optimize element dependencies effect trace performance (#6249)
* perf: add baseline performance report * perf: optmize effect performance * perf: generate performance report * test: update test demo --------- Co-authored-by: antv <antv@antfin.com>
This commit is contained in:
parent
8553175ce0
commit
2c21154855
@ -51,7 +51,7 @@
|
||||
"eslint": "^8.57.0",
|
||||
"eslint-plugin-jsdoc": "^46.10.1",
|
||||
"husky": "^8.0.3",
|
||||
"iperf": "^0.1.0-beta.11",
|
||||
"iperf": "^0.1.0-beta.13",
|
||||
"jest": "^29.7.0",
|
||||
"jest-environment-jsdom": "^29.7.0",
|
||||
"jsdom": "^23.2.0",
|
||||
|
@ -1,6 +1,7 @@
|
||||
import type { DisplayObject, Group } from '@antv/g';
|
||||
import type { BaseComboStyleProps, GraphData, HTMLStyleProps, IElementEvent, NodeData } from '@antv/g6';
|
||||
import { BaseCombo, effect, ExtensionCategory, Graph, HTML, isCollapsed, register } from '@antv/g6';
|
||||
import { BaseCombo, ExtensionCategory, Graph, HTML, isCollapsed, register } from '@antv/g6';
|
||||
import { isEqual } from '@antv/util';
|
||||
|
||||
export const elementHTMLSubGraph: TestCase = async (context) => {
|
||||
interface CardNodeData {
|
||||
@ -38,13 +39,14 @@ export const elementHTMLSubGraph: TestCase = async (context) => {
|
||||
|
||||
private graph?: Graph;
|
||||
|
||||
@effect((self, attributes) => {
|
||||
const { data } = self.data;
|
||||
return { data };
|
||||
})
|
||||
private previousData?: Record<string, unknown>;
|
||||
|
||||
private drawSubGraph() {
|
||||
if (!this.isConnected) return;
|
||||
const data = this.data;
|
||||
if (isEqual(this.previousData, data)) return;
|
||||
this.previousData = data;
|
||||
|
||||
this.drawGraphNode(data!.data as GraphData);
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,246 @@
|
||||
{
|
||||
"version": "1.0",
|
||||
"device": {
|
||||
"os": {
|
||||
"arch": "arm64",
|
||||
"distro": "macOS",
|
||||
"serial": "9821ed36011eee5abf6c71d6fc2c03fb4bf4655e674c56b7f50e2560cb6e924a"
|
||||
},
|
||||
"cpu": {
|
||||
"manufacturer": "Apple",
|
||||
"brand": "M1 Pro",
|
||||
"speed": 2.4,
|
||||
"cores": 10
|
||||
},
|
||||
"memory": {
|
||||
"total": 16384,
|
||||
"free": 540.28125
|
||||
},
|
||||
"gpu": {
|
||||
"vendor": "Apple",
|
||||
"model": "Apple M1 Pro",
|
||||
"cores": "16"
|
||||
}
|
||||
},
|
||||
"repo": "aa87ec67c38f03808c72d82caaa3d064b4ee9c01",
|
||||
"client": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/128.0.6613.18 Safari/537.36",
|
||||
"reports": {
|
||||
"UpdateElementState": {
|
||||
"time": [
|
||||
{
|
||||
"min": 167.09999999403954,
|
||||
"max": 202.29999999701977,
|
||||
"median": 171.79999999701977,
|
||||
"avg": 172.86250000447035,
|
||||
"variance": 18.562343744896353,
|
||||
"reliable": true,
|
||||
"key": "update element state to selected"
|
||||
},
|
||||
{
|
||||
"min": 102.20000000298023,
|
||||
"max": 116.09999999403954,
|
||||
"median": 104.30000001192093,
|
||||
"avg": 105.06250000186265,
|
||||
"variance": 3.0598437559511513,
|
||||
"reliable": true,
|
||||
"key": "update element state to default"
|
||||
},
|
||||
{
|
||||
"min": 86,
|
||||
"max": 105.8999999910593,
|
||||
"median": 88.5,
|
||||
"avg": 88.51249999925494,
|
||||
"variance": 2.073593743089587,
|
||||
"reliable": true,
|
||||
"key": "update element position"
|
||||
}
|
||||
],
|
||||
"status": "passed"
|
||||
},
|
||||
"dataDiff1000": {
|
||||
"time": [
|
||||
{
|
||||
"min": 2.7000000029802322,
|
||||
"max": 11.300000011920929,
|
||||
"median": 5.5,
|
||||
"avg": 5.512499997392297,
|
||||
"variance": 2.638593754386529,
|
||||
"reliable": false,
|
||||
"key": "data diff"
|
||||
}
|
||||
],
|
||||
"status": "passed"
|
||||
},
|
||||
"dataDiff10000": {
|
||||
"time": [
|
||||
{
|
||||
"min": 2.8999999910593033,
|
||||
"max": 12.099999994039536,
|
||||
"median": 6,
|
||||
"avg": 6.325000004842877,
|
||||
"variance": 4.859374997671694,
|
||||
"reliable": false,
|
||||
"key": "data diff"
|
||||
}
|
||||
],
|
||||
"status": "passed"
|
||||
},
|
||||
"dataDiff100000": {
|
||||
"time": [
|
||||
{
|
||||
"min": 11,
|
||||
"max": 39.29999999701977,
|
||||
"median": 12.700000002980232,
|
||||
"avg": 16.387499999254942,
|
||||
"variance": 60.1160937285237,
|
||||
"reliable": true,
|
||||
"key": "data diff"
|
||||
}
|
||||
],
|
||||
"status": "passed"
|
||||
},
|
||||
"dataDiff5000": {
|
||||
"time": [
|
||||
{
|
||||
"min": 1.8999999910593033,
|
||||
"max": 9.5,
|
||||
"median": 8.099999994039536,
|
||||
"avg": 6.374999998137355,
|
||||
"variance": 6.324374991413206,
|
||||
"reliable": false,
|
||||
"key": "data diff"
|
||||
}
|
||||
],
|
||||
"status": "passed"
|
||||
},
|
||||
"dataDiff50000": {
|
||||
"time": [
|
||||
{
|
||||
"min": 5.700000002980232,
|
||||
"max": 17.899999991059303,
|
||||
"median": 8.299999997019768,
|
||||
"avg": 9.5,
|
||||
"variance": 16.31750000014901,
|
||||
"reliable": true,
|
||||
"key": "data diff"
|
||||
}
|
||||
],
|
||||
"status": "passed"
|
||||
},
|
||||
"elementDrawing100": {
|
||||
"time": [
|
||||
{
|
||||
"min": 12.700000002980232,
|
||||
"max": 24.600000008940697,
|
||||
"median": 18.899999991059303,
|
||||
"avg": 19.199999999254942,
|
||||
"variance": 8.127499995231629,
|
||||
"reliable": true,
|
||||
"key": "element drawing"
|
||||
},
|
||||
{
|
||||
"min": 7.9000000059604645,
|
||||
"max": 14.100000008940697,
|
||||
"median": 11.900000005960464,
|
||||
"avg": 11.237500000745058,
|
||||
"variance": 3.8348437521792946,
|
||||
"reliable": true,
|
||||
"key": "grid layout"
|
||||
}
|
||||
],
|
||||
"status": "passed"
|
||||
},
|
||||
"elementDrawing1000": {
|
||||
"time": [
|
||||
{
|
||||
"min": 48.099999994039536,
|
||||
"max": 73.20000000298023,
|
||||
"median": 61.70000000298023,
|
||||
"avg": 61.374999998137355,
|
||||
"variance": 13.14437501249835,
|
||||
"reliable": true,
|
||||
"key": "element drawing"
|
||||
},
|
||||
{
|
||||
"min": 39.20000000298023,
|
||||
"max": 59.1000000089407,
|
||||
"median": 43.29999999701977,
|
||||
"avg": 44.212499998509884,
|
||||
"variance": 14.278593749292193,
|
||||
"reliable": true,
|
||||
"key": "grid layout"
|
||||
}
|
||||
],
|
||||
"status": "passed"
|
||||
},
|
||||
"elementDrawing10000": {
|
||||
"time": [
|
||||
{
|
||||
"min": 409.3999999910593,
|
||||
"max": 536.2000000029802,
|
||||
"median": 434.3999999910593,
|
||||
"avg": 442.1124999951571,
|
||||
"variance": 829.2260936078895,
|
||||
"reliable": true,
|
||||
"key": "element drawing"
|
||||
},
|
||||
{
|
||||
"min": 372.90000000596046,
|
||||
"max": 414.20000000298023,
|
||||
"median": 393.29999999701977,
|
||||
"avg": 389.8499999977648,
|
||||
"variance": 108.12999993681908,
|
||||
"reliable": true,
|
||||
"key": "grid layout"
|
||||
}
|
||||
],
|
||||
"status": "passed"
|
||||
},
|
||||
"elementDrawing500": {
|
||||
"time": [
|
||||
{
|
||||
"min": 28.400000005960464,
|
||||
"max": 51.099999994039536,
|
||||
"median": 41.79999999701977,
|
||||
"avg": 41.07499999925494,
|
||||
"variance": 15.709374984912573,
|
||||
"reliable": true,
|
||||
"key": "element drawing"
|
||||
},
|
||||
{
|
||||
"min": 22.400000005960464,
|
||||
"max": 29,
|
||||
"median": 25.599999994039536,
|
||||
"avg": 25.399999998509884,
|
||||
"variance": 3.570000005811453,
|
||||
"reliable": true,
|
||||
"key": "grid layout"
|
||||
}
|
||||
],
|
||||
"status": "passed"
|
||||
},
|
||||
"elementDrawing5000": {
|
||||
"time": [
|
||||
{
|
||||
"min": 213.79999999701977,
|
||||
"max": 266.5,
|
||||
"median": 223.09999999403954,
|
||||
"avg": 230.39999999850988,
|
||||
"variance": 165.17250003792344,
|
||||
"reliable": true,
|
||||
"key": "element drawing"
|
||||
},
|
||||
{
|
||||
"min": 185.79999999701977,
|
||||
"max": 224.1000000089407,
|
||||
"median": 195.70000000298023,
|
||||
"avg": 193.41250000149012,
|
||||
"variance": 35.76859376054257,
|
||||
"reliable": true,
|
||||
"key": "grid layout"
|
||||
}
|
||||
],
|
||||
"status": "passed"
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,246 @@
|
||||
{
|
||||
"version": "1.0",
|
||||
"device": {
|
||||
"os": {
|
||||
"arch": "arm64",
|
||||
"distro": "macOS",
|
||||
"serial": "9821ed36011eee5abf6c71d6fc2c03fb4bf4655e674c56b7f50e2560cb6e924a"
|
||||
},
|
||||
"cpu": {
|
||||
"manufacturer": "Apple",
|
||||
"brand": "M1 Pro",
|
||||
"speed": 2.4,
|
||||
"cores": 10
|
||||
},
|
||||
"memory": {
|
||||
"total": 16384,
|
||||
"free": 224.8125
|
||||
},
|
||||
"gpu": {
|
||||
"vendor": "Apple",
|
||||
"model": "Apple M1 Pro",
|
||||
"cores": "16"
|
||||
}
|
||||
},
|
||||
"repo": "7fbbb77580d806932e7b777b34856243600dbf35",
|
||||
"client": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/128.0.6613.18 Safari/537.36",
|
||||
"reports": {
|
||||
"UpdateElementState": {
|
||||
"time": [
|
||||
{
|
||||
"min": 115.70000000298023,
|
||||
"max": 146.29999999701977,
|
||||
"median": 118.5,
|
||||
"avg": 119.1750000026077,
|
||||
"variance": 11.989375011604281,
|
||||
"reliable": true,
|
||||
"key": "update element state to selected"
|
||||
},
|
||||
{
|
||||
"min": 72.90000000596046,
|
||||
"max": 81,
|
||||
"median": 75.5,
|
||||
"avg": 75.12500000186265,
|
||||
"variance": 1.299374995883554,
|
||||
"reliable": true,
|
||||
"key": "update element state to default"
|
||||
},
|
||||
{
|
||||
"min": 61.29999999701977,
|
||||
"max": 73.59999999403954,
|
||||
"median": 63.29999999701977,
|
||||
"avg": 63.28749999962747,
|
||||
"variance": 0.2935937537904829,
|
||||
"reliable": true,
|
||||
"key": "update element position"
|
||||
}
|
||||
],
|
||||
"status": "passed"
|
||||
},
|
||||
"dataDiff1000": {
|
||||
"time": [
|
||||
{
|
||||
"min": 4.399999991059303,
|
||||
"max": 13.900000005960464,
|
||||
"median": 6.4000000059604645,
|
||||
"avg": 6.262500002980232,
|
||||
"variance": 1.0873437525331973,
|
||||
"reliable": false,
|
||||
"key": "data diff"
|
||||
}
|
||||
],
|
||||
"status": "passed"
|
||||
},
|
||||
"dataDiff10000": {
|
||||
"time": [
|
||||
{
|
||||
"min": 2.8999999910593033,
|
||||
"max": 13.700000002980232,
|
||||
"median": 6.0999999940395355,
|
||||
"avg": 6.199999999254942,
|
||||
"variance": 3.767500012814999,
|
||||
"reliable": false,
|
||||
"key": "data diff"
|
||||
}
|
||||
],
|
||||
"status": "passed"
|
||||
},
|
||||
"dataDiff100000": {
|
||||
"time": [
|
||||
{
|
||||
"min": 10.899999991059303,
|
||||
"max": 41.70000000298023,
|
||||
"median": 12.299999997019768,
|
||||
"avg": 17.51249999552965,
|
||||
"variance": 96.928593772389,
|
||||
"reliable": true,
|
||||
"key": "data diff"
|
||||
}
|
||||
],
|
||||
"status": "passed"
|
||||
},
|
||||
"dataDiff5000": {
|
||||
"time": [
|
||||
{
|
||||
"min": 3.0999999940395355,
|
||||
"max": 13,
|
||||
"median": 3.6000000089406967,
|
||||
"avg": 5.262499999254942,
|
||||
"variance": 10.062343739960342,
|
||||
"reliable": false,
|
||||
"key": "data diff"
|
||||
}
|
||||
],
|
||||
"status": "passed"
|
||||
},
|
||||
"dataDiff50000": {
|
||||
"time": [
|
||||
{
|
||||
"min": 5.4000000059604645,
|
||||
"max": 15.400000005960464,
|
||||
"median": 8.399999991059303,
|
||||
"avg": 8.549999998882413,
|
||||
"variance": 8.840000012554228,
|
||||
"reliable": true,
|
||||
"key": "data diff"
|
||||
}
|
||||
],
|
||||
"status": "passed"
|
||||
},
|
||||
"elementDrawing100": {
|
||||
"time": [
|
||||
{
|
||||
"min": 10.200000002980232,
|
||||
"max": 30.099999994039536,
|
||||
"median": 18.600000008940697,
|
||||
"avg": 19.499999998137355,
|
||||
"variance": 17.06999999091029,
|
||||
"reliable": true,
|
||||
"key": "element drawing"
|
||||
},
|
||||
{
|
||||
"min": 6.5999999940395355,
|
||||
"max": 11.200000002980232,
|
||||
"median": 8.400000005960464,
|
||||
"avg": 8.550000002607703,
|
||||
"variance": 0.5175000003352761,
|
||||
"reliable": true,
|
||||
"key": "grid layout"
|
||||
}
|
||||
],
|
||||
"status": "passed"
|
||||
},
|
||||
"elementDrawing1000": {
|
||||
"time": [
|
||||
{
|
||||
"min": 55.3999999910593,
|
||||
"max": 75.5,
|
||||
"median": 65.29999999701977,
|
||||
"avg": 65.09999999962747,
|
||||
"variance": 7.902500012740493,
|
||||
"reliable": true,
|
||||
"key": "element drawing"
|
||||
},
|
||||
{
|
||||
"min": 31.700000002980232,
|
||||
"max": 47,
|
||||
"median": 36.20000000298023,
|
||||
"avg": 36.34999999962747,
|
||||
"variance": 5.345000000037253,
|
||||
"reliable": true,
|
||||
"key": "grid layout"
|
||||
}
|
||||
],
|
||||
"status": "passed"
|
||||
},
|
||||
"elementDrawing10000": {
|
||||
"time": [
|
||||
{
|
||||
"min": 404.40000000596046,
|
||||
"max": 515.7999999970198,
|
||||
"median": 420.6000000089407,
|
||||
"avg": 427.75000000186265,
|
||||
"variance": 282.06500002410263,
|
||||
"reliable": true,
|
||||
"key": "element drawing"
|
||||
},
|
||||
{
|
||||
"min": 306.70000000298023,
|
||||
"max": 341,
|
||||
"median": 321.6000000089407,
|
||||
"avg": 319.4125000014901,
|
||||
"variance": 58.756093712486326,
|
||||
"reliable": true,
|
||||
"key": "grid layout"
|
||||
}
|
||||
],
|
||||
"status": "passed"
|
||||
},
|
||||
"elementDrawing500": {
|
||||
"time": [
|
||||
{
|
||||
"min": 25,
|
||||
"max": 59.5,
|
||||
"median": 40.70000000298023,
|
||||
"avg": 42.03749999962747,
|
||||
"variance": 15.694843770219013,
|
||||
"reliable": true,
|
||||
"key": "element drawing"
|
||||
},
|
||||
{
|
||||
"min": 16.69999998807907,
|
||||
"max": 24.299999997019768,
|
||||
"median": 20.799999997019768,
|
||||
"avg": 20.25,
|
||||
"variance": 5.507500002086163,
|
||||
"reliable": true,
|
||||
"key": "grid layout"
|
||||
}
|
||||
],
|
||||
"status": "passed"
|
||||
},
|
||||
"elementDrawing5000": {
|
||||
"time": [
|
||||
{
|
||||
"min": 203.70000000298023,
|
||||
"max": 264.30000001192093,
|
||||
"median": 224.79999999701977,
|
||||
"avg": 225.87499999813735,
|
||||
"variance": 117.29687504237518,
|
||||
"reliable": true,
|
||||
"key": "element drawing"
|
||||
},
|
||||
{
|
||||
"min": 154.29999999701977,
|
||||
"max": 174.6000000089407,
|
||||
"median": 164.19999998807907,
|
||||
"avg": 161,
|
||||
"variance": 32.70499999940395,
|
||||
"reliable": true,
|
||||
"key": "grid layout"
|
||||
}
|
||||
],
|
||||
"status": "passed"
|
||||
}
|
||||
}
|
||||
}
|
38
packages/g6/__tests__/perf/update-state.perf.ts
Normal file
38
packages/g6/__tests__/perf/update-state.perf.ts
Normal file
@ -0,0 +1,38 @@
|
||||
import { Graph } from '@antv/g6';
|
||||
import type { Test } from 'iperf';
|
||||
|
||||
export const UpdateElementState: Test = async ({ container, perf }) => {
|
||||
const nodes = Array(1000)
|
||||
.fill(0)
|
||||
.map((_, i) => ({ id: `${i}` }));
|
||||
const edges = Array(999)
|
||||
.fill(0)
|
||||
.map((_, i) => ({ id: `edge-${i}`, source: `${i}`, target: `${i + 1}` }));
|
||||
|
||||
const graph = new Graph({
|
||||
container,
|
||||
animation: false,
|
||||
data: { nodes, edges },
|
||||
layout: { type: 'grid' },
|
||||
});
|
||||
|
||||
await graph.render();
|
||||
|
||||
const selected = [...nodes, ...edges].map((element) => [element.id, 'selected']);
|
||||
|
||||
await perf.evaluate('update element state to selected', async () => {
|
||||
await graph.setElementState(Object.fromEntries(selected));
|
||||
});
|
||||
|
||||
const none = [...nodes, ...edges].map((element) => [element.id, []]);
|
||||
|
||||
await perf.evaluate('update element state to default', async () => {
|
||||
await graph.setElementState(Object.fromEntries(none));
|
||||
});
|
||||
|
||||
const position = nodes.map((node) => [node.id, [10, 10]]);
|
||||
|
||||
await perf.evaluate('update element position', async () => {
|
||||
await graph.translateElementBy(Object.fromEntries(position), false);
|
||||
});
|
||||
};
|
@ -154,9 +154,10 @@ export abstract class BaseCombo<S extends BaseComboStyleProps = BaseComboStylePr
|
||||
return getExpandedBBox(childrenBBox, padding);
|
||||
}
|
||||
|
||||
@effect((self, attributes) => self.getCollapsedMarkerStyle(attributes))
|
||||
protected drawCollapsedMarkerShape(attributes: Required<S>, container: Group): void {
|
||||
this.upsert('collapsed-marker', Icon, this.getCollapsedMarkerStyle(attributes), container);
|
||||
const style = this.getCollapsedMarkerStyle(attributes);
|
||||
if (!effect(this, 'collapsedMarker', style)) return;
|
||||
this.upsert('collapsed-marker', Icon, style, container);
|
||||
connectImage(this);
|
||||
}
|
||||
|
||||
|
@ -335,6 +335,8 @@ export abstract class BaseEdge extends BaseElement<BaseEdgeStyleProps> implement
|
||||
|
||||
if (enable) {
|
||||
const arrowStyle = this.getArrowStyle(attributes, isStart);
|
||||
if (!effect(this, `arrow-${type}`, arrowStyle)) return;
|
||||
|
||||
const [marker, markerOffset, arrowOffset] = isStart
|
||||
? (['markerStart', 'markerStartOffset', 'startArrowOffset'] as const)
|
||||
: (['markerEnd', 'markerEndOffset', 'endArrowOffset'] as const);
|
||||
@ -376,35 +378,36 @@ export abstract class BaseEdge extends BaseElement<BaseEdgeStyleProps> implement
|
||||
);
|
||||
}
|
||||
|
||||
@effect((self, attributes) => self.getLabelStyle(attributes))
|
||||
protected drawLabelShape(attributes: ParsedBaseEdgeStyleProps, container: Group) {
|
||||
this.upsert('label', Label, this.getLabelStyle(attributes), container);
|
||||
const style = this.getLabelStyle(attributes);
|
||||
if (!effect(this, 'label', style)) return;
|
||||
this.upsert('label', Label, style, container);
|
||||
}
|
||||
|
||||
@effect((self, attributes) => self.getHaloStyle(attributes))
|
||||
protected drawHaloShape(attributes: ParsedBaseEdgeStyleProps, container: Group) {
|
||||
this.upsert('halo', Path, this.getHaloStyle(attributes), container);
|
||||
const style = this.getHaloStyle(attributes);
|
||||
if (!effect(this, 'halo', style)) return;
|
||||
this.upsert('halo', Path, style, container);
|
||||
}
|
||||
|
||||
@effect((self, attributes) => self.getBadgeStyle(attributes))
|
||||
protected drawBadgeShape(attributes: ParsedBaseEdgeStyleProps, container: Group) {
|
||||
this.upsert('badge', Badge, this.getBadgeStyle(attributes), container);
|
||||
const style = this.getBadgeStyle(attributes);
|
||||
if (!effect(this, 'badge', style)) return;
|
||||
this.upsert('badge', Badge, style, container);
|
||||
}
|
||||
|
||||
@effect((self, attributes) => self.getArrowStyle(attributes, 'start'))
|
||||
protected drawSourceArrow(attributes: ParsedBaseEdgeStyleProps) {
|
||||
this.drawArrow(attributes, 'start');
|
||||
}
|
||||
|
||||
@effect((self, attributes) => self.getArrowStyle(attributes, 'end'))
|
||||
protected drawTargetArrow(attributes: ParsedBaseEdgeStyleProps) {
|
||||
this.drawArrow(attributes, 'end');
|
||||
}
|
||||
|
||||
@effect((self, attributes) => self.getKeyStyle(attributes))
|
||||
protected drawKeyShape(attributes: ParsedBaseEdgeStyleProps, container: Group): Path | undefined {
|
||||
const key = this.upsert('key', Path, this.getKeyStyle(attributes), container);
|
||||
return key;
|
||||
const style = this.getKeyStyle(attributes);
|
||||
if (!effect(this, 'key', style)) return;
|
||||
return this.upsert('key', Path, style, container);
|
||||
}
|
||||
|
||||
public render(attributes = this.parsedAttributes, container: Group = this): void {
|
||||
|
@ -1,61 +1,27 @@
|
||||
import type { Element } from '../types';
|
||||
import { getCachedStyle, setCacheStyle } from '../utils/cache';
|
||||
|
||||
const EFFECT_WEAKMAP = new WeakMap<Element, Record<string, any>>();
|
||||
|
||||
/**
|
||||
* <zh/> 优化方法执行次数,仅在样式属性发生变化时执行函数
|
||||
* <zh/> 判定给定样式是否与上一次的样式相同
|
||||
*
|
||||
* <en/> Optimize the number of method executions, and only execute the function when the style attributes change
|
||||
* @param styler - <zh/> 获取样式属性函数 | <en/> Get style attribute function
|
||||
* @returns <zh/> 装饰器 | <en/> Decorator
|
||||
* @remarks
|
||||
* <zh/> 仅指定 getStyle 的情况下,会分别使用当前的 attributes 和 新的 attributes 调用函数,若两者相同,则不执行函数。
|
||||
*
|
||||
* 如果指定了 shapeKey, 则会直接获取该图形的 attributes 作为原始样式属性,通常在 getStyle 函数中获取了包围盒时使用。
|
||||
*
|
||||
* <en/> Only when getStyle is specified, the function will be called with the current attributes and the new attributes respectively. If they are the same, the function will not be executed.
|
||||
*
|
||||
* If shapeKey is specified, the attributes of the shape will be directly obtained as the original style attributes, which is usually used when the bounding box of the element is used in the getStyle function.
|
||||
* @example
|
||||
* <zh/> 仅当 value 发生变化时执行函数
|
||||
*
|
||||
* <en/> Execute the function only when value changes
|
||||
*
|
||||
* ```typescript
|
||||
* class CustomNode extends BaseNode {
|
||||
*
|
||||
* @effect((self, attributes) => {
|
||||
* const { value } = attributes;
|
||||
* return { value }
|
||||
* })
|
||||
* drawCustomShape(attributes, container) {
|
||||
* this.upsert('custom', 'circle', { ...attributes }, container);
|
||||
* }
|
||||
* }
|
||||
* ```
|
||||
* <en/> Determine whether the given style are the same as the previous ones
|
||||
* @param target - <zh/> 目标元素 | <en/> Target element
|
||||
* @param key - <zh/> 缓存 key | <en/> Cache key
|
||||
* @param style - <zh/> 样式属性 | <en/> Style attribute
|
||||
* @returns <zh/> 是否执行函数 | <en/> Whether to execute the function
|
||||
*/
|
||||
export function effect(styler: (self: any, attributes: Record<string, unknown>) => Record<string, unknown>) {
|
||||
return function (target: Element, propertyKey: string, descriptor: PropertyDescriptor) {
|
||||
const fn = descriptor.value;
|
||||
|
||||
descriptor.value = function (this: Element, attr: Record<string, unknown>, ...rest: unknown[]) {
|
||||
// 初始化后需要执行首次调用 / First call after initialization
|
||||
const initKey = `${propertyKey}_invoke`;
|
||||
if (!getCachedStyle(this, initKey)) {
|
||||
setCacheStyle(this, initKey, true);
|
||||
return fn.call(this, attr, ...rest);
|
||||
}
|
||||
|
||||
const styleKey = `${propertyKey}_style`;
|
||||
const original = getCachedStyle(this, styleKey);
|
||||
const modified = styler(this, attr);
|
||||
setCacheStyle(this, styleKey, modified);
|
||||
|
||||
if (isStyleEqual(original, modified)) return null;
|
||||
|
||||
return fn.call(this, attr, ...rest);
|
||||
};
|
||||
return descriptor;
|
||||
};
|
||||
export function effect<T extends false | Record<string, any>>(target: Element, key: string, style: T): boolean {
|
||||
if (!EFFECT_WEAKMAP.has(target)) EFFECT_WEAKMAP.set(target, {});
|
||||
const cache = EFFECT_WEAKMAP.get(target)!;
|
||||
if (!cache[key]) {
|
||||
cache[key] = style;
|
||||
return true;
|
||||
}
|
||||
const original = cache[key];
|
||||
if (isStyleEqual(original, style)) return false;
|
||||
cache[key] = style;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -71,7 +37,7 @@ export function effect(styler: (self: any, attributes: Record<string, unknown>)
|
||||
*
|
||||
* <en/> Perform a second-level shallow comparison to compare complex shape attributes such as badges and ports
|
||||
*/
|
||||
const isStyleEqual = (a: Record<string, unknown>, b: Record<string, unknown>, depth = 2): boolean => {
|
||||
const isStyleEqual = (a: false | Record<string, unknown>, b: false | Record<string, unknown>, depth = 2): boolean => {
|
||||
if (typeof a !== 'object' || typeof b !== 'object') return a === b;
|
||||
|
||||
const keys1 = Object.keys(a);
|
||||
|
@ -361,49 +361,54 @@ export abstract class BaseNode<S extends BaseNodeStyleProps = BaseNodeStyleProps
|
||||
return getRectIntersectPoint(point, keyShapeBounds);
|
||||
}
|
||||
|
||||
@effect((self, attributes) => self.getHaloStyle(attributes))
|
||||
protected drawHaloShape(attributes: Required<S>, container: Group): void {
|
||||
const style = this.getHaloStyle(attributes);
|
||||
if (!effect(this, 'halo', style)) return;
|
||||
|
||||
const keyShape = this.getShape('key');
|
||||
this.upsert(
|
||||
'halo',
|
||||
keyShape.constructor as new (...args: unknown[]) => DisplayObject,
|
||||
this.getHaloStyle(attributes),
|
||||
container,
|
||||
);
|
||||
this.upsert('halo', keyShape.constructor as new (...args: unknown[]) => DisplayObject, style, container);
|
||||
}
|
||||
|
||||
@effect((self, attributes) => self.getIconStyle(attributes))
|
||||
protected drawIconShape(attributes: Required<S>, container: Group): void {
|
||||
this.upsert('icon', Icon, this.getIconStyle(attributes), container);
|
||||
const style = this.getIconStyle(attributes);
|
||||
if (!effect(this, 'icon', style)) return;
|
||||
|
||||
this.upsert('icon', Icon, style, container);
|
||||
connectImage(this);
|
||||
}
|
||||
|
||||
@effect((self, attributes) => self.getBadgesStyle(attributes))
|
||||
protected drawBadgeShapes(attributes: Required<S>, container: Group): void {
|
||||
const badgesStyle = this.getBadgesStyle(attributes);
|
||||
Object.keys(badgesStyle).forEach((key) => {
|
||||
this.upsert(`badge-${key}`, Badge, badgesStyle[key], container);
|
||||
const style = badgesStyle[key];
|
||||
if (!effect(this, `badge-${key}`, style)) return;
|
||||
|
||||
this.upsert(`badge-${key}`, Badge, style, container);
|
||||
});
|
||||
}
|
||||
|
||||
@effect((self, attributes) => self.getPortsStyle(attributes))
|
||||
protected drawPortShapes(attributes: Required<S>, container: Group): void {
|
||||
const portsStyle = this.getPortsStyle(attributes);
|
||||
|
||||
Object.keys(portsStyle).forEach((key) => {
|
||||
this.upsert(`port-${key}`, GCircle, portsStyle[key] as CircleStyleProps, container);
|
||||
const style = portsStyle[key] as CircleStyleProps;
|
||||
const shapeKey = `port-${key}`;
|
||||
if (!effect(this, shapeKey, style)) return;
|
||||
|
||||
this.upsert(shapeKey, GCircle, style, container);
|
||||
});
|
||||
}
|
||||
|
||||
@effect((self, attributes) => self.getLabelStyle(attributes))
|
||||
protected drawLabelShape(attributes: Required<S>, container: Group): void {
|
||||
this.upsert('label', Label, this.getLabelStyle(attributes), container);
|
||||
const style = this.getLabelStyle(attributes);
|
||||
if (!effect(this, 'label', style)) return;
|
||||
|
||||
this.upsert('label', Label, style, container);
|
||||
}
|
||||
|
||||
protected abstract drawKeyShape(attributes: Required<S>, container: Group): DisplayObject | undefined;
|
||||
|
||||
// 用于装饰抽象方法 / Used to decorate abstract methods
|
||||
@effect((self, attributes) => self.getKeyStyle(attributes))
|
||||
private _drawKeyShape(attributes: Required<S>, container: Group) {
|
||||
return this.drawKeyShape(attributes, container);
|
||||
}
|
||||
|
@ -47,7 +47,7 @@ export abstract class BaseShape<StyleProps extends BaseShapeStyleProps> extends
|
||||
* <en> create, update or remove shape
|
||||
* @param className - <zh> 图形名称 | <en> shape name
|
||||
* @param Ctor - <zh> 图形类型 | <en> shape type
|
||||
* @param style - <zh> 图形样式 | <en> shape style
|
||||
* @param style - <zh> 图形样式。若要删除图形,传入 false | <en> shape style. Pass false to remove the shape
|
||||
* @param container - <zh> 容器 | <en> container
|
||||
* @param hooks - <zh> 钩子函数 | <en> hooks
|
||||
* @returns <zh> 图形实例 | <en> shape instance
|
||||
|
@ -28,7 +28,6 @@ export {
|
||||
} from './constants';
|
||||
export { BaseCombo, CircleCombo, RectCombo } from './elements/combos';
|
||||
export { BaseEdge, Cubic, CubicHorizontal, CubicVertical, Line, Polyline, Quadratic } from './elements/edges';
|
||||
export { effect } from './elements/effect';
|
||||
export {
|
||||
BaseNode,
|
||||
Circle,
|
||||
|
Loading…
Reference in New Issue
Block a user