I have an etch-a-sketch program which works fine apart from one small issue. When I click the "change grid size" button and enter certain numbers (such as 51, 44, 45), the right and bottom edges of my grid are white instead of black. Some numbers like 10, 16, 25, don't give this issue.
This issue happens in chrome browser. When I use firefox browser, my grid becomes even worse and goes all wonky with a column outside of the grid.
Here is my code:
let container = document.querySelector(".container");
createGrid(16);
function createGrid(gridDimensions) {
let gridSize = 800;
let gridPixelSize = gridSize / gridDimensions;
for(row = 1; row < (gridDimensions + 1); row++) {
let column = 1;
let square = document.createElement("div");
square.id = "square-" + row + "-" + column;
square.className = "squares";
square.style.height = `${gridPixelSize}px`;
square.style.width = `${gridPixelSize}px`;
container.appendChild(square);
for(column = 2; column < (gridDimensions + 1); column++) {
let square = document.createElement("div");
square.id = "square-" + row + "-" + column;
square.className = "squares";
square.style.height = `${gridPixelSize}px`;
square.style.width = `${gridPixelSize}px`;
container.appendChild(square);
}
}
drawOnGrid(gridPixelSize);
}
function drawOnGrid(gridPixelSize) {
let squares = document.querySelectorAll(".squares");
squares.forEach((square) => {
let squareDark = document.createElement("div");
squareDark.style.height = `${gridPixelSize}px`;
squareDark.style.width = `${gridPixelSize}px`;
squareDark.style.backgroundColor = "black";
let squareDarkOpacity = 0;
squareDark.style.opacity = `${squareDarkOpacity}`;
square.appendChild(squareDark);
square.addEventListener("mouseover", (e) => {
if(!hasColorBeenSet.includes(square)) {
hasColorBeenSet.push(square);
let randomColor = Math.floor(Math.random() * 16777215).toString(16);
square.style.backgroundColor = `#${randomColor}`;
}
squareDarkOpacity += 0.1;
squareDark.style.opacity = `${squareDarkOpacity}`;
});
});
let hasColorBeenSet = [];
}
function changeGridSize() {
let validAnswer = false;
while(!validAnswer) {
let userInput = prompt("How big should the grid be? Enter a number between 8 to 100.");
userInput = Number(userInput);
userInput = Math.round(userInput);
if(userInput >= 8 && userInput <= 100) {
validAnswer = true;
container.innerHTML = "";
createGrid(userInput);
}
}
}
* {
padding: 0;
margin: 0;
box-sizing: border-box;
}
body {
height: 100vh;
background-color: rgb(90, 101, 110);
display: flex;
flex-direction: column;
gap: 50px;
justify-content: center;
align-items: center;
}
.container-border {
border: 1px solid black;
}
.container {
height: 800px;
width: 800px;
background-color: white;
display: flex;
flex-wrap: wrap;
}
.squares {
border: 1px solid black;
}
button {
font-size: 26px;
padding: 10px 20px;
border-radius: 5px;
}
button:hover {
transition: 0.2s;
background-color: orange;
cursor: pointer;
}
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<link rel="stylesheet" href="./style.css">
<script src="main.js" defer></script>
<title>Etch-a-sketch</title>
</head>
<body>
<button onclick="changeGridSize()">Change grid size...</button>
<div class="container-border">
<div class="container"></div>
</div>
</body>
</html>
This is because of how browsers handle subpixel rendering. The problem occurs when 800
is not divisible by userInput
. For example, 800 / 20 = 40
, so it renders fine, but 800 / 14 = 57.143
, and due to inaccuracies, that gap appears. They can appear not only on edges but between cells as well.
I came up with two methods to solve this:
If you change the background-color
of the container
to black and squares
to white, the white gap will become black. As a side effect, the border may become a little thicker:
.container {
height: 800px;
width: 800px;
background-color: black;
display: flex;
flex-wrap: wrap;
}
.squares {
border: 1px solid black;
background-color: white;
}
Another method, which I believe works better, is to first round the gridPixelSize
to the nearest integer in the createGrid
function:
let gridPixelSize = Math.round(gridSize / gridDimensions);
And then calculate the container size based on that:
let containerSize = gridDimensions * gridPixelSize;
Then we can resize the container
with the newly calculated size:
container.style.height = `${containerSize}px`;
container.style.width = `${containerSize}px`;
As a side effect, the container size will change slightly, but the difference is negligible.
This is the final result:
let container = document.querySelector('.container');
createGrid(16);
function createGrid(gridDimensions) {
let gridSize = 800;
let gridPixelSize = Math.round(gridSize / gridDimensions);
let containerSize = gridDimensions * gridPixelSize;
for (let row = 1; row < (gridDimensions + 1); row++) {
for (let column = 1; column < (gridDimensions + 1); column++) {
let square = document.createElement('div');
square.id = 'square-' + row + '-' + column;
square.className = 'squares';
square.style.height = `${gridPixelSize}px`;
square.style.width = `${gridPixelSize}px`;
container.appendChild(square);
}
}
drawOnGrid(gridPixelSize);
container.style.height = `${containerSize}px`;
container.style.width = `${containerSize}px`;
}
function drawOnGrid(gridPixelSize) {
let squares = document.querySelectorAll(".squares");
squares.forEach((square) => {
let squareDark = document.createElement("div");
squareDark.style.height = `${gridPixelSize}px`;
squareDark.style.width = `${gridPixelSize}px`;
squareDark.style.backgroundColor = "black";
let squareDarkOpacity = 0;
squareDark.style.opacity = `${squareDarkOpacity}`;
square.appendChild(squareDark);
square.addEventListener("mouseover", (e) => {
if(!hasColorBeenSet.includes(square)) {
hasColorBeenSet.push(square);
let randomColor = Math.floor(Math.random() * 16777215).toString(16);
square.style.backgroundColor = `#${randomColor}`;
}
squareDarkOpacity += 0.1;
squareDark.style.opacity = `${squareDarkOpacity}`;
});
});
let hasColorBeenSet = [];
}
function changeGridSize() {
let validAnswer = false;
while (!validAnswer) {
let userInput = prompt('How big should the grid be? Enter a number between 8 to 100.');
userInput = Number(userInput);
userInput = Math.round(userInput);
if (userInput >= 8 && userInput <= 100) {
validAnswer = true;
container.innerHTML = '';
createGrid(userInput);
}
}
}
* {
padding: 0;
margin: 0;
box-sizing: border-box;
}
body {
height: 100vh;
background-color: rgb(90, 101, 110);
display: flex;
flex-direction: column;
gap: 50px;
justify-content: center;
align-items: center;
}
.container-border {
border: 1px solid black;
}
.container {
height: 800px;
width: 800px;
background-color: white;
display: flex;
flex-wrap: wrap;
}
.squares {
border: 1px solid black;
}
button {
font-size: 26px;
padding: 10px 20px;
border-radius: 5px;
}
button:hover {
transition: 0.2s;
background-color: orange;
cursor: pointer;
}
<button onclick="changeGridSize()">Change grid size...</button>
<div class="container-border">
<div class="container"></div>
</div>