shinkan-jinkendo/frontend/src/utils/dragAutoScroll.js

67 lines
2.1 KiB
JavaScript
Raw Normal View History

/**
* Während eines Drags automatischen Bildlauf auslösen (Viewport + scrollbare Bereiche unter dem Cursor).
* @param {DragEvent} e
* @param {{ edgePx?: number, scrollStep?: number }} [opts]
*/
export function autoScrollForDragNearEdges(e, opts = {}) {
const edge = opts.edgePx ?? 80
const step = opts.scrollStep ?? Math.max(24, Math.round(window.innerHeight * 0.05))
const { clientX, clientY } = e
const vh = window.innerHeight || 0
const vw = window.innerWidth || 0
let sy =
clientY < edge ? -step : vh > 0 && clientY > vh - edge ? step : 0
let sx =
clientX < edge ? -step : vw > 0 && clientX > vw - edge ? step : 0
if (sy !== 0) window.scrollBy(0, sy)
if (sx !== 0) window.scrollBy(sx, 0)
/** @type {HTMLElement|null} */
let top = /** @type {HTMLElement|null} */ (document.elementFromPoint(clientX, clientY))
/** @type {Set<HTMLElement>} */
const done = new Set()
/** @type {HTMLElement|null} */
let walk = top
const innerEdge = Math.min(edge, 40)
while (walk && walk !== document.body) {
if (!(walk instanceof HTMLElement)) break
const cs = window.getComputedStyle(walk)
const canY =
walk.scrollHeight - walk.clientHeight > 6 &&
(cs.overflowY === 'auto' || cs.overflowY === 'scroll')
const canX =
walk.scrollWidth - walk.clientWidth > 6 &&
(cs.overflowX === 'auto' || cs.overflowX === 'scroll')
if ((canY || canX) && !done.has(walk)) {
done.add(walk)
const rect = walk.getBoundingClientRect()
const relY = clientY - rect.top
const relX = clientX - rect.left
if (canY) {
if (relY < innerEdge && walk.scrollTop > 0) walk.scrollTop -= step
else if (rect.height - relY < innerEdge && walk.scrollTop < walk.scrollHeight - walk.clientHeight) {
walk.scrollTop += step
}
}
if (canX) {
if (relX < innerEdge && walk.scrollLeft > 0) walk.scrollLeft -= step
else if (rect.width - relX < innerEdge && walk.scrollLeft < walk.scrollWidth - walk.clientWidth) {
walk.scrollLeft += step
}
}
}
walk = walk.parentElement
}
}