Commit initial
This commit is contained in:
parent
a5516c430f
commit
f2b4cceb72
|
@ -0,0 +1,5 @@
|
||||||
|
Navigation :
|
||||||
|
- Right arrow : focus next panel
|
||||||
|
- Left arrow : focus previous panel
|
||||||
|
- Ctrl + Right arrow : focus next page
|
||||||
|
- Ctrl + Left arrow : focus current / previous page
|
|
@ -0,0 +1,82 @@
|
||||||
|
#reader-frame {
|
||||||
|
overflow: hidden;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
#reader-pages {
|
||||||
|
position: absolute;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
left: 0px;
|
||||||
|
right: 0px;
|
||||||
|
height: 100%;
|
||||||
|
transition: all 1.5s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
#reader-pages > img {
|
||||||
|
display: inline-block;
|
||||||
|
opacity:0;
|
||||||
|
transition: all 1.5s ease;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#focus-overlay, #focus-overlay * {
|
||||||
|
transition: all 1.5s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
#focus-overlay, #nav-controls {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
|
||||||
|
display: grid;
|
||||||
|
|
||||||
|
grid-template-areas:
|
||||||
|
"left top right"
|
||||||
|
"left center right"
|
||||||
|
"left bottom right";
|
||||||
|
}
|
||||||
|
|
||||||
|
#focus-overlay {
|
||||||
|
grid-template-columns: 50% auto 50%;
|
||||||
|
grid-template-rows: 50% auto 50%;
|
||||||
|
}
|
||||||
|
|
||||||
|
#nav-controls {
|
||||||
|
grid-template-columns: 1fr 15em 1fr;
|
||||||
|
grid-template-rows: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
#nav-controls > div {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.top {
|
||||||
|
grid-area: top;
|
||||||
|
}
|
||||||
|
|
||||||
|
.left {
|
||||||
|
grid-area: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
.right {
|
||||||
|
grid-area: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
.top {
|
||||||
|
grid-area: top;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bottom {
|
||||||
|
grid-area: bottom;
|
||||||
|
}
|
||||||
|
|
||||||
|
#focus-overlay > *:not(.center) {
|
||||||
|
background-color: rgba(0, 0, 0, 0.85);
|
||||||
|
}
|
||||||
|
|
||||||
|
#focus-overlay > .center {
|
||||||
|
box-shadow: inset 0px 0px 5px 5px rgba(0, 0, 0, 0.85);
|
||||||
|
}
|
|
@ -0,0 +1,254 @@
|
||||||
|
|
||||||
|
PAGE_TRANSITION_SPEED = "1.5s"
|
||||||
|
|
||||||
|
READER_FRAME = document.getElementById("reader-frame")
|
||||||
|
|
||||||
|
READER_PAGES = document.getElementById("reader-pages")
|
||||||
|
|
||||||
|
FOCUS_OVERLAY = document.getElementById("focus-overlay")
|
||||||
|
|
||||||
|
CURRENT_ZOOM = 0
|
||||||
|
|
||||||
|
CURRENT_PAGE = 1
|
||||||
|
|
||||||
|
CURRENT_WIDTH = 0
|
||||||
|
CURRENT_HEIGHT = 0
|
||||||
|
CURRENT_X = 0
|
||||||
|
CURRENT_Y = 0
|
||||||
|
|
||||||
|
IS_PAGE_MODE = false
|
||||||
|
|
||||||
|
// ===========
|
||||||
|
// UTILITIES
|
||||||
|
// ===========
|
||||||
|
|
||||||
|
function getFirstZoomOfPage(pageNumber){
|
||||||
|
|
||||||
|
for (var zoom_idx = 0; zoom_idx < zooms.length; zoom_idx++){
|
||||||
|
if (zooms[zoom_idx][0] == pageNumber) {
|
||||||
|
return zoom_idx
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function getLastZoomOfPage(pageNumber){
|
||||||
|
let res = null
|
||||||
|
|
||||||
|
for (var zoom_idx = 0; zoom_idx < zooms.length; zoom_idx++){
|
||||||
|
if (zooms[zoom_idx][0] == pageNumber) {
|
||||||
|
res = zoom_idx
|
||||||
|
}
|
||||||
|
|
||||||
|
if (res != null && zooms[zoom_idx][0] != pageNumber) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
function getReaderFrameRatio() {
|
||||||
|
return READER_FRAME.clientWidth / READER_FRAME.clientHeight
|
||||||
|
}
|
||||||
|
|
||||||
|
function getPagesCount() {
|
||||||
|
return READER_PAGES.childElementCount
|
||||||
|
}
|
||||||
|
|
||||||
|
function getPagesHeight() {
|
||||||
|
return READER_PAGES.dataset.pagesHeight
|
||||||
|
}
|
||||||
|
|
||||||
|
function getPagesWidth() {
|
||||||
|
return READER_PAGES.dataset.pagesWidth
|
||||||
|
}
|
||||||
|
|
||||||
|
function getPagesRatio() {
|
||||||
|
return READER_PAGES.dataset.pagesWidth / READER_PAGES.dataset.pagesHeight
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// =========
|
||||||
|
// ACTIONS
|
||||||
|
// =========
|
||||||
|
|
||||||
|
function initReader(){
|
||||||
|
moveReaderDisplayToZoom(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function moveReaderDisplayToArea(pageNumber, width, height, posx, posy){
|
||||||
|
|
||||||
|
if (width == 0){
|
||||||
|
width = getPagesWidth()
|
||||||
|
}
|
||||||
|
|
||||||
|
if (height == 0){
|
||||||
|
height = getPagesHeight()
|
||||||
|
}
|
||||||
|
|
||||||
|
ratio = width / height
|
||||||
|
|
||||||
|
base_scale_factor = getPagesHeight() / READER_FRAME.clientHeight // This is the factor at 100% height
|
||||||
|
|
||||||
|
if (ratio < getReaderFrameRatio()) {
|
||||||
|
|
||||||
|
scale_factor = READER_FRAME.clientHeight / height
|
||||||
|
|
||||||
|
centering_padding = (READER_FRAME.clientWidth - width * scale_factor) / 2
|
||||||
|
|
||||||
|
pages_negative_padding = getPagesWidth() * scale_factor * (pageNumber - 1)
|
||||||
|
|
||||||
|
READER_PAGES.style.height = base_scale_factor * scale_factor * 100 + "%"
|
||||||
|
|
||||||
|
READER_PAGES.style.left = (- posx * scale_factor + centering_padding - pages_negative_padding) + "px"
|
||||||
|
|
||||||
|
READER_PAGES.style.top = -posy * scale_factor + "px"
|
||||||
|
|
||||||
|
updateFocusByWidth(width * scale_factor)
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
scale_factor = READER_FRAME.clientWidth / width
|
||||||
|
|
||||||
|
pages_negative_padding = getPagesWidth() * scale_factor * (pageNumber - 1)
|
||||||
|
|
||||||
|
scaled_height = height * scale_factor
|
||||||
|
|
||||||
|
READER_PAGES.style.height = base_scale_factor * scale_factor * 100 + "%"
|
||||||
|
|
||||||
|
READER_PAGES.style.left = (- posx * scale_factor - pages_negative_padding) + "px"
|
||||||
|
|
||||||
|
READER_PAGES.style.top = -posy * scale_factor + (READER_FRAME.clientHeight - scaled_height)/2 + "px"
|
||||||
|
|
||||||
|
updateFocusByHeight(scaled_height)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
CURRENT_PAGE = pageNumber
|
||||||
|
CURRENT_WIDTH = width
|
||||||
|
CURRENT_HEIGHT = height
|
||||||
|
CURRENT_X = posx
|
||||||
|
CURRENT_Y = posy
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function moveReaderDisplayToPage(pageNumber) {
|
||||||
|
moveReaderDisplayToArea(pageNumber, 0, 0, 0, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
function moveReaderDisplayToZoom(index) {
|
||||||
|
|
||||||
|
moveReaderDisplayToArea(zooms[index][0], zooms[index][1], zooms[index][2], zooms[index][3], zooms[index][4])
|
||||||
|
|
||||||
|
CURRENT_ZOOM = index
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateFocusByWidth(width){
|
||||||
|
|
||||||
|
side_width = (READER_FRAME.clientWidth - width) / 2
|
||||||
|
|
||||||
|
FOCUS_OVERLAY.style.gridTemplateColumns = side_width +"px auto " + side_width + "px"
|
||||||
|
FOCUS_OVERLAY.style.gridTemplateRows = "0px auto 0px";
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateFocusByHeight(height){
|
||||||
|
|
||||||
|
side_width = (READER_FRAME.clientHeight - height) / 2
|
||||||
|
|
||||||
|
FOCUS_OVERLAY.style.gridTemplateRows = side_width +"px auto " + side_width + "px"
|
||||||
|
FOCUS_OVERLAY.style.gridTemplateColumns = "0px auto 0px";
|
||||||
|
}
|
||||||
|
|
||||||
|
function moveReader(to_next, move_page) {
|
||||||
|
|
||||||
|
if (move_page){
|
||||||
|
|
||||||
|
if (IS_PAGE_MODE){
|
||||||
|
if (to_next && CURRENT_PAGE < getPagesCount()) {
|
||||||
|
moveReaderDisplayToPage(CURRENT_PAGE + 1)
|
||||||
|
IS_PAGE_MODE = true
|
||||||
|
}
|
||||||
|
|
||||||
|
else if (CURRENT_PAGE > 1) {
|
||||||
|
moveReaderDisplayToPage(CURRENT_PAGE - 1)
|
||||||
|
IS_PAGE_MODE = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
else {
|
||||||
|
|
||||||
|
if (to_next && CURRENT_PAGE < getPagesCount()) {
|
||||||
|
moveReaderDisplayToPage(CURRENT_PAGE + 1)
|
||||||
|
IS_PAGE_MODE = true
|
||||||
|
}
|
||||||
|
|
||||||
|
else {
|
||||||
|
moveReaderDisplayToPage(CURRENT_PAGE)
|
||||||
|
IS_PAGE_MODE = true
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
if (IS_PAGE_MODE){
|
||||||
|
|
||||||
|
if (to_next) {
|
||||||
|
moveReaderDisplayToZoom(getFirstZoomOfPage(CURRENT_PAGE))
|
||||||
|
IS_PAGE_MODE = false
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (CURRENT_PAGE == 1) {
|
||||||
|
moveReaderDisplayToZoom(0)
|
||||||
|
IS_PAGE_MODE = false
|
||||||
|
} else {
|
||||||
|
moveReaderDisplayToZoom(getLastZoomOfPage(CURRENT_PAGE - 1))
|
||||||
|
IS_PAGE_MODE = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
if (to_next && CURRENT_ZOOM < zooms.length - 1) {
|
||||||
|
moveReaderDisplayToZoom(CURRENT_ZOOM + 1)
|
||||||
|
IS_PAGE_MODE = false
|
||||||
|
}
|
||||||
|
|
||||||
|
else if (CURRENT_ZOOM > 0) {
|
||||||
|
moveReaderDisplayToZoom(CURRENT_ZOOM - 1)
|
||||||
|
IS_PAGE_MODE = false
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleKeyPress(key, has_ctrl){
|
||||||
|
|
||||||
|
if (key == "ArrowRight") {
|
||||||
|
moveReader(true, has_ctrl)
|
||||||
|
}
|
||||||
|
|
||||||
|
else if (key == "ArrowLeft") {
|
||||||
|
moveReader(false, has_ctrl)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// ======
|
||||||
|
// INIT
|
||||||
|
// ======
|
||||||
|
|
||||||
|
window.addEventListener("load", (event) => {
|
||||||
|
initReader()
|
||||||
|
});
|
||||||
|
|
||||||
|
addEventListener("resize", (event) => {
|
||||||
|
moveReaderDisplayToArea(CURRENT_PAGE, CURRENT_WIDTH, CURRENT_HEIGHT, CURRENT_X, CURRENT_Y)
|
||||||
|
});
|
||||||
|
|
||||||
|
addEventListener("keydown", (event) => {
|
||||||
|
handleKeyPress(event.key, event.ctrlKey)
|
||||||
|
});
|
|
@ -0,0 +1,20 @@
|
||||||
|
|
||||||
|
|
||||||
|
html, body {
|
||||||
|
margin: 0px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
padding: 1em;
|
||||||
|
height: 100vh;
|
||||||
|
background-color: whitesmoke;
|
||||||
|
}
|
||||||
|
|
||||||
|
#reader-frame {
|
||||||
|
height: 100%;
|
||||||
|
width: 100%;
|
||||||
|
border: 2px solid;
|
||||||
|
box-sizing: border-box;
|
||||||
|
background-color: black;
|
||||||
|
}
|
|
@ -0,0 +1,48 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
|
||||||
|
<head>
|
||||||
|
|
||||||
|
<link rel="stylesheet" href="comic_reader_test.css">
|
||||||
|
<link rel="stylesheet" href="comic_reader.css">
|
||||||
|
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<div id="reader-frame">
|
||||||
|
<div id="reader-pages" data-pages-width="2481" data-pages-height="3503">
|
||||||
|
<img loading="lazy" onload="event.target.style.opacity=1" src="https://www.peppercarrot.com/0_sources/ep35_The-Reflection/hi-res/en_Pepper-and-Carrot_by-David-Revoy_E35P01.jpg"/>
|
||||||
|
<img loading="lazy" onload="event.target.style.opacity=1" src="https://www.peppercarrot.com/0_sources/ep35_The-Reflection/hi-res/en_Pepper-and-Carrot_by-David-Revoy_E35P02.jpg"/>
|
||||||
|
<img loading="lazy" onload="event.target.style.opacity=1" src="https://www.peppercarrot.com/0_sources/ep35_The-Reflection/hi-res/en_Pepper-and-Carrot_by-David-Revoy_E35P03.jpg"/>
|
||||||
|
<img loading="lazy" onload="event.target.style.opacity=1" src="https://www.peppercarrot.com/0_sources/ep35_The-Reflection/hi-res/en_Pepper-and-Carrot_by-David-Revoy_E35P04.jpg"/>
|
||||||
|
<img loading="lazy" onload="event.target.style.opacity=1" src="https://www.peppercarrot.com/0_sources/ep35_The-Reflection/hi-res/en_Pepper-and-Carrot_by-David-Revoy_E35P05.jpg"/>
|
||||||
|
<img loading="lazy" onload="event.target.style.opacity=1" src="https://www.peppercarrot.com/0_sources/ep35_The-Reflection/hi-res/en_Pepper-and-Carrot_by-David-Revoy_E35P06.jpg"/>
|
||||||
|
<img loading="lazy" onload="event.target.style.opacity=1" src="https://www.peppercarrot.com/0_sources/ep35_The-Reflection/hi-res/en_Pepper-and-Carrot_by-David-Revoy_E35P07.jpg"/>
|
||||||
|
<img loading="lazy" onload="event.target.style.opacity=1" src="https://www.peppercarrot.com/0_sources/ep35_The-Reflection/hi-res/en_Pepper-and-Carrot_by-David-Revoy_E35P08.jpg"/>
|
||||||
|
<img loading="lazy" onload="event.target.style.opacity=1" src="https://www.peppercarrot.com/0_sources/ep35_The-Reflection/hi-res/en_Pepper-and-Carrot_by-David-Revoy_E35P09.jpg"/>
|
||||||
|
<img loading="lazy" onload="event.target.style.opacity=1" src="https://www.peppercarrot.com/0_sources/ep35_The-Reflection/hi-res/en_Pepper-and-Carrot_by-David-Revoy_E35P10.jpg"/>
|
||||||
|
<img loading="lazy" onload="event.target.style.opacity=1" src="https://www.peppercarrot.com/0_sources/ep35_The-Reflection/hi-res/en_Pepper-and-Carrot_by-David-Revoy_E35P11.jpg"/>
|
||||||
|
<img loading="lazy" onload="event.target.style.opacity=1" src="https://www.peppercarrot.com/0_sources/ep35_The-Reflection/hi-res/en_Pepper-and-Carrot_by-David-Revoy_E35P12.jpg"/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="focus-overlay">
|
||||||
|
<div class="top"></div>
|
||||||
|
<div class="left"></div>
|
||||||
|
<div class="center"></div>
|
||||||
|
<div class="right"></div>
|
||||||
|
<div class="bottom"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="nav-controls">
|
||||||
|
<div class="left" id="nav-left" onclick="moveReader(false,false)"></div>
|
||||||
|
<div class="right" id="nav-right" onclick="moveReader(true,false)"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
|
||||||
|
<script src="zooms_data.js"></script>
|
||||||
|
<script src="comic_reader.js"></script>
|
||||||
|
|
||||||
|
</html>
|
|
@ -0,0 +1,45 @@
|
||||||
|
import sys
|
||||||
|
import re
|
||||||
|
import xml.etree.ElementTree as ET
|
||||||
|
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
|
||||||
|
def extract_zooms(src_folder, dest_file):
|
||||||
|
folder = Path(src_folder)
|
||||||
|
|
||||||
|
zooms = {}
|
||||||
|
|
||||||
|
for svg_path in folder.glob("*.svg"):
|
||||||
|
print(svg_path.name)
|
||||||
|
match = re.search("P(\d+)", svg_path.name)
|
||||||
|
if match:
|
||||||
|
|
||||||
|
page_idx = int(match.group(1))
|
||||||
|
|
||||||
|
zooms[page_idx] = []
|
||||||
|
|
||||||
|
tree = ET.parse(svg_path)
|
||||||
|
root = tree.getroot()
|
||||||
|
|
||||||
|
for area in root.findall('.//{*}rect'):
|
||||||
|
zooms[page_idx].append([
|
||||||
|
float(area.get("width")),
|
||||||
|
float(area.get("height")),
|
||||||
|
float(area.get("x")),
|
||||||
|
float(area.get("y")),
|
||||||
|
])
|
||||||
|
|
||||||
|
with open(dest_file, "w") as data_file:
|
||||||
|
data_file.write("zooms = [\n")
|
||||||
|
for page_idx in sorted(zooms.keys()):
|
||||||
|
for zoom in zooms[page_idx]:
|
||||||
|
|
||||||
|
if zoom[2] < 0 or zoom[3] < 0 :
|
||||||
|
print(f"WARNING: negative pos x / pos y in page {page_idx} for zoom {zoom} (is the rectangle flipped?)")
|
||||||
|
|
||||||
|
data_file.write(f" {[page_idx] + zoom},\n")
|
||||||
|
data_file.write("]\n")
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
extract_zooms(sys.argv[1], sys.argv[2])
|
Loading…
Reference in New Issue