Webtoon Polish (#734)

* Removed some code for a feature I'm not taking further

* Webtoon reader will now use fit to width instead of original to prevent overflow for high res images

* Added an effect when a user bookmarks an image in the reader

* Split the prefetching from the current page calculation code. Dynamically set width on images if they are wider than device screen.

* Dragging on the slider now shows the value on tooltip

* Slider now updates for non-webtoon reader mode when you drag it.

* Fixed a bunch of package security issues

* Updated dependencies for security

* Tweaked the current page check to use top 25% of screen to trigger the page change.
This commit is contained in:
Joseph Milazzo 2021-11-08 11:34:06 -06:00 committed by GitHub
parent 93e5e0a68f
commit 50429777d0
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 219 additions and 129 deletions

View file

@ -2,6 +2,7 @@ import { Component, EventEmitter, Input, OnChanges, OnDestroy, OnInit, Output, R
import { ToastrService } from 'ngx-toastr';
import { BehaviorSubject, fromEvent, ReplaySubject, Subject } from 'rxjs';
import { debounceTime, takeUntil } from 'rxjs/operators';
import { UtilityService } from 'src/app/shared/_services/utility.service';
import { ReaderService } from '../../_services/reader.service';
import { PAGING_DIRECTION } from '../_models/reader-enums';
import { WebtoonImage } from '../_models/webtoon-image';
@ -63,6 +64,7 @@ export class InfiniteScrollerComponent implements OnInit, OnChanges, OnDestroy {
@Output() loadPrevChapter: EventEmitter<void> = new EventEmitter<void>();
@Input() goToPage: ReplaySubject<number> = new ReplaySubject<number>();
@Input() bookmarkPage: ReplaySubject<number> = new ReplaySubject<number>();
/**
* Stores and emits all the src urls
@ -117,7 +119,7 @@ export class InfiniteScrollerComponent implements OnInit, OnChanges, OnDestroy {
/**
* Debug mode. Will show extra information. Use bitwise (|) operators between different modes to enable different output
*/
debugMode: DEBUG_MODES = DEBUG_MODES.None;
debugMode: DEBUG_MODES = DEBUG_MODES.Outline;
get minPageLoaded() {
return Math.min(...Object.values(this.imagesLoaded));
@ -127,12 +129,16 @@ export class InfiniteScrollerComponent implements OnInit, OnChanges, OnDestroy {
return Math.max(...Object.values(this.imagesLoaded));
}
get areImagesWiderThanWindow() {
return this.webtoonImageWidth > (window.innerWidth || document.documentElement.clientWidth);
}
private readonly onDestroy = new Subject<void>();
constructor(private readerService: ReaderService, private renderer: Renderer2, private toastr: ToastrService) {}
constructor(private readerService: ReaderService, private renderer: Renderer2, private utilityService: UtilityService) {}
ngOnChanges(changes: SimpleChanges): void {
if (changes.hasOwnProperty('totalPages') && changes['totalPages'].previousValue != changes['totalPages'].currentValue) {
@ -167,6 +173,18 @@ export class InfiniteScrollerComponent implements OnInit, OnChanges, OnDestroy {
this.setPageNum(page, true);
});
}
if (this.bookmarkPage) {
this.bookmarkPage.pipe(takeUntil(this.onDestroy)).subscribe(page => {
const image = document.querySelector('img[id^="page-' + page + '"]');
if (image) {
this.renderer.addClass(image, 'bookmark-effect');
setTimeout(() => {
this.renderer.removeClass(image, 'bookmark-effect');
}, 1000);
}
});
}
}
/**
@ -191,6 +209,15 @@ export class InfiniteScrollerComponent implements OnInit, OnChanges, OnDestroy {
}
this.prevScrollPosition = verticalOffset;
// Use offset of the image against the scroll container to test if the most of the image is visible on the screen. We can use this
// to mark the current page and separate the prefetching code.
const midlineImages = Array.from(document.querySelectorAll('img[id^="page-"]'))
.filter(entry => this.shouldElementCountAsCurrentPage(entry));
if (midlineImages.length > 0) {
this.setPageNum(parseInt(midlineImages[0].getAttribute('page') || this.pageNum + '', 10));
}
// Check if we hit the last page
this.checkIfShouldTriggerContinuousReader();
@ -224,7 +251,7 @@ export class InfiniteScrollerComponent implements OnInit, OnChanges, OnDestroy {
this.atBottom = true;
this.setPageNum(this.totalPages);
// Scroll user back to original location
this.previousScrollHeightMinusTop = document.documentElement.scrollTop;
this.previousScrollHeightMinusTop = this.getScrollTop();
requestAnimationFrame(() => document.documentElement.scrollTop = this.previousScrollHeightMinusTop + (SPACER_SCROLL_INTO_PX / 2));
} else if (totalScroll >= totalHeight + SPACER_SCROLL_INTO_PX && this.atBottom) {
// This if statement will fire once we scroll into the spacer at all
@ -266,6 +293,41 @@ export class InfiniteScrollerComponent implements OnInit, OnChanges, OnDestroy {
);
}
/**
* Is any part of the element visible in the scrollport and is it above the midline trigger.
* The midline trigger does not mean it is half of the screen. It may be top 25%.
* @param elem HTML Element
* @returns If above midline
*/
shouldElementCountAsCurrentPage(elem: Element) {
if (elem === null || elem === undefined) { return false; }
var rect = elem.getBoundingClientRect();
if (rect.bottom >= 0 &&
rect.right >= 0 &&
rect.top <= (window.innerHeight || document.documentElement.clientHeight) &&
rect.left <= (window.innerWidth || document.documentElement.clientWidth)
) {
const topX = (window.innerHeight || document.documentElement.clientHeight);
const bottomX = this.getScrollTop();
// Check if the image is mostly above the cuttoff point
const cuttoffPoint = bottomX - ((bottomX - topX) / 2);
// without this, it will only trigger once you get the top of the image to the top of the screen: && rect.bottom > cuttoffPoint
// with it, it will trigger at half way
//console.log('Cutoff point: ', cuttoffPoint);
//return rect.top <= cuttoffPoint ; // && rect.bottom > cuttoffPoint
// console.log('device height: ', topX);
// console.log('image ' + elem.getAttribute('page') + ' top: ', rect.top);
// console.log('amount scrolled down: ', rect.top / topX);
// console.log('cutoff point: ', cuttoffPoint);
return Math.abs(rect.top / topX) <= 0.25;
}
return false;
}
initWebtoonReader() {
this.imagesLoaded = {};
@ -327,7 +389,9 @@ export class InfiniteScrollerComponent implements OnInit, OnChanges, OnDestroy {
this.debugLog('[Intersection] Page ' + imagePage + ' is visible: ', entry.isIntersecting);
if (entry.isIntersecting) {
this.debugLog('[Intersection] ! Page ' + imagePage + ' just entered screen');
this.setPageNum(imagePage);
//this.setPageNum(imagePage);
// ?! Changing this so that this just triggers a prefetch
this.prefetchWebtoonImages(imagePage);
}
});
}
@ -414,19 +478,23 @@ export class InfiniteScrollerComponent implements OnInit, OnChanges, OnDestroy {
}
}
calculatePrefetchIndecies() {
calculatePrefetchIndecies(pageNum: number = -1) {
if (pageNum == -1) {
pageNum = this.pageNum;
}
let startingIndex = 0;
let endingIndex = 0;
if (this.isScrollingForwards()) {
startingIndex = Math.min(Math.max(this.pageNum - this.bufferPages, 0), this.totalPages);
endingIndex = Math.min(Math.max(this.pageNum + this.bufferPages, 0), this.totalPages);
startingIndex = Math.min(Math.max(pageNum - this.bufferPages, 0), this.totalPages);
endingIndex = Math.min(Math.max(pageNum + this.bufferPages, 0), this.totalPages);
if (startingIndex === this.totalPages) {
return [0, 0];
}
} else {
startingIndex = Math.min(Math.max(this.pageNum - this.bufferPages, 0), this.totalPages);
endingIndex = Math.min(Math.max(this.pageNum + this.bufferPages, 0), this.totalPages);
startingIndex = Math.min(Math.max(pageNum - this.bufferPages, 0), this.totalPages);
endingIndex = Math.min(Math.max(pageNum + this.bufferPages, 0), this.totalPages);
}
@ -443,8 +511,13 @@ export class InfiniteScrollerComponent implements OnInit, OnChanges, OnDestroy {
return [...Array(size).keys()].map(i => i + startAt);
}
prefetchWebtoonImages() {
const [startingIndex, endingIndex] = this.calculatePrefetchIndecies();
prefetchWebtoonImages(pageNum: number = -1) {
if (pageNum === -1) {
pageNum = this.pageNum;
}
const [startingIndex, endingIndex] = this.calculatePrefetchIndecies(pageNum);
if (startingIndex === 0 && endingIndex === 0) { return; }
this.debugLog('\t[PREFETCH] prefetching pages: ' + startingIndex + ' to ' + endingIndex);