diff options
| author | Stephen Blott | 2014-11-11 06:52:22 +0000 |
|---|---|---|
| committer | Stephen Blott | 2014-11-11 10:52:04 +0000 |
| commit | 7e807c82bb708ed1e7ed50b2a0c302dda47f1a39 (patch) | |
| tree | 2c2755be96d588c057aab03412e5b7efc53dd80a | |
| parent | d19b7dff273674efbde9a3d5f6992bd4aaab41b0 (diff) | |
| download | vimium-7e807c82bb708ed1e7ed50b2a0c302dda47f1a39.tar.bz2 | |
Smooth scroll; initial handle keyboard repeat.
| -rw-r--r-- | content_scripts/scroller.coffee | 95 |
1 files changed, 66 insertions, 29 deletions
diff --git a/content_scripts/scroller.coffee b/content_scripts/scroller.coffee index 1becc523..eba9fafb 100644 --- a/content_scripts/scroller.coffee +++ b/content_scripts/scroller.coffee @@ -2,6 +2,7 @@ # 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. # +root = exports ? window activatedElement = null settings = null @@ -78,44 +79,81 @@ performScroll = (element, axisName, amount, checkVisibility = true) -> element[axisName] - before # Scroll by a relative amount (a number) in some direction, possibly smoothly. -doScrollBy = (element, direction, amount, wantSmooth) -> - axisName = scrollProperties[direction].axisName +doScrollBy = do -> + time = 0 + lastActivationId = -1 + keyupHandler = null + + # When the keyboard is repeating and the key is released, sometimes the last key event is delivered *after* + # the keyup event. This could cause indefinite scrolling with no key pressed, because no further keyup + # event is delivered (we behave as if the final key is still pressed). To get around this, we briefly hold + # a lock to prevent a new smooth scrolling event from beginning too soon after the end of the previous + # scroll. This is a hack. + keyLock = false + + (element, direction, amount, wantSmooth) -> + axisName = scrollProperties[direction].axisName + + unless wantSmooth and settings.get "smoothScroll" + return performScroll element, axisName, amount + + # Assumption. If animator A is started before animator B, then A will also finish before B. We need to + # verify this - it may not always be true. + + console.log("locked") if keyLock + if lastActivationId == time or keyLock + # This is a keyboard repeat, and the most-recently activated animator will handle it. + return + + if not keyupHandler + keyupHandler = root.handlerStack.push keyup: -> + time += 1 + keyLock = true + setTimeout((-> keyLock = false), 10) + console.log "keyup", time, lastActivationId + true # Do not prevent propagation. - unless wantSmooth and settings.get "smoothScroll" - return performScroll element, axisName, amount + lastActivationId = activationId = ++time + console.log activationId - duration = 100 # Duration in ms. - fudgeFactor = 25 + duration = 100 # Duration in ms. + fudgeFactor = 25 - # Allow a bit longer for longer scrolls. - duration += fudgeFactor * Math.log Math.abs amount + # Allow a bit longer for longer scrolls. + 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. + roundOut = if 0 <= amount then Math.ceil else Math.floor + delta = roundOut(amount / duration) - # Round away from 0, so that we don't leave any scroll amount unscrolled. - delta = roundOut(amount / duration) + animatorId = null + lastTime = null + start = null + scrolledAmount = 0 - animatorId = null - start = null - lastTime = null - scrolledAmount = 0 + stopScrolling = (progress) -> + if activationId == time + # This is the most recent animator, and the key is still pressed. + false + else + duration <= progress - animate = (timestamp) -> - start ?= timestamp + animate = (timestamp) -> + start ?= timestamp - progress = Math.min(timestamp - start, duration) - scrollDelta = roundOut(delta * progress) - scrolledAmount - scrolledAmount += scrollDelta + progress = timestamp - start + 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 - animatorId = window.requestAnimationFrame(animate) + if performScroll(element, axisName, scrollDelta, false) != scrollDelta or stopScrolling progress + # One final call of performScroll to check the visibility of the activated element. + performScroll(element, axisName, 0, true) + window.cancelAnimationFrame(animatorId) + time += 1 if activationId == time + else + animatorId = window.requestAnimationFrame(animate) - animatorId = window.requestAnimationFrame(animate) + animatorId = window.requestAnimationFrame(animate) Scroller = init: (frontendSettings) -> @@ -148,5 +186,4 @@ Scroller = amount = getDimension(element,direction,pos) - element[scrollProperties[direction].axisName] doScrollBy element, direction, amount, wantSmooth -root = exports ? window root.Scroller = Scroller |
