初步实现6个圆圈(但连线穿过圆圈了需要优化)
This commit is contained in:
		
							parent
							
								
									b5acf7652d
								
							
						
					
					
						commit
						b00b1293e4
					
				| 
						 | 
				
			
			@ -14,7 +14,6 @@ const containerRef = ref(null);
 | 
			
		|||
onMounted(() => {
 | 
			
		||||
  // --- 1. Data Preparation ---
 | 
			
		||||
 | 
			
		||||
  // Initial node data (no changes needed here)
 | 
			
		||||
  const initialNodes = [
 | 
			
		||||
    { id: '6010609377', name: '外贸发布BBS', default_avatar: '外贸发布BBS_waimaofabuBBS.png' },
 | 
			
		||||
    { id: '6797803070', name: '爱锤盾海桃-霆恩启副', default_avatar: '爱锤盾海桃-霆恩启副_aichuidunhaitao-tingenqifu.png' },
 | 
			
		||||
| 
						 | 
				
			
			@ -37,8 +36,8 @@ onMounted(() => {
 | 
			
		|||
    { id: '2548116484', name: '肥_谍_gg', default_avatar: '肥_谍_gg_fei_die_gg.png' },
 | 
			
		||||
    { id: '1854070075', name: '深海一万米', default_avatar: '深海一万米_shenhaiyiwanmi.png' },
 | 
			
		||||
  ];
 | 
			
		||||
 | 
			
		||||
  // Map node names to their cluster ID
 | 
			
		||||
  
 | 
			
		||||
  // The mapping of nodes to their clusters remains the same
 | 
			
		||||
  const clusterMapping = {
 | 
			
		||||
    '中国海警': 'cluster-0', '十八子91221': 'cluster-0', '大侠啊啊啊啊': 'cluster-0',
 | 
			
		||||
    '苍龙飞天79': 'cluster-1', '平安泸县': 'cluster-1', '新浪军事': 'cluster-1', '环球时报': 'cluster-1', '乐之567': 'cluster-1', 'CGTN记者团': 'cluster-1', '深海一万米': 'cluster-1',
 | 
			
		||||
| 
						 | 
				
			
			@ -48,36 +47,29 @@ onMounted(() => {
 | 
			
		|||
    '肥_谍_gg': 'cluster-5',
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  const nodeSize = 50;
 | 
			
		||||
  
 | 
			
		||||
  const nodes = initialNodes.map(node => ({
 | 
			
		||||
    ...node,
 | 
			
		||||
    comboId: clusterMapping[node.name],
 | 
			
		||||
    label: '', // Labels are disabled for this design
 | 
			
		||||
    label: '',
 | 
			
		||||
    type: 'image',
 | 
			
		||||
    img: new URL(`/src/assets/user3/${node.default_avatar}`, import.meta.url).href,
 | 
			
		||||
    clipCfg: { show: true, type: 'circle', r: 25 },
 | 
			
		||||
  }));
 | 
			
		||||
 | 
			
		||||
  // --- Fixed Combo Layout, Styles, and Connections ---
 | 
			
		||||
  
 | 
			
		||||
  // Define fixed positions, sizes, and styles for each combo based on SwiftUI code and screenshot
 | 
			
		||||
  const comboLayoutData = {
 | 
			
		||||
    'cluster-0': { x: 250, y: 220, r: 120, style: { fill: '#FF5E0014', stroke: '#FF5E007A' } }, // Orange
 | 
			
		||||
    'cluster-1': { x: 500, y: 180, r: 120, style: { fill: '#BA21EB14', stroke: '#BA21EB7A' } }, // Purple
 | 
			
		||||
    'cluster-2': { x: 700, y: 500, r: 80,  style: { fill: '#2191EB29', stroke: '#2191EB' } },     // Blue
 | 
			
		||||
    'cluster-3': { x: 800, y: 250, r: 137, style: { fill: '#FFD90A14', stroke: '#FFD90A7A' } }, // Yellow
 | 
			
		||||
    'cluster-4': { x: 180, y: 480, r: 80,  style: { fill: '#3DD99414', stroke: '#3DD9947A' } }, // Green
 | 
			
		||||
    'cluster-5': { x: 450, y: 450, r: 110, style: { fill: '#D93D6B14', stroke: '#D93D6B7A' } }, // Pink/Red
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  const combos = Object.entries(comboLayoutData).map(([id, { x, y, r, style }]) => ({
 | 
			
		||||
    id,
 | 
			
		||||
    label: '',
 | 
			
		||||
    x, y, fx: x, fy: y, // Fix combo positions
 | 
			
		||||
    r, // Set radius directly
 | 
			
		||||
    style
 | 
			
		||||
    clipCfg: { show: true, type: 'circle', r: nodeSize / 2 },
 | 
			
		||||
  }));
 | 
			
		||||
  
 | 
			
		||||
  // Define connections between combos based on the target image
 | 
			
		||||
  // Define Combos with styles and relative sizes. Positions will be calculated by the layout engine.
 | 
			
		||||
  const combos = [
 | 
			
		||||
    { id: 'cluster-0', label: '', r: 90,  style: { fill: '#FF5E0014', stroke: '#FF5E007A' } },
 | 
			
		||||
    { id: 'cluster-1', label: '', r: 110, style: { fill: '#BA21EB14', stroke: '#BA21EB7A' } },
 | 
			
		||||
    { id: 'cluster-2', label: '', r: 80,  style: { fill: '#2191EB29', stroke: '#2191EB' } },
 | 
			
		||||
    { id: 'cluster-3', label: '', r: 130, style: { fill: '#FFD90A14', stroke: '#FFD90A7A' } },
 | 
			
		||||
    { id: 'cluster-4', label: '', r: 75,  style: { fill: '#3DD99414', stroke: '#3DD9947A' } },
 | 
			
		||||
    { id: 'cluster-5', label: '', r: 100, style: { fill: '#D93D6B14', stroke: '#D93D6B7A' } },
 | 
			
		||||
  ];
 | 
			
		||||
  
 | 
			
		||||
  // Re-introduce edges to define relationships for the force layout.
 | 
			
		||||
  // These connections are based on the visual overlaps in the original image.
 | 
			
		||||
  const comboConnections = [
 | 
			
		||||
    ['cluster-0', 'cluster-1'], ['cluster-0', 'cluster-4'],
 | 
			
		||||
    ['cluster-1', 'cluster-3'], ['cluster-1', 'cluster-5'],
 | 
			
		||||
| 
						 | 
				
			
			@ -86,39 +78,40 @@ onMounted(() => {
 | 
			
		|||
    ['cluster-5', 'cluster-2'],
 | 
			
		||||
  ];
 | 
			
		||||
 | 
			
		||||
  // G6 needs edges between nodes, so we find one node from each combo to connect.
 | 
			
		||||
  const edges = comboConnections.map(([sourceComboId, targetComboId], i) => {
 | 
			
		||||
    const sourceNode = nodes.find(n => n.comboId === sourceComboId);
 | 
			
		||||
    const targetNode = nodes.find(n => n.comboId === targetComboId);
 | 
			
		||||
    return {
 | 
			
		||||
      id: `edge-${i}`,
 | 
			
		||||
      source: sourceNode.id,
 | 
			
		||||
      target: targetNode.id,
 | 
			
		||||
    };
 | 
			
		||||
    return { id: `edge-${i}`, source: sourceComboId, target: targetComboId };
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  const data = { nodes, combos, edges };
 | 
			
		||||
 | 
			
		||||
  // --- 2. G6 Graph Initialization ---
 | 
			
		||||
  const container = containerRef.value;
 | 
			
		||||
  const width = container.scrollWidth;
 | 
			
		||||
  const height = container.scrollHeight || 700;
 | 
			
		||||
  const width = container.scrollWidth || 1200;
 | 
			
		||||
  const height = container.scrollHeight || 800;
 | 
			
		||||
 | 
			
		||||
  graph = new G6.Graph({
 | 
			
		||||
    container,
 | 
			
		||||
    width,
 | 
			
		||||
    height,
 | 
			
		||||
    layout: null, // 或你自定义的布局
 | 
			
		||||
    groupByTypes: false, // 允许 combo-to-combo 边
 | 
			
		||||
    // --- MODIFICATION: Set preventComboOverlap to true to avoid circle overlaps ---
 | 
			
		||||
    layout: {
 | 
			
		||||
      type: 'comboForce',
 | 
			
		||||
      gravity: 80, // Increase gravity to pull nodes towards combo centers
 | 
			
		||||
      nodeSpacing: 25,
 | 
			
		||||
      preventOverlap: true,
 | 
			
		||||
      preventComboOverlap: true, // Should be redundant with fixed combos, but safe to keep
 | 
			
		||||
      preventComboOverlap: true, // 让combo不重叠
 | 
			
		||||
      preventNodeOverlap: true,  // 节点不重叠
 | 
			
		||||
      linkDistance: 250,         // 连接距离
 | 
			
		||||
      nodeSpacing: 50,           // 节点间距
 | 
			
		||||
      comboSpacing: 150,          // combo间距(可根据实际效果调整,数值越大圆圈越分开)
 | 
			
		||||
      gravity: 40,
 | 
			
		||||
      comboGravity: 30,
 | 
			
		||||
    },
 | 
			
		||||
    modes: {
 | 
			
		||||
      default: ['drag-canvas', 'zoom-canvas'], // Disable node and combo dragging
 | 
			
		||||
      default: ['drag-canvas', 'zoom-canvas', 'drag-combo', 'drag-node'],
 | 
			
		||||
    },
 | 
			
		||||
    defaultNode: {
 | 
			
		||||
      size: 50,
 | 
			
		||||
      size: nodeSize,
 | 
			
		||||
      style: {
 | 
			
		||||
        lineWidth: 2,
 | 
			
		||||
        stroke: '#37ACD7',
 | 
			
		||||
| 
						 | 
				
			
			@ -128,15 +121,15 @@ onMounted(() => {
 | 
			
		|||
    },
 | 
			
		||||
    defaultEdge: {
 | 
			
		||||
      style: {
 | 
			
		||||
        stroke: '#37ACD7',
 | 
			
		||||
        lineWidth: 1,
 | 
			
		||||
        opacity: 0.5,
 | 
			
		||||
        stroke: '#FFFFFF',
 | 
			
		||||
        lineWidth: 2,
 | 
			
		||||
        opacity: 0.3,
 | 
			
		||||
      },
 | 
			
		||||
    },
 | 
			
		||||
    defaultCombo: { // This is now a base style, individual styles will override
 | 
			
		||||
    defaultCombo: {
 | 
			
		||||
      type: 'circle',
 | 
			
		||||
      style: {
 | 
			
		||||
        lineWidth: 1,
 | 
			
		||||
        lineWidth: 2,
 | 
			
		||||
      },
 | 
			
		||||
    },
 | 
			
		||||
  });
 | 
			
		||||
| 
						 | 
				
			
			@ -168,7 +161,7 @@ onMounted(() => {
 | 
			
		|||
  .container {
 | 
			
		||||
    width: 100%;
 | 
			
		||||
    height: 100%;
 | 
			
		||||
    background: #0b1120; // Match the dark background from the screenshot
 | 
			
		||||
    background: #0b1120; // Set a background similar to the image
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
</style>
 | 
			
		||||
| 
						 | 
				
			
			@ -10,5 +10,10 @@ export default defineConfig({
 | 
			
		|||
      "@": path.resolve(__dirname, "src"),
 | 
			
		||||
      "@assets": path.resolve(__dirname, "src/assets")
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  },
 | 
			
		||||
  server: {
 | 
			
		||||
    watch: {
 | 
			
		||||
      usePolling: true, // 解决部分环境下热更新失效
 | 
			
		||||
    }
 | 
			
		||||
  },
 | 
			
		||||
});
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue
	
	Block a user