MOBILE-3814 core: Use waitToBeInViewport in onAppear directive

main
Pau Ferrer Ocaña 2022-03-17 12:50:13 +01:00
parent e5d748f5e5
commit 4dcceee3e2
2 changed files with 96 additions and 2 deletions

View File

@ -37,7 +37,7 @@ export class CoreOnAppearDirective implements OnInit, OnDestroy {
* @inheritdoc
*/
async ngOnInit(): Promise<void> {
this.visiblePromise = CoreDomUtils.waitToBeVisible(this.element);
this.visiblePromise = CoreDomUtils.waitToBeInViewport(this.element);
await this.visiblePromise;

View File

@ -141,6 +141,7 @@ export class CoreDomUtilsProvider {
let interval: number | undefined;
// Mutations did not observe for visibility properties.
return new CoreCancellablePromise<void>(
async (resolve) => {
await domPromise;
@ -165,6 +166,60 @@ export class CoreDomUtilsProvider {
);
}
/**
* Wait an element to be in dom and visible.
*
* @param element Element to wait.
* @param intersectionRatio Intersection ratio (From 0 to 1).
* @return Cancellable promise.
*/
waitToBeInViewport(element: HTMLElement, intersectionRatio = 1): CoreCancellablePromise<void> {
const visiblePromise = CoreDomUtils.waitToBeVisible(element);
let intersectionObserver: IntersectionObserver;
let interval: number | undefined;
return new CoreCancellablePromise<void>(
async (resolve) => {
await visiblePromise;
if (CoreDomUtils.isElementInViewport(element, intersectionRatio)) {
return resolve();
}
if ('IntersectionObserver' in window) {
intersectionObserver = new IntersectionObserver((observerEntries) => {
const isIntersecting = observerEntries
.some((entry) => entry.isIntersecting && entry.intersectionRatio >= intersectionRatio);
if (!isIntersecting) {
return;
}
resolve();
intersectionObserver?.disconnect();
});
intersectionObserver.observe(element);
} else {
interval = window.setInterval(() => {
if (!CoreDomUtils.isElementInViewport(element, intersectionRatio)) {
return;
}
resolve();
window.clearInterval(interval);
}, 50);
}
},
() => {
visiblePromise.cancel();
intersectionObserver?.disconnect();
window.clearInterval(interval);
},
);
}
/**
* Window resize is widely checked and may have many performance issues, debouce usage is needed to avoid calling it too much.
* This function helps setting up the debounce feature and remove listener easily.
@ -870,10 +925,21 @@ export class CoreDomUtilsProvider {
return elementPoint > window.innerHeight || elementPoint < scrollTopPos;
}
/**
* Check whether an element has been added to the DOM.
*
* @param element Element.
* @return True if element has been added to the DOM, false otherwise.
*/
isElementInDom(element: HTMLElement): boolean {
return element.getRootNode({ composed: true }) === document;
}
/**
* Check whether an element is visible or not.
*
* @param element Element.
* @return True if element is visible inside the DOM.
*/
isElementVisible(element: HTMLElement): boolean {
if (element.clientWidth === 0 || element.clientHeight === 0) {
@ -885,7 +951,35 @@ export class CoreDomUtilsProvider {
return false;
}
return element.offsetParent !== null;
return CoreDomUtils.isElementInDom(element);
}
/**
* Check whether an element is intersecting the intersectionRatio in viewport.
*
* @param element
* @param intersectionRatio Intersection ratio (From 0 to 1).
* @return True if in viewport.
*/
isElementInViewport(element: HTMLElement, intersectionRatio = 1): boolean {
const elementRectangle = element.getBoundingClientRect();
const elementArea = elementRectangle.width * elementRectangle.height;
if (elementArea == 0) {
return false;
}
const intersectionRectangle = {
top: Math.max(0, elementRectangle.top),
left: Math.max(0, elementRectangle.left),
bottom: Math.min(window.innerHeight, elementRectangle.bottom),
right: Math.min(window.innerWidth, elementRectangle.right),
};
const intersectionArea = (intersectionRectangle.right - intersectionRectangle.left) *
(intersectionRectangle.bottom - intersectionRectangle.top);
return intersectionArea / elementArea >= intersectionRatio;
}
/**