diff --git a/src/CoreSettings.ts b/src/CoreSettings.ts index a9aa106d8..6b0ca2047 100644 --- a/src/CoreSettings.ts +++ b/src/CoreSettings.ts @@ -36,13 +36,6 @@ export class CoreSettings { */ public tracks: unknown = null; - /** - * Gets or sets the interval in which alphaTab should check whether the - * target element for rendering is already visible. - * @target web - */ - public visibilityCheckInterval: number = 500; - /** * Gets or sets whether lazy loading for displayed elements is enabled. */ diff --git a/src/generated/CoreSettingsSerializer.ts b/src/generated/CoreSettingsSerializer.ts index cfa36d789..d11a6d638 100644 --- a/src/generated/CoreSettingsSerializer.ts +++ b/src/generated/CoreSettingsSerializer.ts @@ -28,8 +28,6 @@ export class CoreSettingsSerializer { o.set("tex", obj.tex); /*@target web*/ o.set("tracks", obj.tracks); - /*@target web*/ - o.set("visibilityCheckInterval", obj.visibilityCheckInterval); o.set("enableLazyLoading", obj.enableLazyLoading); o.set("engine", obj.engine); o.set("logLevel", (obj.logLevel as number)); @@ -59,10 +57,6 @@ export class CoreSettingsSerializer { case "tracks": obj.tracks = (v as unknown); return true; - /*@target web*/ - case "visibilitycheckinterval": - obj.visibilityCheckInterval = (v as number); - return true; case "enablelazyloading": obj.enableLazyLoading = (v as boolean); return true; diff --git a/src/platform/javascript/BrowserUiFacade.ts b/src/platform/javascript/BrowserUiFacade.ts index 089a4c681..0c0e6fb10 100644 --- a/src/platform/javascript/BrowserUiFacade.ts +++ b/src/platform/javascript/BrowserUiFacade.ts @@ -34,13 +34,11 @@ export class BrowserUiFacade implements IUiFacade { private _api!: AlphaTabApiBase; private _contents: string | null = null; private _file: string | null = null; - private _visibilityCheckIntervalId: number = 0; - private _visibilityCheckInterval: number = 0; private _totalResultCount: number = 0; private _initialTrackIndexes: number[] | null = null; + private _intersectionObserver: IntersectionObserver; - private _rootContainerBecameVisible: IEventEmitter = new EventEmitter(); - public rootContainerBecameVisible: IEventEmitter; + public rootContainerBecameVisible: IEventEmitter = new EventEmitter(); public canRenderChanged: IEventEmitter = new EventEmitter(); public get resizeThrottle(): number { @@ -61,7 +59,7 @@ export class BrowserUiFacade implements IUiFacade { } let isAnyNotLoaded = false; - for(const checker of this._fontCheckers.values()) { + for (const checker of this._fontCheckers.values()) { if (!checker.isFontLoaded) { isAnyNotLoaded = true; } @@ -88,29 +86,25 @@ export class BrowserUiFacade implements IUiFacade { this.areWorkersSupported = 'Worker' in window; Environment.bravuraFontChecker.fontLoaded.on(this.onFontLoaded.bind(this)); - this.rootContainerBecameVisible = { - on: (value: any) => { - if (this.rootContainer.isVisible) { - value(); - } else { - this._rootContainerBecameVisible.on(value); - - if (this._visibilityCheckIntervalId === 0) { - this._visibilityCheckIntervalId = window.setInterval(() => { - if (this._api.container.isVisible) { - window.clearInterval(this._visibilityCheckIntervalId); - this._visibilityCheckIntervalId = 0; - (this._rootContainerBecameVisible as EventEmitter).trigger(); - } - }, this._visibilityCheckInterval); - } + this._intersectionObserver = new IntersectionObserver(this.onElementVisibilityChanged.bind(this), { + threshold: [0, 0.01, 1] + }); + this._intersectionObserver.observe(rootElement); + } + + private onElementVisibilityChanged(entries: IntersectionObserverEntry[]) { + for (const e of entries) { + if (e.isIntersecting) { + const htmlElement = e.target as HTMLElement; + if (htmlElement === (this.rootContainer as HtmlElementContainer).element) { + (this.rootContainerBecameVisible as EventEmitter).trigger(); + this._intersectionObserver.unobserve((this.rootContainer as HtmlElementContainer).element); + } else if ('svg' in htmlElement.dataset) { + this.replacePlaceholder(htmlElement, htmlElement.dataset['svg'] as string); + this._intersectionObserver.unobserve(htmlElement); } - }, - - off: (value: any) => { - this._rootContainerBecameVisible.off(value); } - }; + } } public createWorkerRenderer(): IScoreRenderer { @@ -132,10 +126,6 @@ export class BrowserUiFacade implements IUiFacade { settings.setSongBookModeSettings(); } api.settings = settings; - if (settings.core.engine === 'default' || settings.core.engine === 'svg') { - api.container.scroll.on(this.showSvgsInViewPort.bind(this)); - api.container.resize.on(this.showSvgsInViewPort.bind(this)); - } this.setupFontCheckers(settings); this._initialTrackIndexes = this.parseTracks(settings.core.tracks); @@ -147,8 +137,6 @@ export class BrowserUiFacade implements IUiFacade { } this.createStyleElement(settings); this._file = settings.core.file; - - this._visibilityCheckInterval = settings.core.visibilityCheckInterval; } private setupFontCheckers(settings: Settings): void { @@ -284,28 +272,6 @@ export class BrowserUiFacade implements IUiFacade { }); } - private showSvgsInViewPort(): void { - let placeholders: NodeList = (this._api.canvasElement as HtmlElementContainer).element.querySelectorAll( - '[data-lazy=true]' - ); - for (let i: number = 0; i < placeholders.length; i++) { - let placeholder: HTMLElement = placeholders.item(i) as HTMLElement; - if (this.isElementInViewPort(placeholder)) { - this.replacePlaceholder(placeholder, (placeholder as any)['svg']); - } - } - } - - public isElementInViewPort(element: HTMLElement): boolean { - let rect: DOMRect = element.getBoundingClientRect(); - return ( - rect.top + rect.height >= 0 && - rect.top <= window.innerHeight && - rect.left + rect.width >= 0 && - rect.left <= window.innerWidth - ); - } - private createStyleElement(settings: Settings): void { let elementDocument: HTMLDocument = (this._api.container as HtmlElementContainer).element.ownerDocument!; Environment.createStyleElement(elementDocument, settings.core.fontDirectory); @@ -402,10 +368,6 @@ export class BrowserUiFacade implements IUiFacade { while (canvasElement.childElementCount > this._totalResultCount) { canvasElement.removeChild(canvasElement.lastChild!); } - // directly show the elements in the viewport once we're done. - if (this._api.settings.core.enableLazyLoading) { - this.showSvgsInViewPort(); - } } else { let body: unknown = renderResult.renderResult; if (typeof body === 'string') { @@ -419,11 +381,11 @@ export class BrowserUiFacade implements IUiFacade { placeholder.style.width = renderResult.width + 'px'; placeholder.style.height = renderResult.height + 'px'; placeholder.style.display = 'inline-block'; - if (!this._api.settings.core.enableLazyLoading || this.isElementInViewPort(placeholder)) { + if (!this._api.settings.core.enableLazyLoading) { this.replacePlaceholder(placeholder, body); } else { - (placeholder as any)['svg'] = body; - placeholder.setAttribute('data-lazy', 'true'); + placeholder.dataset['svg'] = body; + this._intersectionObserver.observe(placeholder); } } else { if (this._totalResultCount < canvasElement.childElementCount) { diff --git a/test/model/JsonConverter.test.ts b/test/model/JsonConverter.test.ts index fd856b02a..6b212b7a5 100644 --- a/test/model/JsonConverter.test.ts +++ b/test/model/JsonConverter.test.ts @@ -94,8 +94,6 @@ describe('JsonConverterTest', () => { expected.core.tex = true; /**@target web*/ expected.core.tracks = [1, 2, 3]; - /**@target web*/ - expected.core.visibilityCheckInterval = 4711; expected.core.enableLazyLoading = false; expected.core.engine = "engine";