55c4995e81c6eea320e77e01ab3b6c38f5a6e3700726f8c913d032ad42908818703aa82a1536b209729550016dd6eadb61b54937784563e8fd845f761aefea 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132
  1. import { chrome, chrome_version, gecko, ie, mac, presto, safari, webkit } from "../util/browser.js"
  2. import { e_preventDefault } from "../util/event.js"
  3. import { updateDisplaySimple } from "./update_display.js"
  4. import { setScrollLeft, updateScrollTop } from "./scrolling.js"
  5. // Since the delta values reported on mouse wheel events are
  6. // unstandardized between browsers and even browser versions, and
  7. // generally horribly unpredictable, this code starts by measuring
  8. // the scroll effect that the first few mouse wheel events have,
  9. // and, from that, detects the way it can convert deltas to pixel
  10. // offsets afterwards.
  11. //
  12. // The reason we want to know the amount a wheel event will scroll
  13. // is that it gives us a chance to update the display before the
  14. // actual scrolling happens, reducing flickering.
  15. let wheelSamples = 0, wheelPixelsPerUnit = null
  16. // Fill in a browser-detected starting value on browsers where we
  17. // know one. These don't have to be accurate -- the result of them
  18. // being wrong would just be a slight flicker on the first wheel
  19. // scroll (if it is large enough).
  20. if (ie) wheelPixelsPerUnit = -.53
  21. else if (gecko) wheelPixelsPerUnit = 15
  22. else if (chrome) wheelPixelsPerUnit = -.7
  23. else if (safari) wheelPixelsPerUnit = -1/3
  24. function wheelEventDelta(e) {
  25. let dx = e.wheelDeltaX, dy = e.wheelDeltaY
  26. if (dx == null && e.detail && e.axis == e.HORIZONTAL_AXIS) dx = e.detail
  27. if (dy == null && e.detail && e.axis == e.VERTICAL_AXIS) dy = e.detail
  28. else if (dy == null) dy = e.wheelDelta
  29. return {x: dx, y: dy}
  30. }
  31. export function wheelEventPixels(e) {
  32. let delta = wheelEventDelta(e)
  33. delta.x *= wheelPixelsPerUnit
  34. delta.y *= wheelPixelsPerUnit
  35. return delta
  36. }
  37. export function onScrollWheel(cm, e) {
  38. // On Chrome 102, viewport updates somehow stop wheel-based
  39. // scrolling. Turning off pointer events during the scroll seems
  40. // to avoid the issue.
  41. if (chrome && chrome_version == 102) {
  42. if (cm.display.chromeScrollHack == null) cm.display.sizer.style.pointerEvents = "none"
  43. else clearTimeout(cm.display.chromeScrollHack)
  44. cm.display.chromeScrollHack = setTimeout(() => {
  45. cm.display.chromeScrollHack = null
  46. cm.display.sizer.style.pointerEvents = ""
  47. }, 100)
  48. }
  49. let delta = wheelEventDelta(e), dx = delta.x, dy = delta.y
  50. let pixelsPerUnit = wheelPixelsPerUnit
  51. if (e.deltaMode === 0) {
  52. dx = e.deltaX
  53. dy = e.deltaY
  54. pixelsPerUnit = 1
  55. }
  56. let display = cm.display, scroll = display.scroller
  57. // Quit if there's nothing to scroll here
  58. let canScrollX = scroll.scrollWidth > scroll.clientWidth
  59. let canScrollY = scroll.scrollHeight > scroll.clientHeight
  60. if (!(dx && canScrollX || dy && canScrollY)) return
  61. // Webkit browsers on OS X abort momentum scrolls when the target
  62. // of the scroll event is removed from the scrollable element.
  63. // This hack (see related code in patchDisplay) makes sure the
  64. // element is kept around.
  65. if (dy && mac && webkit) {
  66. outer: for (let cur = e.target, view = display.view; cur != scroll; cur = cur.parentNode) {
  67. for (let i = 0; i < view.length; i++) {
  68. if (view[i].node == cur) {
  69. cm.display.currentWheelTarget = cur
  70. break outer
  71. }
  72. }
  73. }
  74. }
  75. // On some browsers, horizontal scrolling will cause redraws to
  76. // happen before the gutter has been realigned, causing it to
  77. // wriggle around in a most unseemly way. When we have an
  78. // estimated pixels/delta value, we just handle horizontal
  79. // scrolling entirely here. It'll be slightly off from native, but
  80. // better than glitching out.
  81. if (dx && !gecko && !presto && pixelsPerUnit != null) {
  82. if (dy && canScrollY)
  83. updateScrollTop(cm, Math.max(0, scroll.scrollTop + dy * pixelsPerUnit))
  84. setScrollLeft(cm, Math.max(0, scroll.scrollLeft + dx * pixelsPerUnit))
  85. // Only prevent default scrolling if vertical scrolling is
  86. // actually possible. Otherwise, it causes vertical scroll
  87. // jitter on OSX trackpads when deltaX is small and deltaY
  88. // is large (issue #3579)
  89. if (!dy || (dy && canScrollY))
  90. e_preventDefault(e)
  91. display.wheelStartX = null // Abort measurement, if in progress
  92. return
  93. }
  94. // 'Project' the visible viewport to cover the area that is being
  95. // scrolled into view (if we know enough to estimate it).
  96. if (dy && pixelsPerUnit != null) {
  97. let pixels = dy * pixelsPerUnit
  98. let top = cm.doc.scrollTop, bot = top + display.wrapper.clientHeight
  99. if (pixels < 0) top = Math.max(0, top + pixels - 50)
  100. else bot = Math.min(cm.doc.height, bot + pixels + 50)
  101. updateDisplaySimple(cm, {top: top, bottom: bot})
  102. }
  103. if (wheelSamples < 20 && e.deltaMode !== 0) {
  104. if (display.wheelStartX == null) {
  105. display.wheelStartX = scroll.scrollLeft; display.wheelStartY = scroll.scrollTop
  106. display.wheelDX = dx; display.wheelDY = dy
  107. setTimeout(() => {
  108. if (display.wheelStartX == null) return
  109. let movedX = scroll.scrollLeft - display.wheelStartX
  110. let movedY = scroll.scrollTop - display.wheelStartY
  111. let sample = (movedY && display.wheelDY && movedY / display.wheelDY) ||
  112. (movedX && display.wheelDX && movedX / display.wheelDX)
  113. display.wheelStartX = display.wheelStartY = null
  114. if (!sample) return
  115. wheelPixelsPerUnit = (wheelPixelsPerUnit * wheelSamples + sample) / (wheelSamples + 1)
  116. ++wheelSamples
  117. }, 200)
  118. } else {
  119. display.wheelDX += dx; display.wheelDY += dy
  120. }
  121. }
  122. }