[Perf] useSharedIntersectionObserver: Consider heavy animation
This commit is contained in:
parent
7634094883
commit
94a9f7eba8
@ -1,9 +1,9 @@
|
|||||||
import { useEffect } from '../lib/teact/teact';
|
import { getIsHeavyAnimating, useEffect } from '../lib/teact/teact';
|
||||||
|
|
||||||
import type { CallbackManager } from '../util/callbacks';
|
import type { CallbackManager } from '../util/callbacks';
|
||||||
|
|
||||||
import { createCallbackManager } from '../util/callbacks';
|
import { createCallbackManager } from '../util/callbacks';
|
||||||
import { useStateRef } from './useStateRef';
|
import useLastCallback from './useLastCallback';
|
||||||
|
|
||||||
const elementObserverMap = new Map<HTMLElement, [IntersectionObserver, CallbackManager]>();
|
const elementObserverMap = new Map<HTMLElement, [IntersectionObserver, CallbackManager]>();
|
||||||
|
|
||||||
@ -12,7 +12,7 @@ export default function useSharedIntersectionObserver(
|
|||||||
onIntersectionChange: (entry: IntersectionObserverEntry) => void,
|
onIntersectionChange: (entry: IntersectionObserverEntry) => void,
|
||||||
isDisabled = false,
|
isDisabled = false,
|
||||||
) {
|
) {
|
||||||
const onIntersectionChangeRef = useStateRef(onIntersectionChange);
|
const onIntersectionChangeLast = useLastCallback(onIntersectionChange);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const el = refOrElement && 'current' in refOrElement ? refOrElement.current : refOrElement;
|
const el = refOrElement && 'current' in refOrElement ? refOrElement.current : refOrElement;
|
||||||
@ -20,13 +20,29 @@ export default function useSharedIntersectionObserver(
|
|||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
const callback: IntersectionObserverCallback = ([entry]) => {
|
const entriesAccumulator = new Map<Element, IntersectionObserverEntry>();
|
||||||
// Ignore updates when element is not properly mounted (`display: none`)
|
|
||||||
if (!(entry.target as HTMLElement).offsetWidth || !(entry.target as HTMLElement).offsetHeight) {
|
function flush() {
|
||||||
return;
|
for (const entry of entriesAccumulator.values()) {
|
||||||
|
// Ignore updates when element is not properly mounted (`display: none`)
|
||||||
|
if (!(entry.target as HTMLElement).offsetParent) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
onIntersectionChangeLast(entry);
|
||||||
}
|
}
|
||||||
|
|
||||||
onIntersectionChangeRef.current(entry);
|
entriesAccumulator.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
const callback: IntersectionObserverCallback = ([entry]) => {
|
||||||
|
entriesAccumulator.set(entry.target, entry);
|
||||||
|
|
||||||
|
if (!getIsHeavyAnimating()) {
|
||||||
|
flush();
|
||||||
|
} else {
|
||||||
|
getIsHeavyAnimating.once(flush);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let [observer, callbackManager] = elementObserverMap.get(el) || [undefined, undefined];
|
let [observer, callbackManager] = elementObserverMap.get(el) || [undefined, undefined];
|
||||||
@ -46,5 +62,5 @@ export default function useSharedIntersectionObserver(
|
|||||||
elementObserverMap.delete(el);
|
elementObserverMap.delete(el);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}, [isDisabled, onIntersectionChangeRef, refOrElement]);
|
}, [isDisabled, refOrElement]);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -12,6 +12,7 @@ const SIGNAL_MARK = Symbol('SIGNAL_MARK');
|
|||||||
export type Signal<T = any> = ((() => T) & {
|
export type Signal<T = any> = ((() => T) & {
|
||||||
readonly [SIGNAL_MARK]: symbol;
|
readonly [SIGNAL_MARK]: symbol;
|
||||||
subscribe: (cb: AnyToVoidFunction) => NoneToVoidFunction;
|
subscribe: (cb: AnyToVoidFunction) => NoneToVoidFunction;
|
||||||
|
once: (cb: AnyToVoidFunction) => NoneToVoidFunction;
|
||||||
});
|
});
|
||||||
|
|
||||||
export type SignalSetter = (newValue: any) => void;
|
export type SignalSetter = (newValue: any) => void;
|
||||||
@ -51,6 +52,15 @@ export function createSignal<T>(defaultValue?: T) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function once(effect: NoneToVoidFunction) {
|
||||||
|
const unsub = subscribe(() => {
|
||||||
|
unsub();
|
||||||
|
effect();
|
||||||
|
});
|
||||||
|
|
||||||
|
return unsub;
|
||||||
|
}
|
||||||
|
|
||||||
function getter() {
|
function getter() {
|
||||||
if (currentEffect) {
|
if (currentEffect) {
|
||||||
subscribe(currentEffect);
|
subscribe(currentEffect);
|
||||||
@ -71,6 +81,7 @@ export function createSignal<T>(defaultValue?: T) {
|
|||||||
const signal = Object.assign(getter as Signal<T>, {
|
const signal = Object.assign(getter as Signal<T>, {
|
||||||
[SIGNAL_MARK]: SIGNAL_MARK,
|
[SIGNAL_MARK]: SIGNAL_MARK,
|
||||||
subscribe,
|
subscribe,
|
||||||
|
once,
|
||||||
});
|
});
|
||||||
|
|
||||||
return [signal, setter] as const;
|
return [signal, setter] as const;
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user