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
|
# vim:fileencoding=utf-8
|
||||||
# License: GPL v3 Copyright: 2016, Kovid Goyal <kovid at kovidgoyal.net>
|
# 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 dom import set_css
|
||||||
from read_book.globals import get_boss
|
from read_book.globals import get_boss
|
||||||
@ -99,7 +99,56 @@ def flow_onkeydown(evt):
|
|||||||
def layout(is_single_page):
|
def layout(is_single_page):
|
||||||
set_css(document.body, margin='0', border_width='0', padding='0')
|
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):
|
def handle_gesture(gesture):
|
||||||
|
flick_animator.stop()
|
||||||
if gesture.type is 'swipe':
|
if gesture.type is 'swipe':
|
||||||
if gesture.points.length > 1 and not gesture.is_held:
|
if gesture.points.length > 1 and not gesture.is_held:
|
||||||
delta = gesture.points[-2] - gesture.points[-1]
|
delta = gesture.points[-2] - gesture.points[-1]
|
||||||
@ -107,5 +156,5 @@ def handle_gesture(gesture):
|
|||||||
scroll_by(delta)
|
scroll_by(delta)
|
||||||
else:
|
else:
|
||||||
window.scrollBy(delta, 0)
|
window.scrollBy(delta, 0)
|
||||||
if not gesture.active:
|
if not gesture.active and not gesture.is_held:
|
||||||
print('TODO: Implement momentum scrolling')
|
flick_animator.start(gesture)
|
||||||
|
@ -16,7 +16,7 @@ def copy_touch(t):
|
|||||||
return {
|
return {
|
||||||
'identifier':t.identifier,
|
'identifier':t.identifier,
|
||||||
'page_x':v'[t.pageX]', 'page_y':v'[t.pageY]', 'viewport_x':v'[t.clientX]', 'viewport_y':v'[t.clientY]',
|
'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):
|
def max_displacement(points):
|
||||||
@ -47,11 +47,14 @@ def interpret_single_gesture(touch, gesture_id):
|
|||||||
ans.type = 'swipe'
|
ans.type = 'swipe'
|
||||||
ans.axis = 'vertical' if delta_y > delta_x else 'horizontal'
|
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.points = pts = touch.viewport_y if ans.axis is 'vertical' else touch.viewport_x
|
||||||
|
ans.times = touch.mtimes
|
||||||
positive = pts[-1] > pts[0]
|
positive = pts[-1] > pts[0]
|
||||||
if ans.axis is 'vertical':
|
if ans.axis is 'vertical':
|
||||||
ans.direction = 'down' if positive else 'up'
|
ans.direction = 'down' if positive else 'up'
|
||||||
|
ans.velocity = touch.y_velocity
|
||||||
else:
|
else:
|
||||||
ans.direction = 'right' if positive else 'left'
|
ans.direction = 'right' if positive else 'left'
|
||||||
|
ans.velocity = touch.x_velocity
|
||||||
return ans
|
return ans
|
||||||
return ans
|
return ans
|
||||||
|
|
||||||
@ -97,7 +100,7 @@ class TouchHandler:
|
|||||||
for tid in self.ongoing_touches:
|
for tid in self.ongoing_touches:
|
||||||
t = self.ongoing_touches[tid]
|
t = self.ongoing_touches[tid]
|
||||||
if t.active:
|
if t.active:
|
||||||
if now - t.mtime > 3000:
|
if now - t.mtimes[-1] > 3000:
|
||||||
expired.push(t.identifier)
|
expired.push(t.identifier)
|
||||||
for tid in expired:
|
for tid in expired:
|
||||||
v'delete self.ongoing_touches[tid]'
|
v'delete self.ongoing_touches[tid]'
|
||||||
@ -125,7 +128,7 @@ class TouchHandler:
|
|||||||
found_hold = False
|
found_hold = False
|
||||||
for touchid in self.ongoing_touches:
|
for touchid in self.ongoing_touches:
|
||||||
touch = self.ongoing_touches[touchid]
|
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
|
touch.is_held = True
|
||||||
found_hold = True
|
found_hold = True
|
||||||
if found_hold:
|
if found_hold:
|
||||||
@ -146,7 +149,8 @@ class TouchHandler:
|
|||||||
self.start_hold_timer()
|
self.start_hold_timer()
|
||||||
|
|
||||||
def update_touch(self, t, touch):
|
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.page_x.push(touch.pageX), t.page_y.push(touch.pageY)
|
||||||
t.viewport_x.push(touch.clientX), t.viewport_y.push(touch.clientY)
|
t.viewport_x.push(touch.clientX), t.viewport_y.push(touch.clientY)
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user