Gradient color transition automatically smoothly

8

Hello, I have a div with the following css :

background: linear-gradient(to right, #3e42e4, #e53472);

And I would like colors to change through JavaScript.

Similar to the top of this site: Pixelapse

How can I do this?

Note: I already thought of something like:

setInterval(function() {
var cor1 = (cor gerada);
var cor2 = (cor gerada);

$("div").css("background", "linear-gradient(to right, cor1, cor2)";
}, 500);

But I do not know how I would generate the colors and if my logic is correct.

Edit

Obs2 .: The problem is that so (as in the suggested answers) the colors change abruptly, and in the reference site as you can see the colors are changing according to the current color, and leaves in a more "soft". So that's the effect I'd like.

    
asked by anonymous 26.03.2015 / 19:48

6 answers

7

Although you say "random", the site in question seems to follow a predefined sequence - and nothing random. If you have two colors A and B and would like to make the smooth transition in the direction of the colors C and D, simply increase / decrease each of its components. If you do this at random, it will hardly be as you want, as the trend will always return to the original color.

Here is an example of a single transition. If you want more than one, one option is that at the end of the first one (ie when progresso reaches 1 ) you refresh the current colors and draw new "target" colors, resetting progresso to 0 and starting all over again ...

var origem1 = [0, 255, 0]; // Cor 1 inicial
var origem2 = [255, 128, 64]; // Cor 2 inicial

var alvo1 = [255, 0, 0]; // Cor 1 final
var alvo2 = [0, 0, 255]; // Cor 2 final

// etc
var predefinidas = [
     [[255,255,0],   [255,0,255]],
     [[255,255,255], [255,255,0]],
     [[0,255,255],   [0,255,0]]
];
var indice = -1;

var progresso = 0; // Progresso em relação a cor 2; começa com 0 (só cor inicial)

// Faz uma média ponderada de cada componente das cores 1 e 2
function calcular(cor1, cor2, progresso) {
  var ret = [];
  for ( var i = 0 ; i < cor1.length ; i++ )
    ret[i] = Math.round(cor2[i] * progresso + cor1[i] * (1 - progresso));
  return ret;
}

setInterval(function() {
  // Atualiza o progresso
  progresso += 0.01;
  if ( progresso > 1 ) { // Se chegou ao final
    progresso = 0; // Reseta o progresso

    origem1 = alvo1; // A cor alvo agora é cor origem
    origem2 = alvo2;

    indice++; // Pega o próximo par de cores
    if ( indice < predefinidas.length ) {
        alvo1 = predefinidas[indice][0];
        alvo2 = predefinidas[indice][1];
    }
    else { // Ou sorteia uma nova cor
        alvo1 = [Math.floor(Math.random()*256), Math.floor(Math.random()*256), Math.floor(Math.random()*256)];
        alvo2 = [Math.floor(Math.random()*256), Math.floor(Math.random()*256), Math.floor(Math.random()*256)];
        // Nota: nada garante que as cores sorteadas não sejam iguais ou muito próximas
    }
  }

  // Calcula as cores para essa "rodada"
  var cor1 = "rgb(" + calcular(origem1, alvo1, progresso).join(",") + ")";
  var cor2 = "rgb(" + calcular(origem2, alvo2, progresso).join(",") + ")";

  // Atribui no CSS
  $("div").css("background", "linear-gradient(to right, " + cor1 + ", " + cor2 + ")");
}, 500);
div {
  border: 1px solid black;
  width: 200px;
  height: 200px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div></div>
    
26.03.2015 / 20:48
5

Unhappily CSS transitions do not help to make gradient transitions ... To make smooth gradient transitions you can calculate via JavaScript and apply to each change (heavy medium, I will not give you example) or you use another element and you make the transition between they with opacity .

To randomly generate colors you can use this way:

function getRandomColor() {
    var letters = '0123456789ABCDEF'.split('');
    var color = '#';
    for (var i = 0; i < 6; i++ ) {
        color += letters[Math.floor(Math.random() * 16)];
    }
    return color;
}

To use an element to help transition you need to use a combination of CSS classes and a function that assigns the new CSS to the help / support div and expects the transition to be complete and then hides it again.

jsFiddle: link

function getRandomColor() {
    var letters = '0123456789ABCDEF'.split('');
    var color = '#';
    for (var i = 0; i < 6; i++) {
        color += letters[Math.floor(Math.random() * 16)];
    }
    return color;
}

function colorir() {
    var original = document.getElementById('original'),
        ajuda = document.getElementById('ajuda'),
        a = getRandomColor(),
        b = getRandomColor();

    ajuda.style.backgroundImage = 'linear-gradient(to right, ' + a + ', ' + b + ')';
    ajuda.style.opacity = 1;

    setTimeout(function () {
        original.style.backgroundImage = 'linear-gradient(to right, ' + a + ', ' + b + ')';
        var subst = ajuda.cloneNode(true);
        subst.classList.add('reset');
        ajuda.parentNode.replaceChild(subst, ajuda);
        ajuda = subst;
        ajuda.style.opacity = 0;
        ajuda.classList.remove('reset');

    }, 5200);
}
setInterval(colorir, 5300);
colorir();
div div {
    width: 500px;
    height: 500px;
    background: linear-gradient(to right, #3e42e4, #e53472);
    position: absolute;
    left: 0;
    top: 0;
    transition: opacity .8s;
}
#ajuda {
    opacity: 0;
    z-index: 100;
}
.reset {
    transition: none;
    opacity: 0;
}
<div>
    <div id="original"></div>
    <div id="ajuda"></div>
</div>

Note that I had to apply a bug to reset the transition because of the Browser stream that does not directly apply some changes to improve performance. You can read about it here in English .

    
26.03.2015 / 20:42
5

Poor Life ... Forgot WebGL!

Negative side:

  • old browsers do not support
  • the code is a bit larger (since it requires the initialization of WebGL)
  • requires some knowledge of WebGL (not to miss the order of things)

Positive side:

  • too fast
  • Flexibility
  • This is beautiful to me (hehe, desconsidere =)

There goes the alternative using canvas and WebGL:

window.onload = main;

var randomizeColors;
var fixedColors;
var rotacionar0;
var rotacionar1;

function main() {
  // Obtendo o contexto WebGL
  var canvas = document.getElementById("canvas");
  var gl = canvas.getContext("webgl") || canvas.getContext("experimental-webgl");

  // inicializando o programa GLSL
  var vertexShader = createShaderFromScriptElement(gl, "shader-vs");
  var fragmentShader = createShaderFromScriptElement(gl, "shader-fs");
  var program = createProgram(gl, [vertexShader, fragmentShader]);
  gl.useProgram(program);

  // Criando o buffer e definindo as variáveis de posição dos vértices, além dos dados do buffer
  var aVertexPosition = gl.getAttribLocation(program, "aVertexPosition");
  var buffer = gl.createBuffer();
  gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
  gl.bufferData(
    gl.ARRAY_BUFFER,
    new Float32Array([+1.0, +1.0, +1.0, -1.0, -1.0, +1.0, -1.0, -1.0]),
    gl.STATIC_DRAW);

  gl.enableVertexAttribArray(aVertexPosition);
  gl.vertexAttribPointer(aVertexPosition, 2, gl.FLOAT, false, 0, 0);

  // Criando o buffer e definindo as variáveis de cores iniciais e finais
  var colorLocation0 = gl.getAttribLocation(program, "aVertexColor0");
  gl.enableVertexAttribArray(colorLocation0);
  var bufferColors0 = gl.createBuffer();

  var colorLocation1 = gl.getAttribLocation(program, "aVertexColor1");
  gl.enableVertexAttribArray(colorLocation1);
  var bufferColors1 = gl.createBuffer();

  function corAleatoria() {
    var c = [Math.random(), Math.random(), Math.random()];
    var max = Math.max.apply(null, c);
    var c2 = c.map(function(i) {
      return i / max;
    });
    return c2;
  }

  function corAleatoriaX4() {
    var c = [];
    c.push.apply(c, corAleatoria());
    c.push.apply(c, corAleatoria());
    c.push.apply(c, corAleatoria());
    c.push.apply(c, corAleatoria());
    return c;
  }

  var corInicial, corFinal;

  function setColors(cores0, cores1) {
    corInicial = cores0;
    corFinal = cores1;

    gl.bindBuffer(gl.ARRAY_BUFFER, bufferColors0);
    gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(corInicial), gl.STATIC_DRAW);
    gl.vertexAttribPointer(colorLocation0, 3, gl.FLOAT, false, 0, 0);

    gl.bindBuffer(gl.ARRAY_BUFFER, bufferColors1);
    gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(corFinal), gl.STATIC_DRAW);
    gl.vertexAttribPointer(colorLocation1, 3, gl.FLOAT, false, 0, 0);
  }

  function rotacionar(lista) {
    lista.push(lista.shift());
    lista.push(lista.shift());
    lista.push(lista.shift());
    setColors(corInicial, corFinal);
  }

  rotacionar0 = function() {
    rotacionar(corInicial);
  };
  rotacionar1 = function() {
    rotacionar(corFinal);
  };

  randomizeColors = function() {
    setColors(
      corAleatoriaX4(),
      corAleatoriaX4());
  }

  fixedColors = function() {
    setColors(
      [0, 0, 1, 1, 0, 0, 0, 1, 0, 1, 1, 0], [1, 0, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1]);
  }

  fixedColors();

  var uTempo = gl.getUniformLocation(program, "uTempo");
  var tempo = 0.0;

  setInterval(function() {
    gl.uniform1f(uTempo, tempo);

    // desenhando o fundo
    gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);

    tempo += 0.1;
  }, 100);
}
<script src="http://greggman.github.io/webgl-fundamentals/webgl/resources/webgl-utils.js"></script><divstyle="height:0;">
  <canvas id="canvas" width="600" height="150"></canvas>
