Search code examples
javascriptarraysgraphicshtml5-canvas

"Uncaught TypeError: Cannot read properties of undefined (reading '310')" when running Canvas animation


I'm working on another tunnel effect demo. This time I'm trying to make the tunnel move within the image.

However, the function that handles rendering the tunnel always throws an error, and I'm not entirely sure why:

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);
}

The rest of the code is viewable here, just in case the problem happens to be somewhere else.

I have identified a possible cause -- if the first index used to read from distanceTable or angleTable is anything other than y, the error appears, even if it's simply a value being added to y. Unfortunately, I haven't figured out what causes it, or why the second index isn't affected by this.

I've also searched for similar questions, but it seems like the people asking them all got this error for different reasons, so I'm kind of stuck.


Solution

  • It appears that setting the for loops to use the canvas' height and width as the upper limit instead of the pixel buffer's width and height was enough to fix it.

    I have absolutely no idea why, though. Was it because the buffer was twice the size of the canvas?

    var texWidth = 256;
    var texHeight = 256;
    var screenWidth = 640;
    var screenHeight = 480;
    
    var canvas = document.createElement('canvas');
    canvas.width = screenWidth;
    canvas.height = screenHeight;
    
    var ctx = canvas.getContext("2d");
    
    var texture = new ImageData(texWidth, texHeight);
    var distanceTable = [];
    var angleTable = [];
    var buffer = new ImageData(canvas.width * 2, canvas.height * 2);
    
    for (let y = 0; y < texture.height; y++) {
      for (let x = 0; x < texture.width; x++) {
        let id = (y * texture.width + x) * 4;
        let c = x ^ y;
    
        texture.data[id] = c;
        texture.data[id+1] = c;
        texture.data[id+2] = c;
        texture.data[id+3] = 255;
      }
    }
    
    for (let y = 0; y < buffer.height; y++) {
      distanceTable[y] = [];
      angleTable[y] = [];
    
      let sqy = Math.pow(y - canvas.height, 2);
      for (let x = 0; x < buffer.width; x++) {
        let sqx = Math.pow(x - canvas.width, 2);
        let ratio = 32.0;
    
        let distance = ~~(ratio * texHeight / Math.sqrt(sqx + sqy)) % texHeight;
        let angle = Math.abs(~~(0.5 * texWidth * Math.atan2(y - canvas.height, x - canvas.width)) / Math.PI);
    
        distanceTable[y][x] = distance;
        angleTable[y][x] = angle;
      }
    }
    
    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 * 2.0))
    
      for (y = 0; y < canvas.height; y++) {
        for (x = 0; x < canvas.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);
    }
    
    document.body.appendChild(canvas);
    
    window.requestAnimationFrame(draw);