From c84b4cf23452688e5205e41558a8b286fc12dfed Mon Sep 17 00:00:00 2001 From: Spedon <70063177+Sped0n@users.noreply.github.com> Date: Sun, 11 Feb 2024 14:22:48 +0800 Subject: [PATCH] refactor: better event listener cleanup (#279) * refactor: change hires loader function name * feat: add loading transition animation and improve performance * refactor: refactor gallery creation and update functions * feat: create createDivWithClass utility function * feat: refactor abort signal handling in event listener and promise chain - Add functionality to set up an abort controller for cleanup - Add an event listener to abort the controller when necessary - Modify event listener to include the abort signal - Modify promise chain to include the abort signal --- assets/ts/desktop/stage.ts | 14 +++- assets/ts/globalUtils.ts | 7 ++ assets/ts/mobile/gallery.ts | 143 ++++++++++++++++++------------------ 3 files changed, 89 insertions(+), 75 deletions(-) diff --git a/assets/ts/desktop/stage.ts b/assets/ts/desktop/stage.ts index f96df68..467c92e 100644 --- a/assets/ts/desktop/stage.ts +++ b/assets/ts/desktop/stage.ts @@ -344,6 +344,10 @@ function lores(imgs: DesktopImage[]): void { function setLoaderForHiresImage(e: HTMLImageElement): void { if (!e.complete) { isLoading.set(true) + // abort controller for cleanup + const controller = new AbortController() + const abortSignal = controller.signal + // event listeners e.addEventListener( 'load', () => { @@ -355,8 +359,11 @@ function setLoaderForHiresImage(e: HTMLImageElement): void { .catch((e) => { console.log(e) }) + .finally(() => { + controller.abort() + }) }, - { once: true, passive: true } + { once: true, passive: true, signal: abortSignal } ) e.addEventListener( 'error', @@ -369,8 +376,11 @@ function setLoaderForHiresImage(e: HTMLImageElement): void { .catch((e) => { console.log(e) }) + .finally(() => { + controller.abort() + }) }, - { once: true, passive: true } + { once: true, passive: true, signal: abortSignal } ) } else { _gsap diff --git a/assets/ts/globalUtils.ts b/assets/ts/globalUtils.ts index 6fa22ec..8eec775 100644 --- a/assets/ts/globalUtils.ts +++ b/assets/ts/globalUtils.ts @@ -32,6 +32,13 @@ export function removeDuplicates(arr: T[]): T[] { return [...new Set(arr)] } +export function createDivWithClass(className: string): HTMLDivElement { + const div = document.createElement('div') + if (className === '') return div // optimization + div.classList.add(className) + return div +} + /** * custom "reactive" object */ diff --git a/assets/ts/mobile/gallery.ts b/assets/ts/mobile/gallery.ts index b2c4552..d971917 100644 --- a/assets/ts/mobile/gallery.ts +++ b/assets/ts/mobile/gallery.ts @@ -3,7 +3,7 @@ import { type Swiper } from 'swiper' import { container, scrollable } from '../container' import { isAnimating, navigateVector, setIndex, state } from '../globalState' -import { expand, loadGsap, removeDuplicates } from '../globalUtils' +import { createDivWithClass, expand, loadGsap, removeDuplicates } from '../globalUtils' import { type ImageJSON } from '../resources' import { mounted } from './state' @@ -14,9 +14,11 @@ import { capitalizeFirstLetter, loadSwiper, type MobileImage } from './utils' * variables */ -let swiperNode: HTMLDivElement +let galleryInner: HTMLDivElement let gallery: HTMLDivElement let curtain: HTMLDivElement +let indexDiv: HTMLDivElement +let navDiv: HTMLDivElement let indexDispNums: HTMLSpanElement[] = [] let galleryImages: MobileImage[] = [] let collectionImages: MobileImage[] = [] @@ -89,14 +91,11 @@ function slideDown(): void { export function initGallery(ijs: ImageJSON[]): void { // create gallery - createGallery(ijs) + constructGallery(ijs) // get elements indexDispNums = Array.from( - document.getElementsByClassName('nav').item(0)?.getElementsByClassName('num') ?? [] + indexDiv.getElementsByClassName('num') ?? [] ) as HTMLSpanElement[] - swiperNode = document.getElementsByClassName('galleryInner').item(0) as HTMLDivElement - gallery = document.getElementsByClassName('gallery').item(0) as HTMLDivElement - curtain = document.getElementsByClassName('curtain').item(0) as HTMLDivElement galleryImages = Array.from(gallery.getElementsByTagName('img')) as MobileImage[] collectionImages = Array.from( document @@ -137,7 +136,7 @@ export function initGallery(ijs: ImageJSON[]): void { }) loadSwiper() .then((S) => { - _swiper = new S(swiperNode, { spaceBetween: 20 }) + _swiper = new S(galleryInner, { spaceBetween: 20 }) _swiper.on('slideChange', ({ realIndex }) => { setIndex(realIndex) }) @@ -204,32 +203,47 @@ function galleryLoadImages(): void { }) } -function createGallery(ijs: ImageJSON[]): void { - /** - * gallery - * |- galleryInner - * |- swiper-wrapper - * |- swiper-slide - * |- img - * |- swiper-slide - * |- img - * |- ... - * |- nav - * |- index - * |- close - */ +function constructGalleryNav(): void { + // index + indexDiv = document.createElement('div') + indexDiv.insertAdjacentHTML( + 'afterbegin', + ` + / + ` + ) + // close + const closeDiv = document.createElement('div') + closeDiv.innerText = capitalizeFirstLetter(container.dataset.close) + closeDiv.addEventListener( + 'click', + () => { + slideDown() + }, + { passive: true } + ) + closeDiv.addEventListener( + 'keydown', + () => { + slideDown() + }, + { passive: true } + ) + // nav + navDiv = createDivWithClass('nav') + navDiv.append(indexDiv, closeDiv) +} + +function constructGalleryInner(ijs: ImageJSON[]): void { // swiper wrapper - const _swiperWrapper = document.createElement('div') - _swiperWrapper.className = 'swiper-wrapper' + const swiperWrapper = createDivWithClass('swiper-wrapper') // loading text const loadingText = container.dataset.loading + '...' for (const ij of ijs) { // swiper slide - const _swiperSlide = document.createElement('div') - _swiperSlide.className = 'swiper-slide' + const swiperSlide = createDivWithClass('swiper-slide') // loading indicator - const l = document.createElement('div') - l.className = 'loadingText' + const l = createDivWithClass('loadingText') l.innerText = loadingText // img const e = document.createElement('img') as MobileImage @@ -253,63 +267,46 @@ function createGallery(ijs: ImageJSON[]): void { { once: true, passive: true } ) // parent container - const p = document.createElement('div') - p.className = 'slideContainer' + const p = createDivWithClass('slideContainer') // append - p.append(e) - p.append(l) - _swiperSlide.append(p) - _swiperWrapper.append(_swiperSlide) + p.append(e, l) + swiperSlide.append(p) + swiperWrapper.append(swiperSlide) } // swiper node - const _swiperNode = document.createElement('div') - _swiperNode.className = 'galleryInner' - _swiperNode.append(_swiperWrapper) - // index - const _index = document.createElement('div') - _index.insertAdjacentHTML( - 'afterbegin', - ` - / - ` - ) - // close - const _close = document.createElement('div') - _close.innerText = capitalizeFirstLetter(container.dataset.close) - _close.addEventListener( - 'click', - () => { - slideDown() - }, - { passive: true } - ) - _close.addEventListener( - 'keydown', - () => { - slideDown() - }, - { passive: true } - ) - // nav - const _navDiv = document.createElement('div') - _navDiv.className = 'nav' - _navDiv.append(_index, _close) + galleryInner = createDivWithClass('galleryInner') + galleryInner.append(swiperWrapper) +} + +function constructGallery(ijs: ImageJSON[]): void { + /** + * gallery + * |- galleryInner + * |- swiper-wrapper + * |- swiper-slide + * |- img + * |- swiper-slide + * |- img + * |- ... + * |- nav + * |- index + * |- close + */ // gallery - const _gallery = document.createElement('div') - _gallery.className = 'gallery' - _gallery.append(_swiperNode) - _gallery.append(_navDiv) + gallery = createDivWithClass('gallery') + constructGalleryInner(ijs) + constructGalleryNav() + gallery.append(galleryInner, navDiv) /** * curtain */ - const _curtain = document.createElement('div') - _curtain.className = 'curtain' + curtain = createDivWithClass('curtain') /** * container * |- gallery * |- curtain */ - container.append(_gallery, _curtain) + container.append(gallery, curtain) }