//============ // CONTROLS //============ MOVE_NEXT = "ArrowRight" MOVE_BACK = "ArrowLeft" TOGGLE_FULLSCREEN = "F" //======================== // NAVIGATION CONSTANTS //======================== PAGE_TRANSITION_SPEED = "1.5s" MOUSEWHELL_MIN_DELAY = 50 //==================== // STATES CONSTANTS //==================== 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 MOUSEWHELL_WAIT = false // ============= // UTILITIES // ============= // Zooms utilites // -------------- 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 } // Dimensions utilites // ------------------- function getPagesCount() { return READER_PAGES.childElementCount } function pageOriginalHeight() { return parseInt(READER_PAGES.dataset.pagesHeight) } function pageOriginalWidth() { return parseInt(READER_PAGES.dataset.pagesWidth) } function readerFrameRatio() { return READER_FRAME.clientWidth / READER_FRAME.clientHeight } function pageRatio() { return READER_PAGES.dataset.pagesWidth / READER_PAGES.dataset.pagesHeight } function isFrameRatioWiderThanPage(){ return readerFrameRatio() > pageRatio() } function pageToFrameScaleFactor(useHeight){ // The scale factor to apply to a page so it exactly fit in the reader frame if (useHeight) { return READER_FRAME.clientHeight / pageOriginalHeight() } return READER_FRAME.clientWidth / pageOriginalWidth() } function totalPagesWidth() { // The width of all cumuled pages with scale factor applied return pageOriginalWidth() * getPagesCount() } // ========= // ACTIONS // ========= function initReader(){ moveReaderDisplayToZoom(0) // Smoothly show pictures when they intersect with the viewport let visibilityObserver = new IntersectionObserver((entries, observer) => { entries.forEach((entry) => { if (entry.isIntersecting) { entry.target.style.opacity = 1 entry.target.style.visibility = "visible" } else { entry.target.style.opacity = 0 entry.target.style.visibility = "hidden" } }); }, { root: READER_FRAME, rootMargin: "-10px" }); for (var i = 0; i < READER_PAGES.children.length; i++) { let img = READER_PAGES.children[i]; img.style.width = 100 / getPagesCount() + "%" visibilityObserver.observe(img) } READER_PAGES.style.display = "flex" setTimeout(() => { READER_PAGES.hidden = false }, "300") } function moveReaderDisplayToArea(pageNumber, width, height, posx, posy){ if (width == 0){ width = pageOriginalWidth() } if (height == 0){ height = pageOriginalHeight() } zoomRatio = width / height if (readerFrameRatio() > zoomRatio) { // Frame wider than zoom var zoomToFrameScaleFactor = pageToFrameScaleFactor(true) * pageOriginalHeight() / height var zoomToFrameCenteringOffset = [(READER_FRAME.clientWidth - width * zoomToFrameScaleFactor) / 2, 0] updateFocusByWidth(width * zoomToFrameScaleFactor) } else { var zoomToFrameScaleFactor = pageToFrameScaleFactor(false) * pageOriginalWidth() / width var zoomToFrameCenteringOffset = [0, (READER_FRAME.clientHeight - height * zoomToFrameScaleFactor) / 2] updateFocusByHeight(height * zoomToFrameScaleFactor) } previousPagesOffset = pageOriginalWidth() * (pageNumber - 1) * zoomToFrameScaleFactor READER_PAGES.style.width = totalPagesWidth() * zoomToFrameScaleFactor + "px" READER_PAGES.style.height = pageOriginalHeight() * zoomToFrameScaleFactor + "px" READER_PAGES.style.left = (- posx * zoomToFrameScaleFactor + zoomToFrameCenteringOffset[0] - previousPagesOffset) + "px" READER_PAGES.style.top = (- posy * zoomToFrameScaleFactor + zoomToFrameCenteringOffset[1]) + "px" 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 } } } } // ============= // CALLBACKS // ============= function handleKeyPress(key, has_ctrl){ if (key == MOVE_NEXT) { moveReader(true, has_ctrl) } else if (key == MOVE_BACK) { moveReader(false, has_ctrl) } else if (key.toUpperCase() == TOGGLE_FULLSCREEN){ if (document.fullscreenElement == null){ READER_FRAME.requestFullscreen(); } else { document.exitFullscreen(); } } } function handleMouseWhell(deltaY){ if (MOUSEWHELL_WAIT){ return } else { MOUSEWHELL_WAIT = true setTimeout(() => { MOUSEWHELL_WAIT = false }, MOUSEWHELL_MIN_DELAY) } if (deltaY > 0) { moveReader(true, false) } else { moveReader(false, false) } } // ====== // 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) }); addEventListener("wheel", (event) => { handleMouseWhell(event.deltaY) });