From 41fdff135eff503df3f7e6469793b96bfb1d2500 Mon Sep 17 00:00:00 2001 From: troyeguo <13820674+troyeguo@users.noreply.github.com> Date: Sat, 17 Jul 2021 22:53:00 +0800 Subject: [PATCH] fix bug Former-commit-id: 180a61da604f847884b08890d2df33122625691c --- main.js | 8 +- src/containers/_epubReader/component.tsx | 371 +++++++++++++++++++++++ src/containers/_epubReader/index.css | 220 ++++++++++++++ src/containers/_epubReader/index.tsx | 32 ++ src/containers/_epubReader/interface.tsx | 27 ++ src/containers/epubReader/component.tsx | 172 +++++------ src/containers/epubReader/index.tsx | 11 +- src/containers/epubReader/interface.tsx | 9 +- src/containers/viewArea/component.tsx | 124 ++++++++ src/containers/viewArea/index.css | 35 +++ src/containers/viewArea/index.tsx | 29 ++ src/containers/viewArea/interface.tsx | 26 ++ src/pages/epubPage/component.tsx | 2 +- 13 files changed, 951 insertions(+), 115 deletions(-) create mode 100644 src/containers/_epubReader/component.tsx create mode 100644 src/containers/_epubReader/index.css create mode 100644 src/containers/_epubReader/index.tsx create mode 100644 src/containers/_epubReader/interface.tsx create mode 100644 src/containers/viewArea/component.tsx create mode 100644 src/containers/viewArea/index.css create mode 100644 src/containers/viewArea/index.tsx create mode 100644 src/containers/viewArea/interface.tsx diff --git a/main.js b/main.js index 8aa82c24..d4206f45 100644 --- a/main.js +++ b/main.js @@ -1,6 +1,7 @@ -const { app, BrowserWindow, nativeImage } = require("electron"); +const { app, BrowserWindow, Menu, remote, ipcMain } = require("electron"); const { ebtMain } = require("electron-baidu-tongji"); const path = require("path"); +const isDev = require("electron-is-dev"); let mainWin; let readerWindow; const singleInstance = app.requestSingleInstanceLock(); @@ -38,9 +39,8 @@ app.on("ready", () => { }; mainWin = new BrowserWindow(option); - const isDev = require("electron-is-dev"); + if (!isDev) { - const { Menu } = require("electron"); Menu.setApplicationMenu(null); } @@ -49,7 +49,7 @@ app.on("ready", () => { : `file://${path.join(__dirname, "./build/index.html")}`; mainWin.loadURL(urlLocation); - const { remote, ipcMain } = require("electron"); + ebtMain(ipcMain, isDev); mainWin.on("close", () => { mainWin = null; diff --git a/src/containers/_epubReader/component.tsx b/src/containers/_epubReader/component.tsx new file mode 100644 index 00000000..0e09275c --- /dev/null +++ b/src/containers/_epubReader/component.tsx @@ -0,0 +1,371 @@ +import React from "react"; +import EpubViewer from "../epubViewer"; +import Background from "../background"; +import SettingPanel from "../panels/settingPanel"; +import NavigationPanel from "../panels/navigationPanel"; +import OperationPanel from "../panels/operationPanel"; +import MessageBox from "../messageBox"; +import ProgressPanel from "../panels/progressPanel"; +import { ReaderProps, ReaderState } from "./interface"; +import { MouseEvent } from "../../utils/mouseEvent"; +import OtherUtil from "../../utils/otherUtil"; +import ReadingTime from "../../utils/readUtils/readingTime"; + +class Reader extends React.Component { + messageTimer!: NodeJS.Timeout; + tickTimer!: NodeJS.Timeout; + rendition: any; + + constructor(props: ReaderProps) { + super(props); + this.state = { + isOpenRightPanel: + OtherUtil.getReaderConfig("isSettingLocked") === "yes" ? true : false, + isOpenTopPanel: false, + isOpenBottomPanel: false, + isOpenLeftPanel: + OtherUtil.getReaderConfig("isNavLocked") === "yes" ? true : false, + isMessage: false, + rendition: null, + scale: OtherUtil.getReaderConfig("scale") || 1, + margin: parseInt(OtherUtil.getReaderConfig("margin")) || 30, + time: ReadingTime.getTime(this.props.currentBook.key), + isTouch: OtherUtil.getReaderConfig("isTouch") === "yes", + readerMode: OtherUtil.getReaderConfig("readerMode") || "double", + }; + } + componentWillMount() { + this.props.handleFetchBookmarks(); + this.props.handleFetchPercentage(this.props.currentBook); + this.props.handleFetchNotes(); + this.props.handleFetchBooks(); + this.props.handleFetchChapters(this.props.currentEpub); + } + UNSAFE_componentWillReceiveProps(nextProps: ReaderProps) { + this.setState({ + isMessage: nextProps.isMessage, + }); + + //控制消息提示两秒之后消失 + if (nextProps.isMessage) { + this.messageTimer = global.setTimeout(() => { + this.props.handleMessageBox(false); + this.setState({ isMessage: false }); + }, 2000); + } + } + componentDidMount() { + this.handleRenderBook(); + this.props.handleRenderFunc(this.handleRenderBook); + window.addEventListener("resize", () => { + this.handleRenderBook(); + }); + this.tickTimer = global.setInterval(() => { + let time = this.state.time; + time += 1; + let page = document.querySelector("#page-area"); + //解决快速翻页过程中图书消失的bug + let renderedBook = document.querySelector(".epub-view"); + if ( + renderedBook && + !renderedBook.innerHTML && + this.state.readerMode !== "continuous" + ) { + this.handleRenderBook(); + } + let ele = page!.getElementsByClassName("epub-container")[0]; + if (page!.getElementsByClassName("epub-container").length > 1 && ele) { + ele.parentNode?.removeChild(ele); + } + this.setState({ time }); + this.handleRecord(); + }, 1000); + } + componentWillUnmount() { + clearInterval(this.tickTimer); + } + handleRenderBook = () => { + let page = document.querySelector("#page-area"); + let epub = this.props.currentEpub; + if (page!.innerHTML) { + page!.innerHTML = ""; + } + + this.setState({ rendition: null }, () => { + (window as any).rangy.init(); // 初始化 + this.rendition = epub.renderTo(page, { + manager: + this.state.readerMode === "continuous" ? "continuous" : "default", + flow: this.state.readerMode === "continuous" ? "scrolled" : "auto", + width: "100%", + height: "100%", + snap: true, + spread: + OtherUtil.getReaderConfig("readerMode") === "single" ? "none" : "", + }); + this.setState({ rendition: this.rendition }); + this.state.readerMode !== "continuous" && MouseEvent(this.rendition); // 绑定事件 + }); + }; + handleRecord() { + OtherUtil.setReaderConfig("isFullScreen", "no"); + + OtherUtil.setReaderConfig("windowWidth", document.body.clientWidth + ""); + OtherUtil.setReaderConfig("windowHeight", document.body.clientHeight + ""); + OtherUtil.setReaderConfig("windowX", window.screenX + ""); + OtherUtil.setReaderConfig("windowY", window.screenY + ""); + } + //进入阅读器 + handleEnterReader = (position: string) => { + //控制上下左右的菜单的显示 + switch (position) { + case "right": + this.setState({ + isOpenRightPanel: this.state.isOpenRightPanel ? false : true, + }); + break; + case "left": + this.setState({ + isOpenLeftPanel: this.state.isOpenLeftPanel ? false : true, + }); + break; + case "top": + this.setState({ + isOpenTopPanel: this.state.isOpenTopPanel ? false : true, + }); + break; + case "bottom": + this.setState({ + isOpenBottomPanel: this.state.isOpenBottomPanel ? false : true, + }); + break; + default: + break; + } + }; + //退出阅读器 + handleLeaveReader = (position: string) => { + //控制上下左右的菜单的显示 + switch (position) { + case "right": + if (OtherUtil.getReaderConfig("isSettingLocked") === "yes") { + break; + } else { + this.setState({ isOpenRightPanel: false }); + break; + } + + case "left": + if (OtherUtil.getReaderConfig("isNavLocked") === "yes") { + break; + } else { + this.setState({ isOpenLeftPanel: false }); + break; + } + case "top": + this.setState({ isOpenTopPanel: false }); + break; + case "bottom": + this.setState({ isOpenBottomPanel: false }); + break; + default: + break; + } + }; + nextPage = () => { + this.state.rendition.next(); + }; + prevPage = () => { + this.state.rendition.prev(); + }; + render() { + const renditionProps = { + rendition: this.state.rendition, + handleLeaveReader: this.handleLeaveReader, + handleEnterReader: this.handleEnterReader, + isShow: + this.state.isOpenLeftPanel || + this.state.isOpenTopPanel || + this.state.isOpenBottomPanel || + this.state.isOpenRightPanel, + }; + return ( +
+ {OtherUtil.getReaderConfig("isHidePageButton") !== "yes" && ( + <> +
{ + this.prevPage(); + }} + > + +
+
{ + this.nextPage(); + }} + > + +
+ + )} +
{ + this.handleEnterReader("left"); + this.handleEnterReader("right"); + this.handleEnterReader("bottom"); + this.handleEnterReader("top"); + }} + > + +
+ {this.state.isMessage ? : null} +
{ + if (this.state.isTouch || this.state.isOpenLeftPanel) { + return; + } + this.handleEnterReader("left"); + }} + onClick={() => { + this.handleEnterReader("left"); + }} + >
+
{ + if (this.state.isTouch || this.state.isOpenRightPanel) { + return; + } + this.handleEnterReader("right"); + }} + onClick={() => { + this.handleEnterReader("right"); + }} + >
+
{ + if (this.state.isTouch || this.state.isOpenTopPanel) { + return; + } + this.handleEnterReader("top"); + }} + onClick={() => { + this.handleEnterReader("top"); + }} + >
+
{ + if (this.state.isTouch || this.state.isOpenBottomPanel) { + return; + } + this.handleEnterReader("bottom"); + }} + onClick={() => { + this.handleEnterReader("bottom"); + }} + >
+ + {this.state.rendition && this.props.currentEpub.rendition && ( + + )} +
{ + this.handleLeaveReader("right"); + }} + style={ + this.state.isOpenRightPanel + ? {} + : { + transform: "translateX(309px)", + } + } + > + +
+
{ + this.handleLeaveReader("left"); + }} + style={ + this.state.isOpenLeftPanel + ? {} + : { + transform: "translateX(-309px)", + } + } + > + +
+
{ + this.handleLeaveReader("bottom"); + }} + style={ + this.state.isOpenBottomPanel + ? {} + : { + transform: "translateY(110px)", + } + } + > + +
+
{ + this.handleLeaveReader("top"); + }} + style={ + this.state.isOpenTopPanel + ? {} + : { + transform: "translateY(-110px)", + } + } + > + +
+
+ + +
+ ); + } +} + +export default Reader; diff --git a/src/containers/_epubReader/index.css b/src/containers/_epubReader/index.css new file mode 100644 index 00000000..a0bd30a9 --- /dev/null +++ b/src/containers/_epubReader/index.css @@ -0,0 +1,220 @@ +.viewer { + height: 100%; +} +.view-area { + height: 100%; + touch-action: none; + position: relative; +} + +@keyframes fade-right { + 0% { + transform: translateX(200px); + opacity: 0; + } + 100% { + transform: translateX(0px); + opacity: 1; + } +} + +@keyframes fade-left { + 0% { + transform: translateX(-200px); + opacity: 0; + } + 100% { + transform: translateX(0px); + opacity: 1; + } +} +.background { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + box-sizing: border-box; + z-index: -10; + display: flex; + justify-content: center; + align-items: center; +} + +@keyframes fade-down { + 0% { + transform: translateY(-60px); + opacity: 0; + } + 100% { + transform: translateX(0px); + opacity: 1; + } +} + +@keyframes fade-up { + 0% { + transform: translateY(60px); + opacity: 0; + } + 100% { + transform: translateX(0px); + opacity: 1; + } +} +.left-panel { + width: 50px; + height: 200px; + position: absolute; + left: 0px; + top: calc(50vh - 100px); + background-color: pink; + z-index: 10; + opacity: 0; +} +.right-panel { + width: 50px; + height: 200px; + position: absolute; + right: 0px; + top: calc(50vh - 100px); + background-color: pink; + z-index: 10; + opacity: 0; +} +.top-panel { + width: 200px; + height: 50px; + position: absolute; + right: calc(50vw - 100px); + top: 0px; + background-color: pink; + z-index: 10; + opacity: 0; +} +.bottom-panel { + width: 200px; + height: 50px; + position: absolute; + right: calc(50vw - 100px); + top: calc(100vh - 50px); + background-color: pink; + z-index: 10; + opacity: 0; +} +.operation-panel-container { + width: 412px; + height: 60px; + position: absolute; + top: 0px; + left: calc(50vw - 206px); + z-index: 15; + + transition: transform 0.5s ease; +} +.progress-panel-container { + width: 412px; + height: 60px; + position: absolute; + top: calc(100vh - 60px); + left: calc(50vw - 206px); + z-index: 15; + transition: transform 0.5s ease; +} +.setting-panel-container { + width: 299px; + height: 100vh; + position: absolute; + top: 0px; + right: 0px; + transition: transform 0.5s ease; + z-index: 15; +} +.navigation-panel-container { + width: 299px; + height: 100vh; + position: absolute; + top: 0px; + left: 0px; + transition: transform 0.5s ease; + z-index: 15; +} +.view-area-page { + position: absolute; + left: 0px; + right: 0px; + top: 25px; + bottom: 20px; + /* width: calc(100% - 100px); + height: calc(100% - 100px); */ + z-index: 0; + user-select: text; +} +.previous-chapter-single-container { + width: 50px !important; + height: 50px !important; + border-radius: 50%; + position: absolute; + bottom: 20px; + left: 20px; + font-size: 30px; + transform: rotate(90deg); + cursor: pointer; + opacity: 0.2; + display: flex; + justify-content: center; + align-items: center; + z-index: 5; +} +.next-chapter-single-container { + width: 50px; + height: 50px; + border-radius: 50%; + position: absolute; + bottom: 20px; + right: 20px; + transform: rotate(-90deg); + cursor: pointer; + opacity: 0.2; + display: flex; + justify-content: center; + align-items: center; + z-index: 5; +} +.next-chapter-single-container:hover { + opacity: 0.7; +} +.previous-chapter-single-container:hover { + opacity: 0.5; +} +.previous-chapter-single { + height: 20px; + font-size: 20px; +} +.next-chapter-single { + font-size: 20px; + height: 20px; +} +.reader-setting-icon { + margin: 8px; + opacity: 0.5; +} +.reader-setting-icon-container { + width: 50px; + height: 50px; + position: absolute; + top: 0px; + right: 20px; + font-size: 22px; + cursor: pointer; + z-index: 10; + transition: 0.1s; + line-height: 30px; + display: flex; + justify-content: center; + align-items: center; + transition: 0, 2s; +} +.reader-setting-icon-container:hover { + border-radius: 50%; +} diff --git a/src/containers/_epubReader/index.tsx b/src/containers/_epubReader/index.tsx new file mode 100644 index 00000000..b228eb75 --- /dev/null +++ b/src/containers/_epubReader/index.tsx @@ -0,0 +1,32 @@ +import { + handleFetchNotes, + handleFetchBookmarks, + handleFetchChapters, + handleFetchPercentage, + handleMessageBox, + handleFetchBooks, + handleRenderFunc, +} from "../../store/actions"; + +import "./index.css"; +import { connect } from "react-redux"; +import { stateType } from "../../store"; +import Reader from "./component"; + +const mapStateToProps = (state: stateType) => { + return { + currentEpub: state.book.currentEpub, + currentBook: state.book.currentBook, + isMessage: state.manager.isMessage, + }; +}; +const actionCreator = { + handleFetchNotes, + handleFetchBookmarks, + handleFetchChapters, + handleMessageBox, + handleFetchPercentage, + handleFetchBooks, + handleRenderFunc, +}; +export default connect(mapStateToProps, actionCreator)(Reader); diff --git a/src/containers/_epubReader/interface.tsx b/src/containers/_epubReader/interface.tsx new file mode 100644 index 00000000..7bdb08b0 --- /dev/null +++ b/src/containers/_epubReader/interface.tsx @@ -0,0 +1,27 @@ +import BookModel from "../../model/Book"; +export interface ReaderProps { + currentEpub: any; + currentBook: BookModel; + isMessage: boolean; + handleFetchNotes: () => void; + handleFetchBooks: () => void; + handleRenderFunc: (renderFunc: () => void) => void; + handleFetchBookmarks: () => void; + handleMessageBox: (isShow: boolean) => void; + handleFetchPercentage: (currentBook: BookModel) => void; + handleFetchChapters: (currentEpub: any) => void; +} + +export interface ReaderState { + isOpenRightPanel: boolean; + isOpenTopPanel: boolean; + isOpenBottomPanel: boolean; + isOpenLeftPanel: boolean; + isMessage: boolean; + isTouch: boolean; + readerMode: string; + rendition: any; + time: number; + scale: string; + margin: number; +} diff --git a/src/containers/epubReader/component.tsx b/src/containers/epubReader/component.tsx index 0e09275c..9687b4ea 100644 --- a/src/containers/epubReader/component.tsx +++ b/src/containers/epubReader/component.tsx @@ -1,5 +1,5 @@ import React from "react"; -import EpubViewer from "../epubViewer"; +import ViewArea from "../viewArea"; import Background from "../background"; import SettingPanel from "../panels/settingPanel"; import NavigationPanel from "../panels/navigationPanel"; @@ -12,18 +12,18 @@ import OtherUtil from "../../utils/otherUtil"; import ReadingTime from "../../utils/readUtils/readingTime"; class Reader extends React.Component { - messageTimer!: NodeJS.Timeout; - tickTimer!: NodeJS.Timeout; + messageTimer!: any; + tickTimer!: any; rendition: any; constructor(props: ReaderProps) { super(props); this.state = { - isOpenRightPanel: + isOpenSettingPanel: OtherUtil.getReaderConfig("isSettingLocked") === "yes" ? true : false, - isOpenTopPanel: false, - isOpenBottomPanel: false, - isOpenLeftPanel: + isOpenOperationPanel: false, + isOpenProgressPanel: false, + isOpenNavPanel: OtherUtil.getReaderConfig("isNavLocked") === "yes" ? true : false, isMessage: false, rendition: null, @@ -48,7 +48,7 @@ class Reader extends React.Component { //控制消息提示两秒之后消失 if (nextProps.isMessage) { - this.messageTimer = global.setTimeout(() => { + this.messageTimer = setTimeout(() => { this.props.handleMessageBox(false); this.setState({ isMessage: false }); }, 2000); @@ -56,87 +56,55 @@ class Reader extends React.Component { } componentDidMount() { this.handleRenderBook(); - this.props.handleRenderFunc(this.handleRenderBook); window.addEventListener("resize", () => { this.handleRenderBook(); }); - this.tickTimer = global.setInterval(() => { - let time = this.state.time; - time += 1; - let page = document.querySelector("#page-area"); - //解决快速翻页过程中图书消失的bug - let renderedBook = document.querySelector(".epub-view"); - if ( - renderedBook && - !renderedBook.innerHTML && - this.state.readerMode !== "continuous" - ) { - this.handleRenderBook(); - } - let ele = page!.getElementsByClassName("epub-container")[0]; - if (page!.getElementsByClassName("epub-container").length > 1 && ele) { - ele.parentNode?.removeChild(ele); - } - this.setState({ time }); - this.handleRecord(); - }, 1000); - } - componentWillUnmount() { - clearInterval(this.tickTimer); } handleRenderBook = () => { let page = document.querySelector("#page-area"); let epub = this.props.currentEpub; - if (page!.innerHTML) { - page!.innerHTML = ""; - } - - this.setState({ rendition: null }, () => { - (window as any).rangy.init(); // 初始化 - this.rendition = epub.renderTo(page, { - manager: - this.state.readerMode === "continuous" ? "continuous" : "default", - flow: this.state.readerMode === "continuous" ? "scrolled" : "auto", - width: "100%", - height: "100%", - snap: true, - spread: - OtherUtil.getReaderConfig("readerMode") === "single" ? "none" : "", - }); - this.setState({ rendition: this.rendition }); - this.state.readerMode !== "continuous" && MouseEvent(this.rendition); // 绑定事件 + (window as any).rangy.init(); // 初始化 + this.rendition = epub.renderTo(page, { + manager: + this.state.readerMode === "continuous" ? "continuous" : "default", + flow: this.state.readerMode === "continuous" ? "scrolled" : "auto", + width: "100%", + height: "100%", + snap: true, + spread: + OtherUtil.getReaderConfig("readerMode") === "single" ? "none" : "", }); + this.setState({ rendition: this.rendition }); + this.state.readerMode !== "continuous" && MouseEvent(this.rendition); // 绑定事件 + this.tickTimer = setInterval(() => { + let time = this.state.time; + time += 1; + this.setState({ time }); + }, 1000); }; - handleRecord() { - OtherUtil.setReaderConfig("isFullScreen", "no"); - OtherUtil.setReaderConfig("windowWidth", document.body.clientWidth + ""); - OtherUtil.setReaderConfig("windowHeight", document.body.clientHeight + ""); - OtherUtil.setReaderConfig("windowX", window.screenX + ""); - OtherUtil.setReaderConfig("windowY", window.screenY + ""); - } //进入阅读器 handleEnterReader = (position: string) => { //控制上下左右的菜单的显示 switch (position) { case "right": this.setState({ - isOpenRightPanel: this.state.isOpenRightPanel ? false : true, + isOpenSettingPanel: this.state.isOpenSettingPanel ? false : true, }); break; case "left": this.setState({ - isOpenLeftPanel: this.state.isOpenLeftPanel ? false : true, + isOpenNavPanel: this.state.isOpenNavPanel ? false : true, }); break; case "top": this.setState({ - isOpenTopPanel: this.state.isOpenTopPanel ? false : true, + isOpenOperationPanel: this.state.isOpenOperationPanel ? false : true, }); break; case "bottom": this.setState({ - isOpenBottomPanel: this.state.isOpenBottomPanel ? false : true, + isOpenProgressPanel: this.state.isOpenProgressPanel ? false : true, }); break; default: @@ -151,7 +119,7 @@ class Reader extends React.Component { if (OtherUtil.getReaderConfig("isSettingLocked") === "yes") { break; } else { - this.setState({ isOpenRightPanel: false }); + this.setState({ isOpenSettingPanel: false }); break; } @@ -159,14 +127,14 @@ class Reader extends React.Component { if (OtherUtil.getReaderConfig("isNavLocked") === "yes") { break; } else { - this.setState({ isOpenLeftPanel: false }); + this.setState({ isOpenNavPanel: false }); break; } case "top": - this.setState({ isOpenTopPanel: false }); + this.setState({ isOpenOperationPanel: false }); break; case "bottom": - this.setState({ isOpenBottomPanel: false }); + this.setState({ isOpenProgressPanel: false }); break; default: break; @@ -184,33 +152,38 @@ class Reader extends React.Component { handleLeaveReader: this.handleLeaveReader, handleEnterReader: this.handleEnterReader, isShow: - this.state.isOpenLeftPanel || - this.state.isOpenTopPanel || - this.state.isOpenBottomPanel || - this.state.isOpenRightPanel, + this.state.isOpenNavPanel || + this.state.isOpenOperationPanel || + this.state.isOpenProgressPanel || + this.state.isOpenSettingPanel, }; return ( -
- {OtherUtil.getReaderConfig("isHidePageButton") !== "yes" && ( - <> -
{ - this.prevPage(); - }} - > - -
-
{ - this.nextPage(); - }} - > - -
- - )} +
+
{ + this.prevPage(); + }} + > + +
+
{ + this.nextPage(); + }} + > + +
{ @@ -226,7 +199,7 @@ class Reader extends React.Component {
{ - if (this.state.isTouch || this.state.isOpenLeftPanel) { + if (this.state.isTouch || this.state.isOpenNavPanel) { return; } this.handleEnterReader("left"); @@ -238,7 +211,7 @@ class Reader extends React.Component {
{ - if (this.state.isTouch || this.state.isOpenRightPanel) { + if (this.state.isTouch || this.state.isOpenSettingPanel) { return; } this.handleEnterReader("right"); @@ -250,7 +223,7 @@ class Reader extends React.Component {
{ - if (this.state.isTouch || this.state.isOpenTopPanel) { + if (this.state.isTouch || this.state.isOpenOperationPanel) { return; } this.handleEnterReader("top"); @@ -262,7 +235,7 @@ class Reader extends React.Component {
{ - if (this.state.isTouch || this.state.isOpenBottomPanel) { + if (this.state.isTouch || this.state.isOpenProgressPanel) { return; } this.handleEnterReader("bottom"); @@ -273,7 +246,7 @@ class Reader extends React.Component { >
{this.state.rendition && this.props.currentEpub.rendition && ( - + )}
{ this.handleLeaveReader("right"); }} style={ - this.state.isOpenRightPanel + this.state.isOpenSettingPanel ? {} : { transform: "translateX(309px)", @@ -296,7 +269,7 @@ class Reader extends React.Component { this.handleLeaveReader("left"); }} style={ - this.state.isOpenLeftPanel + this.state.isOpenNavPanel ? {} : { transform: "translateX(-309px)", @@ -311,7 +284,7 @@ class Reader extends React.Component { this.handleLeaveReader("bottom"); }} style={ - this.state.isOpenBottomPanel + this.state.isOpenProgressPanel ? {} : { transform: "translateY(110px)", @@ -326,7 +299,7 @@ class Reader extends React.Component { this.handleLeaveReader("top"); }} style={ - this.state.isOpenTopPanel + this.state.isOpenOperationPanel ? {} : { transform: "translateY(-110px)", @@ -335,6 +308,7 @@ class Reader extends React.Component { >
+
void; handleFetchBooks: () => void; - handleRenderFunc: (renderFunc: () => void) => void; handleFetchBookmarks: () => void; handleMessageBox: (isShow: boolean) => void; handleFetchPercentage: (currentBook: BookModel) => void; @@ -13,10 +12,10 @@ export interface ReaderProps { } export interface ReaderState { - isOpenRightPanel: boolean; - isOpenTopPanel: boolean; - isOpenBottomPanel: boolean; - isOpenLeftPanel: boolean; + isOpenSettingPanel: boolean; + isOpenOperationPanel: boolean; + isOpenProgressPanel: boolean; + isOpenNavPanel: boolean; isMessage: boolean; isTouch: boolean; readerMode: string; diff --git a/src/containers/viewArea/component.tsx b/src/containers/viewArea/component.tsx new file mode 100644 index 00000000..1aa59334 --- /dev/null +++ b/src/containers/viewArea/component.tsx @@ -0,0 +1,124 @@ +//阅读器图书内容区域 +import React from "react"; +import PopupMenu from "../../components/popups/popupMenu"; +import { ViewAreaProps, ViewAreaStates } from "./interface"; +import RecordLocation from "../../utils/readUtils/recordLocation"; +import BookmarkModel from "../../model/Bookmark"; +import StyleUtil from "../../utils/readUtils/styleUtil"; +import ImageViewer from "../../components/imageViewer"; +import Lottie from "react-lottie"; +import animationSiri from "../../assets/lotties/siri.json"; + +const siriOptions = { + loop: true, + autoplay: true, + animationData: animationSiri, + rendererSettings: { + preserveAspectRatio: "xMidYMid slice", + }, +}; +declare var window: any; + +class EpubViewer extends React.Component { + isFirst: boolean; + constructor(props: ViewAreaProps) { + super(props); + this.state = { + cfiRange: null, + contents: null, + rect: null, + loading: true, + }; + this.isFirst = true; + } + + componentDidMount() { + let epub = this.props.currentEpub; + window.rangy.init(); // 初始化 + this.props.rendition.on("locationChanged", () => { + this.props.handleReadingEpub(epub); + this.props.handleOpenMenu(false); + const currentLocation = this.props.rendition.currentLocation(); + if (!currentLocation.start) { + return; + } + const cfi = currentLocation.start.cfi; + this.props.handleShowBookmark( + this.props.bookmarks && + this.props.bookmarks.filter( + (item: BookmarkModel) => item.cfi === cfi + )[0] + ? true + : false + ); + + if (!this.isFirst && this.props.locations) { + let percentage = this.props.locations.percentageFromCfi(cfi); + RecordLocation.recordCfi(this.props.currentBook.key, cfi, percentage); + this.props.handlePercentage(percentage); + } else if (!this.isFirst) { + //如果过暂时没有解析出locations,就直接记录cfi + RecordLocation.recordCfi( + this.props.currentBook.key, + cfi, + RecordLocation.getCfi(this.props.currentBook.key).percentage + ); + } + this.isFirst = false; + }); + this.props.rendition.on("rendered", () => { + this.setState({ loading: false }); + let iframe = document.getElementsByTagName("iframe")[0]; + if (!iframe) return; + let doc = iframe.contentDocument; + if (!doc) { + return; + } + StyleUtil.addDefaultCss(); + this.props.rendition.themes.default(StyleUtil.getCustomCss(false)); + }); + this.props.rendition.on("selected", (cfiRange: any, contents: any) => { + var range = contents.range(cfiRange); + var rect = range.getBoundingClientRect(); + this.setState({ cfiRange, contents, rect }); + }); + this.props.rendition.themes.default(StyleUtil.getCustomCss(false)); + this.props.rendition.display( + RecordLocation.getCfi(this.props.currentBook.key) === null + ? null + : RecordLocation.getCfi(this.props.currentBook.key).cfi + ); + } + + render() { + const popupMenuProps = { + rendition: this.props.rendition, + cfiRange: this.state.cfiRange, + contents: this.state.contents, + rect: this.state.rect, + }; + return ( +
+ + + {this.state.loading ? ( +
+ +
+ ) : null} + <> + {this.props.isShowBookmark ?
: null} + +
+ ); + } +} + +export default EpubViewer; diff --git a/src/containers/viewArea/index.css b/src/containers/viewArea/index.css new file mode 100644 index 00000000..4bd895ff --- /dev/null +++ b/src/containers/viewArea/index.css @@ -0,0 +1,35 @@ +.popup-menu-container { + position: absolute; + left: 100px; + top: 100px; + z-index: 20; + animation: popup 0.075s ease-in-out 0s 1; +} +.bookmark { + height: 40px; + width: 20px; + padding: 0px; + -webkit-transform: rotate(0deg) skew(0deg); + transform: rotate(0deg) skew(0deg); + border-left: 10px solid red; + border-right: 10px solid red; + border-bottom: 10px solid transparent; + position: fixed; + top: 5px; + right: 70px; + z-index: 15; +} +.bookmark, +.bookmark:before, +.bookmark:after { + box-sizing: border-box; + -moz-box-sizing: border-box; + -webkit-box-sizing: border-box; +} +.spinner { + position: absolute; + left: 50%; + top: 50%; + margin-left: -150px; + margin-top: -50px; +} diff --git a/src/containers/viewArea/index.tsx b/src/containers/viewArea/index.tsx new file mode 100644 index 00000000..81f23923 --- /dev/null +++ b/src/containers/viewArea/index.tsx @@ -0,0 +1,29 @@ +import { connect } from "react-redux"; +import { stateType } from "../../store"; +import ViewArea from "./component"; +import { + handlePercentage, + handleOpenMenu, + handleShowBookmark, + handleReadingEpub, +} from "../../store/actions"; +import "./index.css"; + +const mapStateToProps = (state: stateType) => { + return { + chapters: state.reader.chapters, + currentEpub: state.book.currentEpub, + currentBook: state.book.currentBook, + locations: state.progressPanel.locations, + bookmarks: state.reader.bookmarks, + isShowBookmark: state.viewArea.isShowBookmark, + }; +}; +const actionCreator = { + handlePercentage, + handleOpenMenu, + handleShowBookmark, + handleReadingEpub, +}; + +export default connect(mapStateToProps, actionCreator)(ViewArea); diff --git a/src/containers/viewArea/interface.tsx b/src/containers/viewArea/interface.tsx new file mode 100644 index 00000000..02506e91 --- /dev/null +++ b/src/containers/viewArea/interface.tsx @@ -0,0 +1,26 @@ +import BookModel from "../../model/Book"; +import BookmarkModel from "../../model/Bookmark"; + +export interface ViewAreaProps { + rendition: any; + currentBook: BookModel; + currentEpub: any; + bookmarks: BookmarkModel[]; + locations: any; + isShowBookmark: boolean; + chapters: any[]; + isShow: boolean; + handleLeaveReader: (position: string) => void; + handleEnterReader: (position: string) => void; + handlePercentage: (percentage: number) => void; + handleOpenMenu: (isOpenMenu: boolean) => void; + handleShowBookmark: (isShowBookmark: boolean) => void; + handleReadingEpub: (epub: object) => void; +} +export interface ViewAreaStates { + loading: boolean; + cfiRange: any; + contents: any; + // rendition: any; + rect: any; +} diff --git a/src/pages/epubPage/component.tsx b/src/pages/epubPage/component.tsx index 5534fafa..08f53177 100644 --- a/src/pages/epubPage/component.tsx +++ b/src/pages/epubPage/component.tsx @@ -2,7 +2,7 @@ import React from "react"; import RecentBooks from "../../utils/readUtils/recordRecent"; import { EpubReaderProps, EpubReaderState } from "./interface"; import localforage from "localforage"; -import Reader from "../../containers/epubReader"; +import Reader from "../../containers/_epubReader"; import { withRouter } from "react-router-dom"; import _ from "underscore"; import BookUtil from "../../utils/fileUtils/bookUtil";