Здравствуйте.
Есть маленький код, который устанавливает значение top у блока в зависимости от положения scroll'а. Проблема в том, что не во всех браузерах оно плавно отображается, т.е. при scroll'е происходят резкие скачки (браузера: IE, Opera <11 и т.д.). Как убрать эти скачки и сделать полностью плавные движения?
Код:
window.onscroll = function() {
document.getElementsByTagName('div')[0].style.marginTop = window.pageYOffset + 'px';
};
body {height: 2000px; }
div {
width: 200px;
height: 200px;
background-color: black;
}
<div></div>
Отображение со скачками:
На картинке маленькие скачки, но с разными объектами и разными браузерами - скачки разного размера.
Ссылка на сторонний редактор: jsfiddle-gjx7ecd4
Поведение блока сильно похоже на fixed, с тем исключением, что при полной прокрутке окна вверх, он маргинится на несколько пикселей. Возможно, решением будет - присвоение ему класса с необходимыми параметрами, если величина прокрутки окна больше его первоначального отступа.
var elem = document.getElementById('test'); //получаем целевой элемент
var style = elem.currentStyle || window.getComputedStyle(elem);/* тк, если
напрямую обратиться к свойству style,
то мы получим список правил для, непосредственно, ЭЛЕМЕНТА,
нам же нужен именно список правил,
для таблицы стилей(первое выражение для IE)*/
var scrollOffset = style.marginTop;//Собственно, получаем начальный отступ из стилей
scrollOffset = scrollOffset.substr(0, scrollOffset.length-2)//убираем злосчастные `px`
window.onscroll = function() {
if(window.pageYOffset > scrollOffset){
elem.className = 'scrolled';
return;
}
elem.className = '';
};
body {height: 2000px; padding: 0px; margin: 0px;}
div#test {
width: 200px;
height: 200px;
background-color: black;
margin-top: 7px;
}
.scrolled{
position: fixed;
margin-top: 0px !important;
/*Свойства стилей для id перекрывают свойства для классов,
посему так, но для новичков: это BAD PRACTICE!*/
box-sizing: border-box;
background-color: red !important;
/*Просто, дабы понимать, что сейчас на нем висит класс*/
}
<div id='test'></div>
Никакими ухищрениями сделать плавный скролл через onscroll не получится. Проблема в том, что за время поворота колеса мыши вылетает 5-12 событий onwheel, которые трансформируются в onscroll. Ваш код пытается их переварить - и получаются скачки.
Событие scroll не всплывает, поэтому стандартный scroll невозможно отменить через preventDefault.
Возился с этой проблемой довольно долго. Пришлось перехватывать onWheel и анимировать плавно до нужного места. Анимацию делал на jQuery, здесь в рамках ответа на javascript анимацию не ставил - просто скроллинг окна и transfrom: translate.
Важное замечание. Браузер по-разному обрабатывает top и transform, есть примеры в Сети, что transform работает намного более плавно.
Приведенный ниже код перехватывает onWheel, накапливает события в течение 150мс, чтобы отследить полное единичное движение колеса, затем поднимает флажок animating. Во время "анимации" скрипт не воспринимает более никакие новые события скролла, один раз прокручивает окно на полную ранее просуммированную величину скролла, изменяет положение черного квадрата через transform и через 100мс сбрасывает флажок animating.
Эти величины - 150 и 100мс - сугубо эмпирические и подгоняются под конкретную задачу, особенно в том случае, если скрипт осуществляет анимацию элемента.
// Так квадрат дергается
// window.onscroll = function() {
// document.getElementsByTagName('div')[0].style.transform = 'translateY(' + window.pageYOffset + 'px' + ')';
// return false;
// };
// А так - не дергается
var animating = false;
var timeoutID;
function onWheel(event) {
event = event || window.event;
var pos = event.deltaY || event.detail || event.wheelDelta;
console.log(pos);
if (animating) {
finalPos = finalPos + pos;
event.preventDefault();
return false;
}
if (pos > 0) {
// downscroll code
if (!animating) {
// Scroll
animating = true;
finalPos = pos;
event.preventDefault();
clearTimeout(timeoutID);
timeoutID = setTimeout(function() {
console.log('scroll to ' + finalPos);
window.scrollTo(window.scrollX, window.scrollY + finalPos);
document.getElementsByTagName('div')[0].style.transform = 'translateY(' + window.pageYOffset + 'px' + ')';
setTimeout(function() {
animating = false;
}, 100);
}, 150);
}
} else {
// upscroll code
if (!animating) {
// Scroll
animating = true;
finalPos = pos;
event.preventDefault();
clearTimeout(timeoutID);
timeoutID = setTimeout(function() {
console.log('scroll to ' + finalPos);
window.scrollTo(window.scrollX, window.scrollY + finalPos);
document.getElementsByTagName('div')[0].style.transform = 'translateY(' + window.pageYOffset + 'px' + ')';
setTimeout(function() {
animating = false;
}, 100);
}, 150);
}
}
}
if (window.addEventListener) {
if ('onwheel' in document) {
window.addEventListener("wheel", onWheel, {passive: false, capture: true}); // IE9+, FF17+, Ch31+
} else if ('onmousewheel' in document) {
window.addEventListener("mousewheel", onWheel); // устаревший вариант события
} else {
window.addEventListener("MozMousePixelScroll", onWheel); // Firefox < 17
}
} else {
window.attachEvent("onmousewheel", onWheel); // IE8-
}
Посмотреть работающий код можно тут в jsfiddle или тут на странице тестового сайта.
Продвижение своими сайтами как стратегия роста и независимости