| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132 |
- import { chrome, chrome_version, 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) {
- // On Chrome 102, viewport updates somehow stop wheel-based
- // scrolling. Turning off pointer events during the scroll seems
- // to avoid the issue.
- if (chrome && chrome_version == 102) {
- if (cm.display.chromeScrollHack == null) cm.display.sizer.style.pointerEvents = "none"
- else clearTimeout(cm.display.chromeScrollHack)
- cm.display.chromeScrollHack = setTimeout(() => {
- cm.display.chromeScrollHack = null
- cm.display.sizer.style.pointerEvents = ""
- }, 100)
- }
- let delta = wheelEventDelta(e), dx = delta.x, dy = delta.y
- let pixelsPerUnit = wheelPixelsPerUnit
- if (e.deltaMode === 0) {
- dx = e.deltaX
- dy = e.deltaY
- pixelsPerUnit = 1
- }
- 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 && pixelsPerUnit != null) {
- if (dy && canScrollY)
- updateScrollTop(cm, Math.max(0, scroll.scrollTop + dy * pixelsPerUnit))
- setScrollLeft(cm, Math.max(0, scroll.scrollLeft + dx * pixelsPerUnit))
- // 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 && pixelsPerUnit != null) {
- let pixels = dy * pixelsPerUnit
- 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 && e.deltaMode !== 0) {
- 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
- }
- }
- }
|