openrat-cms

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README

scroll_events.js (4841B)


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