Angular+Highcharts:绘制连接实时时间与固定点的动态带箭头圆形
Angular + Highcharts 实现动态带箭头圆形方案
核心思路
利用 Highcharts 的 renderer API 绘制自定义图形(圆形+箭头),通过定时器每秒更新当前时间点坐标,重绘图形实现动态效果;箭头长度通过独立变量控制,不依赖两点间距。
1. 组件内定义关键变量
import { Component, ViewChild, OnInit, OnDestroy } from '@angular/core'; import * as Highcharts from 'highcharts'; import { HighchartsChartComponent } from 'highcharts-angular'; @Component({ selector: 'app-chart-component', templateUrl: './chart-component.component.html' }) export class ChartComponent implements OnInit, OnDestroy { @ViewChild('chartRef') chartRef!: HighchartsChartComponent; Highcharts = Highcharts; // 固定点配置(X为时间戳,Y为数值) fixedPoint = { x: 1717200000000, y: 25 }; // 独立控制左右箭头长度 leftArrowLength = 20; rightArrowLength = 30; // 存储绘制的图形元素,用于更新时销毁 circleElement: Highcharts.SVGElement | null = null; leftArrowElement: Highcharts.SVGElement | null = null; rightArrowElement: Highcharts.SVGElement | null = null; // 实时更新定时器 updateInterval: number | null = null; // 你的现有xAxis、yAxis配置 chartOptions: Highcharts.Options = { xAxis: { type: 'datetime', lineWidth: 0, tickPositions: [...this.tickPositions], labels: { x: 0, y: chartStyles.y, formatter(): any { let dateLabel = Highcharts.dateFormat('%H:%M', new Date(this.value)); return ( '<span style="width:10px; height:24px; color:var(--chart-text-color); background-color: var(--rectangle-bg-color); padding: 3px 4px;margin-top:90px; margin-bottom:10px; border-radius:4px;">' + dateLabel + '</span>' ); }, useHTML: true, }, min: this.slotsStart(), max: this.slotsEnd(), gridLineColor: '#e6e6e6', gridLineWidth: 0.5, plotLines: [ { name: 'Current Time', color: 'var(--default-text-color)', width: 2, value: this.getCurrentDate(), zIndex: 6, label: { align: 'bottom', x: -22, y: 287, enabled: true, rotation: 0, formatter() { return '<span style="display: inline-block;color:var(--chart-text-color); background-color: var(--rectangle-bg-color);padding: 3px 4px;border-radius:4px;">' + Highcharts.dateFormat('%H:%M', new Date(this.options.value)) + '</span>'; }, useHTML: true, } as any, }, ], }, yAxis: { lineWidth: 0, gridLineDashStyle: 'longdash', gridLineColor: 'var(--chart-row-color)', max: 45, min: 0, title: { text: '', }, labels: { x: -5, y: 8, formatter() { return ( '<span style="width:20px; height:24px; color:var(--chart-text-color); background-color: var(--frame-color); padding: 3px 6px; margin-bottom:10px; border-radius:4px;">' + this.value + '</span>' ); }, useHTML: true, } as any, }, series: this.series, chart: { events: { load: () => { const chart = this.chartRef.highcharts(); if (chart) { this.drawDynamicCircle(chart); // 每秒更新图形 this.updateInterval = window.setInterval(() => { this.drawDynamicCircle(chart); }, 1000); } }, destroy: () => { if (this.updateInterval) clearInterval(this.updateInterval); } } } }; // 你的现有方法:getCurrentDate、slotsStart、slotsEnd等... getCurrentDate() { return new Date().getTime(); } slotsStart() { // 返回时间范围起始戳 return new Date().getTime() - 3600000; } slotsEnd() { // 返回时间范围结束戳 return new Date().getTime() + 3600000; }
2. 核心绘制与更新函数
// 绘制动态圆形和箭头 drawDynamicCircle(chart: Highcharts.Chart) { // 获取当前时间点的像素坐标 const currentX = this.getCurrentDate(); const currentYPixel = chart.yAxis[0].toPixels(0, true); // 假设当前时间轴点Y值为0,可按需调整 const currentPixel = chart.xAxis[0].toPixels(currentX, true); // 获取固定点的像素坐标 const fixedPixelX = chart.xAxis[0].toPixels(this.fixedPoint.x, true); const fixedPixelY = chart.yAxis[0].toPixels(this.fixedPoint.y, true); // 计算圆心和半径 const centerX = (currentPixel + fixedPixelX) / 2; const centerY = (currentYPixel + fixedPixelY) / 2; const radius = Math.sqrt( Math.pow(currentPixel - fixedPixelX, 2) + Math.pow(currentYPixel - fixedPixelY, 2) ) / 2; // 销毁旧图形,避免重复渲染 this.circleElement?.destroy(); this.leftArrowElement?.destroy(); this.rightArrowElement?.destroy(); // 绘制圆形 this.circleElement = chart.renderer.circle(centerX, centerY, radius) .attr({ stroke: '#2196F3', strokeWidth: 2, fill: 'rgba(33, 150, 243, 0.1)' }) .add(); // 计算箭头方向角度 const angleToCurrent = Math.atan2(currentYPixel - centerY, currentPixel - centerX); const angleToFixed = Math.atan2(fixedPixelY - centerY, fixedPixelX - centerX); // 绘制指向当前时间点的箭头 const rightArrowEndX = centerX + Math.cos(angleToCurrent) * (radius + this.rightArrowLength); const rightArrowEndY = centerY + Math.sin(angleToCurrent) * (radius + this.rightArrowLength); this.rightArrowElement = this.drawArrow(chart, centerX, centerY, rightArrowEndX, rightArrowEndY, '#2196F3'); // 绘制指向固定点的箭头 const leftArrowEndX = centerX + Math.cos(angleToFixed) * (radius + this.leftArrowLength); const leftArrowEndY = centerY + Math.sin(angleToFixed) * (radius + this.leftArrowLength); this.leftArrowElement = this.drawArrow(chart, centerX, centerY, leftArrowEndX, leftArrowEndY, '#2196F3'); } // 辅助函数:绘制箭头 drawArrow(chart: Highcharts.Chart, x1: number, y1: number, x2: number, y2: number, color: string) { const arrowHeadLength = 8; const arrowAngle = Math.PI / 6; const angle = Math.atan2(y2 - y1, x2 - x1); const arrowX1 = x2 - arrowHeadLength * Math.cos(angle - arrowAngle); const arrowY1 = y2 - arrowHeadLength * Math.sin(angle - arrowAngle); const arrowX2 = x2 - arrowHeadLength * Math.cos(angle + arrowAngle); const arrowY2 = y2 - arrowHeadLength * Math.sin(angle + arrowAngle); return chart.renderer.path([ 'M', x1, y1, 'L', x2, y2, 'L', arrowX1, arrowY1, 'M', x2, y2, 'L', arrowX2, arrowY2 ]) .attr({ stroke: color, strokeWidth: 2 }) .add(); } ngOnDestroy() { // 组件销毁时清理资源 if (this.updateInterval) clearInterval(this.updateInterval); this.circleElement?.destroy(); this.leftArrowElement?.destroy(); this.rightArrowElement?.destroy(); } }
3. 模板绑定图表引用
<highcharts-chart [Highcharts]="Highcharts" [options]="chartOptions" #chartRef ></highcharts-chart>
关键说明
- 动态更新:每秒通过定时器重绘图形,先销毁旧元素再绘制新元素,避免内存泄漏。
- 箭头独立控制:修改
leftArrowLength和rightArrowLength即可单独调整两侧箭头长度,不受两点距离影响。 - 坐标转换:通过
xAxis.toPixels和yAxis.toPixels将数据坐标转为图表像素坐标,确保图形位置精准。 - 资源清理:在组件销毁和图表销毁时清除定时器、销毁图形元素,避免内存泄漏。
内容的提问来源于stack exchange,提问作者angular
相关产品推荐
相关产品推荐

