-1

I have weird issue, it was working before, the issue is on CodePen. I have full size Canvas:

function width() {
  // why -1 ?
  // without this there is horizontal scrollbar
  // I have no idea what is causing this
  return window.innerWidth - 1;
}

// ---------------------------------------------------------------
function height() {
  return window.innerHeight;
}

var canvas = document.getElementsByTagName('canvas')[0];
canvas.width = width();
canvas.height = height();

I'm not sure what is causing it but as you see I've added -1 to the width otherwise there is horizontal scrollbar. I think it was working before, it happen on GNU/Linux Chrome.

The canvas have display: block, you can see the code at this Matrix Rain Demo, it works fine in debug mode.

Is this something with CodePen, anyone have a clue why this 1px.

This works fine in StackSnippets:

function width() {
  return window.innerWidth;
}

// ---------------------------------------------------------------
function height() {
  return window.innerHeight;
}

var canvas = document.getElementsByTagName('canvas')[0];
canvas.width = width();
canvas.height = height();
canvas {
  display: block;
}
body {
  margin: 0;
}
<canvas></canvas>

EDIT:

The problem appear only on certain with of the window. To see you need to resize slowly. For my (I have FullHD laptop) it appear for example at: 1594 but work at 1595. If I use -1 it the scrollbar never appear without it it flicker it shows up in some widths.

To be precise about the browser and OS I have Fedora 32 with Google Chrome stable (just updated to 84.0.4147.105 (Official version) (64-bit))

EDIT2:

My demo works in Stack Snippet no need to fix my demo:

var katagana = gen_unicode(0x30A1, 0x30F6);
var hiragana = gen_unicode(0x3041, 0x3096);

// ---------------------------------------------------------------
class Matrix {
  constructor(canvas, { font_size = 14, width, height } = {}) {
    this._canvas = canvas;
    this._ctx = canvas.getContext('2d');
    this._font_size = font_size;
    this._drops = [];
    this._columns = Math.floor(width / font_size);
    this._chars = katagana.concat(hiragana);
    this.resize(width, height);
  }
  random_char() {
    return rnd(this._chars);
  }
  render_char(char, x, y) {
    this._ctx.fillText(char, x, y);
  }
  start() {
    let frames = 0;
    this._run = true;
    const self = this;
    (function loop() {
      if (frames++ % 2 === 0) {
        self.render(); // slower render
      }
      if (self._run) {
        requestAnimationFrame(loop);
      }
    })()
  }
  stop() {
    this._run = false;
  }
  reset() {
    for (let x = 0; x < this._columns; x++) {
      this._drops[x] = 255;
    }
  }
  resize(width, height) {
    this._width = width;
    this._height = height;
    this._canvas.height = height;
    this._canvas.width = width;
    this.reset();
  }
  clear() {
    this._ctx.fillStyle = 'rgba(0, 0,0,0.05)';
    this._ctx.fillRect(0, 0, this._width, this._height);
    this._ctx.fillStyle = '#0F0';
    this._ctx.font = this._font_size + "px monospace";
  }
  render() {
    this.clear();
    for (let col = 0; col < this._drops.length; col++) {
      const char = this.random_char();
      const x = col * this._font_size;
      const y = this._drops[col] * this._font_size;
      this.render_char(char, x, y);
      if (y > this._height && Math.random() > .975) {
        this._drops[col] = 0;
      }
      this._drops[col]++;
    }
  }
}

// ---------------------------------------------------------------
// :: Init code
// ---------------------------------------------------------------
var canvas = document.getElementsByTagName('canvas')[0];

const matrix = new Matrix(canvas, {
  font_size: 14,
  width: width(),
  height: height()
});

matrix.start();

window.addEventListener('resize', e => {
  matrix.resize(width(), height());
});

// ---------------------------------------------------------------
// :: Utils
// ---------------------------------------------------------------
function gen_unicode(start, end) {
  var chars = [];
  for (var i = start; i <= end; ++i) {
    chars.push(String.fromCharCode(i));
  }
  return chars;
}

// ---------------------------------------------------------------
function rnd(array) {
  return array[Math.floor(Math.random() * array.length)]
}

// ---------------------------------------------------------------
function width() {
  // why -1 ?
  // without this there is horizontal scrollbar
  // I have no idea what is causing this
  return window.innerWidth;
}

// ---------------------------------------------------------------
function height() {
  return window.innerHeight;
}
body {
  background: black;
  margin: 0;
  min-width: 100vw;
  min-height: 100vh;
}
canvas {
  display: block;
  margin: 0;
}
<canvas></canvas>

