expo-popcore-old/components/sker/video-player/useSharedVisibility.ts

91 lines
2.4 KiB
TypeScript

import { useEffect, useRef, useState } from 'react';
type VisibilityCallback = (isVisible: boolean) => void;
class SharedObserver {
private observer: IntersectionObserver | null = null;
private callbacks = new Map<Element, VisibilityCallback>();
private threshold: number;
constructor(threshold: number) {
this.threshold = threshold;
}
observe(element: Element, callback: VisibilityCallback) {
if (!this.observer) {
this.observer = new IntersectionObserver(
(entries) => {
entries.forEach((entry) => {
const cb = this.callbacks.get(entry.target);
cb?.(entry.isIntersecting && entry.intersectionRatio >= this.threshold);
});
},
{ threshold: this.threshold }
);
}
this.callbacks.set(element, callback);
this.observer.observe(element);
}
unobserve(element: Element) {
this.callbacks.delete(element);
this.observer?.unobserve(element);
if (this.callbacks.size === 0) {
this.observer?.disconnect();
this.observer = null;
}
}
}
const observers = new Map<number, SharedObserver>();
function getSharedObserver(threshold: number): SharedObserver {
const key = Math.round(threshold * 100);
if (!observers.has(key)) {
observers.set(key, new SharedObserver(threshold));
}
return observers.get(key)!;
}
export function useSharedVisibility(threshold: number, dwellTime: number) {
const [isVisible, setIsVisible] = useState(false);
const [shouldPlay, setShouldPlay] = useState(false);
const ref = useRef<HTMLDivElement>(null);
const timerRef = useRef<ReturnType<typeof setTimeout> | undefined>(undefined);
useEffect(() => {
const element = ref.current;
if (!element) return;
const observer = getSharedObserver(threshold);
const handleVisibilityChange = (visible: boolean) => {
setIsVisible(visible);
if (visible) {
timerRef.current = setTimeout(() => {
setShouldPlay(true);
}, dwellTime);
} else {
if (timerRef.current !== undefined) {
clearTimeout(timerRef.current);
}
setShouldPlay(false);
}
};
observer.observe(element, handleVisibilityChange);
return () => {
if (timerRef.current !== undefined) {
clearTimeout(timerRef.current);
}
observer.unobserve(element);
};
}, [threshold, dwellTime]);
return { ref, isVisible, shouldPlay };
}