mirror of
https://gitee.com/antv/g6.git
synced 2024-11-30 02:38:20 +08:00
feat(runtime): add element controller (#5393)
* feat(themes): add theme plugin type definition * feat(palettes): add palette utils and plugin type definition * feat(utils): add computeElementCallbackStyle util * refactor(spec): edge data style support config sourcePort and targetPort * refactor(animation): adjust executor to adapt undefined animation * test(spec): fix spec animation test case * feat(palettes): add built-in palettes * refactor(utils): adjust palette default logic * feat(theme): add built in theme * feat(runtime): add element controller * refactor(spec): rename port to anchor * test(registry): update registry test case * refactor(palettes): remove built in palettes to canstants * refactor(runtime): data controler remove event emit and provide getChanges API * refactor(registry): register built-in nodes and edges * refactor(runtime): adapt data controller changes, store animation result * refactor(runtime): element style callback returns index and element data extractly * fix(animation): remove parseAnimation to avoid circular dependencies * test: update test case * refactor: adjust demo env * refactor(animation): executor support specific modifiedStyle, and provide default style value infer * refactor(themes): update built-in themes * refactor(utils): update createAnimationsProxy to avoid sync onframe and onfinish to all instances * refactor(runtime): refactor cavas init function * refactor(spec): support to disable animation * test: update test spec * test: update test case * refactor(runtime): update element controller and integration cases * chore: update editor config * refactor(runtime): update render logic and fix issue that data states change
This commit is contained in:
parent
2b780f489a
commit
c763217197
5
.vscode/settings.json
vendored
5
.vscode/settings.json
vendored
@ -1,7 +1,10 @@
|
||||
{
|
||||
"cSpell.words": [
|
||||
"afterrender",
|
||||
"beforerender",
|
||||
"Fruchterman",
|
||||
"gforce",
|
||||
"graphlib"
|
||||
"graphlib",
|
||||
"onframe"
|
||||
]
|
||||
}
|
@ -0,0 +1,98 @@
|
||||
import type { G6Spec } from '../../../src';
|
||||
import { DataController } from '../../../src/runtime/data';
|
||||
import { ElementController } from '../../../src/runtime/element';
|
||||
import type { RuntimeContext } from '../../../src/runtime/types';
|
||||
import { Graph } from '../../mock';
|
||||
import type { AnimationTestCase } from '../types';
|
||||
|
||||
const createContext = (canvas: any, options: G6Spec): RuntimeContext => {
|
||||
const dataController = new DataController();
|
||||
dataController.setData(options.data || {});
|
||||
return {
|
||||
canvas,
|
||||
graph: new Graph() as any,
|
||||
options,
|
||||
dataController,
|
||||
};
|
||||
};
|
||||
|
||||
export const controllerElementState: AnimationTestCase = async (context) => {
|
||||
const { canvas } = context;
|
||||
|
||||
const options: G6Spec = {
|
||||
data: {
|
||||
nodes: [
|
||||
{ id: 'node-1', style: { cx: 50, cy: 50, states: ['active', 'selected'] } },
|
||||
{ id: 'node-2', style: { cx: 200, cy: 50 } },
|
||||
{ id: 'node-3', style: { cx: 125, cy: 150, states: ['active'] } },
|
||||
],
|
||||
edges: [
|
||||
{ source: 'node-1', target: 'node-2', style: { states: ['active'] } },
|
||||
{ source: 'node-2', target: 'node-3' },
|
||||
{ source: 'node-3', target: 'node-1' },
|
||||
],
|
||||
},
|
||||
theme: 'light',
|
||||
node: {
|
||||
style: {
|
||||
lineWidth: 1,
|
||||
r: 10,
|
||||
},
|
||||
state: {
|
||||
active: {
|
||||
lineWidth: 2,
|
||||
},
|
||||
selected: {
|
||||
fill: 'pink',
|
||||
},
|
||||
},
|
||||
animation: {
|
||||
update: [{ fields: ['lineWidth', 'fill'] }],
|
||||
},
|
||||
},
|
||||
edge: {
|
||||
style: {
|
||||
lineWidth: 1,
|
||||
},
|
||||
state: {
|
||||
active: {
|
||||
lineWidth: 2,
|
||||
stroke: 'pink',
|
||||
},
|
||||
},
|
||||
animation: {
|
||||
update: [
|
||||
{
|
||||
fields: ['lineWidth', 'stroke'],
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const elementContext = createContext(canvas, options);
|
||||
|
||||
const elementController = new ElementController(elementContext);
|
||||
|
||||
const renderResult = await elementController.render(elementContext);
|
||||
|
||||
await renderResult?.finished;
|
||||
|
||||
elementContext.dataController.updateData({
|
||||
nodes: [
|
||||
{ id: 'node-1', style: { states: [] } },
|
||||
{ id: 'node-2', style: { states: ['active'] } },
|
||||
{ id: 'node-3', style: { states: ['selected'] } },
|
||||
],
|
||||
edges: [
|
||||
{ source: 'node-1', target: 'node-2', style: { states: [] } },
|
||||
{ source: 'node-2', target: 'node-3', style: { states: ['active'] } },
|
||||
],
|
||||
});
|
||||
|
||||
const result = await elementController.render(elementContext);
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
controllerElementState.times = [0, 1000];
|
69
packages/g6/__tests__/demo/animation/controller-element.ts
Normal file
69
packages/g6/__tests__/demo/animation/controller-element.ts
Normal file
@ -0,0 +1,69 @@
|
||||
import type { G6Spec } from '../../../src';
|
||||
import { DataController } from '../../../src/runtime/data';
|
||||
import { ElementController } from '../../../src/runtime/element';
|
||||
import type { RuntimeContext } from '../../../src/runtime/types';
|
||||
import { Graph } from '../../mock';
|
||||
import type { AnimationTestCase } from '../types';
|
||||
|
||||
const createContext = (canvas: any, options: G6Spec): RuntimeContext => {
|
||||
const dataController = new DataController();
|
||||
dataController.setData(options.data || {});
|
||||
return {
|
||||
canvas,
|
||||
graph: new Graph() as any,
|
||||
options,
|
||||
dataController,
|
||||
};
|
||||
};
|
||||
|
||||
export const controllerElement: AnimationTestCase = async (context) => {
|
||||
const { canvas } = context;
|
||||
|
||||
const options: G6Spec = {
|
||||
data: {
|
||||
nodes: [
|
||||
{ id: 'node-1', style: { cx: 50, cy: 50 } },
|
||||
{ id: 'node-2', style: { cx: 200, cy: 50 } },
|
||||
{ id: 'node-3', style: { cx: 125, cy: 150 } },
|
||||
],
|
||||
edges: [
|
||||
{ source: 'node-1', target: 'node-2' },
|
||||
{ source: 'node-2', target: 'node-3' },
|
||||
{ source: 'node-3', target: 'node-1' },
|
||||
],
|
||||
},
|
||||
theme: 'light',
|
||||
node: {
|
||||
style: {
|
||||
r: 10,
|
||||
},
|
||||
},
|
||||
edge: {
|
||||
style: {},
|
||||
},
|
||||
};
|
||||
|
||||
const elementContext = createContext(canvas, options);
|
||||
|
||||
const elementController = new ElementController(elementContext);
|
||||
|
||||
const renderResult = await elementController.render(elementContext);
|
||||
|
||||
await renderResult?.finished;
|
||||
|
||||
elementContext.dataController.addNodeData([
|
||||
{ id: 'node-4', style: { cx: 50, cy: 200, stroke: 'orange' } },
|
||||
{ id: 'node-5', style: { cx: 75, cy: 150, stroke: 'purple' } },
|
||||
{ id: 'node-6', style: { cx: 200, cy: 100, stroke: 'cyan' } },
|
||||
]);
|
||||
|
||||
elementContext.dataController.removeNodeData(['node-1']);
|
||||
|
||||
elementContext.dataController.updateNodeData([{ id: 'node-2', style: { cx: 200, cy: 200, stroke: 'green' } }]);
|
||||
|
||||
const result = await elementController.render(elementContext);
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
controllerElement.times = [50, 200, 1000];
|
@ -1,10 +1,10 @@
|
||||
import '../../../src/preset';
|
||||
|
||||
export * from './animation-breathe';
|
||||
export * from './animation-fade-in';
|
||||
export * from './animation-translate';
|
||||
export * from './animation-wave';
|
||||
export * from './animation-zoom-in';
|
||||
export * from './controller-element';
|
||||
export * from './controller-element-state';
|
||||
export * from './edge-cubic';
|
||||
export * from './edge-line';
|
||||
export * from './edge-quadratic';
|
||||
|
58
packages/g6/__tests__/demo/static/controller-element.ts
Normal file
58
packages/g6/__tests__/demo/static/controller-element.ts
Normal file
@ -0,0 +1,58 @@
|
||||
import type { G6Spec } from '../../../src';
|
||||
import { DataController } from '../../../src/runtime/data';
|
||||
import { ElementController } from '../../../src/runtime/element';
|
||||
import type { RuntimeContext } from '../../../src/runtime/types';
|
||||
import { sleep } from '../../integration/utils/sleep';
|
||||
import { Graph } from '../../mock';
|
||||
import type { StaticTestCase } from '../types';
|
||||
|
||||
const createContext = (canvas: any, options: G6Spec): RuntimeContext => {
|
||||
const dataController = new DataController();
|
||||
dataController.setData(options.data || {});
|
||||
return {
|
||||
canvas,
|
||||
graph: new Graph() as any,
|
||||
options,
|
||||
dataController,
|
||||
};
|
||||
};
|
||||
|
||||
export const controllerElement: StaticTestCase = async (context) => {
|
||||
const { canvas } = context;
|
||||
|
||||
const options: G6Spec = {
|
||||
data: {
|
||||
nodes: [
|
||||
{ id: 'node-1', style: { cx: 50, cy: 50 } },
|
||||
{ id: 'node-2', style: { cx: 200, cy: 50 } },
|
||||
{ id: 'node-3', style: { cx: 125, cy: 150 } },
|
||||
],
|
||||
edges: [
|
||||
{ source: 'node-1', target: 'node-2' },
|
||||
{ source: 'node-2', target: 'node-3' },
|
||||
{ source: 'node-3', target: 'node-1' },
|
||||
],
|
||||
},
|
||||
theme: 'light',
|
||||
node: {
|
||||
style: {
|
||||
r: 10,
|
||||
},
|
||||
animation: false,
|
||||
},
|
||||
edge: {
|
||||
style: {},
|
||||
animation: false,
|
||||
},
|
||||
};
|
||||
|
||||
const elementContext = createContext(canvas, options);
|
||||
|
||||
const elementController = new ElementController(elementContext);
|
||||
|
||||
const result = await elementController.render(elementContext);
|
||||
|
||||
await result?.finished;
|
||||
|
||||
await sleep(100);
|
||||
};
|
@ -84,8 +84,6 @@ export const edgeLine: StaticTestCase = async (context) => {
|
||||
},
|
||||
});
|
||||
|
||||
await canvas.init();
|
||||
|
||||
canvas.appendChild(line1);
|
||||
canvas.appendChild(line2);
|
||||
canvas.appendChild(line3);
|
||||
|
@ -73,8 +73,6 @@ export const edgeQuadratic: StaticTestCase = async (context) => {
|
||||
},
|
||||
});
|
||||
|
||||
await canvas.init();
|
||||
|
||||
canvas.appendChild(quadratic1);
|
||||
canvas.appendChild(quadratic2);
|
||||
canvas.appendChild(quadratic3);
|
||||
|
@ -1,3 +1,4 @@
|
||||
export * from './controller-element';
|
||||
export * from './edge-cubic';
|
||||
export * from './edge-cubic-horizontal';
|
||||
export * from './edge-cubic-vertical';
|
||||
|
@ -23,8 +23,6 @@ export const layeredCanvas: StaticTestCase = async (context) => {
|
||||
},
|
||||
});
|
||||
|
||||
await canvas.init();
|
||||
|
||||
canvas.appendChild(circle);
|
||||
canvas.appendChild(rect);
|
||||
|
||||
|
@ -65,8 +65,6 @@ export const nodeCircle: StaticTestCase = async (context) => {
|
||||
},
|
||||
});
|
||||
|
||||
await canvas.init();
|
||||
|
||||
canvas.appendChild(c1);
|
||||
canvas.appendChild(c2);
|
||||
canvas.appendChild(c3);
|
||||
|
@ -53,8 +53,6 @@ export const shapeBadge: StaticTestCase = async (context) => {
|
||||
},
|
||||
});
|
||||
|
||||
await canvas.init();
|
||||
|
||||
canvas.appendChild(badge1);
|
||||
canvas.appendChild(badge2);
|
||||
canvas.appendChild(badge3);
|
||||
|
@ -26,8 +26,6 @@ export const shapeIcon: StaticTestCase = async (context) => {
|
||||
},
|
||||
});
|
||||
|
||||
await canvas.init();
|
||||
|
||||
canvas.appendChild(i1);
|
||||
canvas.appendChild(i2);
|
||||
};
|
||||
|
@ -59,8 +59,6 @@ export const shapeLabel: StaticTestCase = async (context) => {
|
||||
},
|
||||
});
|
||||
|
||||
await canvas.init();
|
||||
|
||||
canvas.appendChild(label1);
|
||||
canvas.appendChild(label2);
|
||||
canvas.appendChild(label3);
|
||||
|
@ -1,32 +1,119 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<html lang="en" data-theme="light">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>G6: Preview</title>
|
||||
<style>
|
||||
body {
|
||||
margin: 0;
|
||||
font-size: 14px;
|
||||
color: var(--text-color);
|
||||
background-color: var(--background-color);
|
||||
background-image: linear-gradient(var(--stroke-color) 1px, transparent 1px),
|
||||
linear-gradient(90deg, var(--stroke-color) 1px, transparent 1px);
|
||||
background-size: 25px 25px;
|
||||
transition: all 0.5s;
|
||||
}
|
||||
|
||||
#app {
|
||||
display: flex;
|
||||
flex-direction: row-reverse;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
#panel {
|
||||
background-color: var(--background-color2);
|
||||
border-radius: 4px;
|
||||
color: var(--text-color);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 5px;
|
||||
left: 500px;
|
||||
padding: 10px;
|
||||
position: absolute;
|
||||
top: 20px;
|
||||
transition:
|
||||
background-color 0.5s,
|
||||
color 0.5s;
|
||||
user-select: none;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
#container {
|
||||
width: 500px;
|
||||
height: 500px;
|
||||
background-image: linear-gradient(rgba(0, 0, 0, 0.1) 1px, transparent 1px),
|
||||
linear-gradient(90deg, rgba(0, 0, 0, 0.1) 1px, transparent 1px);
|
||||
background-size: 25px 25px;
|
||||
}
|
||||
|
||||
[data-theme='light'] {
|
||||
--text-color: #000;
|
||||
--background-color: #fff;
|
||||
--background-color2: #ddd;
|
||||
--stroke-color: #0001;
|
||||
}
|
||||
|
||||
[data-theme='dark'] {
|
||||
--text-color: #fff;
|
||||
--background-color: #000;
|
||||
--background-color2: #333;
|
||||
--stroke-color: #333;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body style="font-family: Arial, Helvetica, sans-serif">
|
||||
<div id="app">
|
||||
<label for="demo-select">Cases: </label>
|
||||
<select id="demo-select" style="cursor: pointer; width: 180px"></select>
|
||||
<label for="renderer-select">Renderer: </label>
|
||||
<select id="renderer-select" style="cursor: pointer">
|
||||
<option value="default" selected>Default</option>
|
||||
<option value="canvas" selected>Canvas</option>
|
||||
<option value="svg">SVG</option>
|
||||
<option value="webgl">WebGL</option>
|
||||
</select>
|
||||
<div id="panel">
|
||||
<label for="demo-select">Cases: </label>
|
||||
<select id="demo-select" style="cursor: pointer; width: 180px"></select>
|
||||
<label for="renderer-select">Renderer: </label>
|
||||
<select id="renderer-select" style="cursor: pointer">
|
||||
<option value="default" selected>Default</option>
|
||||
<option value="canvas" selected>Canvas</option>
|
||||
<option value="svg">SVG</option>
|
||||
<option value="webgl">WebGL</option>
|
||||
</select>
|
||||
<label for="theme">Theme: </label>
|
||||
<button onclick="toggleTheme()">Toggle</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
function toggleTheme() {
|
||||
const theme = document.documentElement.getAttribute('data-theme');
|
||||
if (theme === 'light') {
|
||||
document.documentElement.setAttribute('data-theme', 'dark');
|
||||
} else {
|
||||
document.documentElement.setAttribute('data-theme', 'light');
|
||||
}
|
||||
}
|
||||
|
||||
(function draggable() {
|
||||
const draggableElement = document.getElementById('panel');
|
||||
let offsetX,
|
||||
offsetY,
|
||||
isDragging = false;
|
||||
|
||||
draggableElement.addEventListener('mousedown', function (e) {
|
||||
isDragging = true;
|
||||
offsetX = e.clientX - draggableElement.getBoundingClientRect().left;
|
||||
offsetY = e.clientY - draggableElement.getBoundingClientRect().top;
|
||||
});
|
||||
|
||||
document.addEventListener('mousemove', function (e) {
|
||||
if (isDragging) {
|
||||
const x = e.clientX - offsetX;
|
||||
const y = e.clientY - offsetY;
|
||||
draggableElement.style.left = `${x}px`;
|
||||
draggableElement.style.top = `${y}px`;
|
||||
}
|
||||
});
|
||||
|
||||
document.addEventListener('mouseup', function () {
|
||||
isDragging = false;
|
||||
});
|
||||
})();
|
||||
</script>
|
||||
<script type="module" src="./main.ts"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
@ -16,6 +16,7 @@ describe('static', () => {
|
||||
const { times = [], preprocess, postprocess } = testCase;
|
||||
|
||||
await preprocess?.();
|
||||
await canvas.init();
|
||||
const animationResult = await testCase({ canvas });
|
||||
|
||||
if (!animationResult) throw new Error('animation result should not be null');
|
||||
|
@ -0,0 +1,376 @@
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="500"
|
||||
height="500"
|
||||
style="background: transparent; position: absolute; outline: none;"
|
||||
color-interpolation-filters="sRGB"
|
||||
tabindex="1"
|
||||
>
|
||||
<defs />
|
||||
<g id="g-svg-camera" transform="matrix(1,0,0,1,0,0)">
|
||||
<g id="g-root" fill="none" transform="matrix(1,0,0,1,0,0)">
|
||||
<g id="g-svg-7" fill="none" transform="matrix(1,0,0,1,0,0)" />
|
||||
<g id="g-svg-6" fill="none" transform="matrix(1,0,0,1,0,0)">
|
||||
<g
|
||||
id="g-svg-32"
|
||||
fill="none"
|
||||
marker-start="false"
|
||||
marker-end="false"
|
||||
transform="matrix(1,0,0,1,0,0)"
|
||||
>
|
||||
<g transform="matrix(1,0,0,1,50,50)">
|
||||
<line
|
||||
id="key"
|
||||
fill="none"
|
||||
x1="0"
|
||||
y1="0"
|
||||
x2="150"
|
||||
y2="0"
|
||||
stroke-width="1"
|
||||
stroke="rgba(139,155,175,1)"
|
||||
opacity="0"
|
||||
/>
|
||||
</g>
|
||||
<g id="label" fill="none" transform="matrix(1,0,0,1,129,44)">
|
||||
<g transform="matrix(1,0,0,1,-5,-5)">
|
||||
<path
|
||||
id="background"
|
||||
fill="none"
|
||||
d="M 0,0 l 10,0 l 0,10 l-10 0 z"
|
||||
width="10"
|
||||
height="10"
|
||||
opacity="0"
|
||||
/>
|
||||
</g>
|
||||
<g transform="matrix(1,0,0,1,0,0)">
|
||||
<text
|
||||
id="text"
|
||||
fill="rgba(0,0,0,1)"
|
||||
dominant-baseline="text-after-edge"
|
||||
paint-order="stroke"
|
||||
text-anchor="middle"
|
||||
opacity="0"
|
||||
/>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g
|
||||
id="g-svg-37"
|
||||
fill="none"
|
||||
marker-start="false"
|
||||
marker-end="false"
|
||||
transform="matrix(1,0,0,1,0,0)"
|
||||
>
|
||||
<g transform="matrix(1,0,0,1,125,150)">
|
||||
<line
|
||||
id="key"
|
||||
fill="none"
|
||||
x1="75"
|
||||
y1="50"
|
||||
x2="0"
|
||||
y2="0"
|
||||
stroke-width="1"
|
||||
stroke="rgba(139,155,175,1)"
|
||||
/>
|
||||
</g>
|
||||
<g
|
||||
id="label"
|
||||
fill="none"
|
||||
transform="matrix(0.832050,0.554700,-0.554700,0.832050,162.500000,167.788895)"
|
||||
>
|
||||
<g transform="matrix(1,0,0,1,-5,-5)">
|
||||
<path
|
||||
id="background"
|
||||
fill="none"
|
||||
d="M 0,0 l 10,0 l 0,10 l-10 0 z"
|
||||
width="10"
|
||||
height="10"
|
||||
/>
|
||||
</g>
|
||||
<g transform="matrix(1,0,0,1,0,0)">
|
||||
<text
|
||||
id="text"
|
||||
fill="rgba(0,0,0,1)"
|
||||
dominant-baseline="text-after-edge"
|
||||
paint-order="stroke"
|
||||
text-anchor="middle"
|
||||
/>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="g-svg-5" fill="none" transform="matrix(1,0,0,1,0,0)">
|
||||
<g id="g-svg-16" fill="none" r="10" transform="matrix(1,0,0,1,0,0)">
|
||||
<g transform="matrix(1,0,0,1,200,200)">
|
||||
<circle
|
||||
id="key"
|
||||
fill="rgba(248,248,248,1)"
|
||||
transform="translate(-10,-10)"
|
||||
cx="10"
|
||||
cy="10"
|
||||
stroke="rgba(0,128,0,1)"
|
||||
r="10"
|
||||
/>
|
||||
</g>
|
||||
<g id="label" fill="none" transform="matrix(1,0,0,1,200,200)">
|
||||
<g transform="matrix(1,0,0,1,-5,-5)">
|
||||
<path
|
||||
id="background"
|
||||
fill="none"
|
||||
d="M 0,0 l 10,0 l 0,10 l-10 0 z"
|
||||
width="10"
|
||||
height="10"
|
||||
/>
|
||||
</g>
|
||||
<g transform="matrix(1,0,0,1,0,0)">
|
||||
<text
|
||||
id="text"
|
||||
fill="rgba(0,0,0,1)"
|
||||
dominant-baseline="central"
|
||||
paint-order="stroke"
|
||||
text-anchor="middle"
|
||||
/>
|
||||
</g>
|
||||
</g>
|
||||
<g transform="matrix(1,0,0,1,200,200)">
|
||||
<circle
|
||||
id="halo"
|
||||
fill="rgba(248,248,248,1)"
|
||||
cx="0"
|
||||
cy="0"
|
||||
stroke="rgba(0,128,0,1)"
|
||||
r="0"
|
||||
/>
|
||||
</g>
|
||||
<g id="icon" fill="none" transform="matrix(1,0,0,1,200,200)">
|
||||
<g transform="matrix(1,0,0,1,0,0)">
|
||||
<text
|
||||
id="icon"
|
||||
fill="rgba(0,0,0,1)"
|
||||
dominant-baseline="central"
|
||||
paint-order="stroke"
|
||||
text-anchor="middle"
|
||||
/>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="g-svg-24" fill="none" r="10" transform="matrix(1,0,0,1,0,0)">
|
||||
<g transform="matrix(1,0,0,1,125,150)">
|
||||
<circle
|
||||
id="key"
|
||||
fill="rgba(248,248,248,1)"
|
||||
transform="translate(-10,-10)"
|
||||
cx="10"
|
||||
cy="10"
|
||||
stroke="rgba(139,155,175,1)"
|
||||
r="10"
|
||||
/>
|
||||
</g>
|
||||
<g id="label" fill="none" transform="matrix(1,0,0,1,125,150)">
|
||||
<g transform="matrix(1,0,0,1,-5,-5)">
|
||||
<path
|
||||
id="background"
|
||||
fill="none"
|
||||
d="M 0,0 l 10,0 l 0,10 l-10 0 z"
|
||||
width="10"
|
||||
height="10"
|
||||
/>
|
||||
</g>
|
||||
<g transform="matrix(1,0,0,1,0,0)">
|
||||
<text
|
||||
id="text"
|
||||
fill="rgba(0,0,0,1)"
|
||||
dominant-baseline="central"
|
||||
paint-order="stroke"
|
||||
text-anchor="middle"
|
||||
/>
|
||||
</g>
|
||||
</g>
|
||||
<g transform="matrix(1,0,0,1,125,150)">
|
||||
<circle
|
||||
id="halo"
|
||||
fill="rgba(248,248,248,1)"
|
||||
cx="0"
|
||||
cy="0"
|
||||
stroke="rgba(139,155,175,1)"
|
||||
r="0"
|
||||
/>
|
||||
</g>
|
||||
<g id="icon" fill="none" transform="matrix(1,0,0,1,125,150)">
|
||||
<g transform="matrix(1,0,0,1,0,0)">
|
||||
<text
|
||||
id="icon"
|
||||
fill="rgba(0,0,0,1)"
|
||||
dominant-baseline="central"
|
||||
paint-order="stroke"
|
||||
text-anchor="middle"
|
||||
/>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="g-svg-47" fill="none" r="10" transform="matrix(1,0,0,1,0,0)">
|
||||
<g transform="matrix(1,0,0,1,50,200)">
|
||||
<circle
|
||||
id="key"
|
||||
fill="rgba(248,248,248,1)"
|
||||
transform="translate(-10,-10)"
|
||||
cx="10"
|
||||
cy="10"
|
||||
stroke="rgba(255,165,0,1)"
|
||||
r="10"
|
||||
/>
|
||||
</g>
|
||||
<g id="label" fill="none" transform="matrix(1,0,0,1,50,200)">
|
||||
<g transform="matrix(1,0,0,1,-5,-5)">
|
||||
<path
|
||||
id="background"
|
||||
fill="none"
|
||||
d="M 0,0 l 10,0 l 0,10 l-10 0 z"
|
||||
width="10"
|
||||
height="10"
|
||||
/>
|
||||
</g>
|
||||
<g transform="matrix(1,0,0,1,0,0)">
|
||||
<text
|
||||
id="text"
|
||||
fill="rgba(0,0,0,1)"
|
||||
dominant-baseline="central"
|
||||
paint-order="stroke"
|
||||
text-anchor="middle"
|
||||
/>
|
||||
</g>
|
||||
</g>
|
||||
<g transform="matrix(1,0,0,1,50,200)">
|
||||
<circle
|
||||
id="halo"
|
||||
fill="rgba(248,248,248,1)"
|
||||
cx="0"
|
||||
cy="0"
|
||||
stroke="rgba(255,165,0,1)"
|
||||
r="0"
|
||||
/>
|
||||
</g>
|
||||
<g id="icon" fill="none" transform="matrix(1,0,0,1,50,200)">
|
||||
<g transform="matrix(1,0,0,1,0,0)">
|
||||
<text
|
||||
id="icon"
|
||||
fill="rgba(0,0,0,1)"
|
||||
dominant-baseline="central"
|
||||
paint-order="stroke"
|
||||
text-anchor="middle"
|
||||
/>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="g-svg-55" fill="none" r="10" transform="matrix(1,0,0,1,0,0)">
|
||||
<g transform="matrix(1,0,0,1,75,150)">
|
||||
<circle
|
||||
id="key"
|
||||
fill="rgba(248,248,248,1)"
|
||||
transform="translate(-10,-10)"
|
||||
cx="10"
|
||||
cy="10"
|
||||
stroke="rgba(128,0,128,1)"
|
||||
r="10"
|
||||
/>
|
||||
</g>
|
||||
<g id="label" fill="none" transform="matrix(1,0,0,1,75,150)">
|
||||
<g transform="matrix(1,0,0,1,-5,-5)">
|
||||
<path
|
||||
id="background"
|
||||
fill="none"
|
||||
d="M 0,0 l 10,0 l 0,10 l-10 0 z"
|
||||
width="10"
|
||||
height="10"
|
||||
/>
|
||||
</g>
|
||||
<g transform="matrix(1,0,0,1,0,0)">
|
||||
<text
|
||||
id="text"
|
||||
fill="rgba(0,0,0,1)"
|
||||
dominant-baseline="central"
|
||||
paint-order="stroke"
|
||||
text-anchor="middle"
|
||||
/>
|
||||
</g>
|
||||
</g>
|
||||
<g transform="matrix(1,0,0,1,75,150)">
|
||||
<circle
|
||||
id="halo"
|
||||
fill="rgba(248,248,248,1)"
|
||||
cx="0"
|
||||
cy="0"
|
||||
stroke="rgba(128,0,128,1)"
|
||||
r="0"
|
||||
/>
|
||||
</g>
|
||||
<g id="icon" fill="none" transform="matrix(1,0,0,1,75,150)">
|
||||
<g transform="matrix(1,0,0,1,0,0)">
|
||||
<text
|
||||
id="icon"
|
||||
fill="rgba(0,0,0,1)"
|
||||
dominant-baseline="central"
|
||||
paint-order="stroke"
|
||||
text-anchor="middle"
|
||||
/>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="g-svg-63" fill="none" r="10" transform="matrix(1,0,0,1,0,0)">
|
||||
<g transform="matrix(1,0,0,1,200,100)">
|
||||
<circle
|
||||
id="key"
|
||||
fill="rgba(248,248,248,1)"
|
||||
transform="translate(-10,-10)"
|
||||
cx="10"
|
||||
cy="10"
|
||||
stroke="rgba(0,255,255,1)"
|
||||
r="10"
|
||||
/>
|
||||
</g>
|
||||
<g id="label" fill="none" transform="matrix(1,0,0,1,200,100)">
|
||||
<g transform="matrix(1,0,0,1,-5,-5)">
|
||||
<path
|
||||
id="background"
|
||||
fill="none"
|
||||
d="M 0,0 l 10,0 l 0,10 l-10 0 z"
|
||||
width="10"
|
||||
height="10"
|
||||
/>
|
||||
</g>
|
||||
<g transform="matrix(1,0,0,1,0,0)">
|
||||
<text
|
||||
id="text"
|
||||
fill="rgba(0,0,0,1)"
|
||||
dominant-baseline="central"
|
||||
paint-order="stroke"
|
||||
text-anchor="middle"
|
||||
/>
|
||||
</g>
|
||||
</g>
|
||||
<g transform="matrix(1,0,0,1,200,100)">
|
||||
<circle
|
||||
id="halo"
|
||||
fill="rgba(248,248,248,1)"
|
||||
cx="0"
|
||||
cy="0"
|
||||
stroke="rgba(0,255,255,1)"
|
||||
r="0"
|
||||
/>
|
||||
</g>
|
||||
<g id="icon" fill="none" transform="matrix(1,0,0,1,200,100)">
|
||||
<g transform="matrix(1,0,0,1,0,0)">
|
||||
<text
|
||||
id="icon"
|
||||
fill="rgba(0,0,0,1)"
|
||||
dominant-baseline="central"
|
||||
paint-order="stroke"
|
||||
text-anchor="middle"
|
||||
/>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 11 KiB |
@ -0,0 +1,497 @@
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="500"
|
||||
height="500"
|
||||
style="background: transparent; position: absolute; outline: none;"
|
||||
color-interpolation-filters="sRGB"
|
||||
tabindex="1"
|
||||
>
|
||||
<defs />
|
||||
<g id="g-svg-camera" transform="matrix(1,0,0,1,0,0)">
|
||||
<g id="g-root" fill="none" transform="matrix(1,0,0,1,0,0)">
|
||||
<g id="g-svg-7" fill="none" transform="matrix(1,0,0,1,0,0)" />
|
||||
<g id="g-svg-6" fill="none" transform="matrix(1,0,0,1,0,0)">
|
||||
<g
|
||||
id="g-svg-32"
|
||||
fill="none"
|
||||
marker-start="false"
|
||||
marker-end="false"
|
||||
transform="matrix(1,0,0,1,0,0)"
|
||||
>
|
||||
<g transform="matrix(1,0,0,1,50,50)">
|
||||
<line
|
||||
id="key"
|
||||
fill="none"
|
||||
x1="0"
|
||||
y1="0"
|
||||
x2="150"
|
||||
y2="0"
|
||||
stroke-width="1"
|
||||
stroke="rgba(139,155,175,1)"
|
||||
opacity="0.37293273049835485"
|
||||
/>
|
||||
</g>
|
||||
<g id="label" fill="none" transform="matrix(1,0,0,1,129,44)">
|
||||
<g transform="matrix(1,0,0,1,-5,-5)">
|
||||
<path
|
||||
id="background"
|
||||
fill="none"
|
||||
d="M 0,0 l 10,0 l 0,10 l-10 0 z"
|
||||
width="10"
|
||||
height="10"
|
||||
opacity="0.37293273049835485"
|
||||
/>
|
||||
</g>
|
||||
<g transform="matrix(1,0,0,1,0,0)">
|
||||
<text
|
||||
id="text"
|
||||
fill="rgba(0,0,0,1)"
|
||||
dominant-baseline="text-after-edge"
|
||||
paint-order="stroke"
|
||||
text-anchor="middle"
|
||||
opacity="0.37293273049835485"
|
||||
/>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g
|
||||
id="g-svg-37"
|
||||
fill="none"
|
||||
marker-start="false"
|
||||
marker-end="false"
|
||||
transform="matrix(1,0,0,1,0,0)"
|
||||
>
|
||||
<g transform="matrix(1,0,0,1,125,144.060089)">
|
||||
<line
|
||||
id="key"
|
||||
fill="none"
|
||||
x1="75"
|
||||
y1="0"
|
||||
x2="0"
|
||||
y2="5.939909574753216"
|
||||
stroke-width="1"
|
||||
stroke="rgba(139,155,175,1)"
|
||||
/>
|
||||
</g>
|
||||
<g
|
||||
id="label"
|
||||
fill="none"
|
||||
transform="matrix(0.832050,0.554700,-0.554700,0.832050,162.500000,167.788895)"
|
||||
>
|
||||
<g transform="matrix(1,0,0,1,-5,-5)">
|
||||
<path
|
||||
id="background"
|
||||
fill="none"
|
||||
d="M 0,0 l 10,0 l 0,10 l-10 0 z"
|
||||
width="10"
|
||||
height="10"
|
||||
/>
|
||||
</g>
|
||||
<g transform="matrix(1,0,0,1,0,0)">
|
||||
<text
|
||||
id="text"
|
||||
fill="rgba(0,0,0,1)"
|
||||
dominant-baseline="text-after-edge"
|
||||
paint-order="stroke"
|
||||
text-anchor="middle"
|
||||
/>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g
|
||||
id="g-svg-42"
|
||||
fill="none"
|
||||
marker-start="false"
|
||||
marker-end="false"
|
||||
transform="matrix(1,0,0,1,0,0)"
|
||||
>
|
||||
<g transform="matrix(1,0,0,1,50,50)">
|
||||
<line
|
||||
id="key"
|
||||
fill="none"
|
||||
x1="75"
|
||||
y1="100"
|
||||
x2="0"
|
||||
y2="0"
|
||||
stroke-width="1"
|
||||
stroke="rgba(139,155,175,1)"
|
||||
opacity="0.37293273049835485"
|
||||
/>
|
||||
</g>
|
||||
<g
|
||||
id="label"
|
||||
fill="none"
|
||||
transform="matrix(0.600000,0.800000,-0.800000,0.600000,89.900002,93.199997)"
|
||||
>
|
||||
<g transform="matrix(1,0,0,1,-5,-5)">
|
||||
<path
|
||||
id="background"
|
||||
fill="none"
|
||||
d="M 0,0 l 10,0 l 0,10 l-10 0 z"
|
||||
width="10"
|
||||
height="10"
|
||||
opacity="0.37293273049835485"
|
||||
/>
|
||||
</g>
|
||||
<g transform="matrix(1,0,0,1,0,0)">
|
||||
<text
|
||||
id="text"
|
||||
fill="rgba(0,0,0,1)"
|
||||
dominant-baseline="text-after-edge"
|
||||
paint-order="stroke"
|
||||
text-anchor="middle"
|
||||
opacity="0.37293273049835485"
|
||||
/>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="g-svg-5" fill="none" transform="matrix(1,0,0,1,0,0)">
|
||||
<g id="g-svg-8" fill="none" r="10" transform="matrix(1,0,0,1,0,0)">
|
||||
<g transform="matrix(1,0,0,1,50,50)">
|
||||
<circle
|
||||
id="key"
|
||||
fill="rgba(248,248,248,1)"
|
||||
transform="translate(-10,-10)"
|
||||
cx="10"
|
||||
cy="10"
|
||||
stroke="rgba(139,155,175,1)"
|
||||
r="10"
|
||||
opacity="0.37293273049835485"
|
||||
/>
|
||||
</g>
|
||||
<g id="label" fill="none" transform="matrix(1,0,0,1,50,50)">
|
||||
<g transform="matrix(1,0,0,1,-5,-5)">
|
||||
<path
|
||||
id="background"
|
||||
fill="none"
|
||||
d="M 0,0 l 10,0 l 0,10 l-10 0 z"
|
||||
width="10"
|
||||
height="10"
|
||||
opacity="0.37293273049835485"
|
||||
/>
|
||||
</g>
|
||||
<g transform="matrix(1,0,0,1,0,0)">
|
||||
<text
|
||||
id="text"
|
||||
fill="rgba(0,0,0,1)"
|
||||
dominant-baseline="central"
|
||||
paint-order="stroke"
|
||||
text-anchor="middle"
|
||||
opacity="0.37293273049835485"
|
||||
/>
|
||||
</g>
|
||||
</g>
|
||||
<g transform="matrix(1,0,0,1,50,50)">
|
||||
<circle
|
||||
id="halo"
|
||||
fill="rgba(248,248,248,1)"
|
||||
cx="0"
|
||||
cy="0"
|
||||
stroke="rgba(139,155,175,1)"
|
||||
r="0"
|
||||
opacity="0.37293273049835485"
|
||||
/>
|
||||
</g>
|
||||
<g id="icon" fill="none" transform="matrix(1,0,0,1,50,50)">
|
||||
<g transform="matrix(1,0,0,1,0,0)">
|
||||
<text
|
||||
id="icon"
|
||||
fill="rgba(0,0,0,1)"
|
||||
dominant-baseline="central"
|
||||
paint-order="stroke"
|
||||
text-anchor="middle"
|
||||
opacity="0.37293273049835485"
|
||||
/>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="g-svg-16" fill="none" r="10" transform="matrix(1,0,0,1,0,0)">
|
||||
<g transform="matrix(1,0,0,1,200,144.060089)">
|
||||
<circle
|
||||
id="key"
|
||||
fill="rgba(248,248,248,1)"
|
||||
transform="translate(-10,-10)"
|
||||
cx="10"
|
||||
cy="10"
|
||||
stroke="rgba(0,128,0,1)"
|
||||
r="10"
|
||||
/>
|
||||
</g>
|
||||
<g id="label" fill="none" transform="matrix(1,0,0,1,200,200)">
|
||||
<g transform="matrix(1,0,0,1,-5,-5)">
|
||||
<path
|
||||
id="background"
|
||||
fill="none"
|
||||
d="M 0,0 l 10,0 l 0,10 l-10 0 z"
|
||||
width="10"
|
||||
height="10"
|
||||
/>
|
||||
</g>
|
||||
<g transform="matrix(1,0,0,1,0,0)">
|
||||
<text
|
||||
id="text"
|
||||
fill="rgba(0,0,0,1)"
|
||||
dominant-baseline="central"
|
||||
paint-order="stroke"
|
||||
text-anchor="middle"
|
||||
/>
|
||||
</g>
|
||||
</g>
|
||||
<g transform="matrix(1,0,0,1,200,144.060089)">
|
||||
<circle
|
||||
id="halo"
|
||||
fill="rgba(248,248,248,1)"
|
||||
cx="0"
|
||||
cy="0"
|
||||
stroke="rgba(0,128,0,1)"
|
||||
r="0"
|
||||
/>
|
||||
</g>
|
||||
<g id="icon" fill="none" transform="matrix(1,0,0,1,200,200)">
|
||||
<g transform="matrix(1,0,0,1,0,0)">
|
||||
<text
|
||||
id="icon"
|
||||
fill="rgba(0,0,0,1)"
|
||||
dominant-baseline="central"
|
||||
paint-order="stroke"
|
||||
text-anchor="middle"
|
||||
/>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="g-svg-24" fill="none" r="10" transform="matrix(1,0,0,1,0,0)">
|
||||
<g transform="matrix(1,0,0,1,125,150)">
|
||||
<circle
|
||||
id="key"
|
||||
fill="rgba(248,248,248,1)"
|
||||
transform="translate(-10,-10)"
|
||||
cx="10"
|
||||
cy="10"
|
||||
stroke="rgba(139,155,175,1)"
|
||||
r="10"
|
||||
/>
|
||||
</g>
|
||||
<g id="label" fill="none" transform="matrix(1,0,0,1,125,150)">
|
||||
<g transform="matrix(1,0,0,1,-5,-5)">
|
||||
<path
|
||||
id="background"
|
||||
fill="none"
|
||||
d="M 0,0 l 10,0 l 0,10 l-10 0 z"
|
||||
width="10"
|
||||
height="10"
|
||||
/>
|
||||
</g>
|
||||
<g transform="matrix(1,0,0,1,0,0)">
|
||||
<text
|
||||
id="text"
|
||||
fill="rgba(0,0,0,1)"
|
||||
dominant-baseline="central"
|
||||
paint-order="stroke"
|
||||
text-anchor="middle"
|
||||
/>
|
||||
</g>
|
||||
</g>
|
||||
<g transform="matrix(1,0,0,1,125,150)">
|
||||
<circle
|
||||
id="halo"
|
||||
fill="rgba(248,248,248,1)"
|
||||
cx="0"
|
||||
cy="0"
|
||||
stroke="rgba(139,155,175,1)"
|
||||
r="0"
|
||||
/>
|
||||
</g>
|
||||
<g id="icon" fill="none" transform="matrix(1,0,0,1,125,150)">
|
||||
<g transform="matrix(1,0,0,1,0,0)">
|
||||
<text
|
||||
id="icon"
|
||||
fill="rgba(0,0,0,1)"
|
||||
dominant-baseline="central"
|
||||
paint-order="stroke"
|
||||
text-anchor="middle"
|
||||
/>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="g-svg-47" fill="none" r="10" transform="matrix(1,0,0,1,0,0)">
|
||||
<g transform="matrix(1,0,0,1,50,200)">
|
||||
<circle
|
||||
id="key"
|
||||
fill="rgba(248,248,248,1)"
|
||||
transform="translate(-10,-10)"
|
||||
cx="10"
|
||||
cy="10"
|
||||
stroke="rgba(255,165,0,1)"
|
||||
r="10"
|
||||
opacity="0.6270672695016452"
|
||||
/>
|
||||
</g>
|
||||
<g id="label" fill="none" transform="matrix(1,0,0,1,50,200)">
|
||||
<g transform="matrix(1,0,0,1,-5,-5)">
|
||||
<path
|
||||
id="background"
|
||||
fill="none"
|
||||
d="M 0,0 l 10,0 l 0,10 l-10 0 z"
|
||||
width="10"
|
||||
height="10"
|
||||
opacity="0.6270672695016452"
|
||||
/>
|
||||
</g>
|
||||
<g transform="matrix(1,0,0,1,0,0)">
|
||||
<text
|
||||
id="text"
|
||||
fill="rgba(0,0,0,1)"
|
||||
dominant-baseline="central"
|
||||
paint-order="stroke"
|
||||
text-anchor="middle"
|
||||
opacity="0.6270672695016452"
|
||||
/>
|
||||
</g>
|
||||
</g>
|
||||
<g transform="matrix(1,0,0,1,50,200)">
|
||||
<circle
|
||||
id="halo"
|
||||
fill="rgba(248,248,248,1)"
|
||||
cx="0"
|
||||
cy="0"
|
||||
stroke="rgba(255,165,0,1)"
|
||||
r="0"
|
||||
opacity="0.6270672695016452"
|
||||
/>
|
||||
</g>
|
||||
<g id="icon" fill="none" transform="matrix(1,0,0,1,50,200)">
|
||||
<g transform="matrix(1,0,0,1,0,0)">
|
||||
<text
|
||||
id="icon"
|
||||
fill="rgba(0,0,0,1)"
|
||||
dominant-baseline="central"
|
||||
paint-order="stroke"
|
||||
text-anchor="middle"
|
||||
opacity="0.6270672695016452"
|
||||
/>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="g-svg-55" fill="none" r="10" transform="matrix(1,0,0,1,0,0)">
|
||||
<g transform="matrix(1,0,0,1,75,150)">
|
||||
<circle
|
||||
id="key"
|
||||
fill="rgba(248,248,248,1)"
|
||||
transform="translate(-10,-10)"
|
||||
cx="10"
|
||||
cy="10"
|
||||
stroke="rgba(128,0,128,1)"
|
||||
r="10"
|
||||
opacity="0.6270672695016452"
|
||||
/>
|
||||
</g>
|
||||
<g id="label" fill="none" transform="matrix(1,0,0,1,75,150)">
|
||||
<g transform="matrix(1,0,0,1,-5,-5)">
|
||||
<path
|
||||
id="background"
|
||||
fill="none"
|
||||
d="M 0,0 l 10,0 l 0,10 l-10 0 z"
|
||||
width="10"
|
||||
height="10"
|
||||
opacity="0.6270672695016452"
|
||||
/>
|
||||
</g>
|
||||
<g transform="matrix(1,0,0,1,0,0)">
|
||||
<text
|
||||
id="text"
|
||||
fill="rgba(0,0,0,1)"
|
||||
dominant-baseline="central"
|
||||
paint-order="stroke"
|
||||
text-anchor="middle"
|
||||
opacity="0.6270672695016452"
|
||||
/>
|
||||
</g>
|
||||
</g>
|
||||
<g transform="matrix(1,0,0,1,75,150)">
|
||||
<circle
|
||||
id="halo"
|
||||
fill="rgba(248,248,248,1)"
|
||||
cx="0"
|
||||
cy="0"
|
||||
stroke="rgba(128,0,128,1)"
|
||||
r="0"
|
||||
opacity="0.6270672695016452"
|
||||
/>
|
||||
</g>
|
||||
<g id="icon" fill="none" transform="matrix(1,0,0,1,75,150)">
|
||||
<g transform="matrix(1,0,0,1,0,0)">
|
||||
<text
|
||||
id="icon"
|
||||
fill="rgba(0,0,0,1)"
|
||||
dominant-baseline="central"
|
||||
paint-order="stroke"
|
||||
text-anchor="middle"
|
||||
opacity="0.6270672695016452"
|
||||
/>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="g-svg-63" fill="none" r="10" transform="matrix(1,0,0,1,0,0)">
|
||||
<g transform="matrix(1,0,0,1,200,100)">
|
||||
<circle
|
||||
id="key"
|
||||
fill="rgba(248,248,248,1)"
|
||||
transform="translate(-10,-10)"
|
||||
cx="10"
|
||||
cy="10"
|
||||
stroke="rgba(0,255,255,1)"
|
||||
r="10"
|
||||
opacity="0.6270672695016452"
|
||||
/>
|
||||
</g>
|
||||
<g id="label" fill="none" transform="matrix(1,0,0,1,200,100)">
|
||||
<g transform="matrix(1,0,0,1,-5,-5)">
|
||||
<path
|
||||
id="background"
|
||||
fill="none"
|
||||
d="M 0,0 l 10,0 l 0,10 l-10 0 z"
|
||||
width="10"
|
||||
height="10"
|
||||
opacity="0.6270672695016452"
|
||||
/>
|
||||
</g>
|
||||
<g transform="matrix(1,0,0,1,0,0)">
|
||||
<text
|
||||
id="text"
|
||||
fill="rgba(0,0,0,1)"
|
||||
dominant-baseline="central"
|
||||
paint-order="stroke"
|
||||
text-anchor="middle"
|
||||
opacity="0.6270672695016452"
|
||||
/>
|
||||
</g>
|
||||
</g>
|
||||
<g transform="matrix(1,0,0,1,200,100)">
|
||||
<circle
|
||||
id="halo"
|
||||
fill="rgba(248,248,248,1)"
|
||||
cx="0"
|
||||
cy="0"
|
||||
stroke="rgba(0,255,255,1)"
|
||||
r="0"
|
||||
opacity="0.6270672695016452"
|
||||
/>
|
||||
</g>
|
||||
<g id="icon" fill="none" transform="matrix(1,0,0,1,200,100)">
|
||||
<g transform="matrix(1,0,0,1,0,0)">
|
||||
<text
|
||||
id="icon"
|
||||
fill="rgba(0,0,0,1)"
|
||||
dominant-baseline="central"
|
||||
paint-order="stroke"
|
||||
text-anchor="middle"
|
||||
opacity="0.6270672695016452"
|
||||
/>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 15 KiB |
@ -0,0 +1,497 @@
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="500"
|
||||
height="500"
|
||||
style="background: transparent; position: absolute; outline: none;"
|
||||
color-interpolation-filters="sRGB"
|
||||
tabindex="1"
|
||||
>
|
||||
<defs />
|
||||
<g id="g-svg-camera" transform="matrix(1,0,0,1,0,0)">
|
||||
<g id="g-root" fill="none" transform="matrix(1,0,0,1,0,0)">
|
||||
<g id="g-svg-7" fill="none" transform="matrix(1,0,0,1,0,0)" />
|
||||
<g id="g-svg-6" fill="none" transform="matrix(1,0,0,1,0,0)">
|
||||
<g
|
||||
id="g-svg-32"
|
||||
fill="none"
|
||||
marker-start="false"
|
||||
marker-end="false"
|
||||
transform="matrix(1,0,0,1,0,0)"
|
||||
>
|
||||
<g transform="matrix(1,0,0,1,50,50)">
|
||||
<line
|
||||
id="key"
|
||||
fill="none"
|
||||
x1="0"
|
||||
y1="0"
|
||||
x2="150"
|
||||
y2="0"
|
||||
stroke-width="1"
|
||||
stroke="rgba(139,155,175,1)"
|
||||
opacity="0.8255817601983712"
|
||||
/>
|
||||
</g>
|
||||
<g id="label" fill="none" transform="matrix(1,0,0,1,129,44)">
|
||||
<g transform="matrix(1,0,0,1,-5,-5)">
|
||||
<path
|
||||
id="background"
|
||||
fill="none"
|
||||
d="M 0,0 l 10,0 l 0,10 l-10 0 z"
|
||||
width="10"
|
||||
height="10"
|
||||
opacity="0.8255817601983712"
|
||||
/>
|
||||
</g>
|
||||
<g transform="matrix(1,0,0,1,0,0)">
|
||||
<text
|
||||
id="text"
|
||||
fill="rgba(0,0,0,1)"
|
||||
dominant-baseline="text-after-edge"
|
||||
paint-order="stroke"
|
||||
text-anchor="middle"
|
||||
opacity="0.8255817601983712"
|
||||
/>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g
|
||||
id="g-svg-37"
|
||||
fill="none"
|
||||
marker-start="false"
|
||||
marker-end="false"
|
||||
transform="matrix(1,0,0,1,0,0)"
|
||||
>
|
||||
<g transform="matrix(1,0,0,1,125,76.162735)">
|
||||
<line
|
||||
id="key"
|
||||
fill="none"
|
||||
x1="75"
|
||||
y1="0"
|
||||
x2="0"
|
||||
y2="73.83726402975567"
|
||||
stroke-width="1"
|
||||
stroke="rgba(139,155,175,1)"
|
||||
/>
|
||||
</g>
|
||||
<g
|
||||
id="label"
|
||||
fill="none"
|
||||
transform="matrix(0.832050,0.554700,-0.554700,0.832050,162.500000,167.788895)"
|
||||
>
|
||||
<g transform="matrix(1,0,0,1,-5,-5)">
|
||||
<path
|
||||
id="background"
|
||||
fill="none"
|
||||
d="M 0,0 l 10,0 l 0,10 l-10 0 z"
|
||||
width="10"
|
||||
height="10"
|
||||
/>
|
||||
</g>
|
||||
<g transform="matrix(1,0,0,1,0,0)">
|
||||
<text
|
||||
id="text"
|
||||
fill="rgba(0,0,0,1)"
|
||||
dominant-baseline="text-after-edge"
|
||||
paint-order="stroke"
|
||||
text-anchor="middle"
|
||||
/>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g
|
||||
id="g-svg-42"
|
||||
fill="none"
|
||||
marker-start="false"
|
||||
marker-end="false"
|
||||
transform="matrix(1,0,0,1,0,0)"
|
||||
>
|
||||
<g transform="matrix(1,0,0,1,50,50)">
|
||||
<line
|
||||
id="key"
|
||||
fill="none"
|
||||
x1="75"
|
||||
y1="100"
|
||||
x2="0"
|
||||
y2="0"
|
||||
stroke-width="1"
|
||||
stroke="rgba(139,155,175,1)"
|
||||
opacity="0.8255817601983712"
|
||||
/>
|
||||
</g>
|
||||
<g
|
||||
id="label"
|
||||
fill="none"
|
||||
transform="matrix(0.600000,0.800000,-0.800000,0.600000,89.900002,93.199997)"
|
||||
>
|
||||
<g transform="matrix(1,0,0,1,-5,-5)">
|
||||
<path
|
||||
id="background"
|
||||
fill="none"
|
||||
d="M 0,0 l 10,0 l 0,10 l-10 0 z"
|
||||
width="10"
|
||||
height="10"
|
||||
opacity="0.8255817601983712"
|
||||
/>
|
||||
</g>
|
||||
<g transform="matrix(1,0,0,1,0,0)">
|
||||
<text
|
||||
id="text"
|
||||
fill="rgba(0,0,0,1)"
|
||||
dominant-baseline="text-after-edge"
|
||||
paint-order="stroke"
|
||||
text-anchor="middle"
|
||||
opacity="0.8255817601983712"
|
||||
/>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="g-svg-5" fill="none" transform="matrix(1,0,0,1,0,0)">
|
||||
<g id="g-svg-8" fill="none" r="10" transform="matrix(1,0,0,1,0,0)">
|
||||
<g transform="matrix(1,0,0,1,50,50)">
|
||||
<circle
|
||||
id="key"
|
||||
fill="rgba(248,248,248,1)"
|
||||
transform="translate(-10,-10)"
|
||||
cx="10"
|
||||
cy="10"
|
||||
stroke="rgba(139,155,175,1)"
|
||||
r="10"
|
||||
opacity="0.8255817601983712"
|
||||
/>
|
||||
</g>
|
||||
<g id="label" fill="none" transform="matrix(1,0,0,1,50,50)">
|
||||
<g transform="matrix(1,0,0,1,-5,-5)">
|
||||
<path
|
||||
id="background"
|
||||
fill="none"
|
||||
d="M 0,0 l 10,0 l 0,10 l-10 0 z"
|
||||
width="10"
|
||||
height="10"
|
||||
opacity="0.8255817601983712"
|
||||
/>
|
||||
</g>
|
||||
<g transform="matrix(1,0,0,1,0,0)">
|
||||
<text
|
||||
id="text"
|
||||
fill="rgba(0,0,0,1)"
|
||||
dominant-baseline="central"
|
||||
paint-order="stroke"
|
||||
text-anchor="middle"
|
||||
opacity="0.8255817601983712"
|
||||
/>
|
||||
</g>
|
||||
</g>
|
||||
<g transform="matrix(1,0,0,1,50,50)">
|
||||
<circle
|
||||
id="halo"
|
||||
fill="rgba(248,248,248,1)"
|
||||
cx="0"
|
||||
cy="0"
|
||||
stroke="rgba(139,155,175,1)"
|
||||
r="0"
|
||||
opacity="0.8255817601983712"
|
||||
/>
|
||||
</g>
|
||||
<g id="icon" fill="none" transform="matrix(1,0,0,1,50,50)">
|
||||
<g transform="matrix(1,0,0,1,0,0)">
|
||||
<text
|
||||
id="icon"
|
||||
fill="rgba(0,0,0,1)"
|
||||
dominant-baseline="central"
|
||||
paint-order="stroke"
|
||||
text-anchor="middle"
|
||||
opacity="0.8255817601983712"
|
||||
/>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="g-svg-16" fill="none" r="10" transform="matrix(1,0,0,1,0,0)">
|
||||
<g transform="matrix(1,0,0,1,200,76.162735)">
|
||||
<circle
|
||||
id="key"
|
||||
fill="rgba(248,248,248,1)"
|
||||
transform="translate(-10,-10)"
|
||||
cx="10"
|
||||
cy="10"
|
||||
stroke="rgba(0,128,0,1)"
|
||||
r="10"
|
||||
/>
|
||||
</g>
|
||||
<g id="label" fill="none" transform="matrix(1,0,0,1,200,200)">
|
||||
<g transform="matrix(1,0,0,1,-5,-5)">
|
||||
<path
|
||||
id="background"
|
||||
fill="none"
|
||||
d="M 0,0 l 10,0 l 0,10 l-10 0 z"
|
||||
width="10"
|
||||
height="10"
|
||||
/>
|
||||
</g>
|
||||
<g transform="matrix(1,0,0,1,0,0)">
|
||||
<text
|
||||
id="text"
|
||||
fill="rgba(0,0,0,1)"
|
||||
dominant-baseline="central"
|
||||
paint-order="stroke"
|
||||
text-anchor="middle"
|
||||
/>
|
||||
</g>
|
||||
</g>
|
||||
<g transform="matrix(1,0,0,1,200,76.162735)">
|
||||
<circle
|
||||
id="halo"
|
||||
fill="rgba(248,248,248,1)"
|
||||
cx="0"
|
||||
cy="0"
|
||||
stroke="rgba(0,128,0,1)"
|
||||
r="0"
|
||||
/>
|
||||
</g>
|
||||
<g id="icon" fill="none" transform="matrix(1,0,0,1,200,200)">
|
||||
<g transform="matrix(1,0,0,1,0,0)">
|
||||
<text
|
||||
id="icon"
|
||||
fill="rgba(0,0,0,1)"
|
||||
dominant-baseline="central"
|
||||
paint-order="stroke"
|
||||
text-anchor="middle"
|
||||
/>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="g-svg-24" fill="none" r="10" transform="matrix(1,0,0,1,0,0)">
|
||||
<g transform="matrix(1,0,0,1,125,150)">
|
||||
<circle
|
||||
id="key"
|
||||
fill="rgba(248,248,248,1)"
|
||||
transform="translate(-10,-10)"
|
||||
cx="10"
|
||||
cy="10"
|
||||
stroke="rgba(139,155,175,1)"
|
||||
r="10"
|
||||
/>
|
||||
</g>
|
||||
<g id="label" fill="none" transform="matrix(1,0,0,1,125,150)">
|
||||
<g transform="matrix(1,0,0,1,-5,-5)">
|
||||
<path
|
||||
id="background"
|
||||
fill="none"
|
||||
d="M 0,0 l 10,0 l 0,10 l-10 0 z"
|
||||
width="10"
|
||||
height="10"
|
||||
/>
|
||||
</g>
|
||||
<g transform="matrix(1,0,0,1,0,0)">
|
||||
<text
|
||||
id="text"
|
||||
fill="rgba(0,0,0,1)"
|
||||
dominant-baseline="central"
|
||||
paint-order="stroke"
|
||||
text-anchor="middle"
|
||||
/>
|
||||
</g>
|
||||
</g>
|
||||
<g transform="matrix(1,0,0,1,125,150)">
|
||||
<circle
|
||||
id="halo"
|
||||
fill="rgba(248,248,248,1)"
|
||||
cx="0"
|
||||
cy="0"
|
||||
stroke="rgba(139,155,175,1)"
|
||||
r="0"
|
||||
/>
|
||||
</g>
|
||||
<g id="icon" fill="none" transform="matrix(1,0,0,1,125,150)">
|
||||
<g transform="matrix(1,0,0,1,0,0)">
|
||||
<text
|
||||
id="icon"
|
||||
fill="rgba(0,0,0,1)"
|
||||
dominant-baseline="central"
|
||||
paint-order="stroke"
|
||||
text-anchor="middle"
|
||||
/>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="g-svg-47" fill="none" r="10" transform="matrix(1,0,0,1,0,0)">
|
||||
<g transform="matrix(1,0,0,1,50,200)">
|
||||
<circle
|
||||
id="key"
|
||||
fill="rgba(248,248,248,1)"
|
||||
transform="translate(-10,-10)"
|
||||
cx="10"
|
||||
cy="10"
|
||||
stroke="rgba(255,165,0,1)"
|
||||
r="10"
|
||||
opacity="0.17441823980162885"
|
||||
/>
|
||||
</g>
|
||||
<g id="label" fill="none" transform="matrix(1,0,0,1,50,200)">
|
||||
<g transform="matrix(1,0,0,1,-5,-5)">
|
||||
<path
|
||||
id="background"
|
||||
fill="none"
|
||||
d="M 0,0 l 10,0 l 0,10 l-10 0 z"
|
||||
width="10"
|
||||
height="10"
|
||||
opacity="0.17441823980162885"
|
||||
/>
|
||||
</g>
|
||||
<g transform="matrix(1,0,0,1,0,0)">
|
||||
<text
|
||||
id="text"
|
||||
fill="rgba(0,0,0,1)"
|
||||
dominant-baseline="central"
|
||||
paint-order="stroke"
|
||||
text-anchor="middle"
|
||||
opacity="0.17441823980162885"
|
||||
/>
|
||||
</g>
|
||||
</g>
|
||||
<g transform="matrix(1,0,0,1,50,200)">
|
||||
<circle
|
||||
id="halo"
|
||||
fill="rgba(248,248,248,1)"
|
||||
cx="0"
|
||||
cy="0"
|
||||
stroke="rgba(255,165,0,1)"
|
||||
r="0"
|
||||
opacity="0.17441823980162885"
|
||||
/>
|
||||
</g>
|
||||
<g id="icon" fill="none" transform="matrix(1,0,0,1,50,200)">
|
||||
<g transform="matrix(1,0,0,1,0,0)">
|
||||
<text
|
||||
id="icon"
|
||||
fill="rgba(0,0,0,1)"
|
||||
dominant-baseline="central"
|
||||
paint-order="stroke"
|
||||
text-anchor="middle"
|
||||
opacity="0.17441823980162885"
|
||||
/>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="g-svg-55" fill="none" r="10" transform="matrix(1,0,0,1,0,0)">
|
||||
<g transform="matrix(1,0,0,1,75,150)">
|
||||
<circle
|
||||
id="key"
|
||||
fill="rgba(248,248,248,1)"
|
||||
transform="translate(-10,-10)"
|
||||
cx="10"
|
||||
cy="10"
|
||||
stroke="rgba(128,0,128,1)"
|
||||
r="10"
|
||||
opacity="0.17441823980162885"
|
||||
/>
|
||||
</g>
|
||||
<g id="label" fill="none" transform="matrix(1,0,0,1,75,150)">
|
||||
<g transform="matrix(1,0,0,1,-5,-5)">
|
||||
<path
|
||||
id="background"
|
||||
fill="none"
|
||||
d="M 0,0 l 10,0 l 0,10 l-10 0 z"
|
||||
width="10"
|
||||
height="10"
|
||||
opacity="0.17441823980162885"
|
||||
/>
|
||||
</g>
|
||||
<g transform="matrix(1,0,0,1,0,0)">
|
||||
<text
|
||||
id="text"
|
||||
fill="rgba(0,0,0,1)"
|
||||
dominant-baseline="central"
|
||||
paint-order="stroke"
|
||||
text-anchor="middle"
|
||||
opacity="0.17441823980162885"
|
||||
/>
|
||||
</g>
|
||||
</g>
|
||||
<g transform="matrix(1,0,0,1,75,150)">
|
||||
<circle
|
||||
id="halo"
|
||||
fill="rgba(248,248,248,1)"
|
||||
cx="0"
|
||||
cy="0"
|
||||
stroke="rgba(128,0,128,1)"
|
||||
r="0"
|
||||
opacity="0.17441823980162885"
|
||||
/>
|
||||
</g>
|
||||
<g id="icon" fill="none" transform="matrix(1,0,0,1,75,150)">
|
||||
<g transform="matrix(1,0,0,1,0,0)">
|
||||
<text
|
||||
id="icon"
|
||||
fill="rgba(0,0,0,1)"
|
||||
dominant-baseline="central"
|
||||
paint-order="stroke"
|
||||
text-anchor="middle"
|
||||
opacity="0.17441823980162885"
|
||||
/>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="g-svg-63" fill="none" r="10" transform="matrix(1,0,0,1,0,0)">
|
||||
<g transform="matrix(1,0,0,1,200,100)">
|
||||
<circle
|
||||
id="key"
|
||||
fill="rgba(248,248,248,1)"
|
||||
transform="translate(-10,-10)"
|
||||
cx="10"
|
||||
cy="10"
|
||||
stroke="rgba(0,255,255,1)"
|
||||
r="10"
|
||||
opacity="0.17441823980162885"
|
||||
/>
|
||||
</g>
|
||||
<g id="label" fill="none" transform="matrix(1,0,0,1,200,100)">
|
||||
<g transform="matrix(1,0,0,1,-5,-5)">
|
||||
<path
|
||||
id="background"
|
||||
fill="none"
|
||||
d="M 0,0 l 10,0 l 0,10 l-10 0 z"
|
||||
width="10"
|
||||
height="10"
|
||||
opacity="0.17441823980162885"
|
||||
/>
|
||||
</g>
|
||||
<g transform="matrix(1,0,0,1,0,0)">
|
||||
<text
|
||||
id="text"
|
||||
fill="rgba(0,0,0,1)"
|
||||
dominant-baseline="central"
|
||||
paint-order="stroke"
|
||||
text-anchor="middle"
|
||||
opacity="0.17441823980162885"
|
||||
/>
|
||||
</g>
|
||||
</g>
|
||||
<g transform="matrix(1,0,0,1,200,100)">
|
||||
<circle
|
||||
id="halo"
|
||||
fill="rgba(248,248,248,1)"
|
||||
cx="0"
|
||||
cy="0"
|
||||
stroke="rgba(0,255,255,1)"
|
||||
r="0"
|
||||
opacity="0.17441823980162885"
|
||||
/>
|
||||
</g>
|
||||
<g id="icon" fill="none" transform="matrix(1,0,0,1,200,100)">
|
||||
<g transform="matrix(1,0,0,1,0,0)">
|
||||
<text
|
||||
id="icon"
|
||||
fill="rgba(0,0,0,1)"
|
||||
dominant-baseline="central"
|
||||
paint-order="stroke"
|
||||
text-anchor="middle"
|
||||
opacity="0.17441823980162885"
|
||||
/>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 15 KiB |
@ -0,0 +1,315 @@
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="500"
|
||||
height="500"
|
||||
style="background: transparent; position: absolute; outline: none;"
|
||||
color-interpolation-filters="sRGB"
|
||||
tabindex="1"
|
||||
>
|
||||
<defs />
|
||||
<g id="g-svg-camera" transform="matrix(1,0,0,1,0,0)">
|
||||
<g id="g-root" fill="none" transform="matrix(1,0,0,1,0,0)">
|
||||
<g id="g-svg-7" fill="none" transform="matrix(1,0,0,1,0,0)" />
|
||||
<g id="g-svg-6" fill="none" transform="matrix(1,0,0,1,0,0)">
|
||||
<g
|
||||
id="g-svg-32"
|
||||
fill="none"
|
||||
marker-start="false"
|
||||
marker-end="false"
|
||||
transform="matrix(1,0,0,1,0,0)"
|
||||
>
|
||||
<g transform="matrix(1,0,0,1,50,50)">
|
||||
<line
|
||||
id="key"
|
||||
fill="none"
|
||||
x1="0"
|
||||
y1="0"
|
||||
x2="150"
|
||||
y2="0"
|
||||
stroke-width="2"
|
||||
stroke="rgba(255,192,203,1)"
|
||||
/>
|
||||
</g>
|
||||
<g id="label" fill="none" transform="matrix(1,0,0,1,129,44)">
|
||||
<g transform="matrix(1,0,0,1,-5,-5)">
|
||||
<path
|
||||
id="background"
|
||||
fill="none"
|
||||
d="M 0,0 l 10,0 l 0,10 l-10 0 z"
|
||||
width="10"
|
||||
height="10"
|
||||
/>
|
||||
</g>
|
||||
<g transform="matrix(1,0,0,1,0,0)">
|
||||
<text
|
||||
id="text"
|
||||
fill="rgba(0,0,0,1)"
|
||||
dominant-baseline="text-after-edge"
|
||||
paint-order="stroke"
|
||||
text-anchor="middle"
|
||||
/>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g
|
||||
id="g-svg-37"
|
||||
fill="none"
|
||||
marker-start="false"
|
||||
marker-end="false"
|
||||
transform="matrix(1,0,0,1,0,0)"
|
||||
>
|
||||
<g transform="matrix(1,0,0,1,125,50)">
|
||||
<line
|
||||
id="key"
|
||||
fill="none"
|
||||
x1="75"
|
||||
y1="0"
|
||||
x2="0"
|
||||
y2="100"
|
||||
stroke-width="1"
|
||||
stroke="rgba(139,155,175,1)"
|
||||
/>
|
||||
</g>
|
||||
<g
|
||||
id="label"
|
||||
fill="none"
|
||||
transform="matrix(0.600000,-0.800000,0.800000,0.600000,155.300003,99.599998)"
|
||||
>
|
||||
<g transform="matrix(1,0,0,1,-5,-5)">
|
||||
<path
|
||||
id="background"
|
||||
fill="none"
|
||||
d="M 0,0 l 10,0 l 0,10 l-10 0 z"
|
||||
width="10"
|
||||
height="10"
|
||||
/>
|
||||
</g>
|
||||
<g transform="matrix(1,0,0,1,0,0)">
|
||||
<text
|
||||
id="text"
|
||||
fill="rgba(0,0,0,1)"
|
||||
dominant-baseline="text-after-edge"
|
||||
paint-order="stroke"
|
||||
text-anchor="middle"
|
||||
/>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g
|
||||
id="g-svg-42"
|
||||
fill="none"
|
||||
marker-start="false"
|
||||
marker-end="false"
|
||||
transform="matrix(1,0,0,1,0,0)"
|
||||
>
|
||||
<g transform="matrix(1,0,0,1,50,50)">
|
||||
<line
|
||||
id="key"
|
||||
fill="none"
|
||||
x1="75"
|
||||
y1="100"
|
||||
x2="0"
|
||||
y2="0"
|
||||
stroke-width="1"
|
||||
stroke="rgba(139,155,175,1)"
|
||||
/>
|
||||
</g>
|
||||
<g
|
||||
id="label"
|
||||
fill="none"
|
||||
transform="matrix(0.600000,0.800000,-0.800000,0.600000,89.900002,93.199997)"
|
||||
>
|
||||
<g transform="matrix(1,0,0,1,-5,-5)">
|
||||
<path
|
||||
id="background"
|
||||
fill="none"
|
||||
d="M 0,0 l 10,0 l 0,10 l-10 0 z"
|
||||
width="10"
|
||||
height="10"
|
||||
/>
|
||||
</g>
|
||||
<g transform="matrix(1,0,0,1,0,0)">
|
||||
<text
|
||||
id="text"
|
||||
fill="rgba(0,0,0,1)"
|
||||
dominant-baseline="text-after-edge"
|
||||
paint-order="stroke"
|
||||
text-anchor="middle"
|
||||
/>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="g-svg-5" fill="none" transform="matrix(1,0,0,1,0,0)">
|
||||
<g id="g-svg-8" fill="none" r="10" transform="matrix(1,0,0,1,0,0)">
|
||||
<g transform="matrix(1,0,0,1,50,50)">
|
||||
<circle
|
||||
id="key"
|
||||
fill="rgba(255,192,203,1)"
|
||||
transform="translate(-10,-10)"
|
||||
cx="10"
|
||||
cy="10"
|
||||
stroke="rgba(139,155,175,1)"
|
||||
stroke-width="2"
|
||||
r="10"
|
||||
/>
|
||||
</g>
|
||||
<g id="label" fill="none" transform="matrix(1,0,0,1,50,50)">
|
||||
<g transform="matrix(1,0,0,1,-5,-5)">
|
||||
<path
|
||||
id="background"
|
||||
fill="none"
|
||||
d="M 0,0 l 10,0 l 0,10 l-10 0 z"
|
||||
width="10"
|
||||
height="10"
|
||||
/>
|
||||
</g>
|
||||
<g transform="matrix(1,0,0,1,0,0)">
|
||||
<text
|
||||
id="text"
|
||||
fill="rgba(0,0,0,1)"
|
||||
dominant-baseline="central"
|
||||
paint-order="stroke"
|
||||
text-anchor="middle"
|
||||
/>
|
||||
</g>
|
||||
</g>
|
||||
<g transform="matrix(1,0,0,1,50,50)">
|
||||
<circle
|
||||
id="halo"
|
||||
fill="rgba(255,192,203,1)"
|
||||
cx="0"
|
||||
cy="0"
|
||||
stroke="rgba(139,155,175,1)"
|
||||
stroke-width="2"
|
||||
r="0"
|
||||
/>
|
||||
</g>
|
||||
<g id="icon" fill="none" transform="matrix(1,0,0,1,50,50)">
|
||||
<g transform="matrix(1,0,0,1,0,0)">
|
||||
<text
|
||||
id="icon"
|
||||
fill="rgba(0,0,0,1)"
|
||||
dominant-baseline="central"
|
||||
paint-order="stroke"
|
||||
text-anchor="middle"
|
||||
/>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="g-svg-16" fill="none" r="10" transform="matrix(1,0,0,1,0,0)">
|
||||
<g transform="matrix(1,0,0,1,200,50)">
|
||||
<circle
|
||||
id="key"
|
||||
fill="rgba(248,248,248,1)"
|
||||
transform="translate(-10,-10)"
|
||||
cx="10"
|
||||
cy="10"
|
||||
stroke="rgba(139,155,175,1)"
|
||||
stroke-width="1"
|
||||
r="10"
|
||||
/>
|
||||
</g>
|
||||
<g id="label" fill="none" transform="matrix(1,0,0,1,200,50)">
|
||||
<g transform="matrix(1,0,0,1,-5,-5)">
|
||||
<path
|
||||
id="background"
|
||||
fill="none"
|
||||
d="M 0,0 l 10,0 l 0,10 l-10 0 z"
|
||||
width="10"
|
||||
height="10"
|
||||
/>
|
||||
</g>
|
||||
<g transform="matrix(1,0,0,1,0,0)">
|
||||
<text
|
||||
id="text"
|
||||
fill="rgba(0,0,0,1)"
|
||||
dominant-baseline="central"
|
||||
paint-order="stroke"
|
||||
text-anchor="middle"
|
||||
/>
|
||||
</g>
|
||||
</g>
|
||||
<g transform="matrix(1,0,0,1,200,50)">
|
||||
<circle
|
||||
id="halo"
|
||||
fill="rgba(248,248,248,1)"
|
||||
cx="0"
|
||||
cy="0"
|
||||
stroke="rgba(139,155,175,1)"
|
||||
stroke-width="1"
|
||||
r="0"
|
||||
/>
|
||||
</g>
|
||||
<g id="icon" fill="none" transform="matrix(1,0,0,1,200,50)">
|
||||
<g transform="matrix(1,0,0,1,0,0)">
|
||||
<text
|
||||
id="icon"
|
||||
fill="rgba(0,0,0,1)"
|
||||
dominant-baseline="central"
|
||||
paint-order="stroke"
|
||||
text-anchor="middle"
|
||||
/>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="g-svg-24" fill="none" r="10" transform="matrix(1,0,0,1,0,0)">
|
||||
<g transform="matrix(1,0,0,1,125,150)">
|
||||
<circle
|
||||
id="key"
|
||||
fill="rgba(248,248,248,1)"
|
||||
transform="translate(-10,-10)"
|
||||
cx="10"
|
||||
cy="10"
|
||||
stroke="rgba(139,155,175,1)"
|
||||
stroke-width="2"
|
||||
r="10"
|
||||
/>
|
||||
</g>
|
||||
<g id="label" fill="none" transform="matrix(1,0,0,1,125,150)">
|
||||
<g transform="matrix(1,0,0,1,-5,-5)">
|
||||
<path
|
||||
id="background"
|
||||
fill="none"
|
||||
d="M 0,0 l 10,0 l 0,10 l-10 0 z"
|
||||
width="10"
|
||||
height="10"
|
||||
/>
|
||||
</g>
|
||||
<g transform="matrix(1,0,0,1,0,0)">
|
||||
<text
|
||||
id="text"
|
||||
fill="rgba(0,0,0,1)"
|
||||
dominant-baseline="central"
|
||||
paint-order="stroke"
|
||||
text-anchor="middle"
|
||||
/>
|
||||
</g>
|
||||
</g>
|
||||
<g transform="matrix(1,0,0,1,125,150)">
|
||||
<circle
|
||||
id="halo"
|
||||
fill="rgba(248,248,248,1)"
|
||||
cx="0"
|
||||
cy="0"
|
||||
stroke="rgba(139,155,175,1)"
|
||||
stroke-width="2"
|
||||
r="0"
|
||||
/>
|
||||
</g>
|
||||
<g id="icon" fill="none" transform="matrix(1,0,0,1,125,150)">
|
||||
<g transform="matrix(1,0,0,1,0,0)">
|
||||
<text
|
||||
id="icon"
|
||||
fill="rgba(0,0,0,1)"
|
||||
dominant-baseline="central"
|
||||
paint-order="stroke"
|
||||
text-anchor="middle"
|
||||
/>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 9.2 KiB |
@ -0,0 +1,315 @@
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="500"
|
||||
height="500"
|
||||
style="background: transparent; position: absolute; outline: none;"
|
||||
color-interpolation-filters="sRGB"
|
||||
tabindex="1"
|
||||
>
|
||||
<defs />
|
||||
<g id="g-svg-camera" transform="matrix(1,0,0,1,0,0)">
|
||||
<g id="g-root" fill="none" transform="matrix(1,0,0,1,0,0)">
|
||||
<g id="g-svg-7" fill="none" transform="matrix(1,0,0,1,0,0)" />
|
||||
<g id="g-svg-6" fill="none" transform="matrix(1,0,0,1,0,0)">
|
||||
<g
|
||||
id="g-svg-32"
|
||||
fill="none"
|
||||
marker-start="false"
|
||||
marker-end="false"
|
||||
transform="matrix(1,0,0,1,0,0)"
|
||||
>
|
||||
<g transform="matrix(1,0,0,1,50,50)">
|
||||
<line
|
||||
id="key"
|
||||
fill="none"
|
||||
x1="0"
|
||||
y1="0"
|
||||
x2="150"
|
||||
y2="0"
|
||||
stroke-width="1"
|
||||
stroke="rgba(139,155,175,1)"
|
||||
/>
|
||||
</g>
|
||||
<g id="label" fill="none" transform="matrix(1,0,0,1,129,44)">
|
||||
<g transform="matrix(1,0,0,1,-5,-5)">
|
||||
<path
|
||||
id="background"
|
||||
fill="none"
|
||||
d="M 0,0 l 10,0 l 0,10 l-10 0 z"
|
||||
width="10"
|
||||
height="10"
|
||||
/>
|
||||
</g>
|
||||
<g transform="matrix(1,0,0,1,0,0)">
|
||||
<text
|
||||
id="text"
|
||||
fill="rgba(0,0,0,1)"
|
||||
dominant-baseline="text-after-edge"
|
||||
paint-order="stroke"
|
||||
text-anchor="middle"
|
||||
/>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g
|
||||
id="g-svg-37"
|
||||
fill="none"
|
||||
marker-start="false"
|
||||
marker-end="false"
|
||||
transform="matrix(1,0,0,1,0,0)"
|
||||
>
|
||||
<g transform="matrix(1,0,0,1,125,50)">
|
||||
<line
|
||||
id="key"
|
||||
fill="none"
|
||||
x1="75"
|
||||
y1="0"
|
||||
x2="0"
|
||||
y2="100"
|
||||
stroke-width="2"
|
||||
stroke="rgba(255,192,203,1)"
|
||||
/>
|
||||
</g>
|
||||
<g
|
||||
id="label"
|
||||
fill="none"
|
||||
transform="matrix(0.600000,-0.800000,0.800000,0.600000,155.300003,99.599998)"
|
||||
>
|
||||
<g transform="matrix(1,0,0,1,-5,-5)">
|
||||
<path
|
||||
id="background"
|
||||
fill="none"
|
||||
d="M 0,0 l 10,0 l 0,10 l-10 0 z"
|
||||
width="10"
|
||||
height="10"
|
||||
/>
|
||||
</g>
|
||||
<g transform="matrix(1,0,0,1,0,0)">
|
||||
<text
|
||||
id="text"
|
||||
fill="rgba(0,0,0,1)"
|
||||
dominant-baseline="text-after-edge"
|
||||
paint-order="stroke"
|
||||
text-anchor="middle"
|
||||
/>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g
|
||||
id="g-svg-42"
|
||||
fill="none"
|
||||
marker-start="false"
|
||||
marker-end="false"
|
||||
transform="matrix(1,0,0,1,0,0)"
|
||||
>
|
||||
<g transform="matrix(1,0,0,1,50,50)">
|
||||
<line
|
||||
id="key"
|
||||
fill="none"
|
||||
x1="75"
|
||||
y1="100"
|
||||
x2="0"
|
||||
y2="0"
|
||||
stroke-width="1"
|
||||
stroke="rgba(139,155,175,1)"
|
||||
/>
|
||||
</g>
|
||||
<g
|
||||
id="label"
|
||||
fill="none"
|
||||
transform="matrix(0.600000,0.800000,-0.800000,0.600000,89.900002,93.199997)"
|
||||
>
|
||||
<g transform="matrix(1,0,0,1,-5,-5)">
|
||||
<path
|
||||
id="background"
|
||||
fill="none"
|
||||
d="M 0,0 l 10,0 l 0,10 l-10 0 z"
|
||||
width="10"
|
||||
height="10"
|
||||
/>
|
||||
</g>
|
||||
<g transform="matrix(1,0,0,1,0,0)">
|
||||
<text
|
||||
id="text"
|
||||
fill="rgba(0,0,0,1)"
|
||||
dominant-baseline="text-after-edge"
|
||||
paint-order="stroke"
|
||||
text-anchor="middle"
|
||||
/>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="g-svg-5" fill="none" transform="matrix(1,0,0,1,0,0)">
|
||||
<g id="g-svg-8" fill="none" r="10" transform="matrix(1,0,0,1,0,0)">
|
||||
<g transform="matrix(1,0,0,1,50,50)">
|
||||
<circle
|
||||
id="key"
|
||||
fill="rgba(248,248,248,1)"
|
||||
transform="translate(-10,-10)"
|
||||
cx="10"
|
||||
cy="10"
|
||||
stroke="rgba(139,155,175,1)"
|
||||
stroke-width="1"
|
||||
r="10"
|
||||
/>
|
||||
</g>
|
||||
<g id="label" fill="none" transform="matrix(1,0,0,1,50,50)">
|
||||
<g transform="matrix(1,0,0,1,-5,-5)">
|
||||
<path
|
||||
id="background"
|
||||
fill="none"
|
||||
d="M 0,0 l 10,0 l 0,10 l-10 0 z"
|
||||
width="10"
|
||||
height="10"
|
||||
/>
|
||||
</g>
|
||||
<g transform="matrix(1,0,0,1,0,0)">
|
||||
<text
|
||||
id="text"
|
||||
fill="rgba(0,0,0,1)"
|
||||
dominant-baseline="central"
|
||||
paint-order="stroke"
|
||||
text-anchor="middle"
|
||||
/>
|
||||
</g>
|
||||
</g>
|
||||
<g transform="matrix(1,0,0,1,50,50)">
|
||||
<circle
|
||||
id="halo"
|
||||
fill="rgba(248,248,248,1)"
|
||||
cx="0"
|
||||
cy="0"
|
||||
stroke="rgba(139,155,175,1)"
|
||||
stroke-width="1"
|
||||
r="0"
|
||||
/>
|
||||
</g>
|
||||
<g id="icon" fill="none" transform="matrix(1,0,0,1,50,50)">
|
||||
<g transform="matrix(1,0,0,1,0,0)">
|
||||
<text
|
||||
id="icon"
|
||||
fill="rgba(0,0,0,1)"
|
||||
dominant-baseline="central"
|
||||
paint-order="stroke"
|
||||
text-anchor="middle"
|
||||
/>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="g-svg-16" fill="none" r="10" transform="matrix(1,0,0,1,0,0)">
|
||||
<g transform="matrix(1,0,0,1,200,50)">
|
||||
<circle
|
||||
id="key"
|
||||
fill="rgba(248,248,248,1)"
|
||||
transform="translate(-10,-10)"
|
||||
cx="10"
|
||||
cy="10"
|
||||
stroke="rgba(139,155,175,1)"
|
||||
stroke-width="2"
|
||||
r="10"
|
||||
/>
|
||||
</g>
|
||||
<g id="label" fill="none" transform="matrix(1,0,0,1,200,50)">
|
||||
<g transform="matrix(1,0,0,1,-5,-5)">
|
||||
<path
|
||||
id="background"
|
||||
fill="none"
|
||||
d="M 0,0 l 10,0 l 0,10 l-10 0 z"
|
||||
width="10"
|
||||
height="10"
|
||||
/>
|
||||
</g>
|
||||
<g transform="matrix(1,0,0,1,0,0)">
|
||||
<text
|
||||
id="text"
|
||||
fill="rgba(0,0,0,1)"
|
||||
dominant-baseline="central"
|
||||
paint-order="stroke"
|
||||
text-anchor="middle"
|
||||
/>
|
||||
</g>
|
||||
</g>
|
||||
<g transform="matrix(1,0,0,1,200,50)">
|
||||
<circle
|
||||
id="halo"
|
||||
fill="rgba(248,248,248,1)"
|
||||
cx="0"
|
||||
cy="0"
|
||||
stroke="rgba(139,155,175,1)"
|
||||
stroke-width="2"
|
||||
r="0"
|
||||
/>
|
||||
</g>
|
||||
<g id="icon" fill="none" transform="matrix(1,0,0,1,200,50)">
|
||||
<g transform="matrix(1,0,0,1,0,0)">
|
||||
<text
|
||||
id="icon"
|
||||
fill="rgba(0,0,0,1)"
|
||||
dominant-baseline="central"
|
||||
paint-order="stroke"
|
||||
text-anchor="middle"
|
||||
/>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="g-svg-24" fill="none" r="10" transform="matrix(1,0,0,1,0,0)">
|
||||
<g transform="matrix(1,0,0,1,125,150)">
|
||||
<circle
|
||||
id="key"
|
||||
fill="rgba(255,192,203,1)"
|
||||
transform="translate(-10,-10)"
|
||||
cx="10"
|
||||
cy="10"
|
||||
stroke="rgba(139,155,175,1)"
|
||||
stroke-width="1"
|
||||
r="10"
|
||||
/>
|
||||
</g>
|
||||
<g id="label" fill="none" transform="matrix(1,0,0,1,125,150)">
|
||||
<g transform="matrix(1,0,0,1,-5,-5)">
|
||||
<path
|
||||
id="background"
|
||||
fill="none"
|
||||
d="M 0,0 l 10,0 l 0,10 l-10 0 z"
|
||||
width="10"
|
||||
height="10"
|
||||
/>
|
||||
</g>
|
||||
<g transform="matrix(1,0,0,1,0,0)">
|
||||
<text
|
||||
id="text"
|
||||
fill="rgba(0,0,0,1)"
|
||||
dominant-baseline="central"
|
||||
paint-order="stroke"
|
||||
text-anchor="middle"
|
||||
/>
|
||||
</g>
|
||||
</g>
|
||||
<g transform="matrix(1,0,0,1,125,150)">
|
||||
<circle
|
||||
id="halo"
|
||||
fill="rgba(255,192,203,1)"
|
||||
cx="0"
|
||||
cy="0"
|
||||
stroke="rgba(139,155,175,1)"
|
||||
stroke-width="1"
|
||||
r="0"
|
||||
/>
|
||||
</g>
|
||||
<g id="icon" fill="none" transform="matrix(1,0,0,1,125,150)">
|
||||
<g transform="matrix(1,0,0,1,0,0)">
|
||||
<text
|
||||
id="icon"
|
||||
fill="rgba(0,0,0,1)"
|
||||
dominant-baseline="central"
|
||||
paint-order="stroke"
|
||||
text-anchor="middle"
|
||||
/>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 9.2 KiB |
@ -0,0 +1,309 @@
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="500"
|
||||
height="500"
|
||||
style="background: transparent; position: absolute; outline: none;"
|
||||
color-interpolation-filters="sRGB"
|
||||
tabindex="1"
|
||||
>
|
||||
<defs />
|
||||
<g id="g-svg-camera" transform="matrix(1,0,0,1,0,0)">
|
||||
<g id="g-root" fill="none" transform="matrix(1,0,0,1,0,0)">
|
||||
<g id="g-svg-7" fill="none" transform="matrix(1,0,0,1,0,0)" />
|
||||
<g id="g-svg-6" fill="none" transform="matrix(1,0,0,1,0,0)">
|
||||
<g
|
||||
id="g-svg-32"
|
||||
fill="none"
|
||||
marker-start="false"
|
||||
marker-end="false"
|
||||
transform="matrix(1,0,0,1,0,0)"
|
||||
>
|
||||
<g transform="matrix(1,0,0,1,50,50)">
|
||||
<line
|
||||
id="key"
|
||||
fill="none"
|
||||
x1="0"
|
||||
y1="0"
|
||||
x2="150"
|
||||
y2="0"
|
||||
stroke-width="1"
|
||||
stroke="rgba(139,155,175,1)"
|
||||
/>
|
||||
</g>
|
||||
<g id="label" fill="none" transform="matrix(1,0,0,1,129,44)">
|
||||
<g transform="matrix(1,0,0,1,-5,-5)">
|
||||
<path
|
||||
id="background"
|
||||
fill="none"
|
||||
d="M 0,0 l 10,0 l 0,10 l-10 0 z"
|
||||
width="10"
|
||||
height="10"
|
||||
/>
|
||||
</g>
|
||||
<g transform="matrix(1,0,0,1,0,0)">
|
||||
<text
|
||||
id="text"
|
||||
fill="rgba(0,0,0,1)"
|
||||
dominant-baseline="text-after-edge"
|
||||
paint-order="stroke"
|
||||
text-anchor="middle"
|
||||
/>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g
|
||||
id="g-svg-37"
|
||||
fill="none"
|
||||
marker-start="false"
|
||||
marker-end="false"
|
||||
transform="matrix(1,0,0,1,0,0)"
|
||||
>
|
||||
<g transform="matrix(1,0,0,1,125,50)">
|
||||
<line
|
||||
id="key"
|
||||
fill="none"
|
||||
x1="75"
|
||||
y1="0"
|
||||
x2="0"
|
||||
y2="100"
|
||||
stroke-width="1"
|
||||
stroke="rgba(139,155,175,1)"
|
||||
/>
|
||||
</g>
|
||||
<g
|
||||
id="label"
|
||||
fill="none"
|
||||
transform="matrix(0.600000,-0.800000,0.800000,0.600000,155.300003,99.599998)"
|
||||
>
|
||||
<g transform="matrix(1,0,0,1,-5,-5)">
|
||||
<path
|
||||
id="background"
|
||||
fill="none"
|
||||
d="M 0,0 l 10,0 l 0,10 l-10 0 z"
|
||||
width="10"
|
||||
height="10"
|
||||
/>
|
||||
</g>
|
||||
<g transform="matrix(1,0,0,1,0,0)">
|
||||
<text
|
||||
id="text"
|
||||
fill="rgba(0,0,0,1)"
|
||||
dominant-baseline="text-after-edge"
|
||||
paint-order="stroke"
|
||||
text-anchor="middle"
|
||||
/>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g
|
||||
id="g-svg-42"
|
||||
fill="none"
|
||||
marker-start="false"
|
||||
marker-end="false"
|
||||
transform="matrix(1,0,0,1,0,0)"
|
||||
>
|
||||
<g transform="matrix(1,0,0,1,50,50)">
|
||||
<line
|
||||
id="key"
|
||||
fill="none"
|
||||
x1="75"
|
||||
y1="100"
|
||||
x2="0"
|
||||
y2="0"
|
||||
stroke-width="1"
|
||||
stroke="rgba(139,155,175,1)"
|
||||
/>
|
||||
</g>
|
||||
<g
|
||||
id="label"
|
||||
fill="none"
|
||||
transform="matrix(0.600000,0.800000,-0.800000,0.600000,89.900002,93.199997)"
|
||||
>
|
||||
<g transform="matrix(1,0,0,1,-5,-5)">
|
||||
<path
|
||||
id="background"
|
||||
fill="none"
|
||||
d="M 0,0 l 10,0 l 0,10 l-10 0 z"
|
||||
width="10"
|
||||
height="10"
|
||||
/>
|
||||
</g>
|
||||
<g transform="matrix(1,0,0,1,0,0)">
|
||||
<text
|
||||
id="text"
|
||||
fill="rgba(0,0,0,1)"
|
||||
dominant-baseline="text-after-edge"
|
||||
paint-order="stroke"
|
||||
text-anchor="middle"
|
||||
/>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="g-svg-5" fill="none" transform="matrix(1,0,0,1,0,0)">
|
||||
<g id="g-svg-8" fill="none" r="10" transform="matrix(1,0,0,1,0,0)">
|
||||
<g transform="matrix(1,0,0,1,50,50)">
|
||||
<circle
|
||||
id="key"
|
||||
fill="rgba(248,248,248,1)"
|
||||
transform="translate(-10,-10)"
|
||||
cx="10"
|
||||
cy="10"
|
||||
stroke="rgba(139,155,175,1)"
|
||||
r="10"
|
||||
/>
|
||||
</g>
|
||||
<g id="label" fill="none" transform="matrix(1,0,0,1,50,50)">
|
||||
<g transform="matrix(1,0,0,1,-5,-5)">
|
||||
<path
|
||||
id="background"
|
||||
fill="none"
|
||||
d="M 0,0 l 10,0 l 0,10 l-10 0 z"
|
||||
width="10"
|
||||
height="10"
|
||||
/>
|
||||
</g>
|
||||
<g transform="matrix(1,0,0,1,0,0)">
|
||||
<text
|
||||
id="text"
|
||||
fill="rgba(0,0,0,1)"
|
||||
dominant-baseline="central"
|
||||
paint-order="stroke"
|
||||
text-anchor="middle"
|
||||
/>
|
||||
</g>
|
||||
</g>
|
||||
<g transform="matrix(1,0,0,1,50,50)">
|
||||
<circle
|
||||
id="halo"
|
||||
fill="rgba(248,248,248,1)"
|
||||
cx="0"
|
||||
cy="0"
|
||||
stroke="rgba(139,155,175,1)"
|
||||
r="0"
|
||||
/>
|
||||
</g>
|
||||
<g id="icon" fill="none" transform="matrix(1,0,0,1,50,50)">
|
||||
<g transform="matrix(1,0,0,1,0,0)">
|
||||
<text
|
||||
id="icon"
|
||||
fill="rgba(0,0,0,1)"
|
||||
dominant-baseline="central"
|
||||
paint-order="stroke"
|
||||
text-anchor="middle"
|
||||
/>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="g-svg-16" fill="none" r="10" transform="matrix(1,0,0,1,0,0)">
|
||||
<g transform="matrix(1,0,0,1,200,50)">
|
||||
<circle
|
||||
id="key"
|
||||
fill="rgba(248,248,248,1)"
|
||||
transform="translate(-10,-10)"
|
||||
cx="10"
|
||||
cy="10"
|
||||
stroke="rgba(139,155,175,1)"
|
||||
r="10"
|
||||
/>
|
||||
</g>
|
||||
<g id="label" fill="none" transform="matrix(1,0,0,1,200,50)">
|
||||
<g transform="matrix(1,0,0,1,-5,-5)">
|
||||
<path
|
||||
id="background"
|
||||
fill="none"
|
||||
d="M 0,0 l 10,0 l 0,10 l-10 0 z"
|
||||
width="10"
|
||||
height="10"
|
||||
/>
|
||||
</g>
|
||||
<g transform="matrix(1,0,0,1,0,0)">
|
||||
<text
|
||||
id="text"
|
||||
fill="rgba(0,0,0,1)"
|
||||
dominant-baseline="central"
|
||||
paint-order="stroke"
|
||||
text-anchor="middle"
|
||||
/>
|
||||
</g>
|
||||
</g>
|
||||
<g transform="matrix(1,0,0,1,200,50)">
|
||||
<circle
|
||||
id="halo"
|
||||
fill="rgba(248,248,248,1)"
|
||||
cx="0"
|
||||
cy="0"
|
||||
stroke="rgba(139,155,175,1)"
|
||||
r="0"
|
||||
/>
|
||||
</g>
|
||||
<g id="icon" fill="none" transform="matrix(1,0,0,1,200,50)">
|
||||
<g transform="matrix(1,0,0,1,0,0)">
|
||||
<text
|
||||
id="icon"
|
||||
fill="rgba(0,0,0,1)"
|
||||
dominant-baseline="central"
|
||||
paint-order="stroke"
|
||||
text-anchor="middle"
|
||||
/>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="g-svg-24" fill="none" r="10" transform="matrix(1,0,0,1,0,0)">
|
||||
<g transform="matrix(1,0,0,1,125,150)">
|
||||
<circle
|
||||
id="key"
|
||||
fill="rgba(248,248,248,1)"
|
||||
transform="translate(-10,-10)"
|
||||
cx="10"
|
||||
cy="10"
|
||||
stroke="rgba(139,155,175,1)"
|
||||
r="10"
|
||||
/>
|
||||
</g>
|
||||
<g id="label" fill="none" transform="matrix(1,0,0,1,125,150)">
|
||||
<g transform="matrix(1,0,0,1,-5,-5)">
|
||||
<path
|
||||
id="background"
|
||||
fill="none"
|
||||
d="M 0,0 l 10,0 l 0,10 l-10 0 z"
|
||||
width="10"
|
||||
height="10"
|
||||
/>
|
||||
</g>
|
||||
<g transform="matrix(1,0,0,1,0,0)">
|
||||
<text
|
||||
id="text"
|
||||
fill="rgba(0,0,0,1)"
|
||||
dominant-baseline="central"
|
||||
paint-order="stroke"
|
||||
text-anchor="middle"
|
||||
/>
|
||||
</g>
|
||||
</g>
|
||||
<g transform="matrix(1,0,0,1,125,150)">
|
||||
<circle
|
||||
id="halo"
|
||||
fill="rgba(248,248,248,1)"
|
||||
cx="0"
|
||||
cy="0"
|
||||
stroke="rgba(139,155,175,1)"
|
||||
r="0"
|
||||
/>
|
||||
</g>
|
||||
<g id="icon" fill="none" transform="matrix(1,0,0,1,125,150)">
|
||||
<g transform="matrix(1,0,0,1,0,0)">
|
||||
<text
|
||||
id="icon"
|
||||
fill="rgba(0,0,0,1)"
|
||||
dominant-baseline="central"
|
||||
paint-order="stroke"
|
||||
text-anchor="middle"
|
||||
/>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 9.0 KiB |
@ -15,6 +15,7 @@ describe('static', () => {
|
||||
try {
|
||||
const { preprocess, postprocess } = testCase;
|
||||
await preprocess?.();
|
||||
await canvas.init();
|
||||
await testCase({ canvas });
|
||||
await expect(canvas).toMatchSVGSnapshot(`${__dirname}/snapshots/static`, name);
|
||||
await postprocess?.();
|
||||
|
@ -48,13 +48,14 @@ function loadCasesList(select: HTMLSelectElement) {
|
||||
|
||||
function onchange(testCase: TestCase, rendererName: string) {
|
||||
const renderer = getRenderer(rendererName);
|
||||
testCase({
|
||||
canvas: new Canvas({
|
||||
width: 500,
|
||||
height: 500,
|
||||
container: document.getElementById('container')!,
|
||||
renderer,
|
||||
}),
|
||||
const canvas = new Canvas({
|
||||
width: 500,
|
||||
height: 500,
|
||||
container: document.getElementById('container')!,
|
||||
renderer,
|
||||
});
|
||||
canvas.init().then(() => {
|
||||
testCase({ canvas });
|
||||
});
|
||||
}
|
||||
|
||||
|
3
packages/g6/__tests__/mock/graph.ts
Normal file
3
packages/g6/__tests__/mock/graph.ts
Normal file
@ -0,0 +1,3 @@
|
||||
import EventEmitter from '@antv/event-emitter';
|
||||
|
||||
export class Graph extends EventEmitter {}
|
1
packages/g6/__tests__/mock/index.ts
Normal file
1
packages/g6/__tests__/mock/index.ts
Normal file
@ -0,0 +1 @@
|
||||
export * from './graph';
|
@ -1,33 +1,35 @@
|
||||
import { BUILT_IN_EDGES, BUILT_IN_NODES } from '../../src/elements';
|
||||
import { getPlugin, getPlugins, register, registerBuiltInPlugins } from '../../src/registry';
|
||||
import { BUILT_IN_THEMES } from '../../src/themes';
|
||||
|
||||
describe('registry', () => {
|
||||
it('registerBuiltInPlugins', () => {
|
||||
registerBuiltInPlugins();
|
||||
|
||||
// TODO 在变更内置插件后更新此用例 / update this when we have more built-in plugins
|
||||
expect(getPlugins('node')).toEqual({});
|
||||
expect(getPlugins('edge')).toEqual({});
|
||||
expect(getPlugins('node')).toEqual(BUILT_IN_NODES);
|
||||
expect(getPlugins('edge')).toEqual(BUILT_IN_EDGES);
|
||||
expect(getPlugins('combo')).toEqual({});
|
||||
expect(getPlugins('theme')).toEqual({});
|
||||
expect(getPlugins('theme')).toEqual(BUILT_IN_THEMES);
|
||||
});
|
||||
|
||||
it('register, getPlugin, getPlugins', () => {
|
||||
class CircleNode {}
|
||||
class RectNode {}
|
||||
class Edge {}
|
||||
register('node', 'circle-node', CircleNode);
|
||||
register('node', 'rect-node', RectNode);
|
||||
register('edge', 'edge', Edge);
|
||||
register('node', 'circle-node', CircleNode as any);
|
||||
register('node', 'rect-node', RectNode as any);
|
||||
register('edge', 'line-edge', Edge);
|
||||
expect(getPlugin('node', 'circle-node')).toEqual(CircleNode);
|
||||
expect(getPlugin('node', 'rect-node')).toEqual(RectNode);
|
||||
expect(getPlugin('node', 'diamond-node')).toEqual(undefined);
|
||||
expect(getPlugin('edge', 'edge')).toEqual(Edge);
|
||||
expect(getPlugin('edge', 'line-edge')).toEqual(Edge);
|
||||
|
||||
expect(() => {
|
||||
register('node', 'circle-node', CircleNode);
|
||||
register('node', 'circle-node', CircleNode as any);
|
||||
}).toThrow();
|
||||
|
||||
expect(getPlugins('node')).toEqual({
|
||||
...BUILT_IN_NODES,
|
||||
'circle-node': CircleNode,
|
||||
'rect-node': RectNode,
|
||||
});
|
||||
|
@ -411,83 +411,67 @@ describe('DataController', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('changes', (done) => {
|
||||
it('changes', () => {
|
||||
const controller = new DataController();
|
||||
|
||||
controller.once('change', (original: any) => {
|
||||
expect(original).toEqual([
|
||||
{ value: { id: 'combo-1' }, type: 'ComboAdded' },
|
||||
{ value: { id: 'node-1', data: { value: 1 }, style: { fill: 'red' } }, type: 'NodeAdded' },
|
||||
{
|
||||
value: { id: 'node-2', data: { value: 2 }, style: { fill: 'green', parentId: 'combo-1' } },
|
||||
type: 'NodeAdded',
|
||||
},
|
||||
{
|
||||
value: { id: 'node-3', data: { value: 3 }, style: { fill: 'blue', parentId: 'combo-1' } },
|
||||
type: 'NodeAdded',
|
||||
},
|
||||
{ value: { id: 'edge-1', source: 'node-1', target: 'node-2', data: { weight: 1 } }, type: 'EdgeAdded' },
|
||||
{ value: { id: 'edge-2', source: 'node-2', target: 'node-3', data: { weight: 2 } }, type: 'EdgeAdded' },
|
||||
{ value: { id: 'combo-2' }, type: 'ComboAdded' },
|
||||
{ value: { id: 'node-4', data: { value: 4 }, style: { fill: 'yellow' } }, type: 'NodeAdded' },
|
||||
{
|
||||
value: { id: 'node-3', data: { value: 3 }, style: { fill: 'pink', parentId: 'combo-2' } },
|
||||
original: { id: 'node-3', data: { value: 3 }, style: { fill: 'blue', parentId: 'combo-1' } },
|
||||
type: 'NodeUpdated',
|
||||
},
|
||||
{ value: { id: 'edge-1', source: 'node-1', target: 'node-2', data: { weight: 1 } }, type: 'EdgeRemoved' },
|
||||
{ value: { id: 'edge-2', source: 'node-2', target: 'node-3', data: { weight: 2 } }, type: 'EdgeRemoved' },
|
||||
{ value: { id: 'node-1', data: { value: 1 }, style: { fill: 'red' } }, type: 'NodeRemoved' },
|
||||
{
|
||||
value: { id: 'node-2', data: { value: 2 }, style: { fill: 'green', parentId: 'combo-1' } },
|
||||
type: 'NodeRemoved',
|
||||
},
|
||||
{ value: { id: 'combo-1' }, type: 'ComboRemoved' },
|
||||
]);
|
||||
controller.addData(clone(data));
|
||||
|
||||
expect(reduceDataChanges(original)).toEqual([
|
||||
{
|
||||
type: 'NodeAdded',
|
||||
value: { id: 'node-3', data: { value: 3 }, style: { fill: 'pink', parentId: 'combo-2' } },
|
||||
},
|
||||
{ value: { id: 'combo-2' }, type: 'ComboAdded' },
|
||||
{ value: { id: 'node-4', data: { value: 4 }, style: { fill: 'yellow' } }, type: 'NodeAdded' },
|
||||
]);
|
||||
|
||||
done();
|
||||
controller.setData({
|
||||
nodes: [
|
||||
{ id: 'node-3', data: { value: 3 }, style: { fill: 'pink', parentId: 'combo-2' } },
|
||||
{ id: 'node-4', data: { value: 4 }, style: { fill: 'yellow' } },
|
||||
],
|
||||
combos: [{ id: 'combo-2' }],
|
||||
});
|
||||
|
||||
controller.batch(() => {
|
||||
controller.addData(clone(data));
|
||||
const changes = controller.getChanges();
|
||||
|
||||
controller.setData({
|
||||
nodes: [
|
||||
{ id: 'node-3', data: { value: 3 }, style: { fill: 'pink', parentId: 'combo-2' } },
|
||||
{ id: 'node-4', data: { value: 4 }, style: { fill: 'yellow' } },
|
||||
],
|
||||
combos: [{ id: 'combo-2' }],
|
||||
});
|
||||
});
|
||||
expect(changes).toEqual([
|
||||
{ value: { id: 'combo-1' }, type: 'ComboAdded' },
|
||||
{ value: { id: 'node-1', data: { value: 1 }, style: { fill: 'red' } }, type: 'NodeAdded' },
|
||||
{
|
||||
value: { id: 'node-2', data: { value: 2 }, style: { fill: 'green', parentId: 'combo-1' } },
|
||||
type: 'NodeAdded',
|
||||
},
|
||||
{
|
||||
value: { id: 'node-3', data: { value: 3 }, style: { fill: 'blue', parentId: 'combo-1' } },
|
||||
type: 'NodeAdded',
|
||||
},
|
||||
{ value: { id: 'edge-1', source: 'node-1', target: 'node-2', data: { weight: 1 } }, type: 'EdgeAdded' },
|
||||
{ value: { id: 'edge-2', source: 'node-2', target: 'node-3', data: { weight: 2 } }, type: 'EdgeAdded' },
|
||||
{ value: { id: 'combo-2' }, type: 'ComboAdded' },
|
||||
{ value: { id: 'node-4', data: { value: 4 }, style: { fill: 'yellow' } }, type: 'NodeAdded' },
|
||||
{
|
||||
value: { id: 'node-3', data: { value: 3 }, style: { fill: 'pink', parentId: 'combo-2' } },
|
||||
original: { id: 'node-3', data: { value: 3 }, style: { fill: 'blue', parentId: 'combo-1' } },
|
||||
type: 'NodeUpdated',
|
||||
},
|
||||
{ value: { id: 'edge-1', source: 'node-1', target: 'node-2', data: { weight: 1 } }, type: 'EdgeRemoved' },
|
||||
{ value: { id: 'edge-2', source: 'node-2', target: 'node-3', data: { weight: 2 } }, type: 'EdgeRemoved' },
|
||||
{ value: { id: 'node-1', data: { value: 1 }, style: { fill: 'red' } }, type: 'NodeRemoved' },
|
||||
{
|
||||
value: { id: 'node-2', data: { value: 2 }, style: { fill: 'green', parentId: 'combo-1' } },
|
||||
type: 'NodeRemoved',
|
||||
},
|
||||
{ value: { id: 'combo-1' }, type: 'ComboRemoved' },
|
||||
]);
|
||||
|
||||
expect(reduceDataChanges(changes)).toEqual([
|
||||
{
|
||||
type: 'NodeAdded',
|
||||
value: { id: 'node-3', data: { value: 3 }, style: { fill: 'pink', parentId: 'combo-2' } },
|
||||
},
|
||||
{ value: { id: 'combo-2' }, type: 'ComboAdded' },
|
||||
{ value: { id: 'node-4', data: { value: 4 }, style: { fill: 'yellow' } }, type: 'NodeAdded' },
|
||||
]);
|
||||
|
||||
// re pull
|
||||
expect(controller.getChanges()).toEqual([]);
|
||||
});
|
||||
|
||||
it('changes add', (done) => {
|
||||
it('changes add', () => {
|
||||
const controller = new DataController();
|
||||
|
||||
controller.once('change', (original: any) => {
|
||||
expect(original).toEqual(original);
|
||||
|
||||
expect(reduceDataChanges(original)).toEqual([
|
||||
{ value: { id: 'combo-2' }, type: 'ComboAdded' },
|
||||
{
|
||||
type: 'NodeAdded',
|
||||
value: { id: 'node-3', data: { value: 3 }, style: { fill: 'pink', parentId: 'combo-2' } },
|
||||
},
|
||||
{ value: { id: 'node-4', data: { value: 4 }, style: { fill: 'yellow' } }, type: 'NodeAdded' },
|
||||
]);
|
||||
|
||||
done();
|
||||
});
|
||||
|
||||
controller.addData({
|
||||
nodes: [
|
||||
{ id: 'node-3', data: { value: 3 }, style: { fill: 'pink', parentId: 'combo-2' } },
|
||||
@ -495,6 +479,17 @@ describe('DataController', () => {
|
||||
],
|
||||
combos: [{ id: 'combo-2' }],
|
||||
});
|
||||
|
||||
const changes = controller.getChanges();
|
||||
|
||||
expect(reduceDataChanges(changes)).toEqual([
|
||||
{ value: { id: 'combo-2' }, type: 'ComboAdded' },
|
||||
{
|
||||
type: 'NodeAdded',
|
||||
value: { id: 'node-3', data: { value: 3 }, style: { fill: 'pink', parentId: 'combo-2' } },
|
||||
},
|
||||
{ value: { id: 'node-4', data: { value: 4 }, style: { fill: 'yellow' } }, type: 'NodeAdded' },
|
||||
]);
|
||||
});
|
||||
|
||||
it('getElementData', () => {
|
||||
@ -525,16 +520,6 @@ describe('DataController', () => {
|
||||
expect(controller.getNodeLikeData()).toEqual([...data.combos, ...data.nodes]);
|
||||
});
|
||||
|
||||
it('classifyNodeLikeData', () => {
|
||||
const controller = new DataController();
|
||||
|
||||
controller.addData(clone(data));
|
||||
expect(controller.classifyNodeLikeData([...data.combos, ...data.nodes])).toEqual({
|
||||
nodes: data.nodes,
|
||||
combos: data.combos,
|
||||
});
|
||||
});
|
||||
|
||||
it('hasNode', () => {
|
||||
const controller = new DataController();
|
||||
|
||||
|
241
packages/g6/__tests__/unit/runtime/element.spec.ts
Normal file
241
packages/g6/__tests__/unit/runtime/element.spec.ts
Normal file
@ -0,0 +1,241 @@
|
||||
import type { G6Spec } from '../../../src';
|
||||
import { BUILT_IN_PALETTES } from '../../../src/palettes';
|
||||
import '../../../src/preset';
|
||||
import { DataController } from '../../../src/runtime/data';
|
||||
import { ElementController } from '../../../src/runtime/element';
|
||||
import type { RuntimeContext } from '../../../src/runtime/types';
|
||||
import { LIGHT_THEME } from '../../../src/themes/light';
|
||||
import { idOf } from '../../../src/utils/id';
|
||||
import { Graph } from '../../mock';
|
||||
|
||||
class Canvas {
|
||||
init() {
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
children: unknown[] = [];
|
||||
appendChild(node: unknown) {
|
||||
this.children.push(node);
|
||||
return node;
|
||||
}
|
||||
}
|
||||
|
||||
const createContext = (options: G6Spec): RuntimeContext => {
|
||||
const dataController = new DataController();
|
||||
dataController.setData(options.data || {});
|
||||
return {
|
||||
canvas: new Canvas() as any,
|
||||
graph: new Graph() as any,
|
||||
options,
|
||||
dataController,
|
||||
};
|
||||
};
|
||||
|
||||
describe('ElementController', () => {
|
||||
it('static', async () => {
|
||||
const options: G6Spec = {
|
||||
data: {
|
||||
nodes: [
|
||||
{ id: 'node-1', style: { fill: 'red', stroke: 'pink', lineWidth: 1 }, data: { value: 100 } },
|
||||
{ id: 'node-2', data: { value: 150 } },
|
||||
{ id: 'node-3', style: { parentId: 'combo-1', states: ['selected'] }, data: { value: 150 } },
|
||||
],
|
||||
edges: [
|
||||
{ source: 'node-1', target: 'node-2', data: { weight: 250 } },
|
||||
{
|
||||
source: 'node-2',
|
||||
target: 'node-3',
|
||||
style: { lineWidth: 5, states: ['active', 'selected'] },
|
||||
data: { weight: 300 },
|
||||
},
|
||||
],
|
||||
combos: [{ id: 'combo-1' }],
|
||||
},
|
||||
theme: 'light',
|
||||
node: {
|
||||
style: {
|
||||
fill: (datum: any) => (datum?.data?.value > 100 ? 'red' : 'blue'),
|
||||
border: (datum: any, index: number, data: any) => (index % 2 === 0 ? 0 : 10),
|
||||
},
|
||||
state: {
|
||||
selected: {
|
||||
fill: (datum: any) => (datum?.data?.value > 100 ? 'purple' : 'cyan'),
|
||||
},
|
||||
},
|
||||
palette: 'spectral',
|
||||
},
|
||||
edge: {
|
||||
style: {},
|
||||
state: {
|
||||
selected: {
|
||||
stroke: 'red',
|
||||
},
|
||||
active: {
|
||||
stroke: 'pink',
|
||||
lineWidth: 4,
|
||||
},
|
||||
},
|
||||
palette: { type: 'group', color: 'oranges', invert: true },
|
||||
},
|
||||
combo: {
|
||||
style: {},
|
||||
state: {},
|
||||
palette: 'blues',
|
||||
},
|
||||
};
|
||||
|
||||
const context = createContext(options);
|
||||
|
||||
const elementController = new ElementController(context);
|
||||
|
||||
const edge1Id = idOf(options.data!.edges![0]);
|
||||
const edge2Id = idOf(options.data!.edges![1]);
|
||||
|
||||
// @ts-expect-error computeStyle is private
|
||||
elementController.computeStyle();
|
||||
|
||||
expect(elementController.getDataStyle('node', 'node-1')).toEqual(options.data!.nodes![0].style || {});
|
||||
// 没有属性 / no style
|
||||
expect(elementController.getDataStyle('node', 'node-2')).toEqual({});
|
||||
// 没有样式属性 / No style attribute
|
||||
expect(elementController.getDataStyle('node', 'node-3')).toEqual({});
|
||||
expect(elementController.getDataStyle('edge', edge1Id)).toEqual(options.data!.edges![0].style || {});
|
||||
expect(elementController.getDataStyle('combo', 'combo-1')).toEqual({});
|
||||
|
||||
// ref light theme
|
||||
expect(elementController.getThemeStyle('node')).toEqual(LIGHT_THEME.node!.style);
|
||||
expect(elementController.getThemeStateStyle('node', [])).toEqual({});
|
||||
|
||||
expect(elementController.getThemeStateStyle('node', ['selected'])).toEqual({
|
||||
...LIGHT_THEME.node!.state!.selected,
|
||||
});
|
||||
expect(elementController.getThemeStateStyle('node', ['selected', 'active'])).toEqual({
|
||||
...LIGHT_THEME.node!.state!.selected,
|
||||
...LIGHT_THEME.node!.state!.active,
|
||||
});
|
||||
const paletteKey = 'keyShapeColor';
|
||||
|
||||
expect(elementController.getPaletteStyle('node-1')[paletteKey]).toBe(BUILT_IN_PALETTES.spectral[0]);
|
||||
expect(elementController.getPaletteStyle('node-2')[paletteKey]).toBe(BUILT_IN_PALETTES.spectral[1]);
|
||||
expect(elementController.getPaletteStyle('node-3')[paletteKey]).toBe(BUILT_IN_PALETTES.spectral[2]);
|
||||
// invert
|
||||
expect(elementController.getPaletteStyle(edge1Id)[paletteKey]).toBe(BUILT_IN_PALETTES.oranges.at(-1));
|
||||
expect(elementController.getPaletteStyle(edge2Id)[paletteKey]).toBe(BUILT_IN_PALETTES.oranges.at(-2));
|
||||
expect(elementController.getPaletteStyle('combo-1')[paletteKey]).toBe(BUILT_IN_PALETTES.blues[0]);
|
||||
|
||||
expect(elementController.getDefaultStyle('node-1')).toEqual({ fill: 'blue', border: 0 });
|
||||
expect(elementController.getDefaultStyle('node-2')).toEqual({ fill: 'red', border: 10 });
|
||||
expect(elementController.getDefaultStyle('node-3')).toEqual({ fill: 'red', border: 0 });
|
||||
expect(elementController.getDefaultStyle(edge1Id)).toEqual({});
|
||||
expect(elementController.getDefaultStyle('combo-1')).toEqual({});
|
||||
|
||||
expect(elementController.getStateStyle('node-1')).toEqual({});
|
||||
expect(elementController.getStateStyle('node-2')).toEqual({});
|
||||
expect(elementController.getStateStyle('node-3')).toEqual({ fill: 'purple' });
|
||||
expect(elementController.getStateStyle(idOf(options.data!.edges![1]))).toEqual({
|
||||
stroke: 'red',
|
||||
lineWidth: 4,
|
||||
});
|
||||
expect(elementController.getStateStyle('combo-1')).toEqual({});
|
||||
|
||||
expect(Object.keys(elementController.getElementsByState('selected'))).toEqual([
|
||||
'node-3',
|
||||
idOf(options.data!.edges![1]),
|
||||
]);
|
||||
|
||||
expect(elementController.getElementStates('node-1')).toEqual([]);
|
||||
expect(elementController.getElementStates('node-2')).toEqual([]);
|
||||
expect(elementController.getElementStates('node-3')).toEqual(['selected']);
|
||||
expect(elementController.getElementStates('edge-1')).toEqual([]);
|
||||
expect(elementController.getElementStates(idOf(options.data!.edges![1]))).toEqual(['active', 'selected']);
|
||||
|
||||
expect(elementController.getElementComputedStyle('node', 'node-1')).toEqual({
|
||||
...LIGHT_THEME.node?.style,
|
||||
fill: 'blue',
|
||||
stroke: 'pink',
|
||||
lineWidth: 1,
|
||||
border: 0,
|
||||
// from palette
|
||||
keyShapeColor: BUILT_IN_PALETTES.spectral[0],
|
||||
});
|
||||
|
||||
expect(elementController.getElementComputedStyle('node', 'node-2')).toEqual({
|
||||
...LIGHT_THEME.node?.style,
|
||||
fill: 'red',
|
||||
border: 10,
|
||||
// from palette
|
||||
keyShapeColor: BUILT_IN_PALETTES.spectral[1],
|
||||
});
|
||||
|
||||
expect(elementController.getElementComputedStyle('node', 'node-3')).toEqual({
|
||||
...LIGHT_THEME.node?.style,
|
||||
...LIGHT_THEME.node?.state?.selected,
|
||||
border: 0,
|
||||
// from state
|
||||
fill: 'purple',
|
||||
// from palette
|
||||
keyShapeColor: BUILT_IN_PALETTES.spectral[2],
|
||||
});
|
||||
|
||||
expect(elementController.getElementComputedStyle('edge', edge1Id)).toEqual({
|
||||
...LIGHT_THEME.edge?.style,
|
||||
sourcePoint: [0, 0, 0],
|
||||
targetPoint: [0, 0, 0],
|
||||
keyShapeColor: BUILT_IN_PALETTES.oranges.at(-1),
|
||||
});
|
||||
expect(elementController.getElementComputedStyle('edge', edge2Id)).toEqual({
|
||||
...LIGHT_THEME.edge?.style,
|
||||
...LIGHT_THEME.edge?.state?.active,
|
||||
...LIGHT_THEME.edge?.state?.selected,
|
||||
lineWidth: 4,
|
||||
stroke: 'red',
|
||||
// 在运行时环境测试 / Test in runtime environment
|
||||
sourceNode: undefined,
|
||||
targetNode: undefined,
|
||||
// 暂未实现 / Not implemented yet
|
||||
sourcePoint: [0, 0, 0],
|
||||
targetPoint: [0, 0, 0],
|
||||
keyShapeColor: BUILT_IN_PALETTES.oranges.at(-2),
|
||||
});
|
||||
|
||||
expect(elementController.getElementComputedStyle('combo', 'combo-1')).toEqual({
|
||||
...LIGHT_THEME.combo?.style,
|
||||
keyShapeColor: BUILT_IN_PALETTES.blues[0],
|
||||
children: {
|
||||
// 值为 undefined 是因为在非运行时环境 / The value is undefined because it is not in the runtime environment
|
||||
'node-3': undefined,
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('mock runtime', async () => {
|
||||
const options: G6Spec = {
|
||||
data: {
|
||||
nodes: [
|
||||
{ id: 'node-1' },
|
||||
{ id: 'node-2', style: { parentId: 'combo-1' } },
|
||||
{ id: 'node-3', style: { parentId: 'combo-1' } },
|
||||
],
|
||||
edges: [
|
||||
{ source: 'node-1', target: 'node-2' },
|
||||
{ source: 'node-2', target: 'node-3' },
|
||||
],
|
||||
combos: [{ id: 'combo-1' }],
|
||||
},
|
||||
};
|
||||
|
||||
const context = createContext(options);
|
||||
|
||||
const elementController = new ElementController(context);
|
||||
|
||||
await elementController.render(context);
|
||||
|
||||
// @ts-expect-error container is private
|
||||
const container = elementController.container;
|
||||
|
||||
expect(container.node.children.length).toBe(3);
|
||||
expect(container.edge.children.length).toBe(2);
|
||||
// TODO 目前暂未提供 combo 图形,因此无法渲染 / Currently, combo graphics are not provided, so they cannot be rendered
|
||||
expect(container.combo.children.length).toBe(0);
|
||||
});
|
||||
});
|
@ -41,9 +41,7 @@ describe('spec', () => {
|
||||
},
|
||||
},
|
||||
animation: {
|
||||
enter: {
|
||||
type: 'fade-in',
|
||||
},
|
||||
enter: 'fade',
|
||||
},
|
||||
palette: {
|
||||
type: 'group',
|
||||
|
@ -1,9 +1,8 @@
|
||||
import type { IAnimation } from '@antv/g';
|
||||
import { register } from '../../../src';
|
||||
import {
|
||||
createAnimationsProxy,
|
||||
executeAnimation,
|
||||
parseAnimation,
|
||||
inferDefaultValue,
|
||||
preprocessKeyframes,
|
||||
} from '../../../src/utils/animation';
|
||||
|
||||
@ -35,25 +34,6 @@ describe('animation', () => {
|
||||
expect(targetPause).toHaveBeenCalledTimes(2);
|
||||
});
|
||||
|
||||
it('parseAnimation', () => {
|
||||
expect(parseAnimation('custom')).toEqual([]);
|
||||
register('animation', 'custom', [{ fields: ['size'] }]);
|
||||
expect(parseAnimation('custom')).toEqual([{ fields: ['size'] }]);
|
||||
|
||||
// built it
|
||||
expect(parseAnimation('fade')).toEqual([{ fields: ['opacity'] }]);
|
||||
expect(parseAnimation([{ fields: ['opacity'] }])).toEqual([{ fields: ['opacity'] }]);
|
||||
expect(
|
||||
parseAnimation([
|
||||
{ fields: ['opacity', 'size'], shape: 'key', duration: 500 },
|
||||
{ fields: ['opacity'], shape: 'halo', duration: 500 },
|
||||
]),
|
||||
).toEqual([
|
||||
{ fields: ['opacity', 'size'], shape: 'key', duration: 500 },
|
||||
{ fields: ['opacity'], shape: 'halo', duration: 500 },
|
||||
]);
|
||||
});
|
||||
|
||||
it('preprocessKeyframes', () => {
|
||||
expect(
|
||||
preprocessKeyframes([
|
||||
@ -124,4 +104,9 @@ describe('animation', () => {
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
it('inferDefaultValue', () => {
|
||||
expect(inferDefaultValue('opacity')).toBe(1);
|
||||
expect(inferDefaultValue('stroke')).toBe(undefined);
|
||||
});
|
||||
});
|
||||
|
178
packages/g6/__tests__/unit/utils/palette.spec.ts
Normal file
178
packages/g6/__tests__/unit/utils/palette.spec.ts
Normal file
@ -0,0 +1,178 @@
|
||||
import '../../../src/preset';
|
||||
import { register } from '../../../src/registry';
|
||||
import { assignColorByPalette, parsePalette } from '../../../src/utils/palette';
|
||||
|
||||
describe('palette', () => {
|
||||
it('parsePalette', () => {
|
||||
expect(parsePalette('category3')).toEqual({ type: 'group', color: 'category3' });
|
||||
expect(parsePalette(['red', 'green', 'blue'])).toEqual({
|
||||
type: 'group',
|
||||
color: ['red', 'green', 'blue'],
|
||||
});
|
||||
expect(parsePalette({ type: 'value', color: 'custom-blues', field: 'value' })).toEqual({
|
||||
type: 'value',
|
||||
color: 'custom-blues',
|
||||
field: 'value',
|
||||
});
|
||||
});
|
||||
|
||||
it('assignColorByPalette unset', () => {
|
||||
const data = [
|
||||
{ id: 'node-1', data: { value: 100, category: 'A' } },
|
||||
{ id: 'node-2', data: { value: 200, category: 'B' } },
|
||||
{ id: 'node-3', data: { value: 300, category: 'C' } },
|
||||
];
|
||||
|
||||
expect(assignColorByPalette(data)).toEqual({});
|
||||
});
|
||||
|
||||
it('assignColorByPalette discrete', () => {
|
||||
register('palette', 'category3', ['#1f77b4', '#ff7f0e', '#2ca02c']);
|
||||
|
||||
const data3 = [
|
||||
{ id: 'node-1', data: { value: 100, category: 'A' } },
|
||||
{ id: 'node-2', data: { value: 200, category: 'B' } },
|
||||
{ id: 'node-3', data: { value: 300, category: 'C' } },
|
||||
];
|
||||
|
||||
const data4 = [...data3, { id: 'node-4', data: { value: 400, category: 'D' } }];
|
||||
|
||||
const data5 = [...data4, { id: 'node-5', data: { value: 500, category: 'A' } }];
|
||||
|
||||
expect(
|
||||
assignColorByPalette(data3, {
|
||||
type: 'group',
|
||||
color: 'category3',
|
||||
field: 'category',
|
||||
}),
|
||||
).toEqual({
|
||||
'node-1': '#1f77b4',
|
||||
'node-2': '#ff7f0e',
|
||||
'node-3': '#2ca02c',
|
||||
});
|
||||
|
||||
// invert
|
||||
expect(
|
||||
assignColorByPalette(data3, {
|
||||
type: 'group',
|
||||
color: 'category3',
|
||||
field: 'category',
|
||||
invert: true,
|
||||
}),
|
||||
).toEqual({
|
||||
'node-1': '#2ca02c',
|
||||
'node-2': '#ff7f0e',
|
||||
'node-3': '#1f77b4',
|
||||
});
|
||||
|
||||
expect(
|
||||
assignColorByPalette(data4, {
|
||||
type: 'group',
|
||||
color: 'category3',
|
||||
field: 'category',
|
||||
}),
|
||||
).toEqual({
|
||||
'node-1': '#1f77b4',
|
||||
'node-2': '#ff7f0e',
|
||||
'node-3': '#2ca02c',
|
||||
'node-4': '#1f77b4',
|
||||
});
|
||||
|
||||
expect(
|
||||
assignColorByPalette(data5, {
|
||||
type: 'group',
|
||||
color: 'category3',
|
||||
field: 'category',
|
||||
}),
|
||||
).toEqual({
|
||||
'node-1': '#1f77b4',
|
||||
'node-2': '#ff7f0e',
|
||||
'node-3': '#2ca02c',
|
||||
'node-4': '#1f77b4',
|
||||
'node-5': '#1f77b4',
|
||||
});
|
||||
|
||||
expect(
|
||||
assignColorByPalette(data5, {
|
||||
type: 'group',
|
||||
color: 'category3',
|
||||
}),
|
||||
).toEqual({
|
||||
'node-1': '#1f77b4',
|
||||
'node-2': '#ff7f0e',
|
||||
'node-3': '#2ca02c',
|
||||
'node-4': '#1f77b4',
|
||||
'node-5': '#ff7f0e',
|
||||
});
|
||||
|
||||
expect(
|
||||
assignColorByPalette(data5, {
|
||||
type: 'group',
|
||||
color: 'spectral',
|
||||
}),
|
||||
).toEqual({
|
||||
'node-1': 'rgb(158, 1, 66)',
|
||||
'node-2': 'rgb(213, 62, 79)',
|
||||
'node-3': 'rgb(244, 109, 67)',
|
||||
'node-4': 'rgb(253, 174, 97)',
|
||||
'node-5': 'rgb(254, 224, 139)',
|
||||
});
|
||||
});
|
||||
|
||||
it('assignColorByPalette continuous', () => {
|
||||
register('palette', 'custom-blues', (value) => `rgb(0, 0, ${(value * 255).toFixed(0)})`);
|
||||
|
||||
const createData = (length: number) => {
|
||||
return Array.from({ length }, (_, index) => ({ id: `node-${index + 1}`, data: { value: index * 100 + 100 } }));
|
||||
};
|
||||
|
||||
const data3 = createData(3);
|
||||
|
||||
expect(
|
||||
assignColorByPalette(data3, {
|
||||
type: 'value',
|
||||
color: 'custom-blues',
|
||||
field: 'value',
|
||||
}),
|
||||
).toEqual({
|
||||
'node-1': 'rgb(0, 0, 0)',
|
||||
'node-2': 'rgb(0, 0, 128)',
|
||||
'node-3': 'rgb(0, 0, 255)',
|
||||
});
|
||||
|
||||
// invert
|
||||
expect(
|
||||
assignColorByPalette(data3, {
|
||||
type: 'value',
|
||||
color: 'custom-blues',
|
||||
field: 'value',
|
||||
invert: true,
|
||||
}),
|
||||
).toEqual({
|
||||
'node-1': 'rgb(0, 0, 255)',
|
||||
'node-2': 'rgb(0, 0, 128)',
|
||||
'node-3': 'rgb(0, 0, 0)',
|
||||
});
|
||||
|
||||
const data11 = createData(11);
|
||||
expect(
|
||||
assignColorByPalette(data11, {
|
||||
type: 'value',
|
||||
color: 'custom-blues',
|
||||
field: 'value',
|
||||
}),
|
||||
).toEqual({
|
||||
'node-1': 'rgb(0, 0, 0)',
|
||||
'node-2': 'rgb(0, 0, 26)',
|
||||
'node-3': 'rgb(0, 0, 51)',
|
||||
'node-4': 'rgb(0, 0, 77)',
|
||||
'node-5': 'rgb(0, 0, 102)',
|
||||
'node-6': 'rgb(0, 0, 128)',
|
||||
'node-7': 'rgb(0, 0, 153)',
|
||||
'node-8': 'rgb(0, 0, 179)',
|
||||
'node-9': 'rgb(0, 0, 204)',
|
||||
'node-10': 'rgb(0, 0, 230)',
|
||||
'node-11': 'rgb(0, 0, 255)',
|
||||
});
|
||||
});
|
||||
});
|
31
packages/g6/__tests__/unit/utils/style.spec.ts
Normal file
31
packages/g6/__tests__/unit/utils/style.spec.ts
Normal file
@ -0,0 +1,31 @@
|
||||
import { computeElementCallbackStyle } from '../../../src/utils/style';
|
||||
|
||||
describe('style', () => {
|
||||
it('computeElementCallbackStyle', () => {
|
||||
const datum = {
|
||||
id: 'node-1',
|
||||
data: {
|
||||
value: 100,
|
||||
},
|
||||
type: 'A',
|
||||
style: {
|
||||
fill: 'pink',
|
||||
lineWidth: 5,
|
||||
},
|
||||
};
|
||||
|
||||
const style = {
|
||||
stroke: 'blue',
|
||||
size: (data: any) => data.data.value / 2,
|
||||
fill: (data: any) => (data.data.type === 'B' ? 'green' : 'red'),
|
||||
};
|
||||
|
||||
const computedStyle = computeElementCallbackStyle(style, { datum, index: 0, elementData: [datum] });
|
||||
|
||||
expect(computedStyle).toEqual({
|
||||
stroke: 'blue',
|
||||
size: 50,
|
||||
fill: 'red',
|
||||
});
|
||||
});
|
||||
});
|
@ -1,7 +1,7 @@
|
||||
import type { STDAnimation } from './types';
|
||||
|
||||
export const DEFAULT_ANIMATION_OPTIONS: KeyframeAnimationOptions = {
|
||||
duration: 2000,
|
||||
duration: 1000,
|
||||
easing: 'cubic-bezier(0.250, 0.460, 0.450, 0.940)',
|
||||
iterations: 1,
|
||||
fill: 'both',
|
||||
|
@ -1,8 +1,9 @@
|
||||
import type { DisplayObject, IAnimation } from '@antv/g';
|
||||
import { upperFirst } from '@antv/util';
|
||||
import { createAnimationsProxy, executeAnimation, parseAnimation, preprocessKeyframes } from '../utils/animation';
|
||||
import { isString, upperFirst } from '@antv/util';
|
||||
import { getPlugin } from '../registry';
|
||||
import { createAnimationsProxy, executeAnimation, inferDefaultValue, preprocessKeyframes } from '../utils/animation';
|
||||
import { DEFAULT_ANIMATION_OPTIONS } from './constants';
|
||||
import type { Animation, AnimationContext, AnimationEffectTiming } from './types';
|
||||
import type { AnimationExecutor } from './types';
|
||||
|
||||
/**
|
||||
* <zh/> 动画 Spec 执行器
|
||||
@ -14,16 +15,12 @@ import type { Animation, AnimationContext, AnimationEffectTiming } from './types
|
||||
* @param context - <zh/> 动画执行上下文 | <en/> animation execution context
|
||||
* @returns <zh/> 动画实例 | <en/> animation instance
|
||||
*/
|
||||
export function executor(
|
||||
shape: DisplayObject,
|
||||
animation: Animation,
|
||||
effectTiming: AnimationEffectTiming,
|
||||
context: AnimationContext,
|
||||
): IAnimation | null {
|
||||
const animations = parseAnimation(animation);
|
||||
export const executor: AnimationExecutor = (shape, animation, effectTiming, context) => {
|
||||
if (!animation) return null;
|
||||
const animations = isString(animation) ? getPlugin('animation', animation) || [] : animation;
|
||||
if (animations.length === 0) return null;
|
||||
|
||||
const { originalStyle, states } = context;
|
||||
const { originalStyle, modifiedStyle, states } = context;
|
||||
|
||||
/**
|
||||
* <zh/> 获取图形关键帧样式
|
||||
@ -47,7 +44,7 @@ export function executor(
|
||||
} else {
|
||||
const target = shape;
|
||||
const fromStyle = originalStyle;
|
||||
const toStyle = { ...target.attributes };
|
||||
const toStyle = { ...target.attributes, ...modifiedStyle };
|
||||
return { target, fromStyle, toStyle };
|
||||
}
|
||||
};
|
||||
@ -64,8 +61,8 @@ export function executor(
|
||||
const keyframes: Keyframe[] = [{}, {}];
|
||||
|
||||
fields.forEach((attr) => {
|
||||
Object.assign(keyframes[0], { [attr]: fromStyle[attr] });
|
||||
Object.assign(keyframes[1], { [attr]: toStyle[attr] });
|
||||
Object.assign(keyframes[0], { [attr]: fromStyle[attr] ?? inferDefaultValue(attr) });
|
||||
Object.assign(keyframes[1], { [attr]: toStyle[attr] ?? inferDefaultValue(attr) });
|
||||
});
|
||||
|
||||
const result = executeAnimation(target, preprocessKeyframes(keyframes), {
|
||||
@ -90,4 +87,4 @@ export function executor(
|
||||
mainResult,
|
||||
results.filter((result) => result !== mainResult),
|
||||
);
|
||||
}
|
||||
};
|
||||
|
@ -1,4 +1,4 @@
|
||||
import type { IAnimationEffectTiming } from '@antv/g';
|
||||
import type { DisplayObject, IAnimation, IAnimationEffectTiming } from '@antv/g';
|
||||
import type { State } from '../types';
|
||||
|
||||
/**
|
||||
@ -7,7 +7,7 @@ import type { State } from '../types';
|
||||
*
|
||||
* <en/> When it is a string, it will be obtained from the registered animation
|
||||
*/
|
||||
export type Animation = string | STDAnimation;
|
||||
export type Animation = false | string | STDAnimation;
|
||||
|
||||
export type STDAnimation = ConfigurableAnimationOptions[];
|
||||
|
||||
@ -28,6 +28,16 @@ export interface AnimationContext {
|
||||
* <en/> Used to set the style of shape to the source style before the animation is executed. For example, the move-to animation needs to set the x and y of shape to the source style
|
||||
*/
|
||||
originalStyle: Record<string, unknown>;
|
||||
/**
|
||||
* <zh/> 额外的动画终态样式
|
||||
*
|
||||
* <en/> Additional animation final state style
|
||||
* @description
|
||||
* <zh/> 例如元素销毁前,需要将元素的终态透明度设置为 0
|
||||
*
|
||||
* <en/> For example, before the element is destroyed, the final state opacity of the element needs to be set to 0
|
||||
*/
|
||||
modifiedStyle?: Record<string, unknown>;
|
||||
/**
|
||||
* <zh/> 元素状态
|
||||
*
|
||||
@ -39,3 +49,10 @@ export interface AnimationContext {
|
||||
export type AnimationEffectTiming = Partial<
|
||||
Pick<IAnimationEffectTiming, 'duration' | 'delay' | 'easing' | 'iterations' | 'direction' | 'fill'>
|
||||
>;
|
||||
|
||||
export type AnimationExecutor = (
|
||||
shape: DisplayObject,
|
||||
animation: Animation | false,
|
||||
effectTiming: AnimationEffectTiming,
|
||||
context: AnimationContext,
|
||||
) => IAnimation | null;
|
||||
|
6
packages/g6/src/constants/events.ts
Normal file
6
packages/g6/src/constants/events.ts
Normal file
@ -0,0 +1,6 @@
|
||||
export const enum GraphEvent {
|
||||
/** 开始渲染 */
|
||||
BEFORE_RENDER = 'beforerender',
|
||||
/** 结束渲染 */
|
||||
AFTER_RENDER = 'afterrender',
|
||||
}
|
@ -1 +1,2 @@
|
||||
export * from './change';
|
||||
export * from './events';
|
||||
|
11
packages/g6/src/elements/constants.ts
Normal file
11
packages/g6/src/elements/constants.ts
Normal file
@ -0,0 +1,11 @@
|
||||
import { Line, Quadratic } from './edges';
|
||||
import { Circle } from './nodes';
|
||||
|
||||
export const BUILT_IN_NODES = {
|
||||
circle: Circle,
|
||||
};
|
||||
|
||||
export const BUILT_IN_EDGES = {
|
||||
line: Line,
|
||||
quadratic: Quadratic,
|
||||
};
|
@ -8,12 +8,11 @@ import type {
|
||||
} from '@antv/g';
|
||||
import { Path } from '@antv/g';
|
||||
import { deepMix, isFunction } from '@antv/util';
|
||||
import type { PrefixObject } from '../../types';
|
||||
import type { EdgeKey, EdgeLabelStyleProps } from '../../types/edge';
|
||||
import type { EdgeKey, EdgeLabelStyleProps, PrefixObject } from '../../types';
|
||||
import { getLabelPositionStyle } from '../../utils/edge';
|
||||
import { omitStyleProps, subStyleProps } from '../../utils/prefix';
|
||||
import type { SymbolFactor } from '../../utils/symbol';
|
||||
import * as Symbol from '../../utils/symbol';
|
||||
import { SymbolFactor } from '../../utils/symbol';
|
||||
import type { LabelStyleProps } from '../shapes';
|
||||
import { Label } from '../shapes';
|
||||
import type { BaseShapeStyleProps } from '../shapes/base-shape';
|
||||
|
@ -1,7 +1 @@
|
||||
/**
|
||||
* <zh/> 内置元素
|
||||
*
|
||||
* <en/> Built-in elements
|
||||
*/
|
||||
|
||||
export {};
|
||||
export { BUILT_IN_EDGES, BUILT_IN_NODES } from './constants';
|
||||
|
@ -1,7 +1,6 @@
|
||||
import type { DisplayObjectConfig, CircleStyleProps as GCircleStyleProps, Group } from '@antv/g';
|
||||
import { Circle as GCircle } from '@antv/g';
|
||||
import type { PrefixObject } from '../../types';
|
||||
import type { AnchorPosition, BadgePosition, LabelPosition } from '../../types/node';
|
||||
import type { AnchorPosition, BadgePosition, LabelPosition, PrefixObject } from '../../types';
|
||||
import { getAnchorPosition, getTextStyleByPosition, getXYByPosition } from '../../utils/element';
|
||||
import { omitStyleProps, subStyleProps } from '../../utils/prefix';
|
||||
import type { BadgeStyleProps, BaseShapeStyleProps, IconStyleProps, LabelStyleProps } from '../shapes';
|
||||
|
53
packages/g6/src/palettes/constants.ts
Normal file
53
packages/g6/src/palettes/constants.ts
Normal file
@ -0,0 +1,53 @@
|
||||
/**
|
||||
* <zh/> 内置色板
|
||||
*
|
||||
* <en/> Built-in palettes
|
||||
*/
|
||||
export const BUILT_IN_PALETTES = {
|
||||
spectral: [
|
||||
'rgb(158, 1, 66)',
|
||||
'rgb(213, 62, 79)',
|
||||
'rgb(244, 109, 67)',
|
||||
'rgb(253, 174, 97)',
|
||||
'rgb(254, 224, 139)',
|
||||
'rgb(255, 255, 191)',
|
||||
'rgb(230, 245, 152)',
|
||||
'rgb(171, 221, 164)',
|
||||
'rgb(102, 194, 165)',
|
||||
'rgb(50, 136, 189)',
|
||||
'rgb(94, 79, 162)',
|
||||
],
|
||||
oranges: [
|
||||
'rgb(255, 245, 235)',
|
||||
'rgb(254, 230, 206)',
|
||||
'rgb(253, 208, 162)',
|
||||
'rgb(253, 174, 107)',
|
||||
'rgb(253, 141, 60)',
|
||||
'rgb(241, 105, 19)',
|
||||
'rgb(217, 72, 1)',
|
||||
'rgb(166, 54, 3)',
|
||||
'rgb(127, 39, 4)',
|
||||
],
|
||||
greens: [
|
||||
'rgb(247, 252, 245)',
|
||||
'rgb(229, 245, 224)',
|
||||
'rgb(199, 233, 192)',
|
||||
'rgb(161, 217, 155)',
|
||||
'rgb(116, 196, 118)',
|
||||
'rgb(65, 171, 93)',
|
||||
'rgb(35, 139, 69)',
|
||||
'rgb(0, 109, 44)',
|
||||
'rgb(0, 68, 27)',
|
||||
],
|
||||
blues: [
|
||||
'rgb(247, 251, 255)',
|
||||
'rgb(222, 235, 247)',
|
||||
'rgb(198, 219, 239)',
|
||||
'rgb(158, 202, 225)',
|
||||
'rgb(107, 174, 214)',
|
||||
'rgb(66, 146, 198)',
|
||||
'rgb(33, 113, 181)',
|
||||
'rgb(8, 81, 156)',
|
||||
'rgb(8, 48, 107)',
|
||||
],
|
||||
};
|
@ -1,7 +1 @@
|
||||
/**
|
||||
* <zh/> 内置色板
|
||||
*
|
||||
* <en/> Built-in palettes
|
||||
*/
|
||||
|
||||
export {};
|
||||
export { BUILT_IN_PALETTES } from './constants';
|
||||
|
@ -1,6 +1,10 @@
|
||||
import type { BUILT_IN_PALETTES } from './constants';
|
||||
|
||||
export type Palette = string | BuiltInPalette | CategoricalPalette | ContinuousPalette;
|
||||
|
||||
export type BuiltInPalette = 'category10' | 'category20';
|
||||
export type STDPalette = CategoricalPalette | ContinuousPalette;
|
||||
|
||||
export type BuiltInPalette = keyof typeof BUILT_IN_PALETTES;
|
||||
|
||||
export type CategoricalPalette = string[];
|
||||
|
||||
|
@ -1,12 +1,16 @@
|
||||
import { BUILT_IN_ANIMATIONS } from '../animations';
|
||||
import { BUILT_IN_EDGES, BUILT_IN_NODES } from '../elements';
|
||||
import { BUILT_IN_PALETTES } from '../palettes';
|
||||
import { BUILT_IN_THEMES } from '../themes';
|
||||
|
||||
export const BUILT_IN_PLUGINS = {
|
||||
animation: BUILT_IN_ANIMATIONS,
|
||||
behavior: {},
|
||||
combo: {},
|
||||
edge: {},
|
||||
edge: BUILT_IN_EDGES,
|
||||
layout: {},
|
||||
node: {},
|
||||
theme: {},
|
||||
node: BUILT_IN_NODES,
|
||||
palette: BUILT_IN_PALETTES,
|
||||
theme: BUILT_IN_THEMES,
|
||||
widget: {},
|
||||
};
|
||||
|
@ -7,14 +7,15 @@ import type { PluginCategory, PluginRegistry } from './types';
|
||||
* <en/> Plugin registry
|
||||
*/
|
||||
const PLUGIN_REGISTRY: PluginRegistry = {
|
||||
node: {},
|
||||
edge: {},
|
||||
combo: {},
|
||||
theme: {},
|
||||
layout: {},
|
||||
behavior: {},
|
||||
widget: {},
|
||||
animation: {},
|
||||
behavior: {},
|
||||
combo: {},
|
||||
edge: {},
|
||||
layout: {},
|
||||
node: {},
|
||||
palette: {},
|
||||
theme: {},
|
||||
widget: {},
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -1,12 +1,13 @@
|
||||
import type { DisplayObject } from '@antv/g';
|
||||
import type { STDAnimation } from '../animations/types';
|
||||
import type { BaseNode, BaseNodeStyleProps } from '../elements/nodes';
|
||||
import type { STDPalette } from '../palettes/types';
|
||||
import type { Theme } from '../themes/types';
|
||||
|
||||
// TODO 待使用正式类型定义 / To be used formal type definition
|
||||
declare type Node = BaseNode<BaseNodeStyleProps<any>, DisplayObject>;
|
||||
declare type Edge = unknown;
|
||||
declare type Combo = unknown;
|
||||
declare type Theme = unknown;
|
||||
declare type Layout = unknown;
|
||||
declare type Behavior = unknown;
|
||||
declare type Widget = unknown;
|
||||
@ -21,6 +22,7 @@ export interface PluginRegistry {
|
||||
edge: Record<string, { new (...args: any[]): Edge }>;
|
||||
combo: Record<string, { new (...args: any[]): Combo }>;
|
||||
theme: Record<string, Theme>; // theme is a object options
|
||||
palette: Record<string, STDPalette>;
|
||||
layout: Record<string, { new (...args: any[]): Layout }>;
|
||||
behavior: Record<string, { new (...args: any[]): Behavior }>;
|
||||
widget: Record<string, { new (...args: any[]): Widget }>;
|
||||
|
@ -40,42 +40,57 @@ export class Canvas {
|
||||
};
|
||||
}
|
||||
|
||||
public renderers: Record<CanvasLayer, IRenderer>;
|
||||
public renderers!: Record<CanvasLayer, IRenderer>;
|
||||
|
||||
constructor(config: CanvasConfig) {
|
||||
this.config = config;
|
||||
const { renderer: getRenderer, ...restConfig } = config;
|
||||
const names: CanvasLayer[] = ['main', 'label', 'transient', 'transientLabel', 'background'];
|
||||
}
|
||||
|
||||
const renderers = names.map((name) => {
|
||||
const renderer = isFunction(getRenderer) ? getRenderer?.(name) : new CanvasRenderer();
|
||||
public async init() {
|
||||
const allCanvas = Object.entries(this.canvas);
|
||||
|
||||
renderer.registerPlugin(
|
||||
new DragNDropPlugin({
|
||||
isDocumentDraggable: true,
|
||||
isDocumentDroppable: true,
|
||||
dragstartDistanceThreshold: 10,
|
||||
dragstartTimeThreshold: 100,
|
||||
}),
|
||||
if (allCanvas.every(([, canvas]) => !canvas)) {
|
||||
const { renderer: getRenderer, ...restConfig } = this.config;
|
||||
const names: CanvasLayer[] = ['main', 'label', 'transient', 'transientLabel', 'background'];
|
||||
|
||||
const { renderers, canvas } = names.reduce(
|
||||
(acc, name) => {
|
||||
const renderer = isFunction(getRenderer) ? getRenderer?.(name) : new CanvasRenderer();
|
||||
|
||||
renderer.registerPlugin(
|
||||
new DragNDropPlugin({
|
||||
isDocumentDraggable: true,
|
||||
isDocumentDroppable: true,
|
||||
dragstartDistanceThreshold: 10,
|
||||
dragstartTimeThreshold: 100,
|
||||
}),
|
||||
);
|
||||
|
||||
if (name !== 'main') {
|
||||
renderer.unregisterPlugin(renderer.getPlugin('dom-interaction'));
|
||||
}
|
||||
|
||||
const canvas = new GCanvas({
|
||||
renderer,
|
||||
supportsMutipleCanvasesInOneContainer: true,
|
||||
...restConfig,
|
||||
});
|
||||
|
||||
acc.renderers[name] = renderer;
|
||||
acc.canvas[name] = canvas;
|
||||
this[name] = canvas;
|
||||
|
||||
return acc;
|
||||
},
|
||||
{ renderers: {}, canvas: {} } as {
|
||||
renderers: Record<CanvasLayer, IRenderer>;
|
||||
canvas: Record<CanvasLayer, GCanvas>;
|
||||
},
|
||||
);
|
||||
|
||||
if (name !== 'main') {
|
||||
renderer.unregisterPlugin(renderer.getPlugin('dom-interaction'));
|
||||
}
|
||||
this.renderers = renderers;
|
||||
|
||||
this[name] = new GCanvas({
|
||||
renderer,
|
||||
supportsMutipleCanvasesInOneContainer: true,
|
||||
...restConfig,
|
||||
});
|
||||
|
||||
return [name, renderer];
|
||||
});
|
||||
|
||||
this.renderers = Object.fromEntries(renderers);
|
||||
|
||||
this.init().then(() => {
|
||||
Object.entries(this.canvas).forEach(([name, canvas]) => {
|
||||
Object.entries(canvas).forEach(([name, canvas]) => {
|
||||
const domElement = canvas.getContextService().getDomElement() as unknown as HTMLElement;
|
||||
|
||||
domElement.style.position = 'absolute';
|
||||
@ -84,10 +99,8 @@ export class Canvas {
|
||||
|
||||
if (name !== 'main') domElement.style.pointerEvents = 'none';
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public init() {
|
||||
return Promise.all(Object.values(this.canvas).map((canvas) => canvas.ready));
|
||||
}
|
||||
|
||||
|
@ -1,7 +1,6 @@
|
||||
import EventEmitter from '@antv/event-emitter';
|
||||
import { Graph as GraphLib, ID } from '@antv/graphlib';
|
||||
import { isEqual } from '@antv/util';
|
||||
import { ChangeEvent, ChangeTypeEnum } from '../constants';
|
||||
import { ChangeTypeEnum } from '../constants';
|
||||
import type { ComboData, DataOptions, EdgeData, NodeData } from '../spec';
|
||||
import type {
|
||||
DataAdded,
|
||||
@ -26,7 +25,9 @@ import { dfs } from '../utils/traverse';
|
||||
|
||||
const COMBO_KEY = 'combo';
|
||||
|
||||
export class DataController extends EventEmitter {
|
||||
const TREE_KEY = 'tree';
|
||||
|
||||
export class DataController {
|
||||
public model: GraphlibData;
|
||||
|
||||
/**
|
||||
@ -59,7 +60,6 @@ export class DataController extends EventEmitter {
|
||||
private batchCount = 0;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this.model = new GraphLib();
|
||||
}
|
||||
|
||||
@ -78,14 +78,22 @@ export class DataController extends EventEmitter {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* <zh/> [警告] 此 API 仅供 Element Controller 调用
|
||||
*
|
||||
* <en/> [WARNING] This API is only for Element Controller
|
||||
* @returns <zh/> 数据变更 | <en/> data changes
|
||||
*/
|
||||
public getChanges(): DataChange[] {
|
||||
const changes = this.changes;
|
||||
this.changes = [];
|
||||
return changes;
|
||||
}
|
||||
|
||||
public batch(callback: () => void) {
|
||||
this.batchCount++;
|
||||
this.model.batch(callback);
|
||||
this.batchCount--;
|
||||
if (this.batchCount === 0) {
|
||||
this.emit(ChangeEvent.CHANGE, [...this.changes]);
|
||||
this.changes = [];
|
||||
}
|
||||
}
|
||||
|
||||
public isCombo(id: ID) {
|
||||
@ -128,6 +136,22 @@ export class DataController extends EventEmitter {
|
||||
}, [] as ComboData[]);
|
||||
}
|
||||
|
||||
/**
|
||||
* <zh/> 获取节点的子节点数据
|
||||
*
|
||||
* <en/> Get the child node data
|
||||
* @param id - <zh/> 节点 ID | <en/> node ID
|
||||
* @returns <zh/> 子节点数据 | <en/> child node data
|
||||
* @description
|
||||
* <zh/> 仅在树图中有效
|
||||
*
|
||||
* <en/> Only valid in tree graph
|
||||
*/
|
||||
public getChildrenData(id: ID): NodeData[] {
|
||||
if (!this.model.hasNode(id)) return [];
|
||||
return this.model.getChildren(id, TREE_KEY).map((node) => node.data);
|
||||
}
|
||||
|
||||
/**
|
||||
* <zh/> 根据 ID 获取元素的数据,不用关心元素的类型
|
||||
*
|
||||
@ -166,24 +190,6 @@ export class DataController extends EventEmitter {
|
||||
}, [] as NodeLikeData[]);
|
||||
}
|
||||
|
||||
/**
|
||||
* <zh/> 对节点和 combo 的数据进行分类
|
||||
*
|
||||
* <en/> Classify node and combo data
|
||||
* @param data - <zh/> 待分类的数据 | <en/> data to be classified
|
||||
* @returns <zh/> 节点和 combo 的数据 | <en/> node and combo data
|
||||
*/
|
||||
public classifyNodeLikeData(data: NodeLikeData[]) {
|
||||
return data.reduce(
|
||||
(acc, item) => {
|
||||
if (this.isCombo(idOf(item))) acc.combos.push(item);
|
||||
else acc.nodes.push(item);
|
||||
return acc;
|
||||
},
|
||||
{ nodes: [] as NodeData[], combos: [] as ComboData[] },
|
||||
);
|
||||
}
|
||||
|
||||
public hasNode(id: ID) {
|
||||
return this.model.hasNode(id) && !this.isCombo(id);
|
||||
}
|
||||
@ -467,6 +473,11 @@ export class DataController extends EventEmitter {
|
||||
if (!ids.length) return;
|
||||
this.batch(() => {
|
||||
ids.forEach((id) => {
|
||||
// 移除关联边、子节点
|
||||
// remove related edges and child nodes
|
||||
this.removeEdgeData(this.getRelatedEdgesData(id).map(idOf));
|
||||
// TODO 树图情况下移除子节点
|
||||
|
||||
this.pushChange({ value: this.getNodeData([id])[0], type: ChangeTypeEnum.NodeRemoved });
|
||||
this.removeNodeLikeHierarchy(id);
|
||||
});
|
||||
|
668
packages/g6/src/runtime/element.ts
Normal file
668
packages/g6/src/runtime/element.ts
Normal file
@ -0,0 +1,668 @@
|
||||
/* eslint-disable jsdoc/require-returns */
|
||||
/* eslint-disable jsdoc/require-param */
|
||||
import type { DisplayObject, IAnimation } from '@antv/g';
|
||||
import { Group } from '@antv/g';
|
||||
import type { ID } from '@antv/graphlib';
|
||||
import { groupBy } from '@antv/util';
|
||||
import { executor as animationExecutor } from '../animations';
|
||||
import { ChangeTypeEnum, GraphEvent } from '../constants';
|
||||
import { BaseNode } from '../elements/nodes';
|
||||
import type { BaseShape } from '../elements/shapes';
|
||||
import { getPlugin } from '../registry';
|
||||
import type { ComboData, DataOptions, EdgeData, G6Spec, NodeData } from '../spec';
|
||||
import type { AnimationStage } from '../spec/element/animation';
|
||||
import type { EdgeStyle } from '../spec/element/edge';
|
||||
import type { NodeLikeStyle } from '../spec/element/node';
|
||||
import type { DataChange, ElementData, ElementDatum, ElementType, State, StyleIterationContext } from '../types';
|
||||
import { createAnimationsProxy } from '../utils/animation';
|
||||
import { reduceDataChanges } from '../utils/change';
|
||||
import { idOf } from '../utils/id';
|
||||
import { assignColorByPalette, parsePalette } from '../utils/palette';
|
||||
import { computeElementCallbackStyle } from '../utils/style';
|
||||
import type { RuntimeContext } from './types';
|
||||
|
||||
type AnimationExecutor = (
|
||||
id: ID,
|
||||
shape: DisplayObject,
|
||||
originalStyle: Record<string, unknown>,
|
||||
modifiedStyle?: Record<string, unknown>,
|
||||
) => IAnimation | null;
|
||||
|
||||
type RenderContext = {
|
||||
taskId: TaskID;
|
||||
animate?: AnimationExecutor;
|
||||
};
|
||||
|
||||
type TaskID = number;
|
||||
|
||||
export class ElementController {
|
||||
private context: RuntimeContext;
|
||||
|
||||
private container!: {
|
||||
node: Group;
|
||||
edge: Group;
|
||||
combo: Group;
|
||||
};
|
||||
|
||||
private elementMap: Record<ID, DisplayObject> = {};
|
||||
|
||||
private shapeTypeMap: Record<ID, string> = {};
|
||||
|
||||
private taskIdCounter = 0;
|
||||
|
||||
/**
|
||||
* <zh/> 获取渲染任务 id
|
||||
*
|
||||
* <en/> Get render task id
|
||||
*/
|
||||
private getTaskId() {
|
||||
return this.taskIdCounter++;
|
||||
}
|
||||
|
||||
private postRenderTasks: Record<TaskID, (() => Promise<void>)[]> = {};
|
||||
|
||||
private getTasks(taskId: TaskID) {
|
||||
return this.postRenderTasks[taskId] || [];
|
||||
}
|
||||
|
||||
private animationMap: Record<TaskID, Record<ID, IAnimation>> = {};
|
||||
|
||||
constructor(context: RuntimeContext) {
|
||||
this.context = context;
|
||||
this.setElementStates(context.options.data);
|
||||
}
|
||||
|
||||
public async init() {
|
||||
const { canvas } = this.context;
|
||||
if (!this.container) {
|
||||
await canvas.init();
|
||||
this.container = {
|
||||
node: canvas.appendChild(new Group({ style: { zIndex: 2 } })),
|
||||
edge: canvas.appendChild(new Group({ style: { zIndex: 1 } })),
|
||||
combo: canvas.appendChild(new Group({ style: { zIndex: 0 } })),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
private emit(event: GraphEvent, payload?: any) {
|
||||
const { graph } = this.context;
|
||||
graph.emit(event, payload);
|
||||
}
|
||||
|
||||
private getElementData(elementType: ElementType, ids?: ID[]) {
|
||||
const { dataController } = this.context;
|
||||
|
||||
switch (elementType) {
|
||||
case 'node':
|
||||
return dataController.getNodeData(ids);
|
||||
case 'edge':
|
||||
return dataController.getEdgeData(ids);
|
||||
case 'combo':
|
||||
return dataController.getComboData(ids);
|
||||
default:
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
private forEachElementData(callback: (elementType: ElementType, elementData: ElementData) => void) {
|
||||
const elementTypes: ElementType[] = ['node', 'edge', 'combo'];
|
||||
elementTypes.forEach((elementType) => {
|
||||
const elementData = this.getElementData(elementType);
|
||||
callback(elementType, elementData);
|
||||
});
|
||||
}
|
||||
|
||||
private getTheme(elementType: ElementType) {
|
||||
const { theme } = this.context.options;
|
||||
if (!theme) return {};
|
||||
|
||||
const themeConfig = getPlugin('theme', theme);
|
||||
return themeConfig?.[elementType] || {};
|
||||
}
|
||||
|
||||
public getThemeStyle(elementType: ElementType) {
|
||||
return this.getTheme(elementType).style || {};
|
||||
}
|
||||
|
||||
public getThemeStateStyle(elementType: ElementType, states: State[]) {
|
||||
const { state = {} } = this.getTheme(elementType);
|
||||
return Object.assign({}, ...states.map((name) => state[name] || {}));
|
||||
}
|
||||
|
||||
private paletteStyle: Record<ID, string> = {};
|
||||
|
||||
private computePaletteStyle() {
|
||||
const { options } = this.context;
|
||||
|
||||
this.paletteStyle = {};
|
||||
|
||||
this.forEachElementData((elementType, elementData) => {
|
||||
const palette = parsePalette(this.getTheme(elementType)?.palette || options[elementType]?.palette);
|
||||
if (palette) {
|
||||
Object.assign(this.paletteStyle, assignColorByPalette(elementData, palette));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public getPaletteStyle(id: ID) {
|
||||
return {
|
||||
keyShapeColor: this.paletteStyle[id],
|
||||
};
|
||||
}
|
||||
|
||||
public getDataStyle(elementType: ElementType, id: ID): NodeLikeStyle | EdgeStyle {
|
||||
const datum = this.getElementData(elementType, [id])?.[0];
|
||||
if (!datum) return {};
|
||||
|
||||
// `data.style` 中一些样式例如 parentId, collapsed, type 并非直接给元素使用,因此需要过滤掉这些字段
|
||||
// Some styles in `data.style`, such as parentId, collapsed, type, are not directly used by the element, so these fields need to be filtered out
|
||||
const { parentId, collapsed, type, states, ...style } = datum.style || {};
|
||||
return style;
|
||||
}
|
||||
|
||||
private defaultStyle: Record<ID, Record<string, unknown>> = {};
|
||||
|
||||
/**
|
||||
* <zh/> 计算单个元素的默认样式
|
||||
*
|
||||
* <en/> compute default style of single element
|
||||
*/
|
||||
private computedElementDefaultStyle(elementType: ElementType, context: StyleIterationContext) {
|
||||
const { options } = this.context;
|
||||
const defaultStyle = options[elementType]?.style || {};
|
||||
this.defaultStyle[idOf(context.datum)] = computeElementCallbackStyle(defaultStyle, context);
|
||||
}
|
||||
|
||||
private computeElementsDefaultStyle() {
|
||||
this.forEachElementData((elementType, elementData) => {
|
||||
elementData.forEach((datum, index) => {
|
||||
this.computedElementDefaultStyle(elementType, { datum, index, elementData });
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
public getDefaultStyle(id: ID) {
|
||||
return this.defaultStyle[id] || {};
|
||||
}
|
||||
|
||||
private elementState: Record<ID, State[]> = {};
|
||||
|
||||
/**
|
||||
* <zh/> 从数据中初始化元素状态
|
||||
*
|
||||
* <en/> Initialize element state from data
|
||||
*/
|
||||
private setElementStates(data: G6Spec['data']) {
|
||||
const { nodes = [], edges = [], combos = [] } = data || {};
|
||||
[...nodes, ...edges, ...combos].forEach((elementData) => {
|
||||
const states = elementData.style?.states || [];
|
||||
const id = idOf(elementData);
|
||||
this.elementState[id] = states;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* <zh/> 获取指定元素的状态
|
||||
*
|
||||
* <en/> Get the state of the specified element
|
||||
* @param id - <zh/> 元素 id | <en/> element id
|
||||
* @returns <zh/> 元素状态数组 | <en/> element state array
|
||||
*/
|
||||
public getElementStates(id: ID): State[] {
|
||||
return this.elementState[id] || [];
|
||||
}
|
||||
|
||||
private stateStyle: Record<ID, Record<string, unknown>> = {};
|
||||
|
||||
/**
|
||||
* <zh/> 获取单个元素的单个状态的样式
|
||||
*
|
||||
* <en/> get single state style of single element
|
||||
*/
|
||||
private getElementStateStyle(elementType: ElementType, state: State, context: StyleIterationContext) {
|
||||
const { options } = this.context;
|
||||
const stateStyle = options[elementType]?.state?.[state] || {};
|
||||
return computeElementCallbackStyle(stateStyle, context);
|
||||
}
|
||||
|
||||
/**
|
||||
* <zh/> 计算单个元素的合并状态样式
|
||||
*
|
||||
* <en/> compute merged state style of single element
|
||||
*/
|
||||
private computeElementStatesStyle(elementType: ElementType, states: State[], context: StyleIterationContext) {
|
||||
this.stateStyle[idOf(context.datum)] = Object.assign(
|
||||
{},
|
||||
...states.map((state) => this.getElementStateStyle(elementType, state, context)),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* <zh/> 计算全部元素的状态样式
|
||||
*
|
||||
* <en/> compute state style of all elements
|
||||
* @param ids - <zh/> 计算指定元素的状态样式 | <en/> compute state style of specified elements
|
||||
*/
|
||||
private computeElementsStatesStyle(ids?: ID[]) {
|
||||
this.forEachElementData((elementType, elementData) => {
|
||||
elementData.forEach((datum, index) => {
|
||||
const id = idOf(datum);
|
||||
|
||||
if ((ids && ids.includes(id)) || ids === undefined) {
|
||||
const states = this.getElementStates(id);
|
||||
this.computeElementStatesStyle(elementType, states, { datum, index, elementData });
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
public getStateStyle(id: ID) {
|
||||
return this.stateStyle[id] || {};
|
||||
}
|
||||
|
||||
private computeStyle() {
|
||||
this.computePaletteStyle();
|
||||
this.computeElementsDefaultStyle();
|
||||
this.computeElementsStatesStyle();
|
||||
}
|
||||
|
||||
private getElement<T extends DisplayObject = BaseShape<any>>(id: ID): T | undefined {
|
||||
return this.elementMap[id] as T;
|
||||
}
|
||||
|
||||
private getAnimationExecutor(elementType: ElementType, stage: AnimationStage): AnimationExecutor {
|
||||
const { options } = this.context;
|
||||
|
||||
const getAnimation = () => {
|
||||
const userDefined = options?.[elementType]?.animation;
|
||||
if (userDefined === false) return false;
|
||||
const userDefinedStage = userDefined?.[stage];
|
||||
if (userDefinedStage) return userDefinedStage;
|
||||
|
||||
const themeDefined = this.getTheme(elementType)?.animation;
|
||||
if (themeDefined === false) return false;
|
||||
const themeDefinedStage = themeDefined?.[stage];
|
||||
|
||||
return themeDefinedStage ?? false;
|
||||
};
|
||||
|
||||
return (
|
||||
id: ID,
|
||||
shape: DisplayObject,
|
||||
originalStyle: Record<string, unknown>,
|
||||
modifiedStyle?: Record<string, unknown>,
|
||||
) => {
|
||||
return animationExecutor(
|
||||
shape,
|
||||
getAnimation(),
|
||||
{},
|
||||
{
|
||||
originalStyle,
|
||||
modifiedStyle,
|
||||
states: this.getElementStates(id),
|
||||
},
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* <zh/> 获取状态为指定状态的元素
|
||||
*
|
||||
* <en/> Get elements with the specified state
|
||||
* @param state - <zh/> 状态或状态数组 | <en/> state or state array
|
||||
* @returns <zh/> 元素 id 与元素实例的键值对 | <en/> key-value pairs of element id and element instance
|
||||
*/
|
||||
public getElementsByState(state: State | State[]): Record<string, DisplayObject> {
|
||||
return Object.fromEntries(
|
||||
Object.entries(this.elementState)
|
||||
.filter(([, states]) => {
|
||||
return (Array.isArray(state) ? state : [state]).every((s) => states.includes(s));
|
||||
})
|
||||
.map(([id]) => [id, this.elementMap[id]]),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* <zh/> 获取边端点连接上下文
|
||||
*
|
||||
* <en/> Get edge end context
|
||||
* @param id - <zh/> 边 id | <en/> edge id
|
||||
* @returns <zh/> 边端点连接上下文 | <en/> edge end context
|
||||
* @description
|
||||
* <zh/> 只提供了最基本的节点示例和连接点位置信息,更多的上下文信息需要在边元素中计算
|
||||
*
|
||||
* <en/> Only the most basic node instances and connection point position information are provided, and more context information needs to be calculated in the edge element
|
||||
*/
|
||||
private getEdgeEndsContext(id: ID) {
|
||||
const { dataController } = this.context;
|
||||
|
||||
const data = dataController.getEdgeData([id])?.[0];
|
||||
if (!data) return {};
|
||||
|
||||
const { source, target } = data;
|
||||
const sourceNode = this.getElement<BaseNode<any, any>>(source);
|
||||
const targetNode = this.getElement<BaseNode<any, any>>(target);
|
||||
|
||||
const sourcePoint = sourceNode?.getBounds().center || [0, 0, 0];
|
||||
const targetPoint = targetNode?.getBounds().center || [0, 0, 0];
|
||||
|
||||
return {
|
||||
sourcePoint,
|
||||
targetPoint,
|
||||
sourceNode,
|
||||
targetNode,
|
||||
};
|
||||
}
|
||||
|
||||
private getComboChildren(id: ID) {
|
||||
const { dataController } = this.context;
|
||||
return Object.fromEntries(
|
||||
dataController.getComboChildrenData(id).map((datum) => [idOf(datum), this.getElement(idOf(datum))]),
|
||||
);
|
||||
}
|
||||
|
||||
public getElementComputedStyle(elementType: ElementType, id: ID) {
|
||||
// 优先级(从低到高) Priority (from low to high):
|
||||
const themeStyle = this.getThemeStyle(elementType);
|
||||
const paletteStyle = this.getPaletteStyle(id);
|
||||
const dataStyle = this.getDataStyle(elementType, id);
|
||||
const defaultStyle = this.getDefaultStyle(id);
|
||||
const themeStateStyle = this.getThemeStateStyle(elementType, this.getElementStates(id));
|
||||
const stateStyle = this.getStateStyle(id);
|
||||
|
||||
const style = Object.assign({}, themeStyle, paletteStyle, dataStyle, defaultStyle, themeStateStyle, stateStyle);
|
||||
|
||||
if (elementType === 'edge') {
|
||||
Object.assign(style, this.getEdgeEndsContext(id));
|
||||
} else if (elementType === 'combo') {
|
||||
Object.assign(style, {
|
||||
children: this.getComboChildren(id),
|
||||
});
|
||||
}
|
||||
|
||||
return style;
|
||||
}
|
||||
|
||||
// ---------- Render API ----------
|
||||
|
||||
/**
|
||||
* <zh/> 开始绘制流程
|
||||
*
|
||||
* <en/> start render process
|
||||
*/
|
||||
public async render(context: RuntimeContext): Promise<IAnimation | null> {
|
||||
this.context = context;
|
||||
const { dataController } = context;
|
||||
|
||||
const tasks = reduceDataChanges(dataController.getChanges());
|
||||
if (tasks.length === 0) return null;
|
||||
|
||||
this.emit(GraphEvent.BEFORE_RENDER);
|
||||
await this.init();
|
||||
|
||||
const {
|
||||
NodeAdded = [],
|
||||
NodeUpdated = [],
|
||||
NodeRemoved = [],
|
||||
EdgeAdded = [],
|
||||
EdgeUpdated = [],
|
||||
EdgeRemoved = [],
|
||||
ComboAdded = [],
|
||||
ComboUpdated = [],
|
||||
ComboRemoved = [],
|
||||
} = groupBy(tasks, (change) => change.type) as unknown as Record<`${ChangeTypeEnum}`, DataChange[]>;
|
||||
|
||||
const dataOf = <T extends DataChange['value']>(data: DataChange[]) => data.map((datum) => datum.value) as T[];
|
||||
|
||||
// 计算要新增的元素 / compute elements to add
|
||||
const nodesToAdd = dataOf<NodeData>(NodeAdded);
|
||||
const edgesToAdd = dataOf<EdgeData>(EdgeAdded);
|
||||
const combosToAdd = dataOf<ComboData>(ComboAdded);
|
||||
|
||||
// 计算要更新的元素 / compute elements to update
|
||||
const nodesToUpdate = dataOf<NodeData>(NodeUpdated);
|
||||
const edgesToUpdate = dataOf<EdgeData>(EdgeUpdated);
|
||||
const combosToUpdate = dataOf<ComboData>(ComboUpdated);
|
||||
|
||||
this.setElementStates({ nodes: nodesToUpdate, edges: edgesToUpdate, combos: combosToUpdate });
|
||||
|
||||
// 计算要删除的元素 / compute elements to remove
|
||||
const nodesToRemove = dataOf<NodeData>(NodeRemoved);
|
||||
const edgesToRemove = dataOf<EdgeData>(EdgeRemoved);
|
||||
const combosToRemove = dataOf<ComboData>(ComboRemoved);
|
||||
|
||||
// 如果更新了节点,需要更新连接的边和所处的 combo
|
||||
// If the node is updated, the connected edge and the combo it is in need to be updated
|
||||
// TODO 待优化,仅考虑影响边更新的属性,如 x, y, size 等
|
||||
nodesToUpdate
|
||||
.map((node) => dataController.getRelatedEdgesData(idOf(node)))
|
||||
.flat()
|
||||
.forEach((edge) => {
|
||||
if (!edgesToUpdate.find((item) => idOf(item) === idOf(edge))) edgesToUpdate.push(edge);
|
||||
});
|
||||
|
||||
dataController
|
||||
.getComboData(
|
||||
[...nodesToUpdate, ...nodesToRemove, ...combosToUpdate, ...combosToRemove].reduce((acc, curr) => {
|
||||
const parentId = curr?.style?.parentId;
|
||||
if (parentId) acc.push(parentId);
|
||||
return acc;
|
||||
}, [] as ID[]),
|
||||
)
|
||||
.forEach((combo) => {
|
||||
if (!combosToUpdate.find((item) => item.id === combo.id)) combosToUpdate.push(combo);
|
||||
});
|
||||
|
||||
// 重新计算样式 / Recalculate style
|
||||
this.computeStyle();
|
||||
|
||||
// 创建渲染任务 / Create render task
|
||||
const taskId = this.getTaskId();
|
||||
this.postRenderTasks[taskId] = [];
|
||||
this.animationMap[taskId] = {};
|
||||
|
||||
this.destroyElements({ nodes: nodesToRemove, edges: edgesToRemove, combos: combosToRemove }, { taskId });
|
||||
this.createElements({ nodes: nodesToAdd, edges: edgesToAdd, combos: combosToAdd }, { taskId });
|
||||
this.updateElements({ nodes: nodesToUpdate, edges: edgesToUpdate, combos: combosToUpdate }, { taskId });
|
||||
|
||||
return this.postRender(taskId);
|
||||
}
|
||||
|
||||
private postRender(taskId: TaskID) {
|
||||
const tasks = this.getTasks(taskId);
|
||||
// 执行后续任务 / Execute subsequent tasks
|
||||
Promise.all(tasks.map((task) => task())).then(() => {
|
||||
delete this.postRenderTasks[taskId];
|
||||
delete this.animationMap[taskId];
|
||||
});
|
||||
|
||||
const getRenderResult = (taskId: TaskID): IAnimation | null => {
|
||||
const [source, ...target] = Object.values(this.animationMap[taskId]);
|
||||
if (source) return createAnimationsProxy(source, target);
|
||||
return null;
|
||||
};
|
||||
|
||||
const result = getRenderResult(taskId);
|
||||
|
||||
// 触发成事件 / Trigger event
|
||||
if (result) result.onfinish = () => this.emit(GraphEvent.AFTER_RENDER);
|
||||
else this.emit(GraphEvent.AFTER_RENDER);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private getShapeType(elementType: ElementType, datum: ElementDatum) {
|
||||
const type = datum?.style?.type;
|
||||
if (type) return type;
|
||||
// 推断默认类型 / Infer default type
|
||||
|
||||
return {
|
||||
node: 'circle',
|
||||
edge: 'line',
|
||||
combo: 'circle',
|
||||
}[elementType];
|
||||
}
|
||||
|
||||
private createElement(elementType: ElementType, datum: ElementDatum, context: RenderContext) {
|
||||
const { animate, taskId } = context;
|
||||
|
||||
const id = idOf(datum);
|
||||
const currentShape = this.getElement(id);
|
||||
if (currentShape) return;
|
||||
|
||||
// get shape constructor
|
||||
const shapeType = this.getShapeType(elementType, datum);
|
||||
const Ctor = getPlugin(elementType, shapeType);
|
||||
if (!Ctor) return;
|
||||
|
||||
const shape = this.container[elementType].appendChild(
|
||||
// @ts-expect-error TODO fix type
|
||||
new Ctor({
|
||||
style: {
|
||||
context: this.context,
|
||||
...this.getElementComputedStyle(elementType, id),
|
||||
},
|
||||
}),
|
||||
) as DisplayObject;
|
||||
|
||||
this.shapeTypeMap[id] = shapeType;
|
||||
|
||||
const tasks = this.getTasks(taskId);
|
||||
tasks.push(async () => {
|
||||
const result = animate?.(id, shape, { ...shape.attributes, opacity: 0 });
|
||||
if (result) {
|
||||
this.animationMap[taskId][id] = result;
|
||||
await result.finished;
|
||||
}
|
||||
});
|
||||
|
||||
this.elementMap[id] = shape;
|
||||
}
|
||||
|
||||
private createElements(data: DataOptions, context: RenderContext) {
|
||||
// 新增相应的元素数据
|
||||
// 重新计算色板样式
|
||||
|
||||
const { nodes = [], edges = [], combos = [] } = data;
|
||||
|
||||
const iteration: [ElementType, ElementData][] = [
|
||||
['node', nodes],
|
||||
['edge', edges],
|
||||
['combo', combos],
|
||||
];
|
||||
|
||||
iteration.forEach(([elementType, elementData]) => {
|
||||
if (elementData.length === 0) return;
|
||||
const animate = this.getAnimationExecutor(elementType, 'enter');
|
||||
elementData.forEach((datum) => this.createElement(elementType, datum, { ...context, animate }));
|
||||
});
|
||||
}
|
||||
|
||||
private async updateElement(elementType: ElementType, datum: ElementDatum, context: RenderContext) {
|
||||
const { animate, taskId } = context;
|
||||
this.handleTypeChange(elementType, datum, context);
|
||||
|
||||
const id = idOf(datum);
|
||||
const shape = this.getElement(id);
|
||||
if (!shape) return;
|
||||
const style = this.getElementComputedStyle(elementType, id);
|
||||
const originalStyle = { ...shape.attributes };
|
||||
|
||||
if ('update' in shape) shape.update(style);
|
||||
else (shape as DisplayObject).attr(style);
|
||||
|
||||
const tasks = this.getTasks(taskId);
|
||||
tasks.push(async () => {
|
||||
const result = animate?.(id, shape, originalStyle);
|
||||
if (result) {
|
||||
this.animationMap[taskId][id] = result;
|
||||
await result?.finished;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private updateElements(data: DataOptions, context: RenderContext) {
|
||||
const { nodes = [], edges = [], combos = [] } = data;
|
||||
|
||||
const iteration: [ElementType, ElementData][] = [
|
||||
['node', nodes],
|
||||
['edge', edges],
|
||||
['combo', combos],
|
||||
];
|
||||
|
||||
iteration.forEach(([elementType, elementData]) => {
|
||||
if (elementData.length === 0) return;
|
||||
const animate = this.getAnimationExecutor(elementType, 'update');
|
||||
elementData.forEach((datum) => this.updateElement(elementType, datum, { ...context, animate }));
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* <zh/> 处理元素类型变更
|
||||
*
|
||||
* <en/> handle element type change
|
||||
* @description
|
||||
* <zh/> 销毁原有的图形实例,重新创建图形实例
|
||||
*
|
||||
* <en/> Destroy the original shape instance and recreate the shape instance
|
||||
*/
|
||||
private handleTypeChange(elementType: ElementType, datum: ElementDatum, context: RenderContext) {
|
||||
const id = idOf(datum);
|
||||
const originalShapeType = this.shapeTypeMap[id];
|
||||
const modifiedShapeType = this.getShapeType(elementType, datum);
|
||||
if (originalShapeType && originalShapeType !== modifiedShapeType) {
|
||||
this.destroyElement(datum, context);
|
||||
this.createElement(elementType, datum, context);
|
||||
}
|
||||
}
|
||||
|
||||
protected destroyElement(datum: ElementDatum, context: RenderContext) {
|
||||
const { animate, taskId } = context;
|
||||
const id = idOf(datum);
|
||||
const element = this.elementMap[id];
|
||||
if (!element) return;
|
||||
|
||||
const tasks = this.getTasks(taskId);
|
||||
|
||||
tasks.push(async () => {
|
||||
const result = animate?.(id, element, { ...element.attributes }, { opacity: 0 });
|
||||
|
||||
if (result) {
|
||||
this.animationMap[taskId][id] = result;
|
||||
result.onfinish = () => element.destroy();
|
||||
} else {
|
||||
element.destroy();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
protected destroyElements(data: DataOptions, context: RenderContext) {
|
||||
const { nodes = [], edges = [], combos = [] } = data;
|
||||
|
||||
const iteration: [ElementType, ElementData][] = [
|
||||
['combo', combos],
|
||||
['edge', edges],
|
||||
['node', nodes],
|
||||
];
|
||||
|
||||
// 移除相应的元素数据
|
||||
// 重新计算色板样式,如果是分组色板,则不需要重新计算
|
||||
iteration.forEach(([elementType, elementData]) => {
|
||||
if (elementData.length === 0) return;
|
||||
const animate = this.getAnimationExecutor(elementType, 'exit');
|
||||
elementData.forEach((datum) => this.destroyElement(datum, { ...context, animate }));
|
||||
this.clearElement(elementData.map(idOf));
|
||||
});
|
||||
}
|
||||
|
||||
private clearElement(ids: ID[]) {
|
||||
ids.forEach((id) => {
|
||||
delete this.paletteStyle[id];
|
||||
delete this.defaultStyle[id];
|
||||
delete this.stateStyle[id];
|
||||
delete this.elementState[id];
|
||||
delete this.elementMap[id];
|
||||
delete this.shapeTypeMap[id];
|
||||
});
|
||||
}
|
||||
}
|
11
packages/g6/src/runtime/types.ts
Normal file
11
packages/g6/src/runtime/types.ts
Normal file
@ -0,0 +1,11 @@
|
||||
import type { G6Spec } from '../spec';
|
||||
import type { Canvas } from './canvas';
|
||||
import type { DataController } from './data';
|
||||
import type { Graph } from './graph';
|
||||
|
||||
export interface RuntimeContext {
|
||||
canvas: Canvas;
|
||||
graph: Graph;
|
||||
options: G6Spec;
|
||||
dataController: DataController;
|
||||
}
|
@ -48,7 +48,20 @@ interface NodeLikeDataStyle extends BaseElementStyle, NodeLikeStyle {
|
||||
parentId?: ID;
|
||||
}
|
||||
|
||||
interface EdgeDataStyle extends BaseElementStyle, EdgeStyle {}
|
||||
interface EdgeDataStyle extends BaseElementStyle, EdgeStyle {
|
||||
/**
|
||||
* <zh/> 起点连接桩 id
|
||||
*
|
||||
* <en/> source port id
|
||||
*/
|
||||
sourceAnchor?: string;
|
||||
/**
|
||||
* <zh/> 终点连接桩 id
|
||||
*
|
||||
* <en/> target port id
|
||||
*/
|
||||
targetAnchor?: string;
|
||||
}
|
||||
|
||||
interface BaseElementStyle {
|
||||
/**
|
||||
|
@ -1,10 +1,12 @@
|
||||
import type { Animation } from '../../animations/types';
|
||||
|
||||
export type AnimationOptions = {
|
||||
[STAGE in AnimationStage]?: Animation;
|
||||
} & {
|
||||
[key: string]: Animation;
|
||||
};
|
||||
export type AnimationOptions =
|
||||
| false
|
||||
| ({
|
||||
[STAGE in AnimationStage]?: Animation;
|
||||
} & {
|
||||
[key: string]: Animation;
|
||||
});
|
||||
|
||||
/**
|
||||
* <zh/> 动画阶段
|
||||
|
@ -39,7 +39,7 @@ export type EdgeOptions = {
|
||||
export type StaticEdgeOptions = {
|
||||
style?: EdgeStyle;
|
||||
state?: Record<string, EdgeStyle>;
|
||||
animation?: PaletteOptions;
|
||||
animation?: AnimationOptions;
|
||||
palette?: PaletteOptions;
|
||||
};
|
||||
|
||||
|
36
packages/g6/src/themes/dark.ts
Normal file
36
packages/g6/src/themes/dark.ts
Normal file
@ -0,0 +1,36 @@
|
||||
import { Theme } from './types';
|
||||
|
||||
export const DARK_THEME: Theme = {
|
||||
node: {
|
||||
style: {
|
||||
fill: '#444',
|
||||
stroke: '#f8f8f8',
|
||||
},
|
||||
state: {},
|
||||
animation: {
|
||||
enter: 'fade',
|
||||
update: [{ fields: ['cx', 'cy'] }],
|
||||
exit: 'fade',
|
||||
},
|
||||
},
|
||||
edge: {
|
||||
style: {
|
||||
lineWidth: 1,
|
||||
stroke: '#8b9baf',
|
||||
},
|
||||
state: {},
|
||||
animation: {
|
||||
enter: 'fade',
|
||||
update: [{ fields: ['sourcePoint', 'targetPoint'] }],
|
||||
exit: 'fade',
|
||||
},
|
||||
},
|
||||
combo: {
|
||||
style: {
|
||||
fill: 'rgba(170, 174, 178, 0.2)',
|
||||
stroke: '#aaaeb2',
|
||||
lineWidth: 1,
|
||||
},
|
||||
state: {},
|
||||
},
|
||||
};
|
@ -1,7 +1,12 @@
|
||||
import { DARK_THEME } from './dark';
|
||||
import { LIGHT_THEME } from './light';
|
||||
|
||||
/**
|
||||
* <zh/> 内置主题
|
||||
*
|
||||
* <en/> Built-in themes
|
||||
*/
|
||||
|
||||
export {};
|
||||
export const BUILT_IN_THEMES = {
|
||||
light: LIGHT_THEME,
|
||||
dark: DARK_THEME,
|
||||
};
|
||||
|
36
packages/g6/src/themes/light.ts
Normal file
36
packages/g6/src/themes/light.ts
Normal file
@ -0,0 +1,36 @@
|
||||
import { Theme } from './types';
|
||||
|
||||
export const LIGHT_THEME: Theme = {
|
||||
node: {
|
||||
style: {
|
||||
fill: '#f8f8f8',
|
||||
stroke: '#8b9baf',
|
||||
},
|
||||
state: {},
|
||||
animation: {
|
||||
enter: 'fade',
|
||||
update: [{ fields: ['cx', 'cy'] }],
|
||||
exit: 'fade',
|
||||
},
|
||||
},
|
||||
edge: {
|
||||
style: {
|
||||
lineWidth: 1,
|
||||
stroke: '#8b9baf',
|
||||
},
|
||||
state: {},
|
||||
animation: {
|
||||
enter: 'fade',
|
||||
update: [{ fields: ['sourcePoint', 'targetPoint'] }],
|
||||
exit: 'fade',
|
||||
},
|
||||
},
|
||||
combo: {
|
||||
style: {
|
||||
fill: 'rgba(170, 174, 178, 0.2)',
|
||||
stroke: '#aaaeb2',
|
||||
lineWidth: 1,
|
||||
},
|
||||
state: {},
|
||||
},
|
||||
};
|
@ -1 +1,11 @@
|
||||
import type { StaticComboOptions } from '../spec/element/combo';
|
||||
import type { StaticEdgeOptions } from '../spec/element/edge';
|
||||
import type { StaticNodeOptions } from '../spec/element/node';
|
||||
|
||||
export type BuiltInTheme = 'light' | 'dark';
|
||||
|
||||
export type Theme = {
|
||||
node?: StaticNodeOptions;
|
||||
edge?: StaticEdgeOptions;
|
||||
combo?: StaticComboOptions;
|
||||
};
|
||||
|
@ -11,7 +11,9 @@ export type DataID = {
|
||||
|
||||
export type NodeLikeData = NodeData | ComboData;
|
||||
|
||||
export type ElementData = NodeData | EdgeData | ComboData;
|
||||
export type ElementDatum = NodeData | EdgeData | ComboData;
|
||||
|
||||
export type ElementData = NodeData[] | EdgeData[] | ComboData[];
|
||||
|
||||
/**
|
||||
* <zh/> 节点、边更新可选数据
|
||||
|
@ -1 +1,5 @@
|
||||
import type { ComboOptions, EdgeOptions, NodeOptions } from '../spec';
|
||||
|
||||
export type ElementType = 'node' | 'edge' | 'combo';
|
||||
|
||||
export type ElementOptions = NodeOptions | EdgeOptions | ComboOptions;
|
||||
|
28
packages/g6/src/types/graphlib.ts
Normal file
28
packages/g6/src/types/graphlib.ts
Normal file
@ -0,0 +1,28 @@
|
||||
import type {
|
||||
EdgeAdded,
|
||||
EdgeDataUpdated,
|
||||
EdgeRemoved,
|
||||
EdgeUpdated,
|
||||
NodeAdded,
|
||||
NodeDataUpdated,
|
||||
NodeRemoved,
|
||||
TreeStructureAttached,
|
||||
TreeStructureChanged,
|
||||
TreeStructureDetached,
|
||||
} from '@antv/graphlib';
|
||||
import type { EdgeData } from '../spec';
|
||||
import type { NodeLikeData } from './data';
|
||||
|
||||
export type GraphLibGroupedChanges = {
|
||||
NodeRemoved: NodeRemoved<NodeLikeData>[];
|
||||
EdgeRemoved: EdgeRemoved<EdgeData>[];
|
||||
NodeAdded: NodeAdded<NodeLikeData>[];
|
||||
EdgeAdded: EdgeAdded<EdgeData>[];
|
||||
NodeDataUpdated: NodeDataUpdated<NodeLikeData>[];
|
||||
EdgeUpdated: EdgeUpdated<EdgeData>[];
|
||||
EdgeDataUpdated: EdgeDataUpdated<EdgeData>[];
|
||||
TreeStructureChanged: TreeStructureChanged[];
|
||||
ComboStructureChanged: TreeStructureChanged[];
|
||||
TreeStructureAttached: TreeStructureAttached[];
|
||||
TreeStructureDetached: TreeStructureDetached[];
|
||||
};
|
@ -2,8 +2,13 @@ export type * from './callable';
|
||||
export type * from './canvas';
|
||||
export type * from './change';
|
||||
export type * from './data';
|
||||
export type * from './edge';
|
||||
export type * from './element';
|
||||
export type * from './graphlib';
|
||||
export type * from './node';
|
||||
export type * from './padding';
|
||||
export type * from './point';
|
||||
export type * from './prefix';
|
||||
export type * from './state';
|
||||
export type * from './style';
|
||||
export type * from './vector';
|
||||
|
12
packages/g6/src/types/style.ts
Normal file
12
packages/g6/src/types/style.ts
Normal file
@ -0,0 +1,12 @@
|
||||
import type { ElementData, ElementDatum } from './data';
|
||||
|
||||
/**
|
||||
* <zh/> 样式计算迭代上下文
|
||||
*
|
||||
* <en/> Style iteration context
|
||||
*/
|
||||
export type StyleIterationContext = {
|
||||
datum: ElementDatum;
|
||||
index: number;
|
||||
elementData: ElementData;
|
||||
};
|
@ -1,7 +1,5 @@
|
||||
import type { DisplayObject, IAnimation } from '@antv/g';
|
||||
import { isNil, isString } from '@antv/util';
|
||||
import type { Animation, STDAnimation } from '../animations/types';
|
||||
import { getPlugin } from '../registry';
|
||||
import { isNil } from '@antv/util';
|
||||
import { getDescendantShapes } from './shape';
|
||||
|
||||
/**
|
||||
@ -24,26 +22,18 @@ export function createAnimationsProxy(sourceAnimation: IAnimation, targetAnimati
|
||||
return Reflect.get(target, propKey);
|
||||
},
|
||||
set(target, propKey: keyof IAnimation, value) {
|
||||
targetAnimations.forEach((animation) => ((animation[propKey] as any) = value));
|
||||
// onframe 和 onfinish 特殊处理,不用同步到所有动画实例上
|
||||
// onframe and onfinish are specially processed and do not need to be synchronized to all animation instances
|
||||
if (!['onframe', 'onfinish'].includes(propKey)) {
|
||||
targetAnimations.forEach((animation) => {
|
||||
(animation[propKey] as any) = value;
|
||||
});
|
||||
}
|
||||
return Reflect.set(target, propKey, value);
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* <zh/> 解析动画配置项
|
||||
*
|
||||
* <en/> parse animation options
|
||||
* @param animation - <zh/> 动画配置项 | <en/> animation options
|
||||
* @returns <zh/> 动画配置项 | <en/> animation options
|
||||
*/
|
||||
export function parseAnimation(animation: Animation): STDAnimation {
|
||||
if (isString(animation)) {
|
||||
return getPlugin('animation', animation) || [];
|
||||
}
|
||||
return animation;
|
||||
}
|
||||
|
||||
/**
|
||||
* <zh/> 预处理关键帧,过滤掉无用动画的属性
|
||||
*
|
||||
@ -132,3 +122,23 @@ export function executeAnimation<T extends DisplayObject>(
|
||||
const descendantAnimations = descendants.map((descendant) => descendant.animate(inheritAttrsKeyframes, options)!);
|
||||
return createAnimationsProxy(keyShapeAnimation!, descendantAnimations);
|
||||
}
|
||||
|
||||
/**
|
||||
* <zh/> 获取属性的默认值
|
||||
*
|
||||
* <en/> Get default value of attribute
|
||||
* @param name - <zh/> 属性名 | <en/> Attribute name
|
||||
* @returns <zh/> 属性默认值 | <en/> Attribute default value
|
||||
* @description
|
||||
* <zh/> 执行动画过程中,一些属性没有显式指定属性值,但实际上在 G 中存在属性值,因此通过该方法获取其实际默认值
|
||||
*
|
||||
* <en/> During the animation, some attributes do not explicitly specify the attribute value, but in fact there is an attribute value in G, so use this method to get the actual default value
|
||||
*/
|
||||
export function inferDefaultValue(name: string) {
|
||||
switch (name) {
|
||||
case 'opacity':
|
||||
return 1;
|
||||
default:
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
import type { Edge, Node } from '@antv/graphlib';
|
||||
import type { ComboData, EdgeData, NodeData } from '../spec';
|
||||
import { NodeLikeData } from '../types/data';
|
||||
import { idOf } from './id';
|
||||
import { isEdgeData } from './is';
|
||||
|
||||
export function toGraphlibData(datums: EdgeData): Edge<EdgeData>;
|
||||
@ -18,9 +19,10 @@ export function toGraphlibData(data: NodeData | EdgeData | ComboData): Node<Node
|
||||
return {
|
||||
...rest,
|
||||
data,
|
||||
id: idOf(data),
|
||||
} as Edge<EdgeData>;
|
||||
}
|
||||
return { id: data.id, data } as Node<NodeLikeData>;
|
||||
return { id: idOf(data), data } as Node<NodeLikeData>;
|
||||
}
|
||||
|
||||
export function toG6Data<T extends EdgeData>(data: Edge<T>): T;
|
||||
|
@ -1,5 +1,5 @@
|
||||
import type { EdgeData } from '../spec';
|
||||
import type { ElementData } from '../types/data';
|
||||
import type { ElementDatum } from '../types';
|
||||
|
||||
/**
|
||||
* <zh/> 判断是否为边数据
|
||||
@ -8,7 +8,7 @@ import type { ElementData } from '../types/data';
|
||||
* @param data - <zh/> 元素数据 | <en/> element data
|
||||
* @returns - <zh/> 是否为边数据 | <en/> whether the data is edge data
|
||||
*/
|
||||
export function isEdgeData(data: Partial<ElementData>): data is EdgeData {
|
||||
export function isEdgeData(data: Partial<ElementDatum>): data is EdgeData {
|
||||
if ('source' in data && 'target' in data) return true;
|
||||
return false;
|
||||
}
|
||||
|
101
packages/g6/src/utils/palette.ts
Normal file
101
packages/g6/src/utils/palette.ts
Normal file
@ -0,0 +1,101 @@
|
||||
import type { ID } from '@antv/graphlib';
|
||||
import { groupBy, isFunction, isNumber, isString } from '@antv/util';
|
||||
import { getPlugin } from '../registry';
|
||||
import type { PaletteOptions, STDPaletteOptions } from '../spec/element/palette';
|
||||
import type { ElementData, ElementDatum } from '../types/data';
|
||||
import { idOf } from './id';
|
||||
|
||||
/**
|
||||
* <zh/> 解析色板配置
|
||||
*
|
||||
* <en/> Parse palette options
|
||||
* @param palette - <zh/> 色板配置 | <en/> PaletteOptions options
|
||||
* @returns <zh/> 标准色板配置 | <en/> Standard palette options
|
||||
*/
|
||||
export function parsePalette(palette?: PaletteOptions): STDPaletteOptions | undefined {
|
||||
if (!palette) return undefined;
|
||||
|
||||
if (
|
||||
// 色板名 palette name
|
||||
typeof palette === 'string' ||
|
||||
// 插值函数 interpolate function
|
||||
typeof palette === 'function' ||
|
||||
// 颜色数组 color array
|
||||
Array.isArray(palette)
|
||||
) {
|
||||
// 默认为离散色板,默认分组字段为 id
|
||||
// Default to discrete palette, default group field is id
|
||||
return {
|
||||
type: 'group',
|
||||
color: palette,
|
||||
};
|
||||
}
|
||||
return palette;
|
||||
}
|
||||
|
||||
/**
|
||||
* <zh/> 根据色板分配颜色
|
||||
*
|
||||
* <en/> Assign colors according to the palette
|
||||
* @param data - <zh/> 元素数据 | <en/> Element data
|
||||
* @param palette - <zh/> 色板配置 | <en/> PaletteOptions options
|
||||
* @returns <zh/> 元素颜色 | <en/> Element color
|
||||
* @description
|
||||
* <zh/> 返回值结果是一个以元素 id 为 key,颜色值为 value 的对象
|
||||
*
|
||||
* <en/> The return value is an object with element id as key and color value as value
|
||||
*/
|
||||
export function assignColorByPalette(data: ElementData, palette?: STDPaletteOptions) {
|
||||
if (!palette) return {};
|
||||
|
||||
const { type, color: colorPalette, field, invert } = palette;
|
||||
|
||||
const assignColor = (args: [ID, number][]): Record<ID, string> => {
|
||||
const palette = isString(colorPalette) ? getPlugin('palette', colorPalette) : colorPalette;
|
||||
|
||||
if (isFunction(palette)) {
|
||||
// assign by continuous
|
||||
return Object.fromEntries(args.map(([groupKey, value]) => [groupKey, palette(invert ? 1 - value : value)]));
|
||||
} else if (Array.isArray(palette)) {
|
||||
// assign by discrete
|
||||
const colors = invert ? [...palette].reverse() : palette;
|
||||
return Object.fromEntries(args.map(([id, index]) => [id, colors[index % palette.length]]));
|
||||
}
|
||||
return {};
|
||||
};
|
||||
|
||||
if (type === 'group') {
|
||||
// @ts-expect-error @antv/util groupBy condition 参数应当支持返回 string 或者 number / groupBy condition parameter should support return string or number
|
||||
const groupData = groupBy<ElementDatum>(data, (datum) => {
|
||||
if (!datum.data || !field) {
|
||||
return idOf(datum);
|
||||
}
|
||||
return String(datum.data[field]);
|
||||
});
|
||||
|
||||
const groupKeys = Object.keys(groupData);
|
||||
const assignResult = assignColor(groupKeys.map((key, index) => [key, index]));
|
||||
|
||||
const result: Record<ID, string> = {};
|
||||
Object.entries(groupData).forEach(([groupKey, groupData]) => {
|
||||
groupData.forEach((datum) => {
|
||||
result[idOf(datum)] = assignResult[groupKey];
|
||||
});
|
||||
});
|
||||
return result;
|
||||
} else {
|
||||
const [min, max] = data.reduce(
|
||||
([min, max], datum) => {
|
||||
const value = datum?.data?.[field];
|
||||
if (!isNumber(value)) throw new Error(`Palette field ${field} is not a number`);
|
||||
return [Math.min(min, value), Math.max(max, value)];
|
||||
},
|
||||
[Infinity, -Infinity],
|
||||
);
|
||||
const range = max - min;
|
||||
|
||||
return assignColor(
|
||||
data.map((datum) => [datum.id, ((datum?.data?.[field] as number) - min) / range]) as [ID, number][],
|
||||
);
|
||||
}
|
||||
}
|
23
packages/g6/src/utils/style.ts
Normal file
23
packages/g6/src/utils/style.ts
Normal file
@ -0,0 +1,23 @@
|
||||
import { isFunction } from '@antv/util';
|
||||
import type { CallableObject, ElementData, ElementDatum, StyleIterationContext } from '../types';
|
||||
|
||||
/**
|
||||
* <zh/> 计算支持回调的动态样式
|
||||
*
|
||||
* <en/> compute dynamic style that supports callback
|
||||
* @param callableStyle - <zh/> 动态样式 | <en/> dynamic style
|
||||
* @param context - <zh/> 样式计算迭代上下文 | <en/> style iteration context
|
||||
* @returns <zh/> 静态样式 | <en/> static style
|
||||
*/
|
||||
export function computeElementCallbackStyle(
|
||||
callableStyle: CallableObject<Record<string, unknown>, [ElementDatum, number, ElementData]>,
|
||||
context: StyleIterationContext,
|
||||
) {
|
||||
const { datum, index, elementData } = context;
|
||||
return Object.fromEntries(
|
||||
Object.entries(callableStyle).map(([key, style]) => {
|
||||
if (isFunction(style)) return [key, style(datum, index, elementData)];
|
||||
return [key, style];
|
||||
}),
|
||||
);
|
||||
}
|
Loading…
Reference in New Issue
Block a user