mirror of
https://github.com/Kareadita/Kavita.git
synced 2025-07-09 03:04:19 -04:00
Reworks how the image sizes in the book reader gets handled (#1872)
* Fixes bus in vertical writing mode. * Reworks how the images gets resized. They should be more adaptive and pages with single images will get centered. * Adds back an unintended change * Revert "Fixes bus in vertical writing mode." This reverts commit a1cf9675def45fd626a149d90f573104b5d280cd. * Added debounce and timeout to the updateImageSize to prevent it from getting run before the page loads * Fixes semicolons missing semicolons * Improves the detection in checkSingleImagePage.
This commit is contained in:
parent
e5e2d02e04
commit
1f8b591ed5
@ -212,7 +212,6 @@ $action-bar-height: 38px;
|
|||||||
height: calc((var(--vh) * 100) - calc($action-bar-height)); // * 2
|
height: calc((var(--vh) * 100) - calc($action-bar-height)); // * 2
|
||||||
|
|
||||||
&.writing-style-vertical {
|
&.writing-style-vertical {
|
||||||
height: auto;
|
|
||||||
padding: 0 10px 0 0;
|
padding: 0 10px 0 0;
|
||||||
margin: 20px 0;
|
margin: 20px 0;
|
||||||
}
|
}
|
||||||
@ -222,12 +221,15 @@ $action-bar-height: 38px;
|
|||||||
height: calc((var(--vh) * 100) - calc($action-bar-height)); // * 2
|
height: calc((var(--vh) * 100) - calc($action-bar-height)); // * 2
|
||||||
|
|
||||||
&.writing-style-vertical {
|
&.writing-style-vertical {
|
||||||
height: auto;
|
|
||||||
padding: 0 10px 0 0;
|
padding: 0 10px 0 0;
|
||||||
margin: 20px 0;
|
margin: 20px 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.writing-style-vertical {
|
||||||
|
height: auto;
|
||||||
|
}
|
||||||
|
|
||||||
// &.immersive {
|
// &.immersive {
|
||||||
// // Note: I removed this for bug: https://github.com/Kareadita/Kavita/issues/1726
|
// // Note: I removed this for bug: https://github.com/Kareadita/Kavita/issues/1726
|
||||||
// //height: calc((var(--vh, 1vh) * 100) - $action-bar-height);
|
// //height: calc((var(--vh, 1vh) * 100) - $action-bar-height);
|
||||||
@ -301,11 +303,18 @@ $action-bar-height: 38px;
|
|||||||
::ng-deep .kavita-scale-width-container {
|
::ng-deep .kavita-scale-width-container {
|
||||||
width: auto;
|
width: auto;
|
||||||
max-height: calc(var(--book-reader-content-max-height) - ($action-bar-height)) !important;
|
max-height: calc(var(--book-reader-content-max-height) - ($action-bar-height)) !important;
|
||||||
|
max-width: calc(var(--book-reader-content-max-width)) !important;
|
||||||
|
position: var(--book-reader-content-position) !important;
|
||||||
|
top: var(--book-reader-content-top) !important;
|
||||||
|
left: var(--book-reader-content-left) !important;
|
||||||
|
transform: var(--book-reader-content-transform) !important;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// This is applied to images in the backend
|
// This is applied to images in the backend
|
||||||
::ng-deep .kavita-scale-width {
|
::ng-deep .kavita-scale-width {
|
||||||
max-width: 100%;
|
max-height: calc(var(--book-reader-content-max-height) - ($action-bar-height)) !important;
|
||||||
|
max-width: calc(var(--book-reader-content-max-width)) !important;
|
||||||
object-fit: contain;
|
object-fit: contain;
|
||||||
object-position: top center;
|
object-position: top center;
|
||||||
break-inside: avoid;
|
break-inside: avoid;
|
||||||
|
@ -122,7 +122,10 @@ export class BookReaderComponent implements OnInit, AfterViewInit, OnDestroy {
|
|||||||
* TODO: See if continuousChaptersStack can be moved into reader service so we can reduce code duplication between readers (and also use ChapterInfo with it instead)
|
* TODO: See if continuousChaptersStack can be moved into reader service so we can reduce code duplication between readers (and also use ChapterInfo with it instead)
|
||||||
*/
|
*/
|
||||||
continuousChaptersStack: Stack<number> = new Stack();
|
continuousChaptersStack: Stack<number> = new Stack();
|
||||||
|
/*
|
||||||
|
* The current page only contains an image. This is used to determine if we should show the image in the center of the screen.
|
||||||
|
*/
|
||||||
|
isSingleImagePage = false;
|
||||||
/**
|
/**
|
||||||
* Belongs to the drawer component
|
* Belongs to the drawer component
|
||||||
*/
|
*/
|
||||||
@ -153,6 +156,7 @@ export class BookReaderComponent implements OnInit, AfterViewInit, OnDestroy {
|
|||||||
clickToPaginateVisualOverlay = false;
|
clickToPaginateVisualOverlay = false;
|
||||||
clickToPaginateVisualOverlayTimeout: any = undefined; // For animation
|
clickToPaginateVisualOverlayTimeout: any = undefined; // For animation
|
||||||
clickToPaginateVisualOverlayTimeout2: any = undefined; // For kicking off animation, giving enough time to render html
|
clickToPaginateVisualOverlayTimeout2: any = undefined; // For kicking off animation, giving enough time to render html
|
||||||
|
updateImageSizeTimeout: any = undefined;
|
||||||
/**
|
/**
|
||||||
* This is the html we get from the server
|
* This is the html we get from the server
|
||||||
*/
|
*/
|
||||||
@ -372,17 +376,14 @@ export class BookReaderComponent implements OnInit, AfterViewInit, OnDestroy {
|
|||||||
if (this.layoutMode !== BookPageLayoutMode.Default || this.writingStyle === WritingStyle.Vertical) {
|
if (this.layoutMode !== BookPageLayoutMode.Default || this.writingStyle === WritingStyle.Vertical) {
|
||||||
// Take the height after page loads, subtract the top/bottom bar
|
// Take the height after page loads, subtract the top/bottom bar
|
||||||
const height = this.windowHeight - (this.topOffset * 2);
|
const height = this.windowHeight - (this.topOffset * 2);
|
||||||
this.document.documentElement.style.setProperty('--book-reader-content-max-height', `${height}px`);
|
|
||||||
return height + 'px';
|
return height + 'px';
|
||||||
}
|
}
|
||||||
|
|
||||||
return 'unset';
|
return 'unset';
|
||||||
}
|
}
|
||||||
|
|
||||||
get VerticalBookContentWidth() {
|
get VerticalBookContentWidth() {
|
||||||
if (this.layoutMode !== BookPageLayoutMode.Default && this.writingStyle !== WritingStyle.Horizontal ) {
|
if (this.layoutMode !== BookPageLayoutMode.Default && this.writingStyle !== WritingStyle.Horizontal ) {
|
||||||
const width = this.getVerticalPageWidth()
|
const width = this.getVerticalPageWidth()
|
||||||
this.document.documentElement.style.setProperty('--book-reader-content-max-width', `${width}px`);
|
|
||||||
return width + 'px';
|
return width + 'px';
|
||||||
}
|
}
|
||||||
return '';
|
return '';
|
||||||
@ -592,8 +593,7 @@ export class BookReaderComponent implements OnInit, AfterViewInit, OnDestroy {
|
|||||||
this.libraryType = type;
|
this.libraryType = type;
|
||||||
});
|
});
|
||||||
|
|
||||||
this.updateImagesWithHeight();
|
this.updateImageSizes();
|
||||||
|
|
||||||
|
|
||||||
if (this.pageNum >= this.maxPages) {
|
if (this.pageNum >= this.maxPages) {
|
||||||
this.pageNum = this.maxPages - 1;
|
this.pageNum = this.maxPages - 1;
|
||||||
@ -637,6 +637,7 @@ export class BookReaderComponent implements OnInit, AfterViewInit, OnDestroy {
|
|||||||
onResize(){
|
onResize(){
|
||||||
// Update the window Height
|
// Update the window Height
|
||||||
this.updateWidthAndHeightCalcs();
|
this.updateWidthAndHeightCalcs();
|
||||||
|
this.updateImageSizes();
|
||||||
const resumeElement = this.getFirstVisibleElementXPath();
|
const resumeElement = this.getFirstVisibleElementXPath();
|
||||||
if (this.layoutMode !== BookPageLayoutMode.Default && resumeElement !== null && resumeElement !== undefined) {
|
if (this.layoutMode !== BookPageLayoutMode.Default && resumeElement !== null && resumeElement !== undefined) {
|
||||||
this.scrollTo(resumeElement); // This works pretty well, but not perfect
|
this.scrollTo(resumeElement); // This works pretty well, but not perfect
|
||||||
@ -836,7 +837,11 @@ export class BookReaderComponent implements OnInit, AfterViewInit, OnDestroy {
|
|||||||
this.cdRef.markForCheck();
|
this.cdRef.markForCheck();
|
||||||
|
|
||||||
this.bookService.getBookPage(this.chapterId, this.pageNum).pipe(take(1)).subscribe(content => {
|
this.bookService.getBookPage(this.chapterId, this.pageNum).pipe(take(1)).subscribe(content => {
|
||||||
|
this.isSingleImagePage = this.checkSingleImagePage(content) // This needs be performed before we set this.page to avoid image jumping
|
||||||
|
this.updateSingleImagePageStyles()
|
||||||
this.page = this.domSanitizer.bypassSecurityTrustHtml(content); // PERF: Potential optimization to prefetch next/prev page and store in localStorage
|
this.page = this.domSanitizer.bypassSecurityTrustHtml(content); // PERF: Potential optimization to prefetch next/prev page and store in localStorage
|
||||||
|
|
||||||
|
|
||||||
this.cdRef.markForCheck();
|
this.cdRef.markForCheck();
|
||||||
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
@ -854,40 +859,76 @@ export class BookReaderComponent implements OnInit, AfterViewInit, OnDestroy {
|
|||||||
.map(img => new Promise(resolve => { img.onload = img.onerror = resolve; })))
|
.map(img => new Promise(resolve => { img.onload = img.onerror = resolve; })))
|
||||||
.then(() => {
|
.then(() => {
|
||||||
this.setupPage(part, scrollTop);
|
this.setupPage(part, scrollTop);
|
||||||
this.updateImagesWithHeight();
|
this.updateImageSizes();
|
||||||
});
|
});
|
||||||
}, 10);
|
}, 10);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Applies a max-height inline css property on each image in the page if the layout mode is column-based, else it removes the property
|
* Updates the image properties to fit the current layout mode and screen size
|
||||||
*/
|
*/
|
||||||
updateImagesWithHeight() {
|
updateImageSizes() {
|
||||||
const images = this.readingSectionElemRef?.nativeElement.querySelectorAll('img') || [];
|
const isVerticalWritingStyle = this.writingStyle === WritingStyle.Vertical;
|
||||||
let maxHeight: number | undefined;
|
const height = this.windowHeight - (this.topOffset * 2);
|
||||||
|
let maxHeight = 'unset';
|
||||||
|
let maxWidth = '';
|
||||||
|
switch (this.layoutMode) {
|
||||||
|
case BookPageLayoutMode.Default:
|
||||||
|
if (isVerticalWritingStyle) {
|
||||||
|
maxHeight = `${height}px`;
|
||||||
|
} else {
|
||||||
|
maxWidth = `${this.getVerticalPageWidth()}px`;
|
||||||
|
}
|
||||||
|
break
|
||||||
|
|
||||||
if (this.layoutMode !== BookPageLayoutMode.Default && this.writingStyle !== WritingStyle.Vertical) {
|
case BookPageLayoutMode.Column1:
|
||||||
maxHeight = (parseInt(this.ColumnHeight.replace('px', ''), 10) - (this.topOffset * 2));
|
maxHeight = `${height}px`;
|
||||||
} else if (this.layoutMode !== BookPageLayoutMode.Column2 && this.writingStyle === WritingStyle.Vertical) {
|
maxWidth = `${this.getVerticalPageWidth()}px`;
|
||||||
maxHeight = this.getPageHeight() - COLUMN_GAP;
|
break
|
||||||
} else if (this.layoutMode === BookPageLayoutMode.Column2 && this.writingStyle === WritingStyle.Vertical) {
|
|
||||||
maxHeight = this.getPageHeight() / 2 - COLUMN_GAP;
|
case BookPageLayoutMode.Column2:
|
||||||
} else {
|
maxWidth = `${this.getVerticalPageWidth()}px`;
|
||||||
maxHeight = undefined;
|
if (isVerticalWritingStyle && !this.isSingleImagePage) {
|
||||||
|
maxHeight = `${height / 2}px`;
|
||||||
|
} else {
|
||||||
|
maxHeight = `${height}px`;
|
||||||
|
}
|
||||||
|
break
|
||||||
}
|
}
|
||||||
Array.from(images).forEach(img => {
|
this.document.documentElement.style.setProperty('--book-reader-content-max-height', maxHeight);
|
||||||
if (maxHeight === undefined) {
|
this.document.documentElement.style.setProperty('--book-reader-content-max-width', maxWidth);
|
||||||
this.renderer.removeStyle(img, 'max-height');
|
|
||||||
} else if (this.writingStyle === WritingStyle.Horizontal) {
|
}
|
||||||
this.renderer.setStyle(img, 'max-height', `${maxHeight}px`);
|
|
||||||
} else {
|
updateSingleImagePageStyles() {
|
||||||
const aspectRatio = img.width / img.height;
|
if (this.isSingleImagePage && this.layoutMode !== BookPageLayoutMode.Default) {
|
||||||
const pageWidth = this.getVerticalPageWidth()
|
this.document.documentElement.style.setProperty('--book-reader-content-position', 'absolute');
|
||||||
const maxImgHeight = Math.min(maxHeight, pageWidth / aspectRatio);
|
this.document.documentElement.style.setProperty('--book-reader-content-top', '50%');
|
||||||
this.renderer.setStyle(img, 'max-height', `${maxImgHeight}px`);
|
this.document.documentElement.style.setProperty('--book-reader-content-left', '50%');
|
||||||
}
|
this.document.documentElement.style.setProperty('--book-reader-content-transform', 'translate(-50%, -50%)');
|
||||||
});
|
} else {
|
||||||
|
this.document.documentElement.style.setProperty('--book-reader-content-position', '');
|
||||||
|
this.document.documentElement.style.setProperty('--book-reader-content-top', '');
|
||||||
|
this.document.documentElement.style.setProperty('--book-reader-content-left', '');
|
||||||
|
this.document.documentElement.style.setProperty('--book-reader-content-transform', '');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
checkSingleImagePage(content: string) {
|
||||||
|
// Exclude the style element from the HTML content as it messes up innerText
|
||||||
|
const htmlContent = content.replace(/<style>.*<\/style>/s, '');
|
||||||
|
|
||||||
|
const parser = new DOMParser();
|
||||||
|
const doc = parser.parseFromString(htmlContent, 'text/html');
|
||||||
|
const html = doc.querySelector('html');
|
||||||
|
|
||||||
|
if (html?.innerText.trim() !== '') {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const images = doc.querySelectorAll('img, svg');
|
||||||
|
return images.length === 1;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1166,9 +1207,8 @@ export class BookReaderComponent implements OnInit, AfterViewInit, OnDestroy {
|
|||||||
const resumeElement: string | null | undefined = this.getFirstVisibleElementXPath();
|
const resumeElement: string | null | undefined = this.getFirstVisibleElementXPath();
|
||||||
|
|
||||||
// Needs to update the image size when reading mode is vertically
|
// Needs to update the image size when reading mode is vertically
|
||||||
if (this.writingStyle === WritingStyle.Vertical) {
|
this.updateImageSizes();
|
||||||
this.updateImagesWithHeight();
|
|
||||||
}
|
|
||||||
// Line Height must be placed on each element in the page
|
// Line Height must be placed on each element in the page
|
||||||
|
|
||||||
// Apply page level overrides
|
// Apply page level overrides
|
||||||
@ -1335,11 +1375,11 @@ export class BookReaderComponent implements OnInit, AfterViewInit, OnDestroy {
|
|||||||
|
|
||||||
updateWritingStyle(writingStyle: WritingStyle) {
|
updateWritingStyle(writingStyle: WritingStyle) {
|
||||||
this.writingStyle = writingStyle;
|
this.writingStyle = writingStyle;
|
||||||
|
setTimeout(() => this.updateImageSizes());
|
||||||
if (this.layoutMode !== BookPageLayoutMode.Default) {
|
if (this.layoutMode !== BookPageLayoutMode.Default) {
|
||||||
const lastSelector = this.lastSeenScrollPartPath;
|
const lastSelector = this.lastSeenScrollPartPath;
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
this.scrollTo(lastSelector);
|
this.scrollTo(lastSelector);
|
||||||
this.updateLayoutMode(this.layoutMode);
|
|
||||||
});
|
});
|
||||||
} else if (this.bookContentElemRef !== undefined) {
|
} else if (this.bookContentElemRef !== undefined) {
|
||||||
const resumeElement = this.getFirstVisibleElementXPath();
|
const resumeElement = this.getFirstVisibleElementXPath();
|
||||||
@ -1356,8 +1396,13 @@ export class BookReaderComponent implements OnInit, AfterViewInit, OnDestroy {
|
|||||||
const layoutModeChanged = mode !== this.layoutMode;
|
const layoutModeChanged = mode !== this.layoutMode;
|
||||||
this.layoutMode = mode;
|
this.layoutMode = mode;
|
||||||
this.cdRef.markForCheck();
|
this.cdRef.markForCheck();
|
||||||
// Remove any max-heights from column layout
|
|
||||||
this.updateImagesWithHeight();
|
this.clearTimeout(this.updateImageSizeTimeout);
|
||||||
|
this.updateImageSizeTimeout = setTimeout( () => {
|
||||||
|
this.updateImageSizes()
|
||||||
|
}, 200);
|
||||||
|
|
||||||
|
this.updateSingleImagePageStyles()
|
||||||
|
|
||||||
// Calulate if bottom actionbar is needed. On a timeout to get accurate heights
|
// Calulate if bottom actionbar is needed. On a timeout to get accurate heights
|
||||||
if (this.bookContentElemRef == null) {
|
if (this.bookContentElemRef == null) {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user