Ghostly44
Ghostly44

Reputation: 23

How would I be able to make a vanilla js dynamic background smooth?

I have been trying to smooth out the transitions on this snippet of code but I don't understand why that transition is not working. I'm trying to not use anything outside of vanilla HTML, CSS, and JavaScript. I know I can achieve a similar result with pure CSS using key animations but I'm very curious if it can be done using mostly js. How can this be done?

<head>
<style>
*{
transition: all 5s !important;

}

div{
    width: 100vw;
    height: 100vh;
}
</style>
</head>      

    <div id='gradient'>
      <h1>hello</h1>
    </div>
    
    
    <script>
    document.querySelector("#gradient").style.backgroundImage= 'linear-gradient(60deg, #3d3393 0%, #2b76b9 37%, #2cacd1 65%, #35eb93 100%)';
    
    function dynamicGradient() {
          let color1 = `rgb(${Math.random()*100+55},${51},${Math.random()*20+100})`
          let color2 = `rgb(${Math.random()*10+13},${78},${Math.random()*10+135})`
          let color3 = `rgb(${Math.random()*10+14},${Math.random()*5+184},${Math.random()*10+160})`
          let color4 = `rgb(${Math.random()*5+28},${Math.random()*10+185},${Math.random()*10+67})`
    
          let linearGradient = `linear-gradient(60deg, ${color1} 10%, ${color2} 37%, ${color3} 65%, ${color4} 100%)`
         
          document.querySelector("#gradient").style.backgroundImage        = linearGradient;
          document.querySelector("#gradient").style.backgroundImage.filter = 'blur(200px)';
    
          console.log('called')
    }
    
    setInterval(() => {
          dynamicGradient()
    }, 800);</script> 

Upvotes: 1

Views: 313

Answers (1)

Scott Marcus
Scott Marcus

Reputation: 65796

CSS transitions don't work here because background-image is treated like an image and transitions require numeric values to interpolate.

But there are non-transition solutions, like this one from: https://opticalcortex.com/animating-css-gradients/

// target to give background to
var $div = document.getElementById("gradient");

// rgb vals of the gradients
var gradients = [
  { start: [128,179,171], stop: [30,41,58] },
  { start: [255,207,160], stop: [234,92,68] },
  { start: [212,121,121], stop: [130,105,151] }
];

// how long for each transition
var transition_time = 4;

// internal type vars
var currentIndex = 0; // where we are in the gradients array
var nextIndex = 1; // what index of the gradients array is next
var steps_count = 0; // steps counter
var steps_total = Math.round(transition_time*60); // total amount of steps
var rgb_steps = {
  start: [0,0,0],
  stop: [0,0,0]
}; // how much to alter each rgb value

var rgb_values = {
  start: [0,0,0],
  stop: [0,0,0]
}; // the current rgb values, gets altered by rgb steps on each interval

var prefixes = ["-webkit-","-moz-","-o-","-ms-",""]; // for looping through adding styles
var div_style = $div.style; // short cut to actually adding styles
var color1, color2;

// sets next current and next index of gradients array
function set_next(num) {
  return (num + 1 < gradients.length) ? num + 1 : 0;
}

// work out how big each rgb step is
function calc_step_size(a,b) {
  return (a - b) / steps_total;
}

// populate the rgb_values and rgb_steps objects
function calc_steps() {
  for (var key in rgb_values) {
    if (rgb_values.hasOwnProperty(key)) {
      for(var i = 0; i < 3; i++) {
        rgb_values[key][i] = gradients[currentIndex][key][i];
        rgb_steps[key][i] = calc_step_size(gradients[nextIndex][key][i],rgb_values[key][i]);
      }
    }
  }
}

// update current rgb vals, update DOM element with new CSS background
function updateGradient(){
  // update the current rgb vals
  for (var key in rgb_values) {
    if (rgb_values.hasOwnProperty(key)) {
      for(var i = 0; i < 3; i++) {
        rgb_values[key][i] += rgb_steps[key][i];
      }
    }
  }

  // generate CSS rgb values
  var t_color1 = "rgb("+(rgb_values.start[0] | 0)+","+(rgb_values.start[1] | 0)+","+(rgb_values.start[2] | 0)+")";
  var t_color2 = "rgb("+(rgb_values.stop[0] | 0)+","+(rgb_values.stop[1] | 0)+","+(rgb_values.stop[2] | 0)+")";

  // has anything changed on this interation
  if (t_color1 != color1 || t_color2 != color2) {

    // update cols strings
    color1 = t_color1;
    color2 = t_color2;

    // update DOM element style attribute
    div_style.backgroundImage = "-webkit-gradient(linear, left bottom, right top, from("+color1+"), to("+color2+"))";
    for (var i = 0; i < 4; i++) {
      div_style.backgroundImage = prefixes[i]+"linear-gradient(45deg, "+color1+", "+color2+")";
    }
  }

  // we did another step
  steps_count++;
  // did we do too many steps?
  if (steps_count > steps_total) {
    // reset steps count
    steps_count = 0;
    // set new indexs
    currentIndex = set_next(currentIndex);
    nextIndex = set_next(nextIndex);
    // calc steps
    calc_steps();
  }

  if (div_style.backgroundImage.indexOf("gradient") != -1) {
    window.requestAnimationFrame(updateGradient)
  }
}

// initial step calc
calc_steps();

// go go go!
window.requestAnimationFrame(updateGradient);
#gradient {
  position: absolute;
  left: 0;
  top: 0;
  right: 0;
  bottom: 0;
  background: #836997;
}
<div id="gradient"></div>

Upvotes: 1

Related Questions