Replace js by ts

This commit is contained in:
2026-04-01 01:27:51 +02:00
parent 4613b8e5dd
commit 6306c73f26
291 changed files with 501210 additions and 525 deletions

View File

@@ -0,0 +1,236 @@
/**
* visibility-manager.js — Advanced visibility management with scroll prediction
*
* Uses IntersectionObserver for accurate page visibility detection
* Implements scroll prediction for smooth pre-loading
* Manages buffer zones intelligently
*/
class VisibilityManager {
constructor(container, pageElements, totalPages) {
this.container = container;
this.pageElements = pageElements;
this.totalPages = totalPages;
// Visibility state
this.visiblePages = new Set();
this.bufferPages = new Set();
this.observer = null;
this.bufferSize = 2; // Minimum pages to buffer
this.scrollHistory = [];
this.maxScrollHistory = 10;
// Scroll prediction
this.scrollVelocity = 0;
this.scrollDirection = 0; // 1 for down, -1 for up, 0 for stationary
this.lastScrollTime = 0;
this.lastScrollPosition = 0;
// Performance tracking
this.visibilityChanges = 0;
this.lastVisibilityTime = 0;
// Setup observers
this.setupIntersectionObserver();
this.setupScrollMonitoring();
}
setupIntersectionObserver() {
this.observer = new IntersectionObserver(
(entries) => this.handleIntersectionEntries(entries),
{
root: this.container,
rootMargin: '100% 0px 100% 0px', // Large margin for extensive buffering
threshold: [0, 0.25, 0.5, 0.75, 1.0] // Multiple thresholds for smooth transitions
}
);
// Observe all page elements
this.pageElements.forEach(element => {
this.observer.observe(element);
});
}
setupScrollMonitoring() {
// Track scroll events for velocity calculation
this.container.addEventListener('scroll', (event) => {
this.trackScrollEvent(event);
}, { passive: true });
// Periodic scroll prediction
setInterval(() => {
this.updateScrollPrediction();
}, 100);
}
trackScrollEvent(event) {
const currentTime = performance.now();
const currentPosition = this.container.scrollTop;
// Calculate velocity if we have previous data
if (this.lastScrollPosition !== 0 && this.lastScrollTime !== 0) {
const timeDelta = currentTime - this.lastScrollTime;
if (timeDelta > 0) {
this.scrollVelocity = (currentPosition - this.lastScrollPosition) / timeDelta;
this.scrollDirection = this.scrollVelocity > 0 ? 1 : (this.scrollVelocity < 0 ? -1 : 0);
}
}
// Store scroll history
this.scrollHistory.push({
position: currentPosition,
time: currentTime,
velocity: this.scrollVelocity
});
if (this.scrollHistory.length > this.maxScrollHistory) {
this.scrollHistory.shift();
}
this.lastScrollPosition = currentPosition;
this.lastScrollTime = currentTime;
}
updateScrollPrediction() {
if (this.scrollHistory.length < 3) return;
// Calculate average velocity from recent history
const recentHistory = this.scrollHistory.slice(-5); // Last 5 entries
if (recentHistory.length < 2) return;
const totalDelta = recentHistory[recentHistory.length - 1].position -
recentHistory[0].position;
const totalTime = recentHistory[recentHistory.length - 1].time -
recentHistory[0].time;
if (totalTime > 0) {
this.scrollVelocity = totalDelta / totalTime;
this.scrollDirection = this.scrollVelocity > 5 ? 1 :
(this.scrollVelocity < -5 ? -1 : 0);
}
}
handleIntersectionEntries(entries) {
const newVisiblePages = new Set();
const newBufferPages = new Set();
// Process intersection entries
entries.forEach(entry => {
const pageNum = parseInt(entry.target.dataset.page);
if (isNaN(pageNum)) return;
if (entry.isIntersecting) {
// Page is visible or in buffer zone
if (entry.intersectionRatio >= 0.5) {
newVisiblePages.add(pageNum);
} else {
newBufferPages.add(pageNum);
}
}
});
// Add intelligent buffering based on scroll prediction
this.addPredictiveBuffering(newVisiblePages, newBufferPages);
// Update state if changed
if (!this.setsEqual(this.visiblePages, newVisiblePages) ||
!this.setsEqual(this.bufferPages, newBufferPages)) {
this.visiblePages = newVisiblePages;
this.bufferPages = newBufferPages;
this.visibilityChanges++;
// Notify about visibility changes
this.notifyVisibilityChange();
}
}
addPredictiveBuffering(visiblePages, bufferPages) {
if (visiblePages.size === 0) return;
const visibleArray = Array.from(visiblePages).sort((a, b) => a - b);
const firstVisible = visibleArray[0];
const lastVisible = visibleArray[visibleArray.length - 1];
// Add adjacent pages (minimum buffer)
for (let i = 1; i <= this.bufferSize; i++) {
if (firstVisible - i >= 1) bufferPages.add(firstVisible - i);
if (lastVisible + i <= this.totalPages) bufferPages.add(lastVisible + i);
}
// Add scroll prediction-based buffering
if (Math.abs(this.scrollVelocity) > 10) { // Significant scrolling
const predictionDistance = Math.min(5, Math.floor(Math.abs(this.scrollVelocity) / 2));
if (this.scrollDirection === 1) { // Scrolling down
// Add more pages below
for (let i = 1; i <= predictionDistance; i++) {
const nextPage = lastVisible + this.bufferSize + i;
if (nextPage <= this.totalPages) {
bufferPages.add(nextPage);
}
}
} else if (this.scrollDirection === -1) { // Scrolling up
// Add more pages above
for (let i = 1; i <= predictionDistance; i++) {
const prevPage = firstVisible - this.bufferSize - i;
if (prevPage >= 1) {
bufferPages.add(prevPage);
}
}
}
}
}
setsEqual(a, b) {
if (a.size !== b.size) return false;
return Array.from(a).every(item => b.has(item));
}
notifyVisibilityChange() {
// Throttle visibility notifications to prevent excessive rendering
const now = performance.now();
if (now - this.lastVisibilityTime < 50) return; // Max 20 updates per second
this.lastVisibilityTime = now;
// Notify the render system
if (window.renderSystem) {
window.renderSystem.setVisibility(
Array.from(this.visiblePages),
Array.from(this.bufferPages)
);
}
}
getScrollPrediction() {
return {
velocity: this.scrollVelocity,
direction: this.scrollDirection,
confidence: Math.min(1.0, Math.abs(this.scrollVelocity) / 20)
};
}
cleanup() {
if (this.observer) {
this.observer.disconnect();
}
this.visiblePages.clear();
this.bufferPages.clear();
this.scrollHistory = [];
}
getVisibilityStats() {
return {
visiblePages: Array.from(this.visiblePages).sort((a, b) => a - b),
bufferPages: Array.from(this.bufferPages).sort((a, b) => a - b),
visibilityChanges: this.visibilityChanges,
scrollVelocity: this.scrollVelocity,
scrollDirection: this.scrollDirection
};
}
}
// Export for ES6 modules
export { VisibilityManager };