Плавная анимация цвета при прокрутке

511
06 февраля 2017, 18:11

Здравствуйте!

Задача состоит в том, что бы при прокрутке страницы плавно менялся цвет фона.
Пояснение: Есть два цвета ( к примеру rgb(0, 0, 0) и rgb(255, 255, 255) ). Я в коде указываю позиции scroll'а 0px и 100px. И теперь, нужно, что бы скрипт, по мере прокрутки, плавно превращал rgb(0, 0, 0) в rgb(255, 255, 255), т.е. если я прокрутил scroll на позицию 50px цвет у фона был rgb(122, 122, 122).

Я попытался это решить способом процентов, но это же будет работать только с цветами rgb, а как же hex и другие? И вообще оно странно меняет цвета, если там указаны цветные варианты (красный, жёлтый, синий...)

Код:

var scroll = function(element, style, positions, value) { 
  window.onscroll = function() { 
    var scrollTop = window.pageYOffset, 
        // Высчитываю процент того, на сколько нужно изменить цвет 
        pct = (window.pageYOffset - positions[0]) / positions[1], 
        colorsVal = [], 
        currentColor = []; 
    // Проверяю, не выходит ли scroll за рамки 
    if(window.pageYOffset <= positions[1] && window.pageYOffset >= positions[0]){ 
      // Превращаю значение цветов в массив 
      colorsVal[0] = value[0].split(','); 
      colorsVal[1] = value[1].split(','); 
      for(var i = 0; i < colorsVal[0].length; i++){ 
        // Проверяю, какое значение цвета больше 
        if(colorsVal[0][i] < colorsVal[1][i]){ 
          // Высчитываю текущее значение цвета 
          var val = ((colorsVal[1][i] - colorsVal[0][i]) * pct) + colorsVal[0][i]; 
          // Устанавливаю текущее значение цвета 
          currentColor[i] = Math.round(val); 
        }else{ 
          // Высчитываю текущее значение цвета 
          var val = ((colorsVal[0][i] - colorsVal[1][i]) * pct) + colorsVal[1][i]; 
          // Устанавливаю текущее значение цвета 
          currentColor[i] = Math.round(val); 
        }; 
      }; 
      // Применяю цвет фона к элементу 
      element.style[style] = 'rgb(' + currentColor.join(',') + ')'; 
    }; 
  }; 
}; 
 
