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:
parent
93e5e0a68f
commit
50429777d0
7 changed files with 219 additions and 129 deletions
|
|
@ -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);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue