Additional scroll options...

Option for whether or not to load next file after reaching the start/end when using key based scrolling
Configurable delay before loading the next file after reaching the end with auto-scroll
This commit is contained in:
Michael Ziminsky (Z) 2019-12-26 12:28:42 -07:00
parent 568d32ec16
commit 0a9e52d212
6 changed files with 83 additions and 21 deletions

View File

@ -51,9 +51,9 @@ def add_small_scroll(amt):
def report_human_scroll(amt):
if amt > 0:
if abs(amt) > 0:
h = scroll_viewport.height()
is_large_scroll = (amt / h) >= 0.5
is_large_scroll = (abs(amt) / h) >= 0.5
if is_large_scroll:
clear_small_scrolls()
get_boss().report_human_scroll(amt / document_height())
@ -180,10 +180,19 @@ def handle_shortcut(sc_name, evt):
def layout(is_single_page):
cancel_scroll()
scroll_animator.wait = False
scroll_animator.sync()
set_css(document.body, margin='0', border_width='0', padding='0')
line_height.doc_style = window.getComputedStyle(document.body)
# Pause auto-scroll while minimized
document.addEventListener("visibilitychange", def():
if (document.visibilityState is 'visible'):
scroll_animator.sync()
else:
scroll_animator.pause()
)
def cancel_scroll():
scroll_animator.stop()
@ -202,16 +211,22 @@ class ScrollAnimator:
return self.animation_id != None
def start(self, direction, auto):
if self.wait:
return
now = performance.now()
self.end_time = now + self.DURATION
clearTimeout(self.auto_timer)
if !self.is_running() or direction != self.direction or auto != self.auto:
self.paused = self.direction if self.auto and not auto else False
if self.auto and not auto:
self.pause()
self.stop()
self.auto = auto
self.direction = direction
self.start_time = now
self.start_offset = window.pageYOffset
self.csi_idx = current_spine_item().index
self.animation_id = window.requestAnimationFrame(self.auto_scroll if auto else self.smooth_scroll)
def smooth_scroll(self, ts):
@ -221,15 +236,19 @@ class ScrollAnimator:
scroll_target += Math.trunc(self.direction * progress * duration * line_height() * opts.lines_per_sec_smooth) / 1000
window.scrollTo(0, scroll_target)
if progress < 1:
self.animation_id = window.requestAnimationFrame(self.smooth_scroll)
else:
amt = window.pageYOffset - self.start_offset
if abs(amt) < 3 and duration is self.DURATION and is_scroll_end(scroll_target):
if is_scroll_end(scroll_target) and (not opts.scroll_stop_boundaries or (abs(amt) < 3 and duration is self.DURATION)):
# "Turn the page" if stop at boundaries option is false or
# this is a new scroll action and we were already at the end
self.animation_id = None
self.wait = True
report_human_scroll(amt)
get_boss().send_message('next_spine_item', previous=self.direction is DIRECTION.Up)
elif progress < 1:
self.animation_id = window.requestAnimationFrame(self.smooth_scroll)
elif self.paused:
self.start(self.paused, True)
self.resume()
else:
self.animation_id = None
report_human_scroll(amt)
@ -247,24 +266,45 @@ class ScrollAnimator:
self.sync(ts)
if scroll_finished:
self.stop()
self.pause()
if opts.scroll_auto_boundary_delay:
self.auto_timer = setTimeout(def(): get_boss().send_message('next_spine_item', previous=self.direction is DIRECTION.Up);, opts.scroll_auto_boundary_delay * 1000)
else:
self.animation_id = window.requestAnimationFrame(self.auto_scroll)
def report(self):
amt = window.pageYOffset - self.start_offset
if abs(amt) > 0 and self.csi_idx is current_spine_item().index:
report_human_scroll(amt)
def sync(self, ts):
if self.auto:
report_human_scroll(window.pageYOffset - self.start_offset)
self.report()
self.csi_idx = current_spine_item().index
self.start_time = ts or performance.now()
self.start_offset = window.pageYOffset
else:
self.resume()
def stop(self):
self.auto = False
if self.animation_id is not None:
window.cancelAnimationFrame(self.animation_id)
self.animation_id = None
amt = window.pageYOffset - self.start_offset
if amt > 0:
report_human_scroll(amt)
self.report()
def pause(self):
if self.auto:
self.paused = self.direction
self.stop()
else:
self.paused = False
# Resume auto-scroll
def resume(self):
if self.paused:
self.start(self.paused, True)
self.paused = False
scroll_animator = ScrollAnimator()

View File

@ -270,7 +270,6 @@ class IframeBoss:
self.last_window_width, self.last_window_height = scroll_viewport.width(), scroll_viewport.height()
apply_settings()
fix_fullscreen_svg_images()
self.do_layout(self.is_titlepage)
if self.mathjax:
return apply_mathjax(self.mathjax, self.book.manifest.link_uid, self.content_loaded_stage2)
# window.setTimeout(self.content_loaded_stage2, 1000)
@ -307,6 +306,7 @@ class IframeBoss:
if si:
self.length_before += files[si]?.length or 0
self.onscroll()
self.do_layout(self.is_titlepage)
self.send_message('content_loaded', progress_frac=self.calculate_progress_frac(), file_progress_frac=progress_frac())
self.last_cfi = None
window.setTimeout(self.update_cfi, 0)

View File

@ -16,6 +16,9 @@ CONTAINER = unique_id('standalone-scrolling-settings')
MIN_SCROLL_SPEED_AUTO = 0.25
MAX_SCROLL_SPEED_AUTO = 5
MIN_SCROLL_AUTO_DELAY = 0
MAX_SCROLL_AUTO_DELAY = 10
MIN_SCROLL_SPEED_SMOOTH = 10
MAX_SCROLL_SPEED_SMOOTH = 50
@ -57,7 +60,7 @@ def create_scrolling_panel(container, apply_func, cancel_func):
ans = E.input(type='number', name=name, id=name)
for key, val in Object.entries(kwargs):
ans[key] = val
ans.valueAsNumber = sd.get(name) or defaults[name]
ans.valueAsNumber = sd.get(name, defaults[name])
return E.label("for"=name, text), ans
container.appendChild(E.div(style='margin-top:1ex', _('Control how mouse based scrolling works in paged mode')))
@ -68,8 +71,12 @@ def create_scrolling_panel(container, apply_func, cancel_func):
container.appendChild(E.hr())
container.appendChild(E.div(style='margin-top:1ex', _('Control how smooth scrolling works in flow mode')))
container.appendChild(cb(
'scroll_stop_boundaries',
_('Stop at file boundaries when continuous scrolling while holding down the scroll key')
))
container.appendChild(
E.div(style='display:grid;margin-top:1ex;align-items:center;grid-template-columns:auto auto;grid-gap:1ex;justify-content:flex-start;',
E.div(style='display:grid;margin-top:1ex;align-items:center;grid-template-columns:25em min-content;grid-gap:1ex',
*spinner(
'lines_per_sec_smooth',
_('Smooth scrolling speed in lines/sec'),
@ -83,6 +90,13 @@ def create_scrolling_panel(container, apply_func, cancel_func):
step=MIN_SCROLL_SPEED_AUTO,
min=MIN_SCROLL_SPEED_AUTO,
max=MAX_SCROLL_SPEED_AUTO
),
*spinner(
'scroll_auto_boundary_delay',
_('Seconds to wait before loading the next file after auto-scroll reaches the end; 0 to disable'),
step=0.25,
min=MIN_SCROLL_AUTO_DELAY,
max=MAX_SCROLL_AUTO_DELAY
)
)
)

View File

@ -23,6 +23,8 @@ def update_settings(settings):
opts.margin_right = max(0, settings.margin_right)
opts.override_book_colors = settings.override_book_colors
opts.paged_wheel_scrolls_by_screen = v'!!settings.paged_wheel_scrolls_by_screen'
opts.scroll_auto_boundary_delay = settings.scroll_auto_boundary_delay
opts.scroll_stop_boundaries = v'!!settings.scroll_stop_boundaries'
opts.user_stylesheet = settings.user_stylesheet
update_settings()

View File

@ -712,6 +712,8 @@ class View:
'paged_wheel_scrolls_by_screen': sd.get('paged_wheel_scrolls_by_screen'),
'lines_per_sec_auto': sd.get('lines_per_sec_auto'),
'lines_per_sec_smooth': sd.get('lines_per_sec_smooth'),
'scroll_auto_boundary_delay': sd.get('scroll_auto_boundary_delay'),
'scroll_stop_boundaries': sd.get('scroll_stop_boundaries'),
}
def show_name(self, name, initial_position=None):

View File

@ -49,6 +49,8 @@ defaults = {
'paged_margin_clicks_scroll_by_screen': True,
'paged_wheel_scrolls_by_screen': False,
'read_mode': 'paged',
'scroll_auto_boundary_delay': 5,
'scroll_stop_boundaries': False,
'standalone_font_settings': {},
'standalone_misc_settings': {},
'standalone_recently_opened': v'[]',
@ -75,6 +77,8 @@ is_local_setting = {
'max_text_width': True,
'override_book_colors': True,
'read_mode': 'paged',
'scroll_auto_boundary_delay': True,
'scroll_stop_boundaries': True,
'standalone_font_settings': True,
'standalone_misc_settings': True,
'standalone_recently_opened': True,