0

I have made an image grid using flexbox on a webpage that I am working on. There are 8 boxes in the grid, and I would like to randomly chose one and assign it one of 12 random images every 2 seconds. My approach is to have an array of urls to the images I would like to pick from, generate a random number to select which box with have its image changed, and then pick a random image and assign it to the background image of that box. I would just like to ensure that the same image does not appear twice, as well as include some sort of transition rather than simply changing the image. Any help here is greatly appreciated.

HTML:

<div class="stats__grid--desktop">
    <div class="grid__top">
        <div class="grid__top--item one" style="background-image: url(../css/images/work-template/bench/stats2/a.png);"></div>
        <div class="grid__top--item two" style="background-image: url(../css/images/work-template/bench/stats2/b.png);"></div>
        <div class="grid__top--item three" style="background-image: url(../css/images/work-template/bench/stats2/c.png);"></div>
        <div class="grid__top--item four" style="background-image: url(../css/images/work-template/bench/stats2/d.png);"></div>
    </div>
    <div class="grid__mid">
        <div class="grid__top--item five" style="background-image: url(../css/images/work-template/bench/stats2/e.png);"></div>
        <div class="grid__top--item six" style="background-image: url(../css/images/work-template/bench/stats2/f.png);"></div>
        <div class="grid__top--item seven" style="background-image: url(../css/images/work-template/bench/stats2/g.png);"></div>
        <div class="grid__top--item eight" style="background-image: url(../css/images/work-template/bench/stats2/h.png);"></div>
    </div>
</div>

jQuery:

window.setInterval(function() {
                        var imgArray = ["../css/images/work-template/bench/stats2/j.png", "../css/images/work-template/bench/stats2/k.png", "../css/images/work-template/bench/stats2/l.png"]
                        var imgSelect = 0 + Math.floor(Math.random() * 2);
                        var number = 1 + Math.floor(Math.random() * 8);
                        if (number == 1) {
                            var imgChange = "one";
                        }
                        else if (number == 2) {
                            var imgChange = "two";
                        }
                        else if (number == 3) {
                            var imgChange = "three";
                        }
                        else if (number == 4) {
                            var imgChange = "four";
                        }
                        else if (number == 5) {
                            var imgChange = "five";
                        }
                        else if (number == 6) {
                            var imgChange = "six";
                        }
                        else if (number == 7) {
                            var imgChange = "seven";
                        }
                        else if (number == 8) {
                            var imgChange = "eight";
                        }
                        else {
                            var imgChange = "nine";
                        }
                        $("." + imgChange).css( "background-image", "url("+imgArray[imgSelect]+")" );
                        var temp =  $("." + imgChange).css("background-image")
                        console.log(temp)
                    }, 500);
