Canvas高效绘制时fillRect出现X坐标抖动问题排查
实时无线电瀑布频谱图X坐标偏移问题排查
我正在开发一款接收实时无线电数据的瀑布式频谱图,它会随数据接收垂直滚动。为优化彩色图形的绘制效率,我尝试改用按颜色收集坐标位置的方法,以此减少Canvas fillStyle 的切换次数。该方法效率显著提升,但绘图结果出现了奇怪的X坐标随机轻微偏移问题。
两种绘制方法的简化示例
方法1 - 效率较低的原始实现
// Method 1 - less efficient let paintWaterfallRow = ( ctx: CanvasRenderingContext2D, data: FFTSample[], lineWidth: number, lineHeight: number, z: d3.ScaleSequential<string>, dy?: number ) => { let chunkWidth = Math.ceil(lineWidth / data.length) + 1; dy = dy || 0; for (let i = 0; i < data.length; i++) { let point = data[i]; let color = z(point.magnitude); ctx.fillStyle = color; ctx.fillRect((i / data.length) * lineWidth, dy, chunkWidth, lineHeight); } } for (const sample of data) { ... if ((sample.time - lastTime) > TIME_BETWEEN_LINES) { // sync with desired number of frames let dy = y(sample.time); let offset = dy - (lastDy + LINE_HEIGHT); // compensate for jitters in the time math calculation ... paintWaterfallRow(ctx, sample.payload, width, (LINE_HEIGHT + 0.5) + offset, zScale.scale, (dy - LINE_HEIGHT) - offset); lastTime = sample.time; lastDy = dy; } }
方法2 - 优化后但出现偏移的实现
// Method 2 - more efficient let posData: { [_: string]: { x: number, y: number, height: number }[] } = {}; for (const sample of data) { ... if ((sample.time - lastTime) > TIME_BETWEEN_LINES) { // sync with desired number of frames let dy = y(sample.time); let offset = dy - (lastDy + LINE_HEIGHT); // compensate for jitters in the time math calculation ... // collect position data let payload = sample.payload; for (let i = 0; i < payload.length; i++) { let samplePoint = payload[i]; let color = zScale.scale(samplePoint.magnitude); if (!posData.hasOwnProperty(color)) posData[color] = []; posData[color].push({ x: (i / payload.length) * width, y: (dy - LINE_HEIGHT) - offset, height: (LINE_HEIGHT + 0.5) + offset }) } lastTime = sample.time; lastDy = dy; } } // draw collected points const chunkWidth = Math.ceil(width / sample.payload.length) + 1; for (const [color, points] of Object.entries(posData)) { ctx.fillStyle = color; for (let i = 0; i < points.length; i++) { let point = points[i]; ctx.fillRect(point.x, point.y, chunkWidth, point.height); } }
绘制效果对比
方法1绘制结果(线条清晰笔直)

方法2绘制结果(X坐标随机轻微偏移)

已排查信息
- 每条样本数据的长度始终为64
- 将方法2中所有X坐标存入集合后,集合长度保持64,说明X坐标计算无偏差
请问问题出在哪里?
内容的提问来源于stack exchange,提问作者moltarze
相关产品推荐
相关产品推荐

