Skip to content

Commit d838650

Browse files
committed
Hideable chrome experiment + tweaking
1 parent b107075 commit d838650

8 files changed

Lines changed: 133 additions & 57 deletions

File tree

src/BookReader.js

Lines changed: 32 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -174,36 +174,31 @@ BookReader.prototype.setup = function(options) {
174174
/** @deprecated */
175175
this.bookPath = options.bookPath;
176176
this.fader = utils.debounce(
177-
(source) => {
178-
console.log('Fading UI');
179-
$(document.body)
180-
.addClass('faded')
181-
.toggleClass('faded--scroll', source === 'scroll');
182-
// const animation = this.$('.BRfooter')[0].getAnimations()[0];
183-
// if (!animation) return;
184-
// if (source === 'scroll') animation.pause();
185-
// animation.currentTime = 0;
186-
},
177+
(source) => this.fader.hide(source),
187178
2000,
188-
(source) => {
189-
console.log('Show UI');
190-
$(document.body)
191-
.removeClass('faded')
192-
.toggleClass('faded--scroll', source === 'scroll');
193-
},
179+
(source) => this.fader.show(source),
194180
{
195181
tap: (source) => {
196182
console.log('Show UI ...', source);
197-
// if (source === 'scroll') {
198-
// const animation = this.$('.BRfooter')[0].getAnimations()[0];
199-
// if (animation) {
200-
// // const max = animation.effect.getComputedTiming().duration;
201-
// animation.currentTime += 1;
202-
// }
203-
// }
204183
},
205184
},
206185
);
186+
this.fader.show = (source) => {
187+
console.log('Show UI');
188+
$(document.body).removeClass('BRfaded');
189+
};
190+
this.fader.hide = (source) => {
191+
console.log('Hide UI');
192+
$(document.body).addClass('BRfaded');
193+
};
194+
this.fader.toggle = (source) => {
195+
if ($(document.body).hasClass('BRfaded')) {
196+
this.fader.show(source);
197+
this.fader(source);
198+
} else {
199+
this.fader.hide(source);
200+
}
201+
};
207202

208203
// Construct the usual plugins first to get type hints
209204
this.plugins = {
@@ -773,8 +768,16 @@ BookReader.prototype.init = function() {
773768
}
774769
}
775770

776-
this.refs.$br.on('mousemove', () => this.fader('mousemove'));
777-
this.refs.$brContainer[0].addEventListener('scroll', utils.eventFilterScrollUp(() => this.fader('scroll')), { passive: true });
771+
// Init modes
772+
for (const mode of Object.values(this._modes)) {
773+
mode.init();
774+
}
775+
776+
if (this.plugins.experiments?.isExperimentEnabled('hideable-chrome')) {
777+
this.refs.$br.on('pointermove', utils.eventFilterMouseMove(ev => ev.pointerType != 'touch' && this.fader('pointermove')));
778+
this.refs.$brContainer[0].addEventListener('scroll', utils.eventFilterScrollUp(() => this.fader('scroll')), { passive: true });
779+
this.refs.$brContainer[0].addEventListener('touchstart', utils.eventFilterSameElement(() => this.fader('touchstart')), { passive: true });
780+
}
778781

779782
this.init.initComplete = true;
780783
this.trigger(BookReader.eventNames.PostInit);
@@ -1050,16 +1053,12 @@ BookReader.prototype.zoom = function(direction) {
10501053
* So resize isn't perceived sharp/jerky
10511054
*/
10521055
BookReader.prototype.resizeBRcontainer = function(animate) {
1056+
const top = this.getToolBarHeight();
1057+
const bottom = this.plugins.experiments?.isExperimentEnabled('hideable-chrome') ? 0 : this.getFooterHeight();
10531058
if (animate) {
1054-
this.refs.$brContainer.animate({
1055-
top: this.getToolBarHeight(),
1056-
// bottom: this.getFooterHeight(),
1057-
}, this.constResizeAnimationDuration, 'linear');
1059+
this.refs.$brContainer.animate({ top, bottom }, this.constResizeAnimationDuration, 'linear');
10581060
} else {
1059-
this.refs.$brContainer.css({
1060-
top: this.getToolBarHeight(),
1061-
// bottom: this.getFooterHeight(),
1062-
});
1061+
this.refs.$brContainer.css({ top, bottom });
10631062
}
10641063
};
10651064

src/BookReader/Mode1Up.js

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@ export class Mode1Up extends ModeAbstract {
2626
// as the name of the web component, and the webcomponents polyfill
2727
// uses the name of component as a class for style scoping 😒
2828
.addClass('br-mode-1up__root BRmode1up');
29-
this.$el[0].addEventListener('scroll', eventFilterScrollUp(() => this.br.fader('scroll')), { passive: true });
3029

3130
/** Has mode1up ever been rendered before? */
3231
this.everShown = false;
@@ -40,6 +39,13 @@ export class Mode1Up extends ModeAbstract {
4039
return this.mode1UpLit;
4140
}
4241

42+
init() {
43+
if (this.br.plugins.experiments?.isExperimentEnabled('hideable-chrome')) {
44+
this.$el[0].addEventListener('scroll', eventFilterScrollUp(() => this.br.fader('scroll')), { passive: true });
45+
this.$el[0].addEventListener('click', () => this.br.fader('click'), { passive: true });
46+
}
47+
}
48+
4349
/**
4450
* This is called when we switch to one page view
4551
*/

src/BookReader/Mode2Up.js

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
import { Mode2UpLit } from './Mode2UpLit.js';
33
import { DragScrollable } from './DragScrollable.js';
44
import { ModeAbstract } from './ModeAbstract.js';
5-
import { eventFilterScrollUp } from './utils.js';
5+
import { eventFilterSameElement, eventFilterScrollUp } from './utils.js';
66
/** @typedef {import('../BookReader.js').default} BookReader */
77
/** @typedef {import('./BookModel.js').BookModel} BookModel */
88
/** @typedef {import('./BookModel.js').PageIndex} PageIndex */
@@ -32,12 +32,22 @@ export class Mode2Up extends ModeAbstract {
3232
// as the name of the web component, and the webcomponents polyfill
3333
// uses the name of component as a class for style scoping 😒
3434
.addClass('br-mode-2up__root BRmode2up');
35-
this.$el[0].addEventListener('scroll', eventFilterScrollUp(() => this.br.fader('scroll')), { passive: true });
3635

3736
/** Has mode2up ever been rendered before? */
3837
this.everShown = false;
3938
}
4039

40+
init() {
41+
if (this.br.plugins.experiments?.isExperimentEnabled('hideable-chrome')) {
42+
this.$el[0].addEventListener('scroll', eventFilterScrollUp(() => this.br.fader('scroll')), { passive: true });
43+
this.$el[0].addEventListener('click', eventFilterSameElement(() => this.br.fader.toggle('click')));
44+
this.$el.on('click', '.BRpagecontainer', () => {
45+
if (this.$el.hasClass('BRpageFlipping')) return;
46+
this.br.fader.toggle('click');
47+
});
48+
}
49+
}
50+
4151
/**
4252
* This is called when we switch into this mode
4353
*/

src/BookReader/Mode2UpLit.js

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -680,9 +680,10 @@ export class Mode2UpLit extends LitElement {
680680

681681
const $page = $(ev.target).closest('.BRpagecontainer');
682682
if (!$page.length) return;
683-
if ($page.data('side') == 'L') {
683+
const pageBoundingBox = $page[0].getBoundingClientRect();
684+
if ($page.data('side') == 'L' && ev.clientX < pageBoundingBox.left + pageBoundingBox.width * 3 / 4) {
684685
this.br.left();
685-
} else if ($page.data('side') == 'R') {
686+
} else if ($page.data('side') == 'R' && ev.clientX > pageBoundingBox.right - pageBoundingBox.width * 3 / 4) {
686687
this.br.right();
687688
}
688689
}

src/BookReader/ModeAbstract.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,4 +45,7 @@ export class ModeAbstract {
4545
return this.book.getPage(indexOrDirection);
4646
}
4747
}
48+
49+
/** @abstract */
50+
init() {}
4851
}

src/BookReader/utils.js

Lines changed: 51 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -335,15 +335,64 @@ export function sortBy(array, valueFn) {
335335
export function eventFilterScrollUp(callback, {scrollDelay = 20} = {}) {
336336
let lastScrollTop = 0;
337337
let accumulatedScroll = 0;
338-
return function() {
338+
return function(event) {
339339
const st = this.scrollTop;
340340
if (st < lastScrollTop) {
341341
accumulatedScroll += lastScrollTop - st;
342342
if (accumulatedScroll >= scrollDelay) {
343-
callback();
343+
callback(event);
344344
accumulatedScroll = 0;
345345
}
346346
}
347347
lastScrollTop = st;
348348
};
349349
}
350+
351+
/**
352+
* Filters mousemove events to only call callback when the mouse has moved
353+
* more than minDistance pixels.
354+
* @param {Function} callback
355+
* @param {Object} options
356+
* @param {number} [options.minDistance=10]
357+
*/
358+
export function eventFilterMouseMove(callback, {minDistance = 10} = {}) {
359+
let startX = null;
360+
let startY = null;
361+
362+
// debounce resetting startX/startY to avoid issues with small jitters
363+
const resetStart = debounce(() => {
364+
startX = null;
365+
startY = null;
366+
}, 100);
367+
368+
return function(event) {
369+
if (startX === null || startY === null) {
370+
startX = event.clientX;
371+
startY = event.clientY;
372+
return;
373+
}
374+
const deltaX = event.clientX - startX;
375+
const deltaY = event.clientY - startY;
376+
const distance = Math.sqrt(deltaX * deltaX + deltaY * deltaY);
377+
if (distance >= minDistance) {
378+
callback(event);
379+
startX = event.clientX;
380+
startY = event.clientY;
381+
}
382+
resetStart();
383+
};
384+
};
385+
386+
/**
387+
* Filters event if the target element is the same as the element the event is bound to.
388+
* Useful for avoiding handling events from child elements.
389+
* @param {Function} callback
390+
* @returns {function(Event): void}
391+
*/
392+
export function eventFilterSameElement(callback) {
393+
return function(event) {
394+
if (event.target === event.currentTarget) {
395+
callback(event);
396+
}
397+
};
398+
}

src/css/_BRnav.scss

Lines changed: 3 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -111,21 +111,10 @@ ia-bookreader::part(minimized-menu) {
111111
transition: opacity 0.2s, translate 0.2s;
112112
}
113113

114-
.faded {
115-
.BRfooter:not(:focus-within):not(:hover) {
114+
.BRfaded {
115+
.BookReader:not(.BRmodeThumb) .BRfooter:not(:has(:focus-visible)):not(:hover), ia-bookreader::part(minimized-menu) {
116116
opacity: 0;
117-
}
118-
ia-bookreader::part(minimized-menu) {
119-
opacity: 0;
120-
}
121-
}
122-
123-
.faded--scroll {
124-
.BRfooter:not(:focus-within):not(:hover) {
125-
translate: 0 20px;
126-
}
127-
ia-bookreader::part(minimized-menu) {
128-
translate: 0 20px;
117+
pointer-events: none;
129118
}
130119
}
131120

src/plugins/plugin.experiments.js

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ export class ExperimentsPlugin extends BookReaderPlugin {
5151
localStorageKey: 'BrExperiments',
5252

5353
/** The experiments that should be shown in the experiments panel */
54-
enabledExperiments: ['translate'],
54+
enabledExperiments: ['translate', 'hideable-chrome'],
5555
}
5656

5757
/** @type {ExperimentModel[]} */
@@ -77,6 +77,20 @@ export class ExperimentsPlugin extends BookReaderPlugin {
7777
});
7878
}
7979
}(),
80+
81+
new class extends ExperimentModel {
82+
name = 'hideable-chrome';
83+
title = 'Hideable Chrome';
84+
description = "Automatically hide the BookReader interface when reading.";
85+
enabled = false;
86+
async enable({ manual = false }) {
87+
if (manual) sleep(0).then(() => window.location.reload());
88+
}
89+
async disable() {
90+
sleep(0).then(() => window.location.reload());
91+
}
92+
}(),
93+
8094
new class extends ExperimentModel {
8195
name = 'hypothesis';
8296
title = 'Hypothes.is';
@@ -123,7 +137,7 @@ export class ExperimentsPlugin extends BookReaderPlugin {
123137
for (const experiment of this.allExperiments) {
124138
// TODO: imagesBaseURL should be replaced with assetRoot everywhere
125139
experiment.assetRoot = this.br.options.imagesBaseURL.replace(/images\/$/, '');
126-
experiment.icon = experiment.buildAssetPath(experiment.icon);
140+
experiment.icon = experiment.icon ? experiment.buildAssetPath(experiment.icon) : null;
127141
experiment.br = this.br;
128142
}
129143

@@ -151,6 +165,11 @@ export class ExperimentsPlugin extends BookReaderPlugin {
151165
localStorage.setItem(this.options.localStorageKey, JSON.stringify(states));
152166
}
153167

168+
isExperimentEnabled(experimentName) {
169+
const experiment = this.allExperiments.find(exp => exp.name === experimentName);
170+
return experiment ? experiment.enabled : false;
171+
}
172+
154173
/**
155174
* @param {ExperimentModel} experiment
156175
* @param {boolean} enabled
@@ -269,7 +288,7 @@ export class BrExperimentToggle extends LitElement {
269288
return html`
270289
<div class="experiment-card" style="margin-bottom: 10px;">
271290
<div style="display: flex; align-items: center; gap: 10px;">
272-
<img src="${this.icon}" style="width: 20px; height: 20px;" alt="" />
291+
${this.icon ? html`<img src="${this.icon}" style="width: 20px; height: 20px;" alt="" />` : ''}
273292
<div style="flex-grow: 1; font-weight: bold;">${this.title}</div>
274293
</div>
275294
<p style="opacity: 0.9">

0 commit comments

Comments
 (0)