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

LCJS波形运行20分钟后抖动及X轴缩放异常排查求助

问题原因

  1. 浮点数精度累积误差:JavaScript的64位双精度浮点数无法精确表示所有小数(例如0.1),随着运行时间增加,x = (pushedDataCount + iDp) * 0.1的累积计算会产生越来越大的精度偏差。正弦波对X轴的连续性和精度敏感,这种偏差会导致波形相位偏移,表现为抖动;而直线、方波因Y值变化特性,精度偏差的视觉影响不明显。
  2. X轴区间计算异常:X轴数值精度丢失后,LCJS内部的轴区间计算、缩放逻辑会出现误差,导致无法正常放大、轴起止范围偏离预期。

修复方案

方案1:避免浮点数累积,改用整数X基准+轴格式化

将X轴数据改为整数计数,通过轴的格式化函数将整数转换为时间显示,彻底避免浮点数累加误差。

修改代码示例:

const lcjs = require('@arction/lcjs');
const DATA_FREQUENCY_HZ = 10000
const {
    lightningChart,
    AxisTickStrategies,
    AxisScrollStrategies,
    UIOrigins,
    UIDraggingModes,
    SolidFill,
    ColorRGBA,
    SolidLine,
    Themes
} = lcjs;
const axisStyleHighlight = new SolidFill({ color: ColorRGBA(255, 255, 255, 0) });

const chart = lightningChart()
    .ChartXY({
        theme: Themes.light
    })
    .setPadding({ left: 0, right: 90, top: 20, bottom: 0 })
    .setTitle('')
    .setBackgroundFillStyle(new SolidFill({ color: ColorRGBA(255, 255, 255) }));

// 修改X轴配置:用整数基准+自定义格式化
const axisX = chart
    .getDefaultAxisX()
    .setAnimationsEnabled(false)
    .setOverlayStyle(axisStyleHighlight)
    .setNibOverlayStyle(axisStyleHighlight)
    .setScrollStrategy(AxisScrollStrategies.progressive)
    .setTitle('Time')
    .setTickStrategy(AxisTickStrategies.Numeric, (tickStrategy) => 
        tickStrategy.setFormattingFunction((value) => {
            // 将整数X值转换为秒显示(对应原0.1的step逻辑)
            const seconds = (value * 0.1) / 1000;
            return `${seconds.toFixed(2)}s`;
        })
    )
    .setInterval({ start: 0, end: 20000, animate: false, stopAxisAfter: false });

const axisY = chart
    .getDefaultAxisY()
    .setAnimationsEnabled(false)
    .setOverlayStyle(axisStyleHighlight)
    .setNibOverlayStyle(axisStyleHighlight)
    .setScrollStrategy(AxisScrollStrategies.fitting)
    .setTitle('Amplitude')
    .setInterval({ start: -8, end: 8, animate: false, stopAxisAfter: false }); // 适配正弦波y值范围

const series = chart
    .addLineSeries({
        dataPattern: { pattern: 'ProgressiveX' },
        automaticColorIndex: 0
    })
    .setName(`Channel 1`)
    .setDataCleaning({ minDataPointCount: 10000 })
    .setStrokeStyle(style => style.setThickness(2));

// 图例配置不变
const legend = chart
    .addLegendBox()
    .add(chart)
    .setDraggingMode(UIDraggingModes.notDraggable)
    .setPosition({ x: 100, y: 100 })
    .setOrigin(UIOrigins.RightTop)
    .setMargin({ right: 5, top: 20.3 })
    .setPadding({ left: 3, right: 0, top: 0, bottom: 0 })
    .setEntries((entry, component) =>
        entry
            .setButtonOnStrokeStyle(
                new SolidLine({
                    thickness: 1,
                    fillStyle: new SolidFill({ color: ColorRGBA(0, 0, 0) })
                })
            )
            .setTextFont(font => font.setSize(11))
    )
    .setTitle('');

// 数据集不变
const dataSets = [
    { length: Math.ceil(400 * Math.PI), func: (x) => 8 * Math.sin(x / 100) },
].map((config) => {
    const data = []
    data.length = config.length
    for (let i = 0; i < config.length; i += 1) {
        const y = config.func(i)
        data[i] = y
    }
    return data
})
const dataSet = dataSets[0]

// 修改数据流逻辑:用整数X值
let tStart = window.performance.now();
let pushedDataCount = 0;
const streamData = () => {
    const tNow = window.performance.now();
    const shouldBeDataPointsCount = Math.floor((DATA_FREQUENCY_HZ * (tNow - tStart)) / 1000);
    const newDataPointsCount = Math.min(shouldBeDataPointsCount - pushedDataCount, DATA_FREQUENCY_HZ);

    const newDataPoints = [];
    for (let iDp = 0; iDp < newDataPointsCount; iDp++) {
        const x = pushedDataCount + iDp; // 整数X值,避免浮点数累积
        const iData = (pushedDataCount + iDp) % dataSet.length;
        const y = dataSet[iData];
        const point = { x, y };
        newDataPoints.push(point);
    }
    series.add(newDataPoints)
    pushedDataCount += newDataPointsCount;
    requestAnimationFrame(streamData);
};
streamData();

方案2:定期重置数据累积基准

当累积的数据点达到一定阈值时,重置计数基准并更新X轴偏移量,保证视觉上波形连续,避免数值过大导致精度丢失。

核心修改代码:

// 添加重置阈值和偏移量
const MAX_DATA_POINTS_BEFORE_RESET = 1000000;
let xOffset = 0;

// 初始化X轴区间
const axisX = chart
    .getDefaultAxisX()
    // ... 其他配置不变
    .setInterval({ start: xOffset, end: xOffset + 2000, animate: false, stopAxisAfter: false });

const streamData = () => {
    const tNow = window.performance.now();
    const shouldBeDataPointsCount = Math.floor((DATA_FREQUENCY_HZ * (tNow - tStart)) / 1000);
    const newDataPointsCount = Math.min(shouldBeDataPointsCount - pushedDataCount, DATA_FREQUENCY_HZ);

    const newDataPoints = [];
    for (let iDp = 0; iDp < newDataPointsCount; iDp++) {
        const x = xOffset + (pushedDataCount + iDp) * 0.1; // 叠加偏移量
        const iData = (pushedDataCount + iDp) % dataSet.length;
        const y = dataSet[iData];
        const point = { x, y };
        newDataPoints.push(point);
    }
    series.add(newDataPoints)
    pushedDataCount += newDataPointsCount;

    // 达到阈值时重置
    if (pushedDataCount >= MAX_DATA_POINTS_BEFORE_RESET) {
        pushedDataCount = 0;
        tStart = tNow;
        xOffset += MAX_DATA_POINTS_BEFORE_RESET * 0.1;
        // 更新X轴区间保持显示窗口一致
        axisX.setInterval({ 
            start: xOffset, 
            end: xOffset + 2000, 
            animate: false, 
            stopAxisAfter: false 
        });
    }

    requestAnimationFrame(streamData);
};

方案3:升级LCJS版本

当前使用的LCJS 4.2.0版本较旧,后续版本(v5.x及以上)针对大数值轴的精度处理、波形渲染做了优化,升级到最新稳定版可能直接解决该问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.20 13:14:54