JSON_Derulo
  • 601
  • 8
  • 28
  • Unfortunately you cannot transition between 2 `background-image`s. As far as I am aware, the only approach would be to fade out the current image, then load in an invisible new image and then fade it back in. [This page] (https://coderwall.com/p/vtvmsa/change-the-background-image-with-animate) demonstrates a nice solution. – Daniel Arthur Apr 28 '17 at 19:52
  • @DanielArthur I added transition: background 1s to the class for the background images and it seems to do the trick. All that is left is ensuring the same image does not appear twice. – JSON_Derulo Apr 28 '17 at 19:59
  • I think the best option is to have an array that contains the images that have already been displayed? – Daniel Arthur Apr 28 '17 at 20:01

2 Answers2

2

Based on what you've explained, it looks like you're already properly selecting a random image and random item in the grid. All that's left to do is ensure that you're not displaying the same image at the same time.

I would also suggest changing your class names so that your system is more flexible. Using "one", "two", "three", etc. works, but your HTML and CSS will be much more elegant and scalable if you instead use something like "g1", "g2", "g3", etc., where "g" stands for grid item. In that case, you'd do the following (trying to avoid hardcoding):

var imgArray = ["../css/images/work-template/bench/stats2/a.png", "../css/images/work-template/bench/stats2/b.png", "../css/images/work-template/bench/stats2/c.png", "../css/images/work-template/bench/stats2/d.png", "../css/images/work-template/bench/stats2/e.png", "../css/images/work-template/bench/stats2/f.png", "../css/images/work-template/bench/stats2/g.png", "../css/images/work-template/bench/stats2/h.png", "../css/images/work-template/bench/stats2/i.png", "../css/images/work-template/bench/stats2/j.png", "../css/images/work-template/bench/stats2/k.png", "../css/images/work-template/bench/stats2/l.png"];

setInterval(function() {
    var $gridItems = $(".stats__grid--desktop .grid__top--item");
    var gridLength = $gridItems.length;
    var gridItemNum = 1 + Math.floor(Math.random() * gridLength);
    var $gridItem = $(".stats__grid--desktop .grid__top--item.g" + gridItemNum);
    var illegalImgs = [];
    var legalImgs = [];

    // Loop through images currently in grid to eliminate them
    for (var i = 0; i < gridLength; i++) {
        var curr = $($gridItems[i]).css("background-image");
        curr = curr.replace('url(','').replace(')','').replace(/\"/gi, "");
        curr = curr.replace(/http(?:s)?:\/\/\w+\.\w+/gi, '..');
        var imgIndex = imgArray.indexOf(curr);
        illegalImgs.push(imgIndex);
    }

    // Find images that may be used ("legal" images)
    for (var i = 0; i < imgArray.length; i++) {
        if (illegalImgs.indexOf(i) === -1) { legalImgs.push(i); }
    }

    // Choose image randomly from legal images
    var legalID = Math.floor(Math.random() * legalImgs.length);
    var imgID = legalImgs[legalID];
    var imgURL = imgArray[imgID];

    $gridItem.css("background-image", "url(" + imgURL + ")");        
}, 2000);

Alternatively, check out a working demo that I made on Codepen.

Additionally, here's one way to transition your background image: CSS3 background image transition

I also wanted to note that you don't necessarily need to use classes for each specific grid item. Even using the fix I've included above is a bit tedious. If you need to access each individual grid item separately, try using the :nth-child() selector, and in Javascript, you can use the $(".some-element").children(".optional-selector-goes-here")[someInteger]. More info on the nth-child selector here.

EDIT: Originally, there was an issue because your URLs were local, but I've added a regular expression to fix that. See the following line in the script above:

curr = curr.replace(/http(?:s)?:\/\/\w+\.\w+/gi, '..');
Community
  • 1
  • 1
jmindel
  • 335
  • 5
  • 14
  • unfortunately does not quite work out. Really appreciate the help. If you want to check out the live site you can look here https://gohrvst.com/work/bench.html – JSON_Derulo Apr 28 '17 at 21:43
  • @SpencerM. What section of the website are you referring to, more specifically? The layout I made was just a sample--the CSS is all for the sake of demoing. Could you clarify what kind of effect you're going for? – jmindel Apr 28 '17 at 21:45
  • In the fourth "module" on that page is the grid that I made with 9 images. I set their default background but would like to change a random grid item to a random, not already being used background image, every 0.3 seconds. – JSON_Derulo Apr 28 '17 at 21:48
  • @SpencerM. Ah, I see. So you don't want images to display more than once, right? A simple fix would be to remove the URL from `imgArray` after it's been used. I can make that change to my script right now. Would that solve your problem? – jmindel Apr 28 '17 at 21:49
  • I pasted the code you included above and changed the classes from "one, two, three, etc" to "g1, g2, g3..." – JSON_Derulo Apr 28 '17 at 21:49
  • I don't wan to display the same image twice at the same time, since there is 9 items and 12 images, inevitably the same image will surface more than once. – JSON_Derulo Apr 28 '17 at 21:50
  • @SpencerM. That's not true. The algorithm I've written will not allow the same image to be displayed in the grid more than once. Please take a look at the pen on Codepen that I included, click on the "start demo" button, and let me know if that still doesn't do what you want it to do. You could also increase the time before intervals, if you really want to be sure. – jmindel Apr 28 '17 at 21:51
  • You can see on the link I provided that there is in fact the same image being shown more than once at a time. – JSON_Derulo Apr 28 '17 at 21:56
  • I found a fix. I'll update my code accordingly in just a moment. What happened is that the URLs needed to be truncated to remove the "https://your-url.com". I threw together a regular expression to solve the problem. Just note that you'll need to change that if the URLs are not local--that is, if your URLs are on your server (i.e. "../file.png" vs. "http://x.com/file.png"), you need to use the regular expression that I added (see the new change to `curr`'s value. – jmindel Apr 28 '17 at 22:04
  • @SpencerM. Please let me know if the new solution works for you. – jmindel Apr 28 '17 at 22:18
0

I suggest making a grid of div elements each containing 2 swap img elements which are absolutely positioned on top of each other.

Every time you swap images, use splice to randomly remove a URL from the array and put it in the bottom img and then fade it in as you fade out the top img, creating a cross-fade effect. When the cross-fade finishes, push the src of the top image back onto the URL array. Change the bottom img to the top to be ready to do it again. Rinse and repeat.

I also suggest, as has been stated elsewhere that you uniquely reference each box with an id that ends with its order number. This makes it easy to choose the element programatically, without a big if/then/else statement. It also makes it easy to change the size of the grid, as you don't need to change the code, just the css.

I made a grid, with each box coded like so:

<div class="grid__box" id="g0">
  <img class="swap">
  <img class="swap">
</div>

With g0 being unique in each box. g1, g2, g3, etc.

The jQuery looks like this:

var imgArray = ["yoururlhere", "yoururlhere2", ..., "anotherurl" ];

// load the images from the array initially
function loadImages() {
  $('.grid__box').each(function(i) {
    var newImage = imgArray.splice(0,1)[0];
    $(this).find('.swap').attr('src', newImage);
  });
}

// choose a box at random, swap its image with a random one from the array
function swapImage() {
  var imgChoice = Math.floor(Math.random() * imgArray.length);
  var newImage = imgArray.splice(imgChoice,1)[0];

  var target = Math.floor(Math.random() * $('.grid__box').length);

  var mainImg = $('#g' + target + ' .swap').eq(1);
  var swapImg = $('#g' + target + ' .swap').eq(0);

  swapImg.show().attr('src', newImage);
  imgArray.push(mainImg.attr('src'));

  swapImg.fadeIn(2000);
  mainImg.fadeOut(2000, function() {
    // put the now invisible image in the back 
    // to be used as the swap image next time
    swapImg.insertAfter(mainImg);

    // do it again
    swapImage();
  });
}

loadImages();
swapImage();

JSFiddle Demo: https://jsfiddle.net/ttbpbj2v/

Jeff B
  • 29,005
  • 6
  • 60
  • 85