White space between repositioned elements via javascript

I'm creating an image puzzle with javascript. I creating 14 div elements in 2 row and then I add backrgound image to them, setting their positions and widths ect... It is working fine. But then when I repositioning them (shuffle) unwanted white space appears. Not always, not every image and not the same place. I change only two element at the same time.

Here is my change function:


let original_pos = [];

// I choose two element randomly, and pushing the first element positions into the original_pos array
// and I pass the second element to the shuffle_elem() function
function shuffle() {
    let random_elem = document.querySelectorAll(".bg-elem");
    for(let i = 0; i <= 50; i++) {
    let random_num = Math.floor(Math.random() * random_elem.length);
    let random_num2 = Math.floor(Math.random() * random_elem.length);
    original_pos.push(random_elem[random_num].offsetTop);
    original_pos.push(random_elem[random_num].offsetLeft);
    cserel_elem(random_elem[random_num],random_elem[random_num2]);
}

// Here are the positions change
function shuffle_elem(elem1, elem2) {
    elem1.style.left = elem2.offsetLeft+'px';
    elem1.style.top = elem2.offsetTop+'px';
    elem2.style.top = original_pos[0]+'px';
    elem2.style.left  = original_pos[1]+'px';
    original_pos = [];
}

Everything is working fine, so I can change the two elements, but there will be a little white space. In this Picture you can see it in two elements next to each other, but sometimes there is no white space at all or just one element has or almost every element... Totally random.

enter image description here

Only 1 pixel but it is there and very frustrating. Please help me find out where is this one pixel hiding. No jQuery please

Some additional information (CSS):

// the main holder div where my elements are
.PlayGround {
    position: relative;
    overflow: hidden;
}
// one element
.bg-elem {
    position: absolute;
    margin: 0;
    padding: 0;
}

Upvotes: 1

Views: 79

Answers (1)

Niklas
Niklas

Reputation: 1948

With your link provided I was able reproduce the issue, inspect your code and found a fix or two.

Yes, you can see the original elements positioned by javascript, then I wait 1.5s then shuffle them. Here!
– Bálint Gácsfalvy
Comment quote for documentation purposes.

The issue is fairly simple:
You're images might be all the same size, but they are not rounded to full pixels, as shown by the code:

// ratio is already already not an integers
let new_width = imgwidth * ratio;
let new_height = imgheight * ratio;
// and then some lines later you do divide by 7 and 2.
element[i].style.width = new_width/7+'px';
element[i].style.height = new_height/2+'px';

In addition to that you also set left and top not to integer pixels.
All of this is causing these strange lines of the background flashing between the images. Because the rendering engine of the browser can only display images for "boxes" that are defined by whole number pixel position and size, the browser fits the image by rounding to the nearest pixel.
If there's an rounding error, it can happen, that the images next to each other are separated by exactly one pixel. (They can also overlap by one pixel, but that usually goes unnoticed.)

jsfiddle screenshot: background flasching See video of this issue happening on resize.

Now you know why, let's fix this:
There are many ways to fix this, the best would be to do the rounding to full pixels by yourself when setting size and offset (left and top) and not rely on the rounding of the rendering engine. But that's something that is more time expensive and I'm sure you'll figure it out yourself if you want to do it this way.

The quick and dirty way I have chosen to show you here is to make the new_width and new_height values dividable to integers by the number of pieces before you use them. This is super easy, just do this:

let new_width = Math.floor(imgwidth * ratio / 7) * 7;
let new_height = Math.floor(imgheight * ratio / 2) * 2;

instead of this:

let new_width = imgwidth * ratio;
let new_height = imgheight * ratio;

Downside of this fix: You loose up to 6 pixels horizontal and 1 vertical. (I know. Terrible.)

See how the fixed code works here!

Upvotes: 2

Related Questions