数据脉冲网络

源码
import { Chart } from '@antv/g2';

// 数据脉冲网络 - 节点间的数据传输可视化
// 模拟网络中数据包在节点间的脉冲传输

const NODES = 8; // 节点数量
const FRAMES = 150; // 动画帧数
const FPS = 30; // 每秒帧数

// 生成网络节点(圆形布局)
const nodes = Array.from({ length: NODES }, (_, i) => {
  const angle = (i / NODES) * Math.PI * 2 - Math.PI / 2;
  const radius = 40;
  return {
    id: i,
    name: `Node ${i + 1}`,
    x: 50 + Math.cos(angle) * radius,
    y: 50 + Math.sin(angle) * radius,
    status: Math.random() > 0.7 ? 'active' : 'normal',
  };
});

// 生成连接关系
const connections = [];
for (let i = 0; i < NODES; i++) {
  // 每个节点连接到相邻节点
  const next = (i + 1) % NODES;
  connections.push({ from: i, to: next });

  // 随机添加一些跨节点连接
  if (Math.random() > 0.6) {
    const target = (i + 3) % NODES;
    connections.push({ from: i, to: target });
  }
}

// 生成单帧数据
function generateNetworkData(frame) {
  const data = [];
  const time = frame * 0.05;

  // 添加节点
  nodes.forEach((node) => {
    const pulsePhase = (time * 2 + node.id) % (Math.PI * 2);
    const pulse = 0.7 + Math.sin(pulsePhase) * 0.3;

    data.push({
      type: 'node',
      x: node.x,
      y: node.y,
      name: node.name,
      status: node.status,
      size: node.status === 'active' ? 12 * pulse : 8,
      key: `node-${node.id}`,
    });
  });

  // 添加连接线
  connections.forEach((conn, connIndex) => {
    const from = nodes[conn.from];
    const to = nodes[conn.to];

    // 为每条连接添加多个点形成线段
    const segments = 20;
    for (let i = 0; i <= segments; i++) {
      const t = i / segments;
      const x = from.x + (to.x - from.x) * t;
      const y = from.y + (to.y - from.y) * t;

      data.push({
        type: 'link',
        x,
        y,
        connection: connIndex,
        key: `link-${connIndex}-${i}`,
      });
    }
  });

  // 添加脉冲数据包
  connections.forEach((conn, connIndex) => {
    const from = nodes[conn.from];
    const to = nodes[conn.to];

    // 每条连接上可能有多个数据包
    const packetsPerLink = 2;
    for (let p = 0; p < packetsPerLink; p++) {
      const progress = (time * 0.5 + connIndex * 0.3 + p * 0.5) % 1;
      const x = from.x + (to.x - from.x) * progress;
      const y = from.y + (to.y - from.y) * progress;

      // 数据包尾迹
      for (let tail = 0; tail < 5; tail++) {
        const tailProgress = Math.max(0, progress - tail * 0.05);
        const tailX = from.x + (to.x - from.x) * tailProgress;
        const tailY = from.y + (to.y - from.y) * tailProgress;
        const opacity = (1 - tail / 5) * 0.8;

        if (tailProgress >= 0 && tailProgress <= 1) {
          data.push({
            type: 'pulse',
            x: tailX,
            y: tailY,
            size: 4 - tail,
            opacity,
            key: `pulse-${connIndex}-${p}-${tail}`,
          });
        }
      }
    }
  });

  return data;
}

// 预生成所有帧的 options
const allOptions = [];

for (let frame = 0; frame < FRAMES; frame++) {
  const data = generateNetworkData(frame);

  const option = {
    type: 'point',
    data: data,
    encode: {
      x: 'x',
      y: 'y',
      size: (d) => d.size || 1,
      color: 'type',
      shape: 'point',
      key: 'key',
    },
    scale: {
      x: { domain: [0, 100] },
      y: { domain: [0, 100] },
      size: { range: [1, 15] },
      color: {
        domain: ['node', 'link', 'pulse'],
        range: ['#1890ff', '#2a2a3e', '#52c41a'],
      },
    },
    style: {
      fill: (d) => {
        if (d.type === 'node') {
          return d.status === 'active' ? '#ff4d4f' : '#1890ff';
        }
        if (d.type === 'link') return '#2a2a3e';
        if (d.type === 'pulse') return '#52c41a';
        return '#666';
      },
      fillOpacity: (d) => {
        if (d.type === 'node') return 0.9;
        if (d.type === 'link') return 0.3;
        if (d.type === 'pulse') return d.opacity || 0.8;
        return 0.5;
      },
      stroke: (d) => {
        if (d.type === 'node') return '#fff';
        if (d.type === 'pulse') return '#52c41a';
        return 'none';
      },
      strokeOpacity: (d) => (d.type === 'node' ? 0.8 : d.opacity || 0),
      lineWidth: (d) => (d.type === 'node' ? 2 : 1),
    },
    theme: {
      view: {
        viewFill: '#0a0e1a',
      },
    },
    axis: false,
    legend: false,
    tooltip: {
      items: [
        { field: 'name', name: '节点' },
        {
          field: 'status',
          name: '状态',
          valueFormatter: (v) => (v === 'active' ? '🔴 活跃' : '🔵 正常'),
        },
      ],
    },
  };

  allOptions.push(option);
}

// 创建图表
const chart = new Chart({
  container: 'container',
  autoFit: true,
});

chart.options(allOptions[0]);
chart.render();

// 播放动画
let currentFrame = 0;
const timer = setInterval(() => {
  currentFrame = (currentFrame + 1) % FRAMES;
  chart.options(allOptions[currentFrame]);
  chart.render();
}, 1000 / FPS);
全站搜索
文档与示例输入关键词开始搜索

输入关键词,查找文档、API 与示例

↑↓选择↵打开Esc关闭