0

I am trying to select random divs and change background-color. Only 8 items should be visible at a time, If there are more colors in array, it shows randomly.

Sometimes same div repeats. How can I make it more random?

var c = ["123abc", "12abc3", "abc123", "1dcb23", "1876c3", "a34523", "165423", "1dcb23", "E53E3E", "7B341E", "D69E2E", "48BB78", "38B2AC", "ED64A6"];
var sub;
var shuffled;
var l = document.getElementsByClassName('item');
function disp(){
  var i = 0;
  var s = setInterval(function(){
    var ran = Math.floor(Math.random() * l.length);
    l[ran].style.backgroundColor = '#' + sub[i];
    var opacity = 0;
    function MyFadeFunction() {
     if (opacity<1) {
        opacity += .1;
        setTimeout(function(){MyFadeFunction()},50);
     }
     l[ran].style.opacity = opacity;
    }
    MyFadeFunction();
    i++;
    if(i == sub.length){
      i = 0;
      clearTimeout(s);
      shufflearray();
    }
  }, 1500);
}
function shufflearray(){
  var a = [];
  shuffled = c.sort(() => Math.random() - 0.5);
  sub = shuffled.slice(8, shuffled.length - 1);
  disp();
}
shufflearray();
.item{
  width: 50px; height: 50px; float: left; margin: 10px;
}
<div class="wrap">
  <div class="item" style="background-color:#123abc"></div>
  <div class="item" style="background-color:#12abc3"></div>
  <div class="item" style="background-color:#abc123"></div>
  <div class="item" style="background-color:#1dcb23"></div>
  <div class="item" style="background-color:#12098c"></div>
  <div class="item" style="background-color:#1876c3"></div>
  <div class="item" style="background-color:#a34523"></div>
  <div class="item" style="background-color:#165423"></div>
</div>

Codepen - https://codepen.io/felixaj/pen/LYNZbZY


Felix A J
  • 6,300
  • 2
  • 27
  • 46
  • 1
    Please define "more random". Do you just want to make sure the same div does not change twice in a row, but if it happens every third time thats ok? Or would you like to force the change on a div to happen only after each div in the set has changed once? Where is the threshold? – apena Aug 17 '20 at 17:57
  • 1
    Think more about shuffling the colors instead of randomizing them. A truly random algorithm could generate the same color many times in a row. – Dan Mullin Aug 17 '20 at 17:57
  • @apena Ideally all divs changing color once in a rotation, as in your second question – Felix A J Aug 17 '20 at 18:04

3 Answers3

1

It sounds like you want every .item element to have a random background color, but you don't want the colors to repeat unless you've run out of colors to pick from. To accomplish this, all you need to do is shuffle the colors array and apply the colors in their shuffled order to each div. When you run out of shuffled colors, refill them. It's very simple to do this with rando.js' randoSequence function (which is cryptographically secure), or you can use this post to do the shuffling yourself.

var colors = ["#123abc", "#12abc3", "#abc123", "#1dcb23", "#1876c3", "#a34523", "#165423", "#1dcb23", "#E53E3E", "#7B341E", "#D69E2E", "#48BB78", "#38B2AC", "#ED64A6"];
var shuffledColors = [];

for(var i = 0; i < document.getElementsByClassName("item").length; i++){
  //refill shuffledColors with shuffled info from the colors variable if empty
  shuffledColors = shuffledColors.length ? shuffledColors : randoSequence(colors);
  
  //grab the next value from shuffledColors and apply it to the next div
  document.getElementsByClassName("item")[i].style.backgroundColor = shuffledColors.pop().value;
}
.item {
  height: 20px;
}
<script src="https://randojs.com/2.0.0.js"></script>

<div class="item" style="background-color:#123abc"></div>
<div class="item" style="background-color:#12abc3"></div>
<div class="item" style="background-color:#abc123"></div>
<div class="item" style="background-color:#1dcb23"></div>
<div class="item" style="background-color:#12098c"></div>
<div class="item" style="background-color:#1876c3"></div>
<div class="item" style="background-color:#a34523"></div>
<div class="item" style="background-color:#165423"></div>

This line is what allows you to use rando.js in your javascript, so don't forget to put this in the head of your HTML document if you want to use the randoSequence function:

<script src="https://randojs.com/2.0.0.js"></script>

Just for fun...

It's not the easiest to read, but this js can actually be written in two lines, which is kind of fun:

