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

Canvas动画报错:Uncaught TypeError: Cannot read properties of undefined (reading '310')

隧道渲染函数索引越界问题修复

问题场景

我正在开发隧道效果Demo,尝试让隧道在图像内移动,但渲染函数始终报错。报错触发条件为:读取distanceTable或angleTable时,第一个索引不是y(哪怕只是y加某个值)就会报错,第二个索引似乎不受影响。

相关代码:

function draw(time) {
  let animation = time / 1000.0;
  
  let shiftX = ~~(texWidth * animation);
  let shiftY = ~~(texHeight * 0.25 * animation);
  let shiftLookX = (screenWidth / 2) + ~~(screenWidth / 2 * Math.sin(animation))
  let shiftLookY = (screenHeight / 2) + ~~(screenHeight / 2 * Math.sin(animation))

  for (y = 0; y < buffer.height; y++) {
    for (x = 0; x < buffer.width; x++) {
      let id = (y * buffer.width + x) * 4;
      let d = ~~(distanceTable[y + shiftLookY][x + shiftLookX] + shiftX) % texWidth;
      let a = ~~(angleTable[y + shiftLookY][x + shiftLookX] + shiftY) % texHeight;
      let tex = (a * texture.width + d) * 4;

      buffer.data[id] = texture.data[tex];
      buffer.data[id+1] = texture.data[tex+1];
      buffer.data[id+2] = texture.data[tex+2];
      buffer.data[id+3] = texture.data[tex+3];
    }
  }

  ctx.putImageData(buffer, 0, 0);
  window.requestAnimationFrame(draw);
}

错误原因

错误的核心是行索引越界:

  • y的取值范围是0到buffer.height - 1,而shiftLookY的计算值范围是0到screenHeight(当Math.sin(animation)取1时,shiftLookY = screenHeight/2 + screenHeight/2 = screenHeight)。当y接近buffer.height时,y + shiftLookY会超过distanceTable和angleTable的行数(这两个表的高度通常与屏幕/缓冲高度一致),此时distanceTable[y + shiftLookY]返回undefined,再尝试访问其列索引[x + shiftLookX]就会抛出“Cannot read properties of undefined”的错误。
  • 你观察到第二个索引不受影响,是因为错误在访问行的阶段就已经触发,未执行到列索引的越界检查;或者列索引的计算值刚好在表的宽度范围内,暂时未暴露问题。

解决方案

对行和列索引进行边界约束,确保它们始终在数组的有效范围内。可以选择两种方式:

  1. 取模循环:让索引超出范围后循环回到数组开头(适合隧道循环移动的视觉效果)
  2. 截断(Clamp):让索引固定在数组的最小/最大值上(避免索引越界,但视觉上会有边缘截断)

方式1:取模循环修复代码

function draw(time) {
  let animation = time / 1000.0;
  
  let shiftX = ~~(texWidth * animation);
  let shiftY = ~~(texHeight * 0.25 * animation);
  let shiftLookX = (screenWidth / 2) + ~~(screenWidth / 2 * Math.sin(animation))
  let shiftLookY = (screenHeight / 2) + ~~(screenHeight / 2 * Math.sin(animation));

  // 获取表的行列边界
  const tableRows = distanceTable.length;
  const tableCols = distanceTable[0].length;

  for (let y = 0; y < buffer.height; y++) {
    for (let x = 0; x < buffer.width; x++) {
      let id = (y * buffer.width + x) * 4;
      
      // 计算行索引,取模确保在有效范围内,处理负数情况
      let row = (y + shiftLookY) % tableRows;
      if (row < 0) row += tableRows;
      // 计算列索引,同理处理
      let col = (x + shiftLookX) % tableCols;
      if (col < 0) col += tableCols;

      let d = ~~(distanceTable[row][col] + shiftX) % texWidth;
      let a = ~~(angleTable[row][col] + shiftY) % texHeight;
      let tex = (a * texture.width + d) * 4;

      buffer.data[id] = texture.data[tex];
      buffer.data[id+1] = texture.data[tex+1];
      buffer.data[id+2] = texture.data[tex+2];
      buffer.data[id+3] = texture.data[tex+3];
    }
  }

  ctx.putImageData(buffer, 0, 0);
  window.requestAnimationFrame(draw);
}

方式2:截断(Clamp)修复代码

将上述代码中的索引计算部分替换为:

let row = Math.max(0, Math.min(y + shiftLookY, tableRows - 1));
let col = Math.max(0, Math.min(x + shiftLookX, tableCols - 1));

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.01 07:45:37