-
-
Notifications
You must be signed in to change notification settings - Fork 5k
/
scroll_events.js
132 lines (122 loc) · 5.35 KB
/
scroll_events.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
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
}
}
}