(function() { 
  var obj = document.querySelector('.obj'); 
  scroll(obj, 'background-color', [0, 100], ['0,0,0', '255,255,255']); 
})();
body {height: 2000px; background-color: #eee} 
div { 
  position: fixed; 
  width: 100px; 
  height: 100px; 
  background-color: black; 
}
<div class="obj"></div>

Как можно улучшить эту функцию и сделать более универсальной, что бы можно было указывать значение цвета 'black' и hex цвета? Или у кого есть вариант получше моего?

Заранее спасибо.

Answer 1

Первое, что необходимо сделать — это убрать для удобства цвет из CSS и задавать его уже непосредственно в функции scroll при её инициализации.

Далее я вызвал функцию scroll с параметром positions [0, 1000], чтобы можно было нормально рассмотреть переход и у меня возникала проблема при каждом втором скролле. Проблема была в том, что массив colorsVal заполнялся не числами, а строками, и там, где происходит вычисление val = ((colorsVal[1][i] - colorsVal[0][i]) * pct) +colorsVal[0][i] (проблемное место выделено жирным) ноль прибавляется как строка и получается зашкаливающее значение в rgb. Решением является приведение к типу Number во время формирования массива:

colorsVal[0] = value[0].split(',').map(Number),
colorsVal[1] = value[1].split(',').map(Number);

После этого пример от меньших значений rgb к большим работает. Дальше возникли проблемы с переходом от больших значений к меньшим. Для решения этой проблемы пришлось немного изменить формулу расчета с

val = ((colorsVal[0][i] - colorsVal[1][i]) * pct) + colorsVal[1][i];

на

val = colorsVal[0][i] - ((colorsVal[0][i] - colorsVal[1][i]) * pct);

var scroll = function(element, style, positions, value) { 
  // Превращаю значение цветов в массив 
  var colorsVal = [], val; 
   
  colorsVal[0] = value[0].split(',').map(Number), 
  colorsVal[1] = value[1].split(',').map(Number); 
   
  element.style[style] = 'rgb(' + colorsVal[0].join(',') + ')'; 
     
  window.onscroll = function() { 
    var scrollTop = window.pageYOffset, 
        // Высчитываю процент того, на сколько нужно изменить цвет 
        pct = (window.pageYOffset - positions[0]) / positions[1], 
        currentColor = []; 
     
    // Проверяю, не выходит ли scroll за рамки 
    if(window.pageYOffset <= positions[1] && window.pageYOffset >= positions[0]){ 
      for(var i = 0; i < colorsVal[0].length; i++) { 
        // Проверяю, какое значение цвета больше 
        if(colorsVal[0][i] < colorsVal[1][i]){ 
          // Высчитываю текущее значение цвета 
          val = ((colorsVal[1][i] - colorsVal[0][i]) * pct) + colorsVal[0][i]; 
          // Устанавливаю текущее значение цвета 
          currentColor[i] = Math.round(val); 
        }else{ 
          // Высчитываю текущее значение цвета 
          val = colorsVal[0][i] - ((colorsVal[0][i] - colorsVal[1][i]) * pct); 
          // Устанавливаю текущее значение цвета 
          currentColor[i] = Math.round(val); 
        }; 
      }; 
      // Применяю цвет фона к элементу 
      element.style[style] = 'rgb(' + currentColor.join(',') + ')'; 
    }; 
  }; 
}; 
 
(function() { 
  var obj = document.querySelector('.obj'); 
  scroll(obj, 'background-color', [0, 1000], ['0,0,0', '255,255,255']); 
})();
body {height: 2000px; background-color: #eee} 
div { 
  position: fixed; 
  width: 100px; 
  height: 100px; 
}
<div class="obj"></div>

console.clear(); 
 
var scroll = function(element, style, positions, value) { 
  // Превращаю значение цветов в массив 
  var colorsVal = [], val; 
   
  colorsVal[0] = value[0].split(',').map(Number), 
  colorsVal[1] = value[1].split(',').map(Number); 
   
  element.style[style] = 'rgb(' + colorsVal[0].join(',') + ')'; 
     
  window.onscroll = function() { 
    var scrollTop = window.pageYOffset, 
        // Высчитываю процент того, на сколько нужно изменить цвет 
        pct = (window.pageYOffset - positions[0]) / positions[1], 
        currentColor = []; 
     
    // Проверяю, не выходит ли scroll за рамки 
    if(window.pageYOffset <= positions[1] && window.pageYOffset >= positions[0]){ 
      for(var i = 0; i < colorsVal[0].length; i++) { 
        // Проверяю, какое значение цвета больше 
        if(colorsVal[0][i] < colorsVal[1][i]){ 
          // Высчитываю текущее значение цвета 
          val = ((colorsVal[1][i] - colorsVal[0][i]) * pct) + colorsVal[0][i]; 
          // Устанавливаю текущее значение цвета 
          currentColor[i] = Math.round(val); 
        }else{ 
          // Высчитываю текущее значение цвета 
          val = colorsVal[0][i] - ((colorsVal[0][i] - colorsVal[1][i]) * pct); 
          // Устанавливаю текущее значение цвета 
          currentColor[i] = Math.round(val); 
        }; 
      }; 
      // Применяю цвет фона к элементу 
      element.style[style] = 'rgb(' + currentColor.join(',') + ')'; 
    }; 
  }; 
}; 
 
(function() { 
  var obj = document.querySelector('.obj'); 
  scroll(obj, 'background-color', [0, 1000], ['0,245,0', '255,0,0']); 
})();
body {height: 2000px; background-color: #eee} 
div { 
  position: fixed; 
  width: 100px; 
  height: 100px; 
}
<div class="obj"></div>

Answer 2

Можешь тот же hex конвертировать в rgb.

var hex = document.querySelector('input'), 
    btn = document.querySelector('button'), 
    rgbBlock = document.querySelector('.rgb'), 
    hexval; 
 
btn.addEventListener('click', function() { 
  hexval = hex.value; 
  rgbBlock.innerHTML = hexToRgbA(hexval); 
}); 
 
function hexToRgbA(hex) { 
  var c; 
  if (/^#([A-Fa-f0-9]{3}){1,2}$/.test(hex)) { 
    c = hex.substring(1).split(''); 
    if (c.length == 3) { 
      c = [c[0], c[0], c[1], c[1], c[2], c[2]]; 
    } 
    c = '0x' + c.join(''); 
    return 'rgba(' + [(c >> 16) & 255, (c >> 8) & 255, c & 255] + ')'; 
  } 
   
  throw new Error('не Hex'); 
}
<input type="text" /> 
<button>convert</button> 
 
<div class="rgb"></div>

READ ALSO
Почему возвращает значения RGB каналов?

Почему возвращает значения RGB каналов?

Добрый день уважаемыеСтолкнулся с не пониманием почему JS возвращает значения RGB каналов хотя в CSS задаю под HSL

447
Загрузилась ли страница на ReactJS

Загрузилась ли страница на ReactJS

Пытаюсь написать модальное окно на React Js, чтоб оно появлялось сразу после загрузки страницыПроверка как в нативном JS через window

391
Перенос блока в начало при нехватке места

Перенос блока в начало при нехватке места

[ Здравствуйте,есть вопрос,как можно перенести последний блок в начало,при нехватке места,чтобы в начало попал только тот кусок,который не влез,а...

515