OSCP activity
Loading example…
import { Graph, Shape } from '@antv/x6'
Graph.registerNode(
'custom-node',
{
width: 150,
height: 60,
attrs: {
body: {
stroke: '#000',
strokeWidth: 2,
fill: '#fff',
refWidth: 1,
refHeight: 1,
rx: 10,
},
image: {
width: 16,
height: 16,
ref: 'body',
},
icon: {
ref: 'body',
fill: '#fff',
},
icon2: {
ref: 'body',
fill: '#fff',
},
title: {
text: 'Node',
refX: 45,
refY: 14,
fill: '#fff',
fontSize: 14,
'text-anchor': 'start',
fontWeight: 'bold',
},
text: {
refX: 45,
refY: 38,
fontSize: 12,
fill: '#fff',
'text-anchor': 'start',
},
},
markup: [
{
tagName: 'rect',
selector: 'body',
},
{
tagName: 'image',
selector: 'image',
},
{
tagName: 'text',
selector: 'title',
},
{
tagName: 'text',
selector: 'text',
},
{
tagName: 'path',
selector: 'icon',
},
{
tagName: 'path',
selector: 'icon2',
},
],
},
true,
)
const graph = new Graph({
container: document.getElementById('container'),
grid: true,
connecting: {
snap: true,
connector: { name: 'smooth' },
},
background: {
color: '#fff',
},
})
const mainNode = graph.addNode({
shape: 'custom-node',
x: 300,
y: 100,
width: 300,
height: 150,
attrs: {
body: {
fill: '#FBEAD4',
stroke: '#D7CEA9',
strokeWidth: 2,
rx: 20,
},
image: {
'xlink:href':
'https://mdn.alipayobjects.com/huamei_qa8qxu/afts/img/A*A-lcQbVTpjwAAAAAAAAAAAAADmJ7AQ/original',
width: 100,
height: 100,
refX: 0.5,
refX2: -50,
y: 0,
},
title: {
text: 'OSCP',
fontSize: 20,
refX: 0.5,
refX2: -25,
y: 90,
fontWeight: 800,
fill: 'rgba(0,0,0,0.85)',
},
text: {
text: '',
},
},
})
const issueNode = graph.addNode({
shape: 'custom-node',
x: 800,
y: 140,
attrs: {
body: {
fill: '#4CAF50',
stroke: '',
},
title: {
text: '主线任务',
},
text: {
text: 'issue修复',
},
icon: {
d: 'M20 40 L20 30 L15 30 L20 25 L20 15 L30 15 L30 25 L35 30 L30 30 L30 40 ZM22 30 L28 30 M25 22 L25 28',
refY: 0,
refX: 0,
},
},
animation: [
[
{ 'attrs/icon/refY': -5 },
{
duration: 800,
iterations: Infinity,
direction: 'alternate',
},
],
],
})
const docNode = graph.addNode({
shape: 'custom-node',
x: 650,
y: 260,
attrs: {
body: {
fill: '#4994EC',
stroke: '',
},
title: {
text: '支线任务',
},
text: {
text: '文档捉"虫"',
},
icon: {
d: 'M15.5 14h-.79l-.28-.27A6.471 6.471 0 0 0 16 9.5 6.5 6.5 0 1 0 9.5 16c1.61 0 3.09-.59 4.23-1.57l.27.28v.79l5 4.99L20.49 19l-4.99-5zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14z',
refX: 10,
refY: 10,
},
},
animation: [
[
{ 'attrs/icon/refY': 15 },
{
duration: 600,
iterations: Infinity,
direction: 'alternate',
},
],
[
{ 'attrs/icon/refX': 20 },
{
duration: 700,
iterations: Infinity,
direction: 'alternate',
},
],
],
})
const prNode = graph.addNode({
shape: 'custom-node',
x: 800,
y: 400,
attrs: {
body: {
fill: '#FF9800',
stroke: '',
},
title: {
text: '提交PR',
},
text: {
text: '一对一指导',
},
icon: {
d: 'M20 35 L25 30 L30 35 M25 30 L25 40',
strokeWidth: 2,
refY: -5,
},
icon2: {
d: 'M15 25 L35 25 L35 15 L15 15 Z',
opacity: 0.6,
},
},
animation: [
[
{ 'attrs/icon/refY': 0 },
{
duration: 600,
iterations: Infinity,
direction: 'alternate',
},
],
],
})
const rewardNode = graph.addNode({
shape: 'custom-node',
x: 350,
y: 400,
attrs: {
body: {
fill: '#9C27B0',
stroke: '',
filter: {
name: 'highlight',
args: {
color: '#9C27B0',
blur: 1,
width: 8,
opacity: 0.1,
},
},
},
title: {
text: '获得奖励',
},
text: {
text: '各种精美奖品',
},
icon: {
d: 'M20 35 L15 35 L15 25 L25 25 L25 35 L20 35 ZM18 25 L18 20 C18 18 19 16 20 16 C21 16 22 18 22 20 L22 25',
strokeWidth: 2,
strokeLinecap: 'round',
strokeLinejoin: 'round',
},
},
animation: [
[
{
'attrs/body/opacity': [1, 0.85],
},
{ duration: 1000, iterations: Infinity, direction: 'alternate' },
],
],
})
const chatNode = graph.addNode({
shape: 'custom-node',
x: 0,
y: 240,
attrs: {
body: {
fill: '#00BCD4',
stroke: '',
},
title: {
text: '加入交流群',
},
text: {
text: '获取第一手资讯',
},
icon: {
refX: 10,
refY: 10,
d: 'M20 2H4C2.9 2 2 2.9 2 4V16C2 17.1 2.9 18 4 18H12L16 22V18H20C21.1 18 22 17.1 22 16V4C22 2.9 21.1 2 20 2Z',
strokeWidth: 2,
strokeLinecap: 'round',
strokeLinejoin: 'round',
},
},
})
const edges = [
{
source: mainNode,
target: issueNode,
},
{
source: mainNode,
target: docNode,
attrs: {
line: {
stroke: '#C7D5F6',
strokeWidth: 2,
strokeDasharray: 5,
strokeDashoffset: 0,
},
},
},
{
source: mainNode,
target: chatNode,
markup: [
{
tagName: 'circle',
selector: 'marker',
attrs: {
stroke: 'none',
r: 5,
},
},
...Shape.Edge.getMarkup(),
],
attrs: {
line: {
stroke: '#A3B1BF',
strokeWidth: 2,
strokeDasharray: 5,
strokeDashoffset: 0,
},
marker: {
fill: '#C7D5F6',
atConnectionRatio: 0,
},
},
animation: [
[
{ 'attrs/marker/atConnectionRatio': 1 },
{
duration: 2000,
iterations: Infinity,
},
],
],
},
{
source: issueNode,
target: prNode,
},
{
source: docNode,
target: prNode,
attrs: {
line: {
stroke: '#C7D5F6',
strokeWidth: 2,
strokeDasharray: 5,
strokeDashoffset: 0,
},
},
},
{
source: prNode,
target: rewardNode,
},
]
graph.addEdges(
edges.map((edge) => {
return {
connector: { name: 'smooth' },
attrs: {
line: {
stroke: '#A3B1BF',
strokeWidth: 2,
strokeDasharray: 5,
strokeDashoffset: 0,
},
},
animation: [
[
{ 'attrs/line/strokeDashoffset': -20 },
{
duration: 600,
iterations: Infinity,
},
],
],
...edge,
}
}),
)
graph.centerContent()
graph.zoomToFit()