LightningChart JS微秒/纳秒数据展示及刻度、Tooltip格式化问题
问题分析与解决方案
核心问题根源
Date.parse的精度限制:JavaScript原生Date对象仅支持毫秒级精度,解析带微秒/纳秒的时间戳时,会自动截断小数部分,导致数据精度丢失,同时错误的时间戳会引发X轴刻度显示异常(如441186.59.59.108这类格式错误)。- 默认时间刻度策略不支持高精度格式:LightningChart JS的默认
Time刻度策略仅显示到毫秒,无法适配微秒/纳秒级的时间显示需求。
解决方案步骤
1. 实现高精度时间戳解析函数
替换Date.parse,手动解析时间字符串以保留微秒/纳秒精度,将时间转换为带小数的毫秒数值(适配linear-highPrecision轴的高精度特性)。
// 解析微秒/纳秒级时间戳,返回带小数的毫秒数值 parseHighPrecisionTimestamp(timestamp: string): number { const [datePart, fractionalPart] = timestamp.split('.'); const baseMs = Date.parse(datePart); if (!fractionalPart) return baseMs; const fractionalLength = fractionalPart.length; let fractionalMs = 0; if (fractionalLength === 6) { // 微秒转毫秒:x / 1000 fractionalMs = parseFloat(`0.${fractionalPart}`) * 1000; } else if (fractionalLength === 9) { // 纳秒转毫秒:x / 1000000 fractionalMs = parseFloat(`0.${fractionalPart}`) * 1000; } return baseMs + fractionalMs; }
2. 自定义X轴刻度与AutoCursor格式
修改图表初始化代码,自定义X轴刻度标签和AutoCursor tooltip的显示格式,分别适配微秒/纳秒数据。
createChartInstance() { const container = this.el.nativeElement.querySelector("#chart-container"); const lc = lightningChart({ license: this.LIGHTNING_CHART_LICENSE_KEY, licenseInformation: { appTitle: "LightningChart JS Trial", company: "LightningChart Ltd." }, }) this.chart = lc.ChartXY({ container, theme: Themes.light, defaultAxisX: { type: 'linear-highPrecision' }, animationsEnabled: false }); this.chart.setTitle('FDR Chart'); // 自定义AutoCursor的tooltip格式 this.chart.setAutoCursor((cursor) => { cursor.setResultTableAutoTextStyle(true) .setTickMarkerXVisible(false) .setTickMarkerYAutoTextStyle(true) .setResultTableFormatter((builder, series, x, y, axisX, axisY) => { const date = new Date(Math.floor(x)); const fractionalPart = x - Math.floor(x); // 基础时间格式:HH:mm:ss let timeStr = date.toLocaleTimeString('en-GB', { hour: '2-digit', minute: '2-digit', second: '2-digit' }); // 根据通道类型判断显示微秒还是纳秒(需匹配你的通道命名规则) const isNanoChannel = series.getName().includes('纳秒'); if (isNanoChannel) { const nanoseconds = Math.round(fractionalPart * 1e6); timeStr += `.${nanoseconds.toString().padStart(9, '0')}`; } else { const microseconds = Math.round(fractionalPart * 1000); timeStr += `.${microseconds.toString().padStart(6, '0')}`; } builder.addRow('时间', timeStr); builder.addRow(axisY.getTitle(), y.toFixed(2)); }); }); this.chart.getDefaultAxisY().dispose(); const axisX = this.chart.getDefaultAxisX(); // 自定义X轴刻度标签格式 axisX.setTickStrategy(AxisTickStrategies.Time, (tickStrategy) => { return tickStrategy .setDateOrigin((timestamp) => new Date(Math.floor(timestamp))) .setLabelFormatter((timestamp) => { const date = new Date(Math.floor(timestamp)); const fractionalPart = timestamp - Math.floor(timestamp); let timeStr = date.toLocaleTimeString('en-GB', { hour: '2-digit', minute: '2-digit', second: '2-digit' }); // 根据小数部分判断精度级别 if (fractionalPart > 0.000001) { const microseconds = Math.round(fractionalPart * 1000); timeStr += `.${microseconds.toString().padStart(6, '0')}`; } else if (fractionalPart > 0) { const nanoseconds = Math.round(fractionalPart * 1e6); timeStr += `.${nanoseconds.toString().padStart(9, '0')}`; } return timeStr; }); }) .setScrollStrategy(AxisScrollStrategies.progressive) // 初始化轴范围时使用高精度解析 .setDefaultInterval({ start: this.parseHighPrecisionTimestamp('2020-04-30T23:49:58.5'), end: this.parseHighPrecisionTimestamp('2020-04-30T23:49:59.49998'), stopAxisAfter: false }); this.chart.setMouseInteractions(true); this.chart.setMouseInteractionRectangleFit(false); this.chart.onSeriesBackgroundMouseDoubleClick(() => { this.chart.forEachAxis((axis) => axis.fit()); }); }
3. 修改WebSocket数据处理逻辑
替换Date.parse为自定义解析函数,并使用批量添加数据优化性能(适配10M级纳秒数据量)。
addSeriesToChart(e: any, channelName: string, groupType: string) { if (e.target.checked) { let axisY = this.chart.addAxisY({ iStack: this.lineSeriesCount }) .setTitle(channelName) .setMargins(15, 15); axisY.setMouseInteractions({ pan: true, zoom: false }); const series = this.chart.addLineSeries({ axisY, dataPattern: { pattern: 'ProgressiveX' } }) .setName(channelName); const subject = webSocket<ChartDataPoint[]>(`ws://192.168.11.145:8002/GetChartDataV3?channel=${channelName}&fileId=${this.fileId}&groupType=${groupType}`); subject.subscribe({ next: (msg: any) => { let coordinates = msg.Coordinates; this.websocketRunCount += 1; if (this.websocketRunCount == 1) { const startX = this.parseHighPrecisionTimestamp(coordinates[0].X); const endX = this.parseHighPrecisionTimestamp(coordinates[coordinates.length - 1].X); const axisX = this.chart.getDefaultAxisX(); axisX.setInterval({ start: startX, end: endX, stopAxisAfter: false }); } // 批量添加数据,大幅提升大数据量下的性能 const points = coordinates.map((point: any) => ({ x: this.parseHighPrecisionTimestamp(point.X), y: point.Y })); series.add(points); }, error: err => console.log(err), complete: () => console.log('WebSocket connection closed') }); this.seriesList[channelName] = { series: series, axisY: axisY, subject: subject }; this.lineSeriesCount += 1; } else { this.lineSeriesCount -= 1; this.seriesList[channelName].series.dispose(); this.seriesList[channelName].axisY.dispose(); this.seriesList[channelName].subject.unsubscribe(); delete this.seriesList[channelName]; } }
关键优化点说明
- 精度保留:通过自定义解析函数将微秒/纳秒转换为毫秒的小数部分,完全适配
linear-highPrecision轴的高精度特性。 - 格式控制:针对微秒/纳秒通道分别设置显示格式,确保刻度和tooltip显示符合需求的
HH:mm:ss.ffffff或HH:mm:ss.fffffffff格式。 - 性能优化:使用批量数据添加替代逐个添加,针对10M级纳秒数据大幅降低渲染开销,符合LightningChart JS的大数据处理最佳实践。
内容的提问来源于stack exchange,提问作者Saud Binsaeed
相关产品推荐
相关产品推荐

