diff options
| author | mrmr1993 | 2014-11-10 08:27:55 +0000 | 
|---|---|---|
| committer | mrmr1993 | 2014-11-10 16:43:11 +0000 | 
| commit | df521c26fda9b8d3e8c182fc85deaf5b8c723cd4 (patch) | |
| tree | 3dbc95e188891dc94a88d7cc57df03c289db29f2 | |
| parent | b8b1644a306c1fe12b5faa5204630eb30f1e64b3 (diff) | |
| download | vimium-df521c26fda9b8d3e8c182fc85deaf5b8c723cd4.tar.bz2 | |
Change smooth scrolling to requestAnimationFrame, tidy up scroller code
| -rw-r--r-- | content_scripts/scroller.coffee | 128 | 
1 files changed, 58 insertions, 70 deletions
| diff --git a/content_scripts/scroller.coffee b/content_scripts/scroller.coffee index 34f9b148..2e0d08ad 100644 --- a/content_scripts/scroller.coffee +++ b/content_scripts/scroller.coffee @@ -1,5 +1,3 @@ -window.Scroller = root = {} -  #  # activatedElement is different from document.activeElement -- the latter seems to be reserved mostly for  # input elements. This mechanism allows us to decide whether to scroll a div or to scroll the whole document. @@ -7,10 +5,6 @@ window.Scroller = root = {}  activatedElement = null  settings = null -root.init = (frontendSettings) -> -  settings = frontendSettings -  handlerStack.push DOMActivate: -> activatedElement = event.target -  scrollProperties =    x: {      axisName: 'scrollLeft' @@ -37,8 +31,11 @@ getDimension = (el, direction, amount) ->  # Test whether element should be scrolled.  isScrollAllowed = (element, direction) -> +  computedStyle = window.getComputedStyle(element)    # Elements with `overflow: hidden` should not be scrolled. -  window.getComputedStyle(element).getPropertyValue("overflow-#{direction}") != "hidden" +  return computedStyle.getPropertyValue("overflow-#{direction}") != "hidden" and +         ["hidden", "collapse"].indexOf(computedStyle.getPropertyValue("visibility")) == -1 and +         computedStyle.getPropertyValue("display") != "none"  # Test whether element actually scrolls in the direction required when asked to do so.  # Due to chrome bug 110149, scrollHeight and clientHeight cannot be used to reliably determine whether an @@ -58,7 +55,7 @@ isScrollPossible = (element, direction, amount, factor) ->    before != after  # Find the element we should and can scroll. -findScrollableElement = (element, direction, amount, factor = 1) -> +findScrollableElement = (element = document.body, direction, amount, factor = 1) ->    axisName = scrollProperties[direction].axisName    while element != document.body and      not (isScrollPossible(element, direction, amount, factor) and isScrollAllowed(element, direction)) @@ -81,75 +78,66 @@ performScroll = (element, axisName, amount, checkVisibility = true) ->    element[axisName] - before  # Scroll by a relative amount (a number) in some direction, possibly smoothly. -doScrollBy = do -> -  interval = 10 # Update interval (in ms). -  duration = 120 # This must be a multiple of interval (also in ms). -  fudgeFactor = 25 -  timer = null +doScrollBy = (element, direction, amount, wantSmooth) -> +  axisName = scrollProperties[direction].axisName -  clearTimer = -> -    if timer -      clearInterval timer -      timer = null +  unless wantSmooth and settings.get "smoothScroll" +    return performScroll element, axisName, amount + +  duration = 100 # Duration in ms. +  fudgeFactor = 25    # Allow a bit longer for longer scrolls. -  calculateExtraDuration = (amount) -> -    extra = fudgeFactor * Math.log Math.abs amount -    # Ensure we have a multiple of interval. -    return interval * Math.round (extra / interval) - -  (element, direction, amount, wantSmooth) -> -    axisName = scrollProperties[direction].axisName -    clearTimer() - -    unless wantSmooth and settings.get "smoothScroll" -      return performScroll element, axisName, amount - -    requiredTicks = (duration + calculateExtraDuration amount) / interval -    # Round away from 0, so that we don't leave any scroll amount unscrolled. -    delta = (if 0 <= amount then Math.ceil else Math.floor)(amount / requiredTicks) - -    if delta -      ticks = 0 -      ticker = -> -        if performScroll(element, axisName, delta, false) != delta or ++ticks == requiredTicks -          # One final call of performScroll to check the visibility of the activated element. -          performScroll(element, axisName, 0, true) -          clearTimer() - -      timer = setInterval ticker, interval -      ticker() - -# scroll the active element in :direction by :amount * :factor. -# :factor is needed because :amount can take on string values, which scrollBy converts to element dimensions. -root.scrollBy = (direction, amount, factor = 1) -> -  # if this is called before domReady, just use the window scroll function -  if (!document.body and amount instanceof Number) -    if (direction == "x") -      window.scrollBy(amount, 0) +  duration += fudgeFactor * Math.log Math.abs amount + +  roundOut = if 0 <= amount then Math.ceil else Math.floor + +  # Round away from 0, so that we don't leave any scroll amount unscrolled. +  delta = roundOut(amount / duration) + +  animatorId = null +  start = null +  lastTime = null +  scrolledAmount = 0 + +  animate = (timestamp) -> +    start ?= timestamp + +    progress = Math.min(timestamp - start, duration) +    scrollDelta = roundOut(delta * progress) - scrolledAmount +    scrolledAmount += scrollDelta + +    if performScroll(element, axisName, scrollDelta, false) != scrollDelta or +       progress >= duration +      # One final call of performScroll to check the visibility of the activated element. +      performScroll(element, axisName, 0, true) +      window.cancelAnimationFrame(animatorId)      else -      window.scrollBy(0, amount) -    return +      animatorId = window.requestAnimationFrame(animate) + +  animatorId = window.requestAnimationFrame(animate) -  if (!activatedElement || !isRendered(activatedElement)) -    activatedElement = document.body +Scroller = +  init: (frontendSettings) -> +    settings = frontendSettings +    handlerStack.push DOMActivate: -> activatedElement = event.target -  element = findScrollableElement activatedElement, direction, amount, factor -  elementAmount = factor * getDimension element, direction, amount -  doScrollBy element, direction, elementAmount, true +  # scroll the active element in :direction by :amount * :factor. +  # :factor is needed because :amount can take on string values, which scrollBy converts to element dimensions. +  scrollBy: (direction, amount, factor = 1) -> +    # if this is called before domReady, just use the window scroll function +    return unless document.body -root.scrollTo = (direction, pos, wantSmooth = false) -> -  return unless document.body +    element = findScrollableElement activatedElement, direction, amount, factor +    elementAmount = factor * getDimension element, direction, amount +    doScrollBy element, direction, elementAmount, true -  if (!activatedElement || !isRendered(activatedElement)) -    activatedElement = document.body +  scrollTo: (direction, pos, wantSmooth = false) -> +    return unless document.body -  element = findScrollableElement activatedElement, direction, pos -  amount = getDimension(element,direction,pos) - element[scrollProperties[direction].axisName] -  doScrollBy element, direction, amount, wantSmooth +    element = findScrollableElement activatedElement, direction, pos +    amount = getDimension(element,direction,pos) - element[scrollProperties[direction].axisName] +    doScrollBy element, direction, amount, wantSmooth -# TODO refactor and put this together with the code in getVisibleClientRect -isRendered = (element) -> -  computedStyle = window.getComputedStyle(element, null) -  return !(computedStyle.getPropertyValue("visibility") != "visible" || -      computedStyle.getPropertyValue("display") == "none") +root = exports ? window +root.Scroller = Scroller | 
