add swipes by gestures in settings as an option, hammer.js for mobile
This commit is contained in:
+39
-34
@@ -1,47 +1,52 @@
|
|||||||
.fontSizeWrapper {
|
.fontSizeWrapper {
|
||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
.slider {
|
.slider {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 50%;
|
top: 50%;
|
||||||
transform: translate(0,-50%);
|
transform: translate(0, -50%);
|
||||||
width: 90%;
|
width: 90%;
|
||||||
height: 60px;
|
height: 60px;
|
||||||
background: transparent;
|
background: transparent;
|
||||||
border-radius: 20px;
|
border-radius: 20px;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
box-shadow: 0px 15px 40px #7E6D5766;
|
box-shadow: 0px 15px 40px #7e6d5766;
|
||||||
}
|
}
|
||||||
.slider label {
|
.slider label {
|
||||||
font-size: 20px;
|
font-size: 20px;
|
||||||
font-weight: 400;
|
font-weight: 400;
|
||||||
font-family: Open Sans;
|
font-family: Open Sans;
|
||||||
padding-right: 10px;
|
padding-right: 10px;
|
||||||
color: white;
|
color: white;
|
||||||
}
|
}
|
||||||
.slider input[type="range"] {
|
.slider input[type="range"] {
|
||||||
width: 80%;
|
width: 80%;
|
||||||
height: 5px;
|
height: 5px;
|
||||||
background: black;
|
background: black;
|
||||||
border: none;
|
border: none;
|
||||||
outline: none;
|
outline: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.item {
|
.item {
|
||||||
font-size: 20px;
|
font-size: 20px;
|
||||||
font-weight: 400;
|
font-weight: 400;
|
||||||
font-family: Open Sans;
|
font-family: Open Sans;
|
||||||
padding-right: 10px;
|
padding-right: 10px;
|
||||||
color: white;
|
color: white;
|
||||||
}
|
}
|
||||||
|
|
||||||
.item~button {
|
.item ~ button {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
border: none;
|
border: none;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
margin-top: 5%;
|
margin-top: 5%;
|
||||||
margin-right: 1%;
|
margin-right: 1%;
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#main,
|
||||||
|
#viewer {
|
||||||
|
touch-action: pan-y;
|
||||||
}
|
}
|
||||||
Vendored
+1255
File diff suppressed because it is too large
Load Diff
+105
-24
@@ -24,33 +24,114 @@ var reader;
|
|||||||
$("#bookmark, #show-Bookmarks").remove();
|
$("#bookmark, #show-Bookmarks").remove();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Enable swipe support
|
// Navigation mode: 'sides' or 'gestures'
|
||||||
// I have no idea why swiperRight/swiperLeft from plugins is not working, events just don't get fired
|
var hammerManagers = [];
|
||||||
var touchStart = 0;
|
|
||||||
var touchEnd = 0;
|
|
||||||
|
|
||||||
reader.rendition.on("touchstart", function (event) {
|
function hasActiveSelection(win) {
|
||||||
touchStart = event.changedTouches[0].screenX;
|
try {
|
||||||
});
|
var sel = win.getSelection && win.getSelection();
|
||||||
reader.rendition.on("touchend", function (event) {
|
return sel && sel.type === "Range" && sel.toString().length > 0;
|
||||||
touchEnd = event.changedTouches[0].screenX;
|
} catch (e) {
|
||||||
if (touchStart < touchEnd) {
|
return false;
|
||||||
if (reader.book.package.metadata.direction === "rtl") {
|
|
||||||
reader.rendition.next();
|
|
||||||
} else {
|
|
||||||
reader.rendition.prev();
|
|
||||||
}
|
|
||||||
// Swiped Right
|
|
||||||
}
|
}
|
||||||
if (touchStart > touchEnd) {
|
}
|
||||||
if (reader.book.package.metadata.direction === "rtl") {
|
|
||||||
reader.rendition.prev();
|
function bindHammer(target, isIframeDoc) {
|
||||||
} else {
|
if (typeof Hammer === "undefined") {
|
||||||
reader.rendition.next();
|
return;
|
||||||
}
|
|
||||||
// Swiped Left
|
|
||||||
}
|
}
|
||||||
});
|
var mc = new Hammer(target);
|
||||||
|
mc.get("swipe").set({
|
||||||
|
direction: Hammer.DIRECTION_HORIZONTAL,
|
||||||
|
threshold: 25,
|
||||||
|
velocity: 0.3,
|
||||||
|
});
|
||||||
|
mc.on("swipeleft swiperight", function (ev) {
|
||||||
|
if (!window.cwGesturesEnabled) return;
|
||||||
|
if (ev.pointers && ev.pointers.length > 1) return; // ignore multi-touch
|
||||||
|
var win = isIframeDoc ? target.defaultView : window;
|
||||||
|
if (hasActiveSelection(win)) return; // do not navigate when selecting text
|
||||||
|
// Mapping per requirement: L->R = PREV, R->L = NEXT (independent of RTL)
|
||||||
|
if (ev.type === "swipeleft") reader.rendition.next();
|
||||||
|
else reader.rendition.prev();
|
||||||
|
});
|
||||||
|
hammerManagers.push(mc);
|
||||||
|
}
|
||||||
|
|
||||||
|
function destroyHammers() {
|
||||||
|
while (hammerManagers.length) {
|
||||||
|
var mc = hammerManagers.pop();
|
||||||
|
try {
|
||||||
|
mc.destroy();
|
||||||
|
} catch (e) {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function enableSideClicks() {
|
||||||
|
var prevBtn = document.getElementById("prev");
|
||||||
|
var nextBtn = document.getElementById("next");
|
||||||
|
if (prevBtn) {
|
||||||
|
prevBtn.style.display = "";
|
||||||
|
prevBtn.onclick = function () {
|
||||||
|
reader.rendition.prev();
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if (nextBtn) {
|
||||||
|
nextBtn.style.display = "";
|
||||||
|
nextBtn.onclick = function () {
|
||||||
|
reader.rendition.next();
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function disableSideClicks() {
|
||||||
|
var prevBtn = document.getElementById("prev");
|
||||||
|
var nextBtn = document.getElementById("next");
|
||||||
|
if (prevBtn) {
|
||||||
|
prevBtn.onclick = null;
|
||||||
|
prevBtn.style.display = "none";
|
||||||
|
}
|
||||||
|
if (nextBtn) {
|
||||||
|
nextBtn.onclick = null;
|
||||||
|
nextBtn.style.display = "none";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function enableGestures() {
|
||||||
|
// Bind to outer container
|
||||||
|
if (reader && reader.rendition && reader.rendition.container) {
|
||||||
|
bindHammer(reader.rendition.container, false);
|
||||||
|
}
|
||||||
|
// Bind to inner iframes when rendered
|
||||||
|
reader.rendition.on("rendered", function (section, contents) {
|
||||||
|
var docEl = contents.document;
|
||||||
|
if (docEl && docEl.documentElement && docEl.documentElement.style) {
|
||||||
|
docEl.documentElement.style.touchAction = "pan-y";
|
||||||
|
}
|
||||||
|
bindHammer(docEl, true);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
window.applyNavigationMode = function (mode) {
|
||||||
|
if (!window.cwInitialized) {
|
||||||
|
destroyHammers();
|
||||||
|
enableGestures();
|
||||||
|
window.cwInitialized = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mode === "sides") {
|
||||||
|
enableSideClicks();
|
||||||
|
window.cwGesturesEnabled = false;
|
||||||
|
} else {
|
||||||
|
disableSideClicks();
|
||||||
|
window.cwGesturesEnabled = true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Apply saved or default mode on load
|
||||||
|
var savedMode =
|
||||||
|
localStorage.getItem("calibre.reader.navMode") || "gestures";
|
||||||
|
window.applyNavigationMode(savedMode);
|
||||||
|
|
||||||
// Update progress percentage
|
// Update progress percentage
|
||||||
let progressDiv = document.getElementById("progress");
|
let progressDiv = document.getElementById("progress");
|
||||||
|
|||||||
@@ -157,6 +157,19 @@
|
|||||||
/>{{_('Reflow text when sidebars are open.')}}
|
/>{{_('Reflow text when sidebars are open.')}}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="form-group" id="navigationMode">
|
||||||
|
<label class="item">{{ _('Navigation') }}:</label>
|
||||||
|
<button type="button" id="navSides" onclick="selectNavMode('sides')">
|
||||||
|
<span id="navSidesTick"> </span>{{_('Sides')}}
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
id="navGestures"
|
||||||
|
onclick="selectNavMode('gestures')"
|
||||||
|
>
|
||||||
|
<span id="navGesturesTick"> </span>{{_('Gestures')}}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
<div class="form-group fontSizeWrapper">
|
<div class="form-group fontSizeWrapper">
|
||||||
<label>{{ _('Font Size') }}</label>
|
<label>{{ _('Font Size') }}</label>
|
||||||
<div
|
<div
|
||||||
@@ -364,8 +377,25 @@
|
|||||||
document.getElementById(id).querySelector("span").textContent = "✓";
|
document.getElementById(id).querySelector("span").textContent = "✓";
|
||||||
reader.rendition.spread(id === "spread" ? true : "none");
|
reader.rendition.spread(id === "spread" ? true : "none");
|
||||||
}
|
}
|
||||||
|
function selectNavMode(mode) {
|
||||||
|
var tickSides = document.getElementById("navSidesTick");
|
||||||
|
var tickGest = document.getElementById("navGesturesTick");
|
||||||
|
tickSides.textContent = mode === "sides" ? "✓" : "";
|
||||||
|
tickGest.textContent = mode === "gestures" ? "✓" : "";
|
||||||
|
localStorage.setItem("calibre.reader.navMode", mode);
|
||||||
|
if (window.applyNavigationMode) {
|
||||||
|
window.applyNavigationMode(mode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Initialize nav mode on load
|
||||||
|
(function () {
|
||||||
|
var savedMode =
|
||||||
|
localStorage.getItem("calibre.reader.navMode") || "gestures";
|
||||||
|
selectNavMode(savedMode);
|
||||||
|
})();
|
||||||
</script>
|
</script>
|
||||||
<script src="{{ url_for('static', filename='js/libs/screenfull.min.js') }}"></script>
|
<script src="{{ url_for('static', filename='js/libs/screenfull.min.js') }}"></script>
|
||||||
|
<script src="{{ url_for('static', filename='js/libs/hammer.min.js') }}"></script>
|
||||||
<script src="{{ url_for('static', filename='js/libs/reader.min.js') }}"></script>
|
<script src="{{ url_for('static', filename='js/libs/reader.min.js') }}"></script>
|
||||||
<script src="{{ url_for('static', filename='js/reading/epub.js') }}"></script>
|
<script src="{{ url_for('static', filename='js/reading/epub.js') }}"></script>
|
||||||
</body>
|
</body>
|
||||||
|
|||||||
Reference in New Issue
Block a user