mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Momentum scrolling in flow mode
This commit is contained in:
parent
6f93debe6e
commit
5eaccd6619
@ -1,6 +1,6 @@
|
||||
# vim:fileencoding=utf-8
|
||||
# License: GPL v3 Copyright: 2016, Kovid Goyal <kovid at kovidgoyal.net>
|
||||
from __python__ import hash_literals
|
||||
from __python__ import hash_literals, bound_methods
|
||||
|
||||
from dom import set_css
|
||||
from read_book.globals import get_boss
|
||||
@ -99,7 +99,56 @@ def flow_onkeydown(evt):
|
||||
def layout(is_single_page):
|
||||
set_css(document.body, margin='0', border_width='0', padding='0')
|
||||
|
||||
class FlickAnimator:
|
||||
|
||||
SPEED_FACTOR = 0.04
|
||||
DECEL_TIME_CONSTANT = 325 # milliseconds
|
||||
VELOCITY_HISTORY = 300 # milliseconds
|
||||
MIMUMUM_VELOCITY = 100 # pixels/sec
|
||||
|
||||
def __init__(self):
|
||||
self.animation_id = None
|
||||
|
||||
def start(self, gesture):
|
||||
self.vertical = gesture.axis is 'vertical'
|
||||
now = window.performance.now()
|
||||
points = times = None
|
||||
for i, t in enumerate(gesture.times):
|
||||
if now - t < self.VELOCITY_HISTORY:
|
||||
points, times = gesture.points[i:], gesture.times[i:]
|
||||
break
|
||||
if times and times.length > 1:
|
||||
elapsed = (times[-1] - times[0]) / 1000
|
||||
if elapsed > 0 and points.length > 1:
|
||||
delta = points[0] - points[-1]
|
||||
velocity = delta / elapsed
|
||||
if abs(velocity) > self.MIMUMUM_VELOCITY:
|
||||
self.amplitude = self.SPEED_FACTOR * velocity
|
||||
self.start_time = now
|
||||
self.animation_id = window.requestAnimationFrame(self.auto_scroll)
|
||||
|
||||
def auto_scroll(self, ts):
|
||||
if self.animation_id is None:
|
||||
return
|
||||
elapsed = window.performance.now() - self.start_time
|
||||
delta = self.amplitude * Math.exp(-elapsed / self.DECEL_TIME_CONSTANT)
|
||||
if abs(delta) >= 1:
|
||||
delta = Math.round(delta)
|
||||
if self.vertical:
|
||||
window.scrollBy(0, delta)
|
||||
else:
|
||||
window.scrollBy(delta, 0)
|
||||
self.animation_id = window.requestAnimationFrame(self.auto_scroll)
|
||||
|
||||
def stop(self):
|
||||
if self.animation_id is not None:
|
||||
window.cancelAnimationFrame(self.animation_id)
|
||||
self.animation_id = None
|
||||
|
||||
flick_animator = FlickAnimator()
|
||||
|
||||
def handle_gesture(gesture):
|
||||
flick_animator.stop()
|
||||
if gesture.type is 'swipe':
|
||||
if gesture.points.length > 1 and not gesture.is_held:
|
||||
delta = gesture.points[-2] - gesture.points[-1]
|
||||
@ -107,5 +156,5 @@ def handle_gesture(gesture):
|
||||
scroll_by(delta)
|
||||
else:
|
||||
window.scrollBy(delta, 0)
|
||||
if not gesture.active:
|
||||
print('TODO: Implement momentum scrolling')
|
||||
if not gesture.active and not gesture.is_held:
|
||||
flick_animator.start(gesture)
|
||||
|
@ -16,7 +16,7 @@ def copy_touch(t):
|
||||
return {
|
||||
'identifier':t.identifier,
|
||||
'page_x':v'[t.pageX]', 'page_y':v'[t.pageY]', 'viewport_x':v'[t.clientX]', 'viewport_y':v'[t.clientY]',
|
||||
'active':True, 'mtime':now, 'ctime':now, 'is_held':False
|
||||
'active':True, 'mtimes':v'[now]', 'ctime':now, 'is_held':False, 'x_velocity': 0, 'y_velocity': 0
|
||||
}
|
||||
|
||||
def max_displacement(points):
|
||||
@ -47,11 +47,14 @@ def interpret_single_gesture(touch, gesture_id):
|
||||
ans.type = 'swipe'
|
||||
ans.axis = 'vertical' if delta_y > delta_x else 'horizontal'
|
||||
ans.points = pts = touch.viewport_y if ans.axis is 'vertical' else touch.viewport_x
|
||||
ans.times = touch.mtimes
|
||||
positive = pts[-1] > pts[0]
|
||||
if ans.axis is 'vertical':
|
||||
ans.direction = 'down' if positive else 'up'
|
||||
ans.velocity = touch.y_velocity
|
||||
else:
|
||||
ans.direction = 'right' if positive else 'left'
|
||||
ans.velocity = touch.x_velocity
|
||||
return ans
|
||||
return ans
|
||||
|
||||
@ -97,7 +100,7 @@ class TouchHandler:
|
||||
for tid in self.ongoing_touches:
|
||||
t = self.ongoing_touches[tid]
|
||||
if t.active:
|
||||
if now - t.mtime > 3000:
|
||||
if now - t.mtimes[-1] > 3000:
|
||||
expired.push(t.identifier)
|
||||
for tid in expired:
|
||||
v'delete self.ongoing_touches[tid]'
|
||||
@ -125,7 +128,7 @@ class TouchHandler:
|
||||
found_hold = False
|
||||
for touchid in self.ongoing_touches:
|
||||
touch = self.ongoing_touches[touchid]
|
||||
if touch.active and now - touch.mtime > HOLD_THRESHOLD:
|
||||
if touch.active and now - touch.mtimes[-1] > HOLD_THRESHOLD:
|
||||
touch.is_held = True
|
||||
found_hold = True
|
||||
if found_hold:
|
||||
@ -146,7 +149,8 @@ class TouchHandler:
|
||||
self.start_hold_timer()
|
||||
|
||||
def update_touch(self, t, touch):
|
||||
t.mtime = window.performance.now()
|
||||
now = window.performance.now()
|
||||
t.mtimes.push(now)
|
||||
t.page_x.push(touch.pageX), t.page_y.push(touch.pageY)
|
||||
t.viewport_x.push(touch.clientX), t.viewport_y.push(touch.clientY)
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user