EDIT: the 1px was not the fix it happen with or without it.

jcubic
  • 61,973
  • 54
  • 229
  • 402

2 Answers2

0

This will probably not fix your issue but here is a different idea to avoid having to refresh the canvas on resize. You simply use big width/height attribute and you consider object-fit to avoid the distortion (related: How does object-fit work with canvas element?)

var katagana = gen_unicode(0x30A1, 0x30F6);
var hiragana = gen_unicode(0x3041, 0x3096);

// ---------------------------------------------------------------
class Matrix {
  constructor(canvas, { font_size = 14} = {}) {
    this._canvas = canvas;
    this._ctx = canvas.getContext('2d');
    this._font_size = font_size;
    this._drops = [];
    this._columns = Math.floor(2000 / font_size);
    this._chars = katagana.concat(hiragana);
    this._width = 2000;
    this._height = 2000;
    this.reset();
  }
  random_char() {
    return rnd(this._chars);
  }
  render_char(char, x, y) {
    this._ctx.fillText(char, x, y);
  }
  start() {
    let frames = 0;
    this._run = true;
    const self = this;
    (function loop() {
      if (frames++ % 2 === 0) {
        self.render(); // slower render
      }
      if (self._run) {
        requestAnimationFrame(loop);
      }
    })()
  }
  stop() {
    this._run = false;
  }
  reset() {
    for (let x = 0; x < this._columns; x++) {
      this._drops[x] = 255;
    }
  }
  clear() {
    this._ctx.fillStyle = 'rgba(0, 0,0,0.05)';
    this._ctx.fillRect(0, 0, this._width, this._height);
    this._ctx.fillStyle = '#0F0';
    this._ctx.font = this._font_size + "px monospace";
  }
  render() {
    this.clear();
    for (let col = 0; col < this._drops.length; col++) {
      const char = this.random_char();
      const x = col * this._font_size;
      const y = this._drops[col] * this._font_size;
      this.render_char(char, x, y);
      if (y > this._height && Math.random() > .975) {
        this._drops[col] = 0;
      }
      this._drops[col]++;
    }
  }
}

// ---------------------------------------------------------------
// :: Init code
// ---------------------------------------------------------------
var canvas = document.getElementsByTagName('canvas')[0];

const matrix = new Matrix(canvas, {
  font_size: 14
});

matrix.start();

// ---------------------------------------------------------------
// :: Utils
// ---------------------------------------------------------------
function gen_unicode(start, end) {
  var chars = [];
  for (var i = start; i <= end; ++i) {
    chars.push(String.fromCharCode(i));
  }
  return chars;
}

// ---------------------------------------------------------------
function rnd(array) {
  return array[Math.floor(Math.random() * array.length)]
}
body {
  background: black;
  margin: 0;
  height:100vh;
}
canvas {
  width:100%;
  height:100%;
  display:block;
  object-fit:none;
  object-position:top left;
}
<canvas width="2000" height="2000"></canvas>
Temani Afif
  • 245,468
  • 26
  • 309
  • 415
  • 1
    What about if the screen will be larger then 2000px. Also this will be overkill to render 2000px on a Phone? BTW: I was asking about 1px bug not about resizing, I'm in a process of creating reproducible example for bug report. – jcubic Aug 06 '20 at 23:18
  • @jcubic the 1px bug is for sure due to the resize feature and the width/height you are setting dynamically. I provided an alternative way to avoid all this and use static width/height (it can be helpful for someone else) – Temani Afif Aug 06 '20 at 23:20
  • As you can see from stack snippet this happen only on CodePen it work fine on stack snippet so your example don't fix anything because my Matrix Rain works fine in stack snippet. – jcubic Aug 06 '20 at 23:32
  • No, I don't want to run fixed 2000x2000 canvas on mobile. It nice as example but no use to me. – jcubic Aug 06 '20 at 23:40
0

Found this old article by Chris Coyier on CodePen blog Full Screen Canvas

This the solution was:

function resizeCanvas() {
  can.style.width = window.innerWidth + "px";
  setTimeout(function() {
    can.style.height = window.innerHeight + "px";
  }, 0);
}

This is the same as:

function resizeCanvas() {
  canvas.width = window.innerWidth;
  setTimeout(function() {
    canvas.height = window.innerHeight;
  }, 0);
}

and the solution was to also hide the scrollbar using:

body {
  margin: 0;
  overflow: hidden;
}
jcubic
  • 61,973
  • 54
  • 229
  • 402