Введение
У меня есть страница с полноэкранными блоками fixed
, и я хотел бы перемещаться по этим блокам, используя событие javascript wheel
.
Если это не такне имеет смысла;Проще говоря, у меня есть специальная карусель, по которой я хотел бы перемещаться с помощью колесика мыши по одному блоку за раз, т.е. один прокрутка / клик перемещается к предыдущему / следующему элементу.
Мой текущийрешение работает, но для его улучшения требуется небольшая настройка.
Проблема
Проблема, с которой я столкнулся, состоит в том, что 1 прокрутка / нажатие на колесе мыши иногда прокручивает один блок, ииногда прокручивает 2 блока.
Я использую следующий алгоритм:
- Для каждого события
wheel
, увеличение / уменьшение значения глобальной переменной deltaY
на 1 (мое колесо мыши имеет тенденцию вызывать около 35-40 событий каждый раз, когда колесо прокручивается в режиме щелчка, не уверен, является ли это стандартным) - При каждом изменении значения
deltaY
определить, какой блок (по индексу)должно быть active
.Я делаю это со следующим;Math.floor(deltaY / 35)
, где 35
- количество wheel
событий, запущенных одним движением моего колеса мыши (очень неуверенно в этом, поскольку это может отличаться для других мышей) - Обновление индекса
active
и обновлениеdeltaY
с целым числом, кратным 35
Код
Я использую Vue.js, но не волнуйтесь, если вы с ним не знакомы, все, что мне действительно нужноэто алгоритм, и я могу реализовать его в своем коде.
Vanilla JS
Я не тестировал этот код, я просто смоделировал его на основе своего кода Vue.js дляте из вас, кто не знаком с Vue.
// Set the default values
let items = ['x', 'y', 'z'];
let active = 0;
let deltaY = 0;
/**
* Update the delta value and any other relevant
* values.
*
* @param {event} event
* @return void
*/
function updateDelta (value) {
active = Math.floor(value / 35);
deltaY = active * 35;
}
// Register the `wheel` event
window.addEventListener('wheel', (event) => {
// The navigation is only active when the page has not
// been scrolled
if (document.documentElement.scrollTop === 0) {
// If the last item is currently active then we do not need to
// listen to `down` scrolls, or, if the first item is active,
// then we do not need to listen to `up` scrolls
if (
(event.deltaY > 0 && (active - 1) === items.length)
|| (event.deltaY < 0 && deltaY === 0)
) {
return;
}
updateDelta(Math.sign(event.deltaY));
}
}, { passive: true });
Мой код (Vue JS)
Я добавил четкие комментарии, чтобы проиллюстрировать, что делает Vue
export default {
data() {
return {
items: ['x', 'y', 'z'],
active: 0, // Define the active index
deltaY: 0 // This is used for the scroll wheel navigation
}
},
created() {
// Register the `wheel` event
window.addEventListener('wheel', this._handleWheel, { passive: true });
},
destroyed() {
// Remove the `wheel` event
window.removeEventListener('wheel', this._handleWheel, { passive: true });
},
watch: {
// I have added watchers to both `deltaY` and `active`, however,
// this may not be necessary. These will not create an endless loop
// because the watcher is only called when a value is changed
active: function(index) {
// Whenever the `active` index changes, update the `deltaY` value
this.deltaY = index * 35;
},
deltaY: function(value) {
// Whenever the `deltaY` value changes, update the `active` index
this.active = Math.floor(value / 35);
}
},
methods: {
/**
* Handle the window wheel event.
*
* @param {event} event
* @return void
*/
_handleWheel (event) {
// The navigation is only active when the page has not
// been scrolled
if (document.documentElement.scrollTop === 0) {
// If the last item is currently active then we do not need to
// listen to `down` scrolls, or, if the first item is active,
// then we do not need to listen to `up` scrolls
if (
(event.deltaY > 0 && (this.active - 1) === this.items.length)
|| (event.deltaY < 0 && this.deltaY === 0)
) {
return;
}
this.deltaY += Math.sign(event.deltaY);
}
}
}
}