116 lines
4.7 KiB
JavaScript
Executable File
116 lines
4.7 KiB
JavaScript
Executable File
import { chrome, gecko, ie, mac, presto, safari, webkit } from "../util/browser.js"
|
|
import { e_preventDefault } from "../util/event.js"
|
|
|
|
import { updateDisplaySimple } from "./update_display.js"
|
|
import { setScrollLeft, updateScrollTop } from "./scrolling.js"
|
|
|
|
// Since the delta values reported on mouse wheel events are
|
|
// unstandardized between browsers and even browser versions, and
|
|
// generally horribly unpredictable, this code starts by measuring
|
|
// the scroll effect that the first few mouse wheel events have,
|
|
// and, from that, detects the way it can convert deltas to pixel
|
|
// offsets afterwards.
|
|
//
|
|
// The reason we want to know the amount a wheel event will scroll
|
|
// is that it gives us a chance to update the display before the
|
|
// actual scrolling happens, reducing flickering.
|
|
|
|
let wheelSamples = 0, wheelPixelsPerUnit = null
|
|
// Fill in a browser-detected starting value on browsers where we
|
|
// know one. These don't have to be accurate -- the result of them
|
|
// being wrong would just be a slight flicker on the first wheel
|
|
// scroll (if it is large enough).
|
|
if (ie) wheelPixelsPerUnit = -.53
|
|
else if (gecko) wheelPixelsPerUnit = 15
|
|
else if (chrome) wheelPixelsPerUnit = -.7
|
|
else if (safari) wheelPixelsPerUnit = -1/3
|
|
|
|
function wheelEventDelta(e) {
|
|
let dx = e.wheelDeltaX, dy = e.wheelDeltaY
|
|
if (dx == null && e.detail && e.axis == e.HORIZONTAL_AXIS) dx = e.detail
|
|
if (dy == null && e.detail && e.axis == e.VERTICAL_AXIS) dy = e.detail
|
|
else if (dy == null) dy = e.wheelDelta
|
|
return {x: dx, y: dy}
|
|
}
|
|
export function wheelEventPixels(e) {
|
|
let delta = wheelEventDelta(e)
|
|
delta.x *= wheelPixelsPerUnit
|
|
delta.y *= wheelPixelsPerUnit
|
|
return delta
|
|
}
|
|
|
|
export function onScrollWheel(cm, e) {
|
|
let delta = wheelEventDelta(e), dx = delta.x, dy = delta.y
|
|
|
|
let display = cm.display, scroll = display.scroller
|
|
// Quit if there's nothing to scroll here
|
|
let canScrollX = scroll.scrollWidth > scroll.clientWidth
|
|
let canScrollY = scroll.scrollHeight > scroll.clientHeight
|
|
if (!(dx && canScrollX || dy && canScrollY)) return
|
|
|
|
// Webkit browsers on OS X abort momentum scrolls when the target
|
|
// of the scroll event is removed from the scrollable element.
|
|
// This hack (see related code in patchDisplay) makes sure the
|
|
// element is kept around.
|
|
if (dy && mac && webkit) {
|
|
outer: for (let cur = e.target, view = display.view; cur != scroll; cur = cur.parentNode) {
|
|
for (let i = 0; i < view.length; i++) {
|
|
if (view[i].node == cur) {
|
|
cm.display.currentWheelTarget = cur
|
|
break outer
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// On some browsers, horizontal scrolling will cause redraws to
|
|
// happen before the gutter has been realigned, causing it to
|
|
// wriggle around in a most unseemly way. When we have an
|
|
// estimated pixels/delta value, we just handle horizontal
|
|
// scrolling entirely here. It'll be slightly off from native, but
|
|
// better than glitching out.
|
|
if (dx && !gecko && !presto && wheelPixelsPerUnit != null) {
|
|
if (dy && canScrollY)
|
|
updateScrollTop(cm, Math.max(0, scroll.scrollTop + dy * wheelPixelsPerUnit))
|
|
setScrollLeft(cm, Math.max(0, scroll.scrollLeft + dx * wheelPixelsPerUnit))
|
|
// Only prevent default scrolling if vertical scrolling is
|
|
// actually possible. Otherwise, it causes vertical scroll
|
|
// jitter on OSX trackpads when deltaX is small and deltaY
|
|
// is large (issue #3579)
|
|
if (!dy || (dy && canScrollY))
|
|
e_preventDefault(e)
|
|
display.wheelStartX = null // Abort measurement, if in progress
|
|
return
|
|
}
|
|
|
|
// 'Project' the visible viewport to cover the area that is being
|
|
// scrolled into view (if we know enough to estimate it).
|
|
if (dy && wheelPixelsPerUnit != null) {
|
|
let pixels = dy * wheelPixelsPerUnit
|
|
let top = cm.doc.scrollTop, bot = top + display.wrapper.clientHeight
|
|
if (pixels < 0) top = Math.max(0, top + pixels - 50)
|
|
else bot = Math.min(cm.doc.height, bot + pixels + 50)
|
|
updateDisplaySimple(cm, {top: top, bottom: bot})
|
|
}
|
|
|
|
if (wheelSamples < 20) {
|
|
if (display.wheelStartX == null) {
|
|
display.wheelStartX = scroll.scrollLeft; display.wheelStartY = scroll.scrollTop
|
|
display.wheelDX = dx; display.wheelDY = dy
|
|
setTimeout(() => {
|
|
if (display.wheelStartX == null) return
|
|
let movedX = scroll.scrollLeft - display.wheelStartX
|
|
let movedY = scroll.scrollTop - display.wheelStartY
|
|
let sample = (movedY && display.wheelDY && movedY / display.wheelDY) ||
|
|
(movedX && display.wheelDX && movedX / display.wheelDX)
|
|
display.wheelStartX = display.wheelStartY = null
|
|
if (!sample) return
|
|
wheelPixelsPerUnit = (wheelPixelsPerUnit * wheelSamples + sample) / (wheelSamples + 1)
|
|
++wheelSamples
|
|
}, 200)
|
|
} else {
|
|
display.wheelDX += dx; display.wheelDY += dy
|
|
}
|
|
}
|
|
}
|