aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authormrmr19932014-11-10 08:27:55 +0000
committermrmr19932014-11-10 16:43:11 +0000
commitdf521c26fda9b8d3e8c182fc85deaf5b8c723cd4 (patch)
tree3dbc95e188891dc94a88d7cc57df03c289db29f2
parentb8b1644a306c1fe12b5faa5204630eb30f1e64b3 (diff)
downloadvimium-df521c26fda9b8d3e8c182fc85deaf5b8c723cd4.tar.bz2
Change smooth scrolling to requestAnimationFrame, tidy up scroller code
-rw-r--r--content_scripts/scroller.coffee128
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