Commit initial

This commit is contained in:
Christian Aribaud 2023-04-15 13:12:49 +02:00
parent a5516c430f
commit f2b4cceb72
6 changed files with 454 additions and 0 deletions

5
README.txt Normal file
View File

@ -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

82
comic_reader.css Normal file
View File

@ -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);
}

254
comic_reader.js Normal file
View File

@ -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)
});

20
comic_reader_test.css Normal file
View File

@ -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;
}

View File

@ -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>

45
zooms_generator.py Normal file
View File

@ -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])