mirror of
https://github.com/Kareadita/Kavita.git
synced 2025-07-09 03:04:19 -04:00
Reader scroll area fix (#1257)
* Changes to make the pagination area scrollable (not working, debug code for Robbie) * Adjusted the html to be easier to understand and more streamlined * Fixed inability to scroll in manga reader over pagination areas for everything but the bottom bar * Book reader now allows you to scroll over pagination area
This commit is contained in:
parent
ccb6414e9e
commit
cdc4931770
@ -116,6 +116,7 @@ namespace API.Controllers
|
|||||||
[HttpGet("chapter-info")]
|
[HttpGet("chapter-info")]
|
||||||
public async Task<ActionResult<ChapterInfoDto>> GetChapterInfo(int chapterId)
|
public async Task<ActionResult<ChapterInfoDto>> GetChapterInfo(int chapterId)
|
||||||
{
|
{
|
||||||
|
if (chapterId <= 0) return null; // This can happen occasionally from UI, we should just ignore
|
||||||
var chapter = await _cacheService.Ensure(chapterId);
|
var chapter = await _cacheService.Ensure(chapterId);
|
||||||
if (chapter == null) return BadRequest("Could not find Chapter");
|
if (chapter == null) return BadRequest("Could not find Chapter");
|
||||||
|
|
||||||
|
@ -73,15 +73,21 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div #readingSection class="reading-section {{ColumnLayout}}" [@isLoading]="isLoading ? true : false">
|
<div #readingSection class="reading-section {{ColumnLayout}}" [@isLoading]="isLoading ? true : false">
|
||||||
<div #readingCont class="book-container {{ColumnLayout}}">
|
|
||||||
|
<ng-container *ngIf="clickToPaginate">
|
||||||
|
<div class="left {{clickOverlayClass('left')}} no-observe"
|
||||||
|
(click)="movePage(readingDirection === ReadingDirection.LeftToRight ? PAGING_DIRECTION.BACKWARDS : PAGING_DIRECTION.FORWARD)"
|
||||||
|
tabindex="-1" [ngStyle]="{height: PageHeightForPagination}"></div>
|
||||||
|
<div class="{{scrollbarNeeded ? 'right-with-scrollbar' : 'right'}} {{clickOverlayClass('right')}} no-observe"
|
||||||
|
(click)="movePage(readingDirection === ReadingDirection.LeftToRight ? PAGING_DIRECTION.FORWARD : PAGING_DIRECTION.BACKWARDS)"
|
||||||
|
tabindex="-1" [ngStyle]="{height: PageHeightForPagination}"></div>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<div class="book-container {{ColumnLayout}}">
|
||||||
|
|
||||||
<div #readingHtml class="book-content {{ColumnLayout}}" [ngStyle]="{'max-height': ColumnHeight, 'column-width': ColumnWidth}"
|
<div #readingHtml class="book-content {{ColumnLayout}}" [ngStyle]="{'max-height': ColumnHeight, 'column-width': ColumnWidth}"
|
||||||
[innerHtml]="page" *ngIf="page !== undefined" (click)="toggleMenu($event)" (mousedown)="mouseDown($event)"></div>
|
[innerHtml]="page" *ngIf="page !== undefined" (click)="toggleMenu($event)" (mousedown)="mouseDown($event)"></div>
|
||||||
|
|
||||||
<ng-container *ngIf="clickToPaginate">
|
|
||||||
<div class="left {{clickOverlayClass('left')}} no-observe" (click)="movePage(readingDirection === ReadingDirection.LeftToRight ? PAGING_DIRECTION.BACKWARDS : PAGING_DIRECTION.FORWARD)" tabindex="-1"></div>
|
|
||||||
<div class="{{scrollbarNeeded ? 'right-with-scrollbar' : 'right'}} {{clickOverlayClass('right')}} no-observe" (click)="movePage(readingDirection === ReadingDirection.LeftToRight ? PAGING_DIRECTION.FORWARD : PAGING_DIRECTION.BACKWARDS)" tabindex="-1"></div>
|
|
||||||
</ng-container>
|
|
||||||
|
|
||||||
<div *ngIf="page !== undefined && (scrollbarNeeded || layoutMode !== BookPageLayoutMode.Default)" (click)="$event.stopPropagation();">
|
<div *ngIf="page !== undefined && (scrollbarNeeded || layoutMode !== BookPageLayoutMode.Default)" (click)="$event.stopPropagation();">
|
||||||
<ng-container [ngTemplateOutlet]="actionBar"></ng-container>
|
<ng-container [ngTemplateOutlet]="actionBar"></ng-container>
|
||||||
|
@ -263,48 +263,41 @@ $action-bar-height: 38px;
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
.right {
|
.right {
|
||||||
position: fixed;
|
position: absolute;
|
||||||
right: 0px; // with scrollbar: 17px
|
right: 0px; // with scrollbar: 17px
|
||||||
top: 0px;
|
top: $action-bar-height;
|
||||||
width: 20%; // with scrollbar: 18%
|
width: 20%; // with scrollbar: 18%
|
||||||
height: 100%;
|
|
||||||
z-index: 2;
|
z-index: 2;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
opacity: 0;
|
|
||||||
background: transparent;
|
background: transparent;
|
||||||
padding-top: $action-bar-height;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// This class pushes the click area to the left a bit to let users click the scrollbar
|
// This class pushes the click area to the left a bit to let users click the scrollbar
|
||||||
.right-with-scrollbar {
|
.right-with-scrollbar {
|
||||||
position: fixed;
|
position: absolute;
|
||||||
right: 17px;
|
right: 17px;
|
||||||
top: 0px;
|
top: $action-bar-height;
|
||||||
width: 18%;
|
width: 18%;
|
||||||
height: 100%;
|
|
||||||
z-index: 2;
|
z-index: 2;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
opacity: 0;
|
|
||||||
background: transparent;
|
background: transparent;
|
||||||
padding-top: $action-bar-height;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.left {
|
.left {
|
||||||
position: fixed;
|
position: absolute;
|
||||||
left: 0px;
|
left: 0px;
|
||||||
top: 0px;
|
top: $action-bar-height;
|
||||||
width: 20%;
|
width: 20%;
|
||||||
height: 100%;
|
background: transparent;
|
||||||
|
|
||||||
z-index: 2;
|
z-index: 2;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
opacity: 0;
|
|
||||||
background: transparent;
|
|
||||||
padding-top: $action-bar-height;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
.highlight {
|
.highlight {
|
||||||
background-color: rgba(65, 225, 100, 0.5) !important;
|
background-color: rgba(65, 225, 100, 0.5) !important;
|
||||||
animation: fadein .5s both;
|
animation: fadein .5s both;
|
||||||
|
@ -27,6 +27,7 @@ import { User } from 'src/app/_models/user';
|
|||||||
import { ThemeService } from 'src/app/_services/theme.service';
|
import { ThemeService } from 'src/app/_services/theme.service';
|
||||||
import { ScrollService } from 'src/app/_services/scroll.service';
|
import { ScrollService } from 'src/app/_services/scroll.service';
|
||||||
import { PAGING_DIRECTION } from 'src/app/manga-reader/_models/reader-enums';
|
import { PAGING_DIRECTION } from 'src/app/manga-reader/_models/reader-enums';
|
||||||
|
import { LayoutMode } from 'src/app/manga-reader/_models/layout-mode';
|
||||||
|
|
||||||
|
|
||||||
enum TabID {
|
enum TabID {
|
||||||
@ -364,6 +365,14 @@ export class BookReaderComponent implements OnInit, AfterViewInit, OnDestroy {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get PageHeightForPagination() {
|
||||||
|
if (this.layoutMode === BookPageLayoutMode.Default) {
|
||||||
|
return (this.readingSectionElemRef?.nativeElement?.scrollHeight || 0) - (this.topOffset * 2) + 'px';
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.ColumnHeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
constructor(private route: ActivatedRoute, private router: Router, private accountService: AccountService,
|
constructor(private route: ActivatedRoute, private router: Router, private accountService: AccountService,
|
||||||
private seriesService: SeriesService, private readerService: ReaderService, private location: Location,
|
private seriesService: SeriesService, private readerService: ReaderService, private location: Location,
|
||||||
@ -1000,6 +1009,8 @@ export class BookReaderComponent implements OnInit, AfterViewInit, OnDestroy {
|
|||||||
|
|
||||||
getFirstVisibleElementXPath() {
|
getFirstVisibleElementXPath() {
|
||||||
let resumeElement: string | null = null;
|
let resumeElement: string | null = null;
|
||||||
|
if (this.readingHtml === null) return null;
|
||||||
|
|
||||||
const intersectingEntries = Array.from(this.readingHtml.nativeElement.querySelectorAll('div,o,p,ul,li,a,img,h1,h2,h3,h4,h5,h6,span'))
|
const intersectingEntries = Array.from(this.readingHtml.nativeElement.querySelectorAll('div,o,p,ul,li,a,img,h1,h2,h3,h4,h5,h6,span'))
|
||||||
.filter(element => !element.classList.contains('no-observe'))
|
.filter(element => !element.classList.contains('no-observe'))
|
||||||
.filter(entry => {
|
.filter(entry => {
|
||||||
|
@ -31,13 +31,30 @@
|
|||||||
</div>
|
</div>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
|
||||||
<div (click)="toggleMenu()" class="reading-area" [ngStyle]="{'background-color': backgroundColor}">
|
<div (click)="toggleMenu()" class="reading-area" [ngStyle]="{'background-color': backgroundColor}" #readingArea>
|
||||||
<ng-container *ngIf="readerMode !== ReaderMode.Webtoon; else webtoon">
|
<ng-container *ngIf="readerMode !== ReaderMode.Webtoon; else webtoon">
|
||||||
<div [ngClass]="{'d-none': !renderWithCanvas }">
|
<div [ngClass]="{'d-none': !renderWithCanvas }">
|
||||||
<canvas #content class="{{getFittingOptionClass()}}"
|
<canvas #content class="{{getFittingOptionClass()}}"
|
||||||
ondragstart="return false;" onselectstart="return false;">
|
ondragstart="return false;" onselectstart="return false;">
|
||||||
</canvas>
|
</canvas>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="pagination-area">
|
||||||
|
<!-- Pagination controls and screen hints-->
|
||||||
|
<div class="{{readerMode === ReaderMode.LeftRight ? 'left' : 'top'}} {{clickOverlayClass('left')}}" (click)="handlePageChange($event, 'left')" [ngStyle]="{'height': (readerMode === ReaderMode.LeftRight ? WindowHeight: 25 + '%')}">
|
||||||
|
<div *ngIf="showClickOverlay">
|
||||||
|
<i class="fa fa-angle-{{readingDirection === ReadingDirection.RightToLeft ? 'double-' : ''}}{{readerMode === ReaderMode.LeftRight ? 'left' : 'up'}}"
|
||||||
|
title="Previous Page" aria-hidden="true"></i>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="{{readerMode === ReaderMode.LeftRight ? 'right' : 'bottom'}} {{clickOverlayClass('right')}}" (click)="handlePageChange($event, 'right')" [ngStyle]="{'height': (readerMode === ReaderMode.LeftRight ? WindowHeight: 25 + '%')}">
|
||||||
|
<div *ngIf="showClickOverlay">
|
||||||
|
<i class="fa fa-angle-{{readingDirection === ReadingDirection.LeftToRight ? 'double-' : ''}}{{readerMode === ReaderMode.LeftRight ? 'right' : 'down'}}"
|
||||||
|
title="Next Page" aria-hidden="true"></i>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="image-container {{getFittingOptionClass()}}" [ngClass]="{'d-none': renderWithCanvas, 'center-double': ShouldRenderDoublePage,
|
<div class="image-container {{getFittingOptionClass()}}" [ngClass]="{'d-none': renderWithCanvas, 'center-double': ShouldRenderDoublePage,
|
||||||
'fit-to-width-double-offset' : this.generalSettingsForm.get('fittingOption')?.value === FITTING_OPTION.WIDTH && ShouldRenderDoublePage,
|
'fit-to-width-double-offset' : this.generalSettingsForm.get('fittingOption')?.value === FITTING_OPTION.WIDTH && ShouldRenderDoublePage,
|
||||||
'fit-to-height-double-offset': this.generalSettingsForm.get('fittingOption')?.value === FITTING_OPTION.HEIGHT && ShouldRenderDoublePage,
|
'fit-to-height-double-offset': this.generalSettingsForm.get('fittingOption')?.value === FITTING_OPTION.HEIGHT && ShouldRenderDoublePage,
|
||||||
@ -51,22 +68,6 @@
|
|||||||
</ng-container>
|
</ng-container>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<ng-container>
|
|
||||||
<!-- Pagination controls and screen hints-->
|
|
||||||
<div class="pagination-area {{readerMode === ReaderMode.LeftRight ? 'right' : 'bottom'}} {{clickOverlayClass('right')}}" (click)="handlePageChange($event, 'right')">
|
|
||||||
<div *ngIf="showClickOverlay">
|
|
||||||
<i class="fa fa-angle-{{readingDirection === ReadingDirection.LeftToRight ? 'double-' : ''}}{{readerMode === ReaderMode.LeftRight ? 'right' : 'down'}}"
|
|
||||||
title="Next Page" aria-hidden="true"></i>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="pagination-area {{readerMode === ReaderMode.LeftRight ? 'left' : 'top'}} {{clickOverlayClass('left')}}" (click)="handlePageChange($event, 'left')">
|
|
||||||
<div *ngIf="showClickOverlay">
|
|
||||||
<i class="fa fa-angle-{{readingDirection === ReadingDirection.RightToLeft ? 'double-' : ''}}{{readerMode === ReaderMode.LeftRight ? 'left' : 'up'}}"
|
|
||||||
title="Previous Page" aria-hidden="true"></i>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</ng-container>
|
|
||||||
|
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
|
||||||
<ng-template #webtoon>
|
<ng-template #webtoon>
|
||||||
|
@ -159,52 +159,6 @@ img {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
.right {
|
|
||||||
position: fixed;
|
|
||||||
right: 0px;
|
|
||||||
top: 0px;
|
|
||||||
width: $side-width;
|
|
||||||
height: 100vh;
|
|
||||||
background: rgba(0, 0, 0, 0);
|
|
||||||
z-index: 2;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
.top {
|
|
||||||
position: fixed;
|
|
||||||
right: 0px;
|
|
||||||
top: 0px;
|
|
||||||
width: 100%;
|
|
||||||
height: $side-width;
|
|
||||||
background: rgba(0, 0, 0, 0);
|
|
||||||
z-index: 2;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
.left {
|
|
||||||
position: fixed;
|
|
||||||
left: 0px;
|
|
||||||
top: 0px;
|
|
||||||
width: $side-width;
|
|
||||||
height: 100vh;
|
|
||||||
background: rgba(0, 0, 0, 0);
|
|
||||||
z-index: 2;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
.bottom {
|
|
||||||
position: fixed;
|
|
||||||
left: 0px;
|
|
||||||
bottom: 0px;
|
|
||||||
width: 100%;
|
|
||||||
height: $side-width;
|
|
||||||
background: rgba(0, 0, 0, 0);
|
|
||||||
z-index: 2;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// Splitting Icon
|
// Splitting Icon
|
||||||
.split {
|
.split {
|
||||||
height: 20px;
|
height: 20px;
|
||||||
@ -295,14 +249,45 @@ img {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.pagination-area {
|
.pagination-area {
|
||||||
display: flex;
|
cursor: pointer;
|
||||||
align-items: center;
|
z-index: 2;
|
||||||
justify-content: center;
|
|
||||||
|
|
||||||
i {
|
i {
|
||||||
color: white;
|
color: white;
|
||||||
font-size: 42px;
|
font-size: 42px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.right {
|
||||||
|
position: absolute;
|
||||||
|
right: 0px;
|
||||||
|
top: 0px;
|
||||||
|
width: $side-width;
|
||||||
|
background: rgba(0, 0, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
.top {
|
||||||
|
position: absolute;
|
||||||
|
right: 0px;
|
||||||
|
top: 0px;
|
||||||
|
width: 100%;
|
||||||
|
background: rgba(0, 0, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
.left {
|
||||||
|
position: absolute;
|
||||||
|
left: 0px;
|
||||||
|
top: 0px;
|
||||||
|
width: $side-width;
|
||||||
|
background: rgba(0, 0, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
.bottom {
|
||||||
|
position: fixed; // I couldn't figure out how to do this with abs, so only the bottom bar will not be scrollable
|
||||||
|
left: 0px;
|
||||||
|
bottom: 0px;
|
||||||
|
width: 100%;
|
||||||
|
background: rgba(0, 0, 0, 0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.highlight {
|
.highlight {
|
||||||
|
@ -116,6 +116,7 @@ export class MangaReaderComponent implements OnInit, AfterViewInit, OnDestroy {
|
|||||||
isLoading = true;
|
isLoading = true;
|
||||||
|
|
||||||
@ViewChild('reader') reader!: ElementRef;
|
@ViewChild('reader') reader!: ElementRef;
|
||||||
|
@ViewChild('readingArea') readingArea!: ElementRef;
|
||||||
@ViewChild('content') canvas: ElementRef | undefined;
|
@ViewChild('content') canvas: ElementRef | undefined;
|
||||||
private ctx!: CanvasRenderingContext2D;
|
private ctx!: CanvasRenderingContext2D;
|
||||||
/**
|
/**
|
||||||
@ -281,6 +282,10 @@ export class MangaReaderComponent implements OnInit, AfterViewInit, OnDestroy {
|
|||||||
return this.bookmarks.hasOwnProperty(this.pageNum);
|
return this.bookmarks.hasOwnProperty(this.pageNum);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get WindowHeight() {
|
||||||
|
return this.readingArea?.nativeElement.scrollHeight + 'px';
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
get splitIconClass() {
|
get splitIconClass() {
|
||||||
if (this.isSplitLeftToRight()) {
|
if (this.isSplitLeftToRight()) {
|
||||||
@ -1008,6 +1013,7 @@ export class MangaReaderComponent implements OnInit, AfterViewInit, OnDestroy {
|
|||||||
if (!this.ctx || !this.canvas) { return; }
|
if (!this.ctx || !this.canvas) { return; }
|
||||||
|
|
||||||
this.canvasImage.onload = null;
|
this.canvasImage.onload = null;
|
||||||
|
console.log('canvasImage: ', this.canvasImage?.height);
|
||||||
|
|
||||||
this.setCanvasSize();
|
this.setCanvasSize();
|
||||||
|
|
||||||
@ -1106,7 +1112,6 @@ export class MangaReaderComponent implements OnInit, AfterViewInit, OnDestroy {
|
|||||||
|
|
||||||
this.canvasImage = this.cachedImages.current();
|
this.canvasImage = this.cachedImages.current();
|
||||||
|
|
||||||
|
|
||||||
if (this.readerService.imageUrlToPageNum(this.canvasImage.src) !== this.pageNum || this.canvasImage.src === '' || !this.canvasImage.complete) {
|
if (this.readerService.imageUrlToPageNum(this.canvasImage.src) !== this.pageNum || this.canvasImage.src === '' || !this.canvasImage.complete) {
|
||||||
if (this.layoutMode === LayoutMode.Single) {
|
if (this.layoutMode === LayoutMode.Single) {
|
||||||
//this.canvasImage.src = this.readerService.getPageUrl(this.chapterId, this.pageNum);
|
//this.canvasImage.src = this.readerService.getPageUrl(this.chapterId, this.pageNum);
|
||||||
|
@ -8,7 +8,6 @@ body {
|
|||||||
color-scheme: var(--color-scheme);
|
color-scheme: var(--color-scheme);
|
||||||
max-height: 100%;
|
max-height: 100%;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
//margin-top: 56px; // Set by nav service
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user