src/tools/scrollWheel.js
import {getLayerDetailsFromEvent} from '../gui/layerGroup';
// doc imports
/* eslint-disable no-unused-vars */
import {App} from '../app/application';
/* eslint-enable no-unused-vars */
/**
* Get a normalised spin speed in the Y direction to try to support
* trackpads (small and large deltaY) and mouse wheel (large deltaY).
* Should return 1 or -1 for a single mouse wheel tick.
*
* @param {object} event The wheel event.
* @returns {number} The normalised spin Y.
*/
function getSpinY(event) {
// (notes of 03/2024)
// firefox seems to change the value of deltaY
// if you ask for deltaMode before (?????)
// deltaY (for a single mouse wheel tick):
// - chrome: [linux] 120, [mac]: 4
// - firefox: [linux] 132, [mac]: 16
// wheelDelta (for a single mouse wheel tick):
// - chrome: [linux] 120, [mac]: 240
// - firefox: [linux] 120, [mac]: 48
// -> using wheelDelta for mouse wheel detection as
// it is consistently larger than trackpad scroll
// wheelDeltaY and deltaY do not go in the same direction,
// using -deltaY so that they do...
if (typeof event.wheelDeltaY === 'undefined') {
//logger.warn('No wheel delta, scroll could be tricky...);
return -event.deltaY;
} else {
const threshold = 45;
if (event.wheelDeltaY > threshold) {
return 1;
} else if (event.wheelDeltaY < -threshold) {
return -1;
} else {
return -event.deltaY / 60;
}
}
}
/**
* Class to sum wheel events and know if that sum
* corresponds to a 'tick'.
*/
class ScrollSum {
/**
* The scroll sum.
*
* @type {number}
*/
#sum = 0;
/**
* Get the scroll sum.
*
* @returns {number} The scroll sum.
*/
getSum() {
return this.#sum;
}
/**
* Add scroll.
*
* @param {object} event The wheel event.
*/
add(event) {
this.#sum += getSpinY(event);
}
/**
* Clear the scroll sum.
*/
clear() {
this.#sum = 0;
}
/**
* Does the accumulated scroll correspond to a 'tick'.
*
* @returns {boolean} True if the sum corresponds to a 'tick'.
*/
isTick() {
return Math.abs(this.#sum) >= 1;
}
}
/**
* Scroll wheel class: provides a wheel event handler
* that scroll the corresponding data.
*/
export class ScrollWheel {
/**
* Associated app.
*
* @type {App}
*/
#app;
/**
* Accumulated scroll.
*
* @type {ScrollSum}
*/
#scrollSum = new ScrollSum();
/**
* @param {App} app The associated application.
*/
constructor(app) {
this.#app = app;
}
/**
* Handle mouse wheel event.
*
* @param {WheelEvent} event The mouse wheel event.
*/
wheel(event) {
this.#scrollSum.add(event);
const up = this.#scrollSum.getSum() >= 0;
// exit if no tick
if (!this.#scrollSum.isTick()) {
return;
} else {
this.#scrollSum.clear();
}
// prevent default page scroll
event.preventDefault();
const layerDetails = getLayerDetailsFromEvent(event);
const layerGroup = this.#app.getLayerGroupByDivId(layerDetails.groupDivId);
const viewController =
layerGroup.getActiveViewLayer().getViewController();
let newPosition;
if (layerGroup.canScroll()) {
if (up) {
newPosition = viewController.getIncrementScrollPosition();
} else {
newPosition = viewController.getDecrementScrollPosition();
}
} else if (layerGroup.moreThanOne(3)) {
if (up) {
newPosition = viewController.getIncrementPosition(3);
} else {
newPosition = viewController.getDecrementPosition(3);
}
}
// set all layers if at least one can be set
if (typeof newPosition !== 'undefined' &&
layerGroup.isPositionInBounds(newPosition)) {
viewController.setCurrentPosition(newPosition);
}
}
} // ScrollWheel class