按钮点击调用addChart意外清空Canvas问题求助
问题原因分析
核心问题出在WebGL的渲染机制和d3fc WebGL组件的默认行为上:
- d3fc的
seriesWebglLine组件默认在每次调用渲染函数(即webglLine(data))时,会先执行gl.clear(gl.COLOR_BUFFER_BIT)清除整个画布。 - 页面初始化时连续三次调用
addChart(),这三次执行都在同一个浏览器渲染周期(宏任务)内,WebGL的绘制结果会直接累积在缓冲区中,浏览器还没来得及将缓冲区内容显示到屏幕,所以三次绘制的线条都能保留。 - 按钮点击时触发的
addChart()是在单独的宏任务中执行,此时浏览器已经完成了之前的渲染,缓冲区已经交换到屏幕,组件会先清除画布再绘制新线条,导致旧内容全部丢失。
另外,代码里还有一个小问题:按钮的id写为#button虽然能通过getElementById('#button')找到元素,但id属性值一般不建议带#(这是CSS选择器的语法),建议改为id="button"后用getElementById('button')绑定事件,避免混淆。
解决方案
方案1:维护全局数据集合,一次性绘制所有线条
这种方式更符合WebGL的渲染逻辑,每次添加新数据后,重新绘制所有历史数据:
修改chart3.js:
const width = 500, height = 250; const xScale = d3.scaleLinear() .domain([0, 50]) .range([0, width]); const yScale = d3.scaleLinear() .domain([0, 1]) .range([height, 0]); const canvasgl = d3.select('#line-webgl').node(); canvasgl.width = width; canvasgl.height = height; const gl = canvasgl.getContext('webgl'); const webglLine = fc.seriesWebglLine() .xScale(xScale) .yScale(yScale) .crossValue((_, i) => i) .mainValue(d => d) .context(gl); // 维护全局数据数组,存储所有需要绘制的数据集 const allData = []; export const addChart = () => { const data = d3.range(0, 50).map(d => Math.random()); allData.push(data); // 绘制所有数据 webglLine(allData); } // 初始化绘制三次 addChart(); addChart(); addChart(); // 修正按钮绑定(需同步修改html中的按钮id为button) document.getElementById('button').onclick = addChart;
同时修改index.html中的按钮:
<button id="button">Add Chart</button>
方案2:禁用组件的自动清除画布行为
如果需要保留每次单独绘制的逻辑,可以通过手动控制WebGL的清除操作,覆盖组件的默认清除行为:
const webglLine = fc.seriesWebglLine() .xScale(xScale) .yScale(yScale) .crossValue((_, i) => i) .mainValue(d => d) .context(gl) // 覆盖beforeRender方法,不执行默认清除操作 .beforeRender(() => {});
注意:需要在初始化时手动清除一次画布(避免脏数据):
// 初始化时清除画布 gl.clearColor(1, 1, 1, 1); gl.clear(gl.COLOR_BUFFER_BIT); // 执行初始绘制 addChart(); addChart(); addChart();
效果验证
修改后,点击按钮时新的线条会叠加在旧线条之上,所有历史图表都会被保留。
内容的提问来源于stack exchange,提问作者Marina Fedoseeva
相关产品推荐
相关产品推荐