</div>
<div style="position: relative; width:600px; height: 150px; padding: 10px;">
  <button onclick="randomizeColors()">randomizeColors</button>
  <button onclick="fixedColors()">fixedColors</button>
  <button onclick="rotacionar0()">rotacionar0</button>
  <button onclick="rotacionar1()">rotacionar1</button>
</div>

<script id="shader-vs" type="x-shader/x-vertex">
        const float speed = 0.1;

        attribute vec3 aVertexPosition;
        attribute vec3 aVertexColor0;
        attribute vec3 aVertexColor1;
        uniform float uTempo;

        varying lowp vec4 vColor;

        const float PI = 3.14159265359;
        void main(void) {
            gl_Position = vec4(aVertexPosition, 1.0);
            float t = (cos(uTempo*2.*PI*speed)+1.)*0.5;
            vColor = vec4(aVertexColor0, 1.0) * t + vec4(aVertexColor1, 1.0) * (1.-t);
        }
</script>

<script id="shader-fs" type="x-shader/x-fragment">
        varying lowp vec4 vColor;

        void main(void) {
            gl_FragColor = vColor;
        }
</script>
    
27.03.2015 / 01:47
4

You can do this as follows:

Create a function to generate color in RGB at random:

function getRandomColor() {
  var letters = '0123456789ABCDEF'.split('');
  var color = '#';
  for (var i = 0; i < 6; i++ ) {
      color += letters[Math.floor(Math.random() * 16)];
  }
  return color;
}

Then inside your setInterval calls the function to generate the random color in the css exchange:

setInterval(function() {
  $("div").css("background", "linear-gradient(to right,"+ getRandomColor()+", "+getRandomColor()+")");
}, 500);

See working on JSFiddle .

    
26.03.2015 / 20:25
4

Use this code to create random colors:

function corAleatoria(){
    var pad = '000000';
    return '#' + (pad + Math.floor(0x1000000 * Math.random()).toString(16)).slice(-pad.length);
};

And concatenate the random colors with the style at the moment of arrow it with jQuery

Example:

function corAleatoria(){
    var pad = '000000';
    return '#' + (pad + Math.floor(0x1000000 * Math.random()).toString(16)).slice(-pad.length);
};

setInterval(function() {
  var cor1 = corAleatoria();
  var cor2 = corAleatoria();

  $("body").css({
    background: "linear-gradient(to right, " + cor1 + "," + cor2 + ")"
  });
}, 500);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
  

Check out my other solution, with smooth transitions as per AP requests

    
26.03.2015 / 20:18
1
  

I'm posting as another answer, because it's totally different from my first answer (which I want to keep, because it brings a different but useful approach). And it was created to address the new requests made by the AP, in the comments and in the issue issue. (smooth and manageable transitions)

It consists of the following (follow the comments and put to run):

// construtor do gerenciador de cor, você pode especificar varios parâmetros 
// para obter o seu resultado esperado, como a cor RGB inicial, fator de troca 
// de cor para cada cor, valor maximo e minino que cada cor deve chegar 
// (esse é extremamente util para manter uma cor sempre dentro de uma faixa)
function ColorManager(r /* red */ , g /* green */ , b /* blur */ , fatorR, fatorG, fatorB, maxR, maxG, maxB, minR, minG, minB) {
    return {
        r: r,
        g: g,
        b: b,
        fatorR: fatorR,
        fatorB: fatorB,
        fatorG: fatorG,
        maxR: maxR,
        maxG: maxG,
        maxB: maxB,
        minR: minR,
        minG: minG,
        minB: minB,
        toRGBA: function() {
            return "rgba(" + Math.round(this.r) + ", " + Math.round(this.g) + ", " + Math.round(this.b) + ", 1)";
        },
        apply: function() {
            this.r += this.fatorR;
            this.b += this.fatorB;
            this.g += this.fatorG;
            if (this.r > this.maxR || this.r < this.minR) {
                this.r -= this.fatorR;
                this.fatorR = this.fatorR * -1;
            }
            if (this.g > this.maxG || this.g < this.minG) {
                this.g -= this.fatorG;
                this.fatorG = this.fatorG * -1;
            }  
            if (this.b > this.maxB || this.b < this.minB) {
                this.b -= this.fatorB;
                this.fatorB = this.fatorB * -1;
            }
            return this;
        }
    };
};
    
// aqui você faz todas as configurações do Color
var cor1 = new ColorManager(0, 0, 0, 0.5, 0.3, 0.1, 255, 255, 255, 0, 0, 0);
var cor2 = new ColorManager(255, 255, 255, 1, 2, 3, 255, 255, 255, 0, 0, 0);

// cria o timer
setInterval(function() {
    $("body").css({
        background: "linear-gradient(to right, " + cor1.apply().toRGBA() + "," + cor2.apply().toRGBA() + ")"
    });
}, 500);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

Totestsettings

Test changing the parameters in this jsFiddle

    
26.03.2015 / 23:41