You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用HTML、CSS、JS及Angular实现径向汇聚图?

纯原生Angular径向汇聚图实现方案

以下方案无需第三方库,解决连线绘制、气泡径向分布及颜色联动问题:

核心思路

用SVG作为连线载体(原生支持箭头和颜色绑定),在父组件统一计算所有气泡的坐标,将位置、颜色数据同步传给气泡组件和SVG连线,实现完全联动。

1. 父组件(径向图容器)

模板代码

<div class="radial-container" #container>
  <!-- SVG连线层:置于最底层,不遮挡气泡交互 -->
  <svg [attr.width]="container.offsetWidth" [attr.height]="container.offsetHeight">
    <!-- 定义箭头标记,可复用 -->
    <defs>
      <marker id="arrow-marker" markerWidth="12" markerHeight="8" refX="10" refY="4" orient="auto">
        <polygon points="0 0, 12 4, 0 8" fill="currentColor" />
      </marker>
    </defs>
    <!-- 循环生成连线,绑定气泡颜色 -->
    <line
      *ngFor="let item of radialItems"
      [attr.x1]="centerX"
      [attr.y1]="centerY"
      [attr.x2]="item.x"
      [attr.y2]="item.y"
      [attr.stroke]="item.color"
      stroke-width="2"
      marker-end="url(#arrow-marker)"
      stroke-opacity="0.8"
    />
  </svg>

  <!-- 中心首页气泡 -->
  <app-bubble
    [top]="centerY - bubbleSize/2"
    [left]="centerX - bubbleSize/2"
    [text]="'首页'"
    [color]="'#2ecc71'"
  ></app-bubble>

  <!-- 外围气泡:按计算好的坐标渲染 -->
  <app-bubble
    *ngFor="let item of radialItems"
    [top]="item.y - bubbleSize/2"
    [left]="item.x - bubbleSize/2"
    [text]="item.text"
    [color]="item.color"
  ></app-bubble>
</div>

TypeScript代码

import { Component, OnInit, ViewChild, ElementRef } from '@angular/core';

// 定义气泡数据结构,包含文本、颜色、坐标
interface RadialBubble {
  text: string;
  color: string;
  x: number;
  y: number;
}

@Component({
  selector: 'app-radial-graph',
  templateUrl: './radial-graph.component.html',
  styleUrls: ['./radial-graph.component.css']
})
export class RadialGraphComponent implements OnInit {
  @ViewChild('container') containerRef!: ElementRef;
  radialItems: RadialBubble[] = [];
  centerX = 0;
  centerY = 0;
  bubbleSize = 60; // 气泡固定宽高
  circleRadius = 160; // 外围气泡所在虚拟圆周的半径

  // 模拟业务数据:2-10个气泡均可适配
  bubbleDataSource = [
    { text: '用户管理', color: '#3498db' },
    { text: '数据统计', color: '#e74c3c' },
    { text: '系统设置', color: '#f39c12' },
    { text: '日志查询', color: '#9b59b6' },
    { text: '权限控制', color: '#1abc9c' }
  ];

  ngOnInit(): void {
    this.calculateBubblePositions();
  }

  // 计算所有外围气泡的坐标
  calculateBubblePositions(): void {
    const container = this.containerRef.nativeElement;
    // 容器中心坐标
    this.centerX = container.offsetWidth / 2;
    this.centerY = container.offsetHeight / 2;
    const totalBubbles = this.bubbleDataSource.length;

    this.radialItems = this.bubbleDataSource.map((item, index) => {
      // 计算角度:从正上方开始排列,转弧度
      const angle = (index * 360 / totalBubbles) * Math.PI / 180 - Math.PI / 2;
      // 基于圆周半径计算气泡中心坐标
      const x = this.centerX + this.circleRadius * Math.cos(angle);
      const y = this.centerY + this.circleRadius * Math.sin(angle);
      return { ...item, x, y };
    });
  }

  // 窗口 resize 时重新计算坐标,保证自适应
  ngAfterViewInit(): void {
    window.addEventListener('resize', () => this.calculateBubblePositions());
  }
}

父组件样式

.radial-container {
  position: relative;
  width: 450px;
  height: 450px;
  margin: 2rem auto;
}

.radial-container svg {
  position: absolute;
  top: 0;
  left: 0;
  pointer-events: none; /* 禁止SVG拦截气泡的点击事件 */
}

2. 气泡子组件

模板代码

<div class="bubble" [style.backgroundColor]="color" [style.top.px]="top" [style.left.px]="left">
  <span class="bubble-text">{{ text }}</span>
</div>

TypeScript代码

import { Component, Input } from '@angular/core';

@Component({
  selector: 'app-bubble',
  templateUrl: './bubble.component.html',
  styleUrls: ['./bubble.component.css']
})
export class BubbleComponent {
  @Input() top = 0;
  @Input() left = 0;
  @Input() text = '';
  @Input() color = '#cccccc';
}

气泡组件样式

.bubble {
  position: absolute;
  width: 60px;
  height: 60px;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  box-shadow: 0 3px 10px rgba(0,0,0,0.15);
  transition: transform 0.2s;
}

.bubble:hover {
  transform: scale(1.1);
}

.bubble-text {
  color: white;
  font-size: 13px;
  text-align: center;
  padding: 0 5px;
}

关键细节说明

  • 连线颜色联动:每个气泡的color属性直接绑定到SVG连线的stroke属性,实现气泡与连线颜色完全一致
  • 箭头实现:通过SVG的<marker>定义可复用箭头,orient="auto"保证箭头始终指向气泡
  • 坐标计算:所有位置逻辑集中在父组件,避免子组件分散计算导致的坐标偏差
  • 自适应处理:监听窗口resize事件,重新计算所有坐标,保证在容器尺寸变化时布局正常

内容的提问来源于stack exchange,提问作者Zek Teadis

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.14 23:48:15