var colors = ["#123abc", "#12abc3", "#abc123", "#1dcb23", "#1876c3", "#a34523", "#165423", "#1dcb23", "#E53E3E", "#7B341E", "#D69E2E", "#48BB78", "#38B2AC", "#ED64A6"], shuffledColors = [];
for(var i = 0; i < document.getElementsByClassName("item").length; i++) document.getElementsByClassName("item")[i].style.backgroundColor = (shuffledColors = shuffledColors.length ? shuffledColors : randoSequence(colors)).pop().value;
Aaron Plocharczyk
  • 2,776
  • 2
  • 7
  • 15
1

To never repeat animating a div (or a color) until all divs in the 'set' have been animated, you can splice a random element out of a copy of a subset (set) of your main color array untill there are no more items and then at that point create another subset (set) array from your main colors and reapeat the process. I added a console log in the below snippet to show a little more about what is happening.

NOTE: As for fading visual elements via javascript, the snippet will get buggy if you leave the page because it should use a webworker (or css) to do the fading so the timer is not throttled when the page is hidden which throws off the animation.

const tileTotal = 8;
let colors = ["123abc", "12abc3", "abc123", "1dcb23", "1876c3", "a34523", "165423", "1dcb23", "E53E3E", "7B341E", "D69E2E", "48BB78", "38B2AC", "ED64A6"];
let startingColors = colors.slice(0, tileTotal);
const tiles = document.getElementById('tiles');
let tile;
startingColors.forEach(color => {
  tile = document.createElement('div');
  tile.classList.add('tile');
  tile.style.backgroundColor = '#' + color;
  tiles.appendChild(tile);
});

const randomize = (amt) => {
  const allRandomColors = colors.sort(() => Math.random() - 0.5);
  return allRandomColors.slice(0, amt);
}

let randColorsOrig = randomize(tileTotal);
let randColors = Array.from(randColorsOrig);
let count = 0;
let fadeInterval;
function colorize() {
  let randLoc = Math.floor(Math.random() * randColors.length)
  let color = randColors.splice(randLoc, 1)[0];
  let index = randColorsOrig.indexOf(color);

  fadeInterval = setInterval(fade.bind(null, index, color), 50);
  ++count;

  console.log(`fading color ${'#' + color} at tile index ${index}`)
  
  if (count == tileTotal) {
    clearTimeout(fadeInterval)
    console.log('resetting colors and indices') 
    count = 0;
    randColorsOrig = randomize(tileTotal);
    randColors = Array.from(randColorsOrig);
  }
}

let opacity = 0;
function fade(index, color) {
  tiles.childNodes[index].style.opacity = opacity;
  tiles.childNodes[index].style.backgroundColor = '#' + color
  opacity < 1 ? (opacity += .1) : (opacity = 0, clearInterval(fadeInterval));
}

setInterval(colorize, 1500);
#tiles {
  display: flex;
}

.tile {
  width: 6rem;
  height: 6rem;
  margin-top: 1rem;
  margin-right: 1rem;
}
<div id="tiles"></div>
apena
  • 2,091
  • 12
  • 19
0

An alternative is to create two arrays for colors, where one would have the first 8 colors and in the same order as each div, the second would have the new colors that would be added randomly.

Example:

var c1 = ["123abc", "12abc3", "abc123", "1dcb23", '12098c', "1876c3", "a34523", "165423"];
//
var c2 = ["E53E3E", "7B341E", "D69E2E", "48BB78", "38B2AC", "ED64A6"]

var l = document.getElementsByClassName('item');

function callme() {
   let rand1 = Math.floor(Math.random() * l.length)
   let rand2 = Math.floor(Math.random() * c2.length)

   
  l[rand1].style.backgroundColor = '#' + c2[rand2]
  
  let c3 = c1[rand1]
  c1[rand1] = c2[rand2]
  c2[rand2] = c3
  
}
setInterval(callme, 1500)
.item{
  width: 50px; height: 50px; float: left; margin: 10px;
  color: white;
}
<div class="wrap">
  <div class="item" style="background-color:#123abc"> 
  </div>
  <div class="item" style="background-color:#12abc3"></div>
  <div class="item" style="background-color:#abc123"></div>
  <div class="item" style="background-color:#1dcb23"></div>
  <div class="item" style="background-color:#12098c"></div>
  <div class="item" style="background-color:#1876c3"></div>
  <div class="item" style="background-color:#a34523"></div>
  <div class="item" style="background-color:#165423"></div>
</div>

I reduced the code to make the idea clear

Aks Jacoves
  • 849
  • 5
  • 9