conor909 conor909 - 1 month ago 8
Javascript Question

javascript canvas drawn shapes look like they overlap even though x and y say they're side by side

Here I'm trying to draw squares with the same opacity, side by side.

Even though each squares x position is exactly the width of a square apart, they look as though there are more semi transparent squares being drawn on top of them. The first square having the higher opacity level, and less so on the last one, which creates this gradient of squares effect.

Result

I have console logged out the number of squares being rendered and it is as expected. So I dont believe there are extra squares being rendered on top of them.
I'v also checked the x positions to see if there is any overlap, which there isn't. So what could be causing the squares to have different transparencies?

const windowWidth = window.innerWidth;
const windowHeight = window.innerHeight;
const canvas = document.getElementById("app-canvas");
canvas.width = windowWidth;
canvas.height = windowHeight;
canvas.style.position = "absolute";
canvas.style.backgroundColor = "grey";
canvas.style.zIndex = 1;
document.body.appendChild(canvas);

class Scene {
constructor() {
this.orbs = [];
this.squares = [];
this.context = canvas.getContext("2d");
}

addOrb(orb) {
this.orbs.push(orb);
}

addSquare(square) {
this.squares.push(square);
}

render() {
this.context.clearRect(0, 0, windowWidth, windowHeight);
this.squares.forEach(square => {
square.draw(this.context);
});

}
}

class Square {
constructor(options) {
this.options = options;
}

draw(ctx) {
let x = this.options.x;
let y = this.options.y;
let width = this.options.width;
let height = this.options.height;
ctx.moveTo(x,y);
ctx.rect(x, y, width, height);
ctx.fillStyle = "rgba(255,255,255,.1)";
ctx.fill();
}
};

let squares = [];
let squareWidth = 200;
let squareHeight = 200;

let x = 0;
let y = 0;
for (let i = 0; i < windowWidth; i+=squareWidth) {
x += squareWidth;
let square = new Square({
x, y: 0, width: squareWidth, height: squareHeight
});
console.log(square)
squares.push(square);
}
console.log(squares.length);

let scene = new Scene();
squares.forEach(square => scene.addSquare(square));

scene.render();

Answer

You need a call to ctx.beginPath() before every rectangle. Otherwise, the calls to .rect() are just adding to the path, and fill() fills the whole thing.

draw(ctx) {
    let x = this.options.x;
    let y = this.options.y;
    let width = this.options.width;
    let height = this.options.height;
    ctx.beginPath(); // <========= this
    ctx.moveTo(x,y);
    ctx.rect(x, y, width, height);
    ctx.fillStyle = "rgba(255,255,255,.1)";
    ctx.fill();
}

You might also consider just using .fillRect().

Comments