From 2ae5e0742e0ab9e6ab299a70cbaa3263547f6f9e Mon Sep 17 00:00:00 2001 From: Manchewable <35658388+Manchewable@users.noreply.github.com> Date: Fri, 28 May 2021 11:55:04 -0700 Subject: [PATCH 1/3] reference to img elements directly (#106) --- .../manga/reader/pager/DoublePagedPager.tsx | 139 ++++++++---------- 1 file changed, 64 insertions(+), 75 deletions(-) diff --git a/webUI/react/src/components/manga/reader/pager/DoublePagedPager.tsx b/webUI/react/src/components/manga/reader/pager/DoublePagedPager.tsx index 0c19d186..90f10029 100644 --- a/webUI/react/src/components/manga/reader/pager/DoublePagedPager.tsx +++ b/webUI/react/src/components/manga/reader/pager/DoublePagedPager.tsx @@ -12,6 +12,9 @@ import Page from '../Page'; import DoublePage from '../DoublePage'; const useStyles = (settings: IReaderSettings) => makeStyles({ + preload: { + display: 'none', + }, reader: { display: 'flex', flexDirection: (settings.readerType === 'DoubleLTR') ? 'row' : 'row-reverse', @@ -31,16 +34,67 @@ export default function DoublePagedPager(props: IReaderProps) { const classes = useStyles(settings)(); const selfRef = useRef(null); - const pagesRef = useRef([]); + const pagesRef = useRef([]); const pagesDisplayed = useRef(0); const pageLoaded = useRef(Array(pages.length).fill(false)); + function setPagesToDisplay() { + pagesDisplayed.current = 0; + if (curPage < pages.length && pagesRef.current[curPage]) { + if (pageLoaded.current[curPage]) { + pagesDisplayed.current = 1; + const imgElem = pagesRef.current[curPage]; + const aspectRatio = imgElem.height / imgElem.width; + if (aspectRatio < 1) { + return; + } + } + } + if (curPage + 1 < pages.length && pagesRef.current[curPage + 1]) { + if (pageLoaded.current[curPage + 1]) { + const imgElem = pagesRef.current[curPage + 1]; + const aspectRatio = imgElem.height / imgElem.width; + if (aspectRatio < 1) { + return; + } + pagesDisplayed.current = 2; + } + } + } + + function displayPages() { + if (pagesDisplayed.current === 2) { + ReactDOM.render( + , + document.getElementById('display'), + ); + } else { + ReactDOM.render( + {}} + setCurPage={setCurPage} + settings={settings} + />, + document.getElementById('display'), + ); + } + } + function pagesToGoBack() { for (let i = 1; i <= 2; i++) { if (curPage - i > 0 && pagesRef.current[curPage - i]) { - if (pagesRef.current[curPage - i].children[0] instanceof HTMLImageElement) { - const imgElem = pagesRef.current[curPage - i].children[0] as HTMLImageElement; + if (pageLoaded.current[curPage - i]) { + const imgElem = pagesRef.current[curPage - i]; const aspectRatio = imgElem.height / imgElem.width; if (aspectRatio < 1) { return 1; @@ -85,62 +139,6 @@ export default function DoublePagedPager(props: IReaderProps) { } } - function setPagesToDisplay() { - pagesDisplayed.current = 0; - if (curPage < pages.length && pagesRef.current[curPage]) { - if (pagesRef.current[curPage].children[0] instanceof HTMLImageElement) { - pagesDisplayed.current = 1; - const imgElem = pagesRef.current[curPage].children[0] as HTMLImageElement; - const aspectRatio = imgElem.height / imgElem.width; - if (aspectRatio < 1) { - return; - } - } - } - if (curPage + 1 < pages.length && pagesRef.current[curPage + 1]) { - if (pagesRef.current[curPage + 1].children[0] instanceof HTMLImageElement) { - const imgElem = pagesRef.current[curPage + 1].children[0] as HTMLImageElement; - const aspectRatio = imgElem.height / imgElem.width; - if (aspectRatio < 1) { - return; - } - pagesDisplayed.current = 2; - } - } - } - - function showPages() { - if (pagesDisplayed.current === 2) { - ReactDOM.render( - , - document.getElementById('display'), - ); - } else if (pagesDisplayed.current === 1) { - ReactDOM.render( - {}} - setCurPage={setCurPage} - settings={settings} - />, - document.getElementById('display'), - ); - } else { - ReactDOM.render( -
, - document.getElementById('display'), - ); - } - } - function keyboardControl(e:KeyboardEvent) { switch (e.code) { case 'Space': @@ -172,20 +170,13 @@ export default function DoublePagedPager(props: IReaderProps) { }; } - useEffect(() => { - pagesRef.current.forEach((e) => { - const pageRef = e; - pageRef.style.display = 'none'; - }); - }, []); - useEffect(() => { const retryDisplay = setInterval(() => { const isLastPage = (curPage === pages.length - 1); if ((!isLastPage && pageLoaded.current[curPage] && pageLoaded.current[curPage + 1]) || pageLoaded.current[curPage]) { setPagesToDisplay(); - showPages(); + displayPages(); clearInterval(retryDisplay); } }, 50); @@ -202,17 +193,15 @@ export default function DoublePagedPager(props: IReaderProps) { return (
-
+
{ pages.map((page) => ( - { pagesRef.current[page.index] = e; }} + key={`${page.index}`} src={page.src} - onImageLoad={handleImageLoad(page.index)} - setCurPage={setCurPage} - settings={settings} - ref={(e:HTMLDivElement) => { pagesRef.current[page.index] = e; }} + onLoad={handleImageLoad(page.index)} + alt={`${page.index}`} /> )) } From aa5a1083d0ebe5e926919c2a1463434d4b55e7a0 Mon Sep 17 00:00:00 2001 From: Manchewable <35658388+Manchewable@users.noreply.github.com> Date: Fri, 28 May 2021 11:57:31 -0700 Subject: [PATCH 2/3] fit images to height (#108) --- webUI/react/src/components/manga/reader/Page.tsx | 2 +- webUI/react/src/components/navbar/ReaderNavBar.tsx | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/webUI/react/src/components/manga/reader/Page.tsx b/webUI/react/src/components/manga/reader/Page.tsx index 21ee231c..88ac5b4e 100644 --- a/webUI/react/src/components/manga/reader/Page.tsx +++ b/webUI/react/src/components/manga/reader/Page.tsx @@ -11,7 +11,7 @@ import React, { useEffect, useRef } from 'react'; import SpinnerImage from 'components/SpinnerImage'; function imageStyle(settings: IReaderSettings): CSSProperties { - if (settings.readerType === 'DoubleLTR' || settings.readerType === 'DoubleRTL') { + if (settings.readerType === 'DoubleLTR' || settings.readerType === 'DoubleRTL' || settings.readerType === 'ContinuesHorizontal') { return { display: 'block', marginBottom: 0, diff --git a/webUI/react/src/components/navbar/ReaderNavBar.tsx b/webUI/react/src/components/navbar/ReaderNavBar.tsx index 2976e22f..ac18e575 100644 --- a/webUI/react/src/components/navbar/ReaderNavBar.tsx +++ b/webUI/react/src/components/navbar/ReaderNavBar.tsx @@ -313,10 +313,10 @@ export default function ReaderNavBar(props: IProps) { Continues Vertical - {/* + Horizontal(WIP) - */} + From 3777cc646e202d51e38b5224e1db7b21d5774a34 Mon Sep 17 00:00:00 2001 From: Manchewable <35658388+Manchewable@users.noreply.github.com> Date: Sat, 29 May 2021 08:11:59 -0700 Subject: [PATCH 3/3] Improve continuous horizontal reader (#110) * differentiate ContinuesHorizontalLTR and ContinuesHorizontalRTL * fix displaying pages in horizontal viewer * add scroll handler for horizontal mode * update curPage when images pass through center of the screen * add click events to navigate pages * remove console.log * fix click mapping for ContinuesHorizontalRTL * remove disable eslint inline comment * fix ContinuesHorizontalRTL not updating curPage on scroll * add ability to click to drag * add margin in between images --- .../src/components/manga/reader/Page.tsx | 41 ++++-- .../manga/reader/pager/HorizontalPager.tsx | 128 +++++++++++++++--- .../src/components/navbar/ReaderNavBar.tsx | 8 +- webUI/react/src/screens/manga/Reader.tsx | 3 +- webUI/react/src/typings.d.ts | 3 +- 5 files changed, 152 insertions(+), 31 deletions(-) diff --git a/webUI/react/src/components/manga/reader/Page.tsx b/webUI/react/src/components/manga/reader/Page.tsx index 88ac5b4e..5c1d033a 100644 --- a/webUI/react/src/components/manga/reader/Page.tsx +++ b/webUI/react/src/components/manga/reader/Page.tsx @@ -11,15 +11,20 @@ import React, { useEffect, useRef } from 'react'; import SpinnerImage from 'components/SpinnerImage'; function imageStyle(settings: IReaderSettings): CSSProperties { - if (settings.readerType === 'DoubleLTR' || settings.readerType === 'DoubleRTL' || settings.readerType === 'ContinuesHorizontal') { + if (settings.readerType === 'DoubleLTR' + || settings.readerType === 'DoubleRTL' + || settings.readerType === 'ContinuesHorizontalLTR' + || settings.readerType === 'ContinuesHorizontalRTL') { return { display: 'block', - marginBottom: 0, + marginLeft: '7px', + marginRight: '7px', width: 'auto', minHeight: '99vh', height: 'auto', maxHeight: '99vh', objectFit: 'contain', + pointerEvents: 'none', }; } @@ -64,7 +69,7 @@ const Page = React.forwardRef((props: IProps, ref: any) => { const classes = useStyles(settings)(); const imgRef = useRef(null); - const handleScroll = () => { + const handleVerticalScroll = () => { if (imgRef.current) { const rect = imgRef.current.getBoundingClientRect(); if (rect.y < 0 && rect.y + rect.height > 0) { @@ -73,15 +78,29 @@ const Page = React.forwardRef((props: IProps, ref: any) => { } }; - useEffect(() => { - if (settings.readerType === 'Webtoon' || settings.readerType === 'ContinuesVertical') { - window.addEventListener('scroll', handleScroll); + const handleHorizontalScroll = () => { + if (imgRef.current) { + const rect = imgRef.current.getBoundingClientRect(); + if (rect.left <= window.innerWidth / 2 && rect.right > window.innerWidth / 2) { + setCurPage(index); + } + } + }; - return () => { - window.removeEventListener('scroll', handleScroll); - }; - } return () => {}; - }, [handleScroll]); + useEffect(() => { + switch (settings.readerType) { + case 'Webtoon': + case 'ContinuesVertical': + window.addEventListener('scroll', handleVerticalScroll); + return () => window.removeEventListener('scroll', handleVerticalScroll); + case 'ContinuesHorizontalLTR': + case 'ContinuesHorizontalRTL': + window.addEventListener('scroll', handleHorizontalScroll); + return () => window.removeEventListener('scroll', handleHorizontalScroll); + default: + return () => {}; + } + }, [handleVerticalScroll]); return (
diff --git a/webUI/react/src/components/manga/reader/pager/HorizontalPager.tsx b/webUI/react/src/components/manga/reader/pager/HorizontalPager.tsx index b33a98d4..4ee5c26d 100644 --- a/webUI/react/src/components/manga/reader/pager/HorizontalPager.tsx +++ b/webUI/react/src/components/manga/reader/pager/HorizontalPager.tsx @@ -6,34 +6,129 @@ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ import { makeStyles } from '@material-ui/core/styles'; -import React from 'react'; +import React, { useEffect, useRef } from 'react'; import Page from '../Page'; -const useStyles = makeStyles({ +const useStyles = (settings: IReaderSettings) => makeStyles({ reader: { display: 'flex', - flexDirection: 'row', - justifyContent: 'center', + flexDirection: (settings.readerType === 'ContinuesHorizontalLTR') ? 'row' : 'row-reverse', + justifyContent: (settings.readerType === 'ContinuesHorizontalLTR') ? 'flex-start' : 'flex-end', margin: '0 auto', - width: '100%', - height: '100vh', - overflowX: 'scroll', + width: 'auto', + height: 'auto', + overflowX: 'visible', + userSelect: 'none', }, }); -interface IProps { - pages: Array - setCurPage: React.Dispatch> - settings: IReaderSettings -} +export default function HorizontalPager(props: IReaderProps) { + const { + pages, curPage, settings, setCurPage, prevChapter, nextChapter, + } = props; -export default function HorizontalPager(props: IProps) { - const { pages, settings, setCurPage } = props; + const classes = useStyles(settings)(); - const classes = useStyles(); + const selfRef = useRef(null); + const pagesRef = useRef([]); + + function nextPage() { + if (curPage < pages.length - 1) { + pagesRef.current[curPage + 1]?.scrollIntoView({ inline: 'center' }); + setCurPage((page) => page + 1); + } else if (settings.loadNextonEnding) { + nextChapter(); + } + } + + function prevPage() { + if (curPage > 0) { + pagesRef.current[curPage - 1]?.scrollIntoView({ inline: 'center' }); + setCurPage(curPage - 1); + } else if (curPage === 0) { + prevChapter(); + } + } + + function goLeft() { + if (settings.readerType === 'ContinuesHorizontalLTR') { + prevPage(); + } else { + nextPage(); + } + } + + function goRight() { + if (settings.readerType === 'ContinuesHorizontalLTR') { + nextPage(); + } else { + prevPage(); + } + } + + const mouseXPos = useRef(0); + + function dragScreen(e: MouseEvent) { + window.scrollBy(mouseXPos.current - e.pageX, 0); + } + + function dragControl(e:MouseEvent) { + mouseXPos.current = e.pageX; + selfRef.current?.addEventListener('mousemove', dragScreen); + } + + function removeDragControl() { + selfRef.current?.removeEventListener('mousemove', dragScreen); + } + + function clickControl(e:MouseEvent) { + if (e.clientX >= window.innerWidth * 0.85) { + goRight(); + } else if (e.clientX <= window.innerWidth * 0.15) { + goLeft(); + } + } + + const handleLoadNextonEnding = () => { + if (settings.readerType === 'ContinuesHorizontalLTR') { + if (window.scrollX + window.innerWidth >= document.body.scrollWidth) { + nextChapter(); + } + } else if (settings.readerType === 'ContinuesHorizontalRTL') { + if (window.scrollX <= window.innerWidth) { + nextChapter(); + } + } + }; + + useEffect(() => { + pagesRef.current[curPage]?.scrollIntoView({ inline: 'center' }); + }, [settings.readerType]); + + useEffect(() => { + selfRef.current?.addEventListener('mousedown', dragControl); + selfRef.current?.addEventListener('mouseup', removeDragControl); + + return () => { + selfRef.current?.removeEventListener('mousedown', dragControl); + selfRef.current?.removeEventListener('mouseup', removeDragControl); + }; + }, [selfRef]); + + useEffect(() => { + if (settings.loadNextonEnding) { + document.addEventListener('scroll', handleLoadNextonEnding); + } + selfRef.current?.addEventListener('mousedown', clickControl); + + return () => { + document.removeEventListener('scroll', handleLoadNextonEnding); + selfRef.current?.removeEventListener('mousedown', clickControl); + }; + }, [selfRef, curPage]); return ( -
+
{ pages.map((page) => ( {}} setCurPage={setCurPage} settings={settings} + ref={(e:HTMLDivElement) => { pagesRef.current[page.index] = e; }} /> )) } diff --git a/webUI/react/src/components/navbar/ReaderNavBar.tsx b/webUI/react/src/components/navbar/ReaderNavBar.tsx index ac18e575..4d2f84ef 100644 --- a/webUI/react/src/components/navbar/ReaderNavBar.tsx +++ b/webUI/react/src/components/navbar/ReaderNavBar.tsx @@ -313,8 +313,12 @@ export default function ReaderNavBar(props: IProps) { Continues Vertical - - Horizontal(WIP) + + Horizontal (LTR) + + + + Horizontal (RTL) diff --git a/webUI/react/src/screens/manga/Reader.tsx b/webUI/react/src/screens/manga/Reader.tsx index e468d672..05cf1119 100644 --- a/webUI/react/src/screens/manga/Reader.tsx +++ b/webUI/react/src/screens/manga/Reader.tsx @@ -46,7 +46,8 @@ const getReaderComponent = (readerType: ReaderType) => { case 'DoubleLTR': return DoublePagedPager; break; - case 'ContinuesHorizontal': + case 'ContinuesHorizontalLTR': + case 'ContinuesHorizontalRTL': return HorizontalPager; default: return VerticalPager; diff --git a/webUI/react/src/typings.d.ts b/webUI/react/src/typings.d.ts index 99ec207c..7fe92a11 100644 --- a/webUI/react/src/typings.d.ts +++ b/webUI/react/src/typings.d.ts @@ -117,7 +117,8 @@ type ReaderType = 'DoubleVertical' | 'DoubleRTL' | 'DoubleLTR' | -'ContinuesHorizontal'; +'ContinuesHorizontalLTR'| +'ContinuesHorizontalRTL'; interface IReaderSettings{ staticNav: boolean