Este código llena un div con un conjunto predefinido de gradientes y se desvanece a través de ellos en un ciclo usando el método .animate() de jQuery:
/// Background Gradient Cycler var gradients = [ ['#9eb5d7', '#242424'], ['#efe2ae', '#a8acc9'], ['#6f7554', '#eee1ad'] ] var gradientsRev = gradients.reverse() var gradientCover = document.getElementById('gradientCover'); for (var g = 0; g < gradientsRev.length; g++) { var gradEl = document.createElement('div') gradEl.className = 'gradient' gradEl.style.background = `linear-gradient(${gradientsRev[g][0]}, ${gradientsRev[g][1]})`; gradientCover.appendChild(gradEl) } var gradientEls = document.querySelectorAll('#gradientCover .gradient') function gradientCycler() { function gradeFade(i, opDest) { var fadeDur = 20000 $(gradientEls[i]).animate({ 'opacity': opDest }, { duration: fadeDur, complete: function() { if (parseInt(i) > 1) { if (parseInt(opDest) === 0) gradeFade(i - 1, 0) else gradFadeStart() } else { gradeFade(gradientEls.length - 1, 1) } } }) } var gradFadeStart = function() { $('.gradient').css('opacity', 1); gradeFade(gradientEls.length - 1, 0) } gradFadeStart() } gradientCycler() body { margin: 0; overflow: hidden; } #gradientCover, .gradient { position: absolute; top: 0; left: 0; width: 100vw; height: 100vh; } <script src="https://code.jquery.com/jquery-3.6.0.min.js" integrity="sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4=" crossorigin="anonymous"></script> <div id="page"> <div id="gradientCover"></div> </div>El problema es que la transición en ciertas partes es visiblemente entrecortada, con artefactos de bandas.
¿Qué se puede hacer para reducir este artefacto para que la transición entre los gradientes parezca más suave y menos entrecortada?
Creo que está relacionado con la profundidad del color . Como ex artista de CG, he visto estos "artefactos" en software como Maya y Photoshop, para resolver el problema fue necesario aumentar la cantidad de bits por canal ( en Photoshop pasando de 8 Bits/ Canal a 16 ).
Normalmente, este problema de bandas aparece cuando los dos colores del degradado están cerca (en términos de valores RGB) porque hay pocos valores de colores disponibles entre estos dos colores.
Si el degradado se procesa O se muestra (debido a la limitación del monitor) con una cantidad baja de bits por canal, pueden aparecer estos efectos de bandas.
Puede comprobar la profundidad de color de su monitor aquí .
También puede aplicar CSS de acuerdo con este valor:
if (screen.colorDepth <= 8) //simple blue background color for 8 bit screens document.body.style.background = "#0000FF" else //fancy blue background color for modern screens document.body.style.background = "#87CEFA"Incluyendo mis pruebas de codepen aquí como respuesta en caso de que ayude a alguien. Logré obtener un rendimiento mejorado pre-renderizando PNG con el método canvas' .toDataURL() y animando a través de ellos en jQuery (también logré aproximadamente el mismo rendimiento con este método pero usando Three.js):
Aquí hay un ejemplo de trabajo del método JQuery:
/// Background Gradient Cycler var gradients = [ ['#9eb5d7', '#242424'], ['#efe2ae', '#a8acc9'], ['#6f7554', '#eee1ad'] ] var gradientsRev = gradients.reverse() var gradientCover = document.getElementById('gradientCover'); for (var g = 0; g < gradientsRev.length; g++) { var gradEl = document.createElement('div') gradEl.className = 'gradient' var gradCanv = document.createElement('canvas') gradCanv.className = 'gradient' var ctx = gradCanv.getContext("2d"); var grd = ctx.createLinearGradient(0, 0, 0, gradCanv.height); grd.addColorStop(0, gradients[g][0]); grd.addColorStop(1, gradients[g][1]); ctx.fillStyle = grd; ctx.fillRect(0, 0, gradCanv.width, gradCanv.height); var gradIm = gradCanv.toDataURL("img/png") gradEl.style.backgroundImage = `url(${gradIm})` gradientCover.appendChild(gradEl) } var gradientEls = document.querySelectorAll('#gradientCover .gradient') function gradientCycler() { function gradeFade(i, opDest) { var fadeDur = 20000 $(gradientEls[i]).animate({ 'opacity': opDest }, { duration: fadeDur, complete: function() { if (parseInt(i) > 1) { if (parseInt(opDest) === 0) gradeFade(i - 1, 0) else gradFadeStart() } else { gradeFade(gradientEls.length - 1, 1) } } }) } var gradFadeStart = function() { $('.gradient').css('opacity', 1); gradeFade(gradientEls.length - 1, 0) } gradFadeStart() } gradientCycler() body { margin: 0; overflow: hidden; } #gradientCover, .gradient { position: absolute; top: 0; left: 0; width: 100vw; height: 100vh; background-size: 100%; } <script src="https://code.jquery.com/jquery-3.6.0.min.js" integrity="sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4=" crossorigin="anonymous"></script> <div id="page"> <div id="gradientCover"></div> </div>