mirror of
https://github.com/Sped0n/bridget.git
synced 2026-04-18 20:19:30 -07:00
feat: loading transition (#277)
* refactor: change hires loader function name * feat: add loading transition animation and improve performance * refactor: refactor mutation handling in desktop codebase - Modify the `initStage` function in `assets/ts/desktop/stage.ts`: - Change the `onMutation` callback to accept a single mutation instead of an array of mutations. - Update the conditions inside the callback to use `hold` instead of `skip`. - Modify the `onMutation` function in `assets/ts/desktop/utils.ts`: - Change the `callback` parameter to `trigger`. - Update the implementation of the function to iterate over each mutation and check if it triggers the `trigger` function. If it does, disconnect the observer and break the loop. * style: refactor state section and remove unnecessary code - Remove the declaration of `lastIndex` on line 21 - Add a comment block for the state section - Add a declaration of `lastIndex` for the state section * refactor: refactor mobile collection and intersection functions - Modify the `initCollection` function in `assets/ts/mobile/collection.ts` - Remove the nested loop in the `initCollection` function - Modify the `onIntersection` function in `assets/ts/mobile/utils.ts` - Replace the callback parameter with a trigger parameter in the `onIntersection` function - Remove the nested loop in the `onIntersection` function * refactor: refactor Watchable class constructor to include lazy parameter - Add a second parameter `lazy` in the constructor of the `Watchable` class in `globalUtils.ts` - Set the default value of `lazy` to `true` in the constructor - Add a condition to check if `e` is equal to `this.obj` and `this.lazy` is `true` to return in `watch` - Delete the previous constructor definition in the `Watchable` class in `globalUtils.ts` * fix: set state's lazy param to false * refactor: refactor third party lib import
This commit is contained in:
@@ -1,4 +1,4 @@
|
|||||||
import { type Power3, type gsap } from 'gsap'
|
import { type gsap } from 'gsap'
|
||||||
|
|
||||||
import { container } from '../container'
|
import { container } from '../container'
|
||||||
import { incIndex, isAnimating, navigateVector, state } from '../globalState'
|
import { incIndex, isAnimating, navigateVector, state } from '../globalState'
|
||||||
@@ -17,7 +17,10 @@ let imgs: DesktopImage[] = []
|
|||||||
let last = { x: 0, y: 0 }
|
let last = { x: 0, y: 0 }
|
||||||
|
|
||||||
let _gsap: typeof gsap
|
let _gsap: typeof gsap
|
||||||
let _Power3: typeof Power3
|
|
||||||
|
/**
|
||||||
|
* state
|
||||||
|
*/
|
||||||
|
|
||||||
let gsapLoaded = false
|
let gsapLoaded = false
|
||||||
|
|
||||||
@@ -85,33 +88,43 @@ function setPositions(): void {
|
|||||||
|
|
||||||
const elsTrail = getImagesWithIndexArray(trailElsIndex)
|
const elsTrail = getImagesWithIndexArray(trailElsIndex)
|
||||||
|
|
||||||
|
// cached state
|
||||||
|
const _isOpen = isOpen.get()
|
||||||
|
const _cordHist = cordHist.get()
|
||||||
|
const _state = state.get()
|
||||||
|
|
||||||
_gsap.set(elsTrail, {
|
_gsap.set(elsTrail, {
|
||||||
x: (i: number) => cordHist.get()[i].x - window.innerWidth / 2,
|
x: (i: number) => _cordHist[i].x - window.innerWidth / 2,
|
||||||
y: (i: number) => cordHist.get()[i].y - window.innerHeight / 2,
|
y: (i: number) => _cordHist[i].y - window.innerHeight / 2,
|
||||||
opacity: (i: number) =>
|
opacity: (i: number) =>
|
||||||
i + 1 + state.get().trailLength <= cordHist.get().length ? 0 : 1,
|
Math.max(
|
||||||
|
(i + 1 + _state.trailLength <= _cordHist.length ? 0 : 1) - (_isOpen ? 1 : 0),
|
||||||
|
0
|
||||||
|
),
|
||||||
zIndex: (i: number) => i,
|
zIndex: (i: number) => i,
|
||||||
scale: 0.6
|
scale: 0.6
|
||||||
})
|
})
|
||||||
|
|
||||||
if (isOpen.get()) {
|
if (_isOpen) {
|
||||||
const elc = getImagesWithIndexArray([getCurrentElIndex()])[0]
|
const elc = getImagesWithIndexArray([getCurrentElIndex()])[0]
|
||||||
elc.classList.add('hide') // hide image to prevent flash
|
|
||||||
const indexArrayToHires: number[] = []
|
const indexArrayToHires: number[] = []
|
||||||
|
const indexArrayToCleanup: number[] = []
|
||||||
switch (navigateVector.get()) {
|
switch (navigateVector.get()) {
|
||||||
case 'prev':
|
case 'prev':
|
||||||
indexArrayToHires.push(getPrevElIndex())
|
indexArrayToHires.push(getPrevElIndex())
|
||||||
|
indexArrayToCleanup.push(getNextElIndex())
|
||||||
break
|
break
|
||||||
case 'next':
|
case 'next':
|
||||||
indexArrayToHires.push(getNextElIndex())
|
indexArrayToHires.push(getNextElIndex())
|
||||||
|
indexArrayToCleanup.push(getPrevElIndex())
|
||||||
break
|
break
|
||||||
default:
|
default:
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
hires(getImagesWithIndexArray(indexArrayToHires)) // preload
|
hires(getImagesWithIndexArray(indexArrayToHires)) // preload
|
||||||
setLoaderForImage(elc)
|
_gsap.set(getImagesWithIndexArray(indexArrayToCleanup), { opacity: 0 })
|
||||||
_gsap.set(imgs, { opacity: 0 })
|
_gsap.set(elc, { x: 0, y: 0, scale: 1 }) // set current to center
|
||||||
_gsap.set(elc, { opacity: 1, x: 0, y: 0, scale: 1 })
|
setLoaderForHiresImage(elc) // set loader, if loaded set current opacity to 1
|
||||||
} else {
|
} else {
|
||||||
lores(elsTrail)
|
lores(elsTrail)
|
||||||
}
|
}
|
||||||
@@ -130,14 +143,14 @@ function expandImage(): void {
|
|||||||
// elc.classList.add('hide')
|
// elc.classList.add('hide')
|
||||||
|
|
||||||
hires(getImagesWithIndexArray([elcIndex, getPrevElIndex(), getNextElIndex()]))
|
hires(getImagesWithIndexArray([elcIndex, getPrevElIndex(), getNextElIndex()]))
|
||||||
setLoaderForImage(elc)
|
setLoaderForHiresImage(elc)
|
||||||
|
|
||||||
const tl = _gsap.timeline()
|
const tl = _gsap.timeline()
|
||||||
const trailInactiveEls = getImagesWithIndexArray(getTrailInactiveElsIndex())
|
const trailInactiveEls = getImagesWithIndexArray(getTrailInactiveElsIndex())
|
||||||
// move down and hide trail inactive
|
// move down and hide trail inactive
|
||||||
tl.to(trailInactiveEls, {
|
tl.to(trailInactiveEls, {
|
||||||
y: '+=20',
|
y: '+=20',
|
||||||
ease: _Power3.easeIn,
|
ease: 'power3.in',
|
||||||
stagger: 0.075,
|
stagger: 0.075,
|
||||||
duration: 0.3,
|
duration: 0.3,
|
||||||
delay: 0.1,
|
delay: 0.1,
|
||||||
@@ -147,7 +160,7 @@ function expandImage(): void {
|
|||||||
tl.to(elc, {
|
tl.to(elc, {
|
||||||
x: 0,
|
x: 0,
|
||||||
y: 0,
|
y: 0,
|
||||||
ease: _Power3.easeInOut,
|
ease: 'power3.inOut',
|
||||||
duration: 0.7,
|
duration: 0.7,
|
||||||
delay: 0.3
|
delay: 0.3
|
||||||
})
|
})
|
||||||
@@ -155,7 +168,7 @@ function expandImage(): void {
|
|||||||
tl.to(elc, {
|
tl.to(elc, {
|
||||||
delay: 0.1,
|
delay: 0.1,
|
||||||
scale: 1,
|
scale: 1,
|
||||||
ease: _Power3.easeInOut
|
ease: 'power3.inOut'
|
||||||
})
|
})
|
||||||
// finished
|
// finished
|
||||||
tl.then(() => {
|
tl.then(() => {
|
||||||
@@ -184,20 +197,20 @@ export function minimizeImage(): void {
|
|||||||
tl.to(elc, {
|
tl.to(elc, {
|
||||||
scale: 0.6,
|
scale: 0.6,
|
||||||
duration: 0.6,
|
duration: 0.6,
|
||||||
ease: _Power3.easeInOut
|
ease: 'power3.inOut'
|
||||||
})
|
})
|
||||||
// move current to original position
|
// move current to original position
|
||||||
tl.to(elc, {
|
tl.to(elc, {
|
||||||
delay: 0.3,
|
delay: 0.3,
|
||||||
duration: 0.7,
|
duration: 0.7,
|
||||||
ease: _Power3.easeInOut,
|
ease: 'power3.inOut',
|
||||||
x: cordHist.get()[cordHist.get().length - 1].x - window.innerWidth / 2,
|
x: cordHist.get()[cordHist.get().length - 1].x - window.innerWidth / 2,
|
||||||
y: cordHist.get()[cordHist.get().length - 1].y - window.innerHeight / 2
|
y: cordHist.get()[cordHist.get().length - 1].y - window.innerHeight / 2
|
||||||
})
|
})
|
||||||
// show trail inactive
|
// show trail inactive
|
||||||
tl.to(elsTrailInactive, {
|
tl.to(elsTrailInactive, {
|
||||||
y: '-=20',
|
y: '-=20',
|
||||||
ease: _Power3.easeOut,
|
ease: 'power3.out',
|
||||||
stagger: -0.1,
|
stagger: -0.1,
|
||||||
duration: 0.3,
|
duration: 0.3,
|
||||||
opacity: 1
|
opacity: 1
|
||||||
@@ -227,23 +240,20 @@ export function initStage(ijs: ImageJSON[]): void {
|
|||||||
img.src = img.dataset.loUrl
|
img.src = img.dataset.loUrl
|
||||||
}
|
}
|
||||||
// lores preloader for rest of the images
|
// lores preloader for rest of the images
|
||||||
onMutation(img, (mutations, observer) => {
|
onMutation(img, (mutation) => {
|
||||||
mutations.every((mutation) => {
|
// if open or animating, hold
|
||||||
// if open or animating, skip
|
if (isOpen.get() || isAnimating.get()) return false
|
||||||
if (isOpen.get() || isAnimating.get()) return true
|
// if mutation is not about style attribute, hold
|
||||||
// if mutation is not about style attribute, skip
|
if (mutation.attributeName !== 'style') return false
|
||||||
if (mutation.attributeName !== 'style') return true
|
const opacity = parseFloat(img.style.opacity)
|
||||||
const opacity = parseFloat(img.style.opacity)
|
// if opacity is not 1, hold
|
||||||
// if opacity is not 1, skip
|
if (opacity !== 1) return false
|
||||||
if (opacity !== 1) return true
|
// preload the i + 5th image, if it exists
|
||||||
// preload the i + 5th image
|
if (i + 5 < imgs.length) {
|
||||||
if (i + 5 < imgs.length) {
|
imgs[i + 5].src = imgs[i + 5].dataset.loUrl
|
||||||
imgs[i + 5].src = imgs[i + 5].dataset.loUrl
|
}
|
||||||
}
|
// triggered
|
||||||
// disconnect observer and return false to break the loop
|
return true
|
||||||
observer.disconnect()
|
|
||||||
return false
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
// event listeners
|
// event listeners
|
||||||
@@ -331,35 +341,53 @@ function lores(imgs: DesktopImage[]): void {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
function setLoaderForImage(e: HTMLImageElement): void {
|
function setLoaderForHiresImage(e: HTMLImageElement): void {
|
||||||
if (!e.complete) {
|
if (!e.complete) {
|
||||||
isLoading.set(true)
|
isLoading.set(true)
|
||||||
e.addEventListener(
|
e.addEventListener(
|
||||||
'load',
|
'load',
|
||||||
() => {
|
() => {
|
||||||
isLoading.set(false)
|
_gsap
|
||||||
e.classList.remove('hide')
|
.to(e, { opacity: 1, ease: 'power3.out', duration: 0.5 })
|
||||||
|
.then(() => {
|
||||||
|
isLoading.set(false)
|
||||||
|
})
|
||||||
|
.catch((e) => {
|
||||||
|
console.log(e)
|
||||||
|
})
|
||||||
},
|
},
|
||||||
{ once: true, passive: true }
|
{ once: true, passive: true }
|
||||||
)
|
)
|
||||||
e.addEventListener(
|
e.addEventListener(
|
||||||
'error',
|
'error',
|
||||||
() => {
|
() => {
|
||||||
isLoading.set(false)
|
_gsap
|
||||||
|
.set(e, { opacity: 1 })
|
||||||
|
.then(() => {
|
||||||
|
isLoading.set(false)
|
||||||
|
})
|
||||||
|
.catch((e) => {
|
||||||
|
console.log(e)
|
||||||
|
})
|
||||||
},
|
},
|
||||||
{ once: true, passive: true }
|
{ once: true, passive: true }
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
e.classList.remove('hide')
|
_gsap
|
||||||
isLoading.set(false)
|
.set(e, { opacity: 1 })
|
||||||
|
.then(() => {
|
||||||
|
isLoading.set(false)
|
||||||
|
})
|
||||||
|
.catch((e) => {
|
||||||
|
console.log(e)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function loadLib(): void {
|
function loadLib(): void {
|
||||||
loadGsap()
|
loadGsap()
|
||||||
.then((g) => {
|
.then((g) => {
|
||||||
_gsap = g[0]
|
_gsap = g
|
||||||
_Power3 = g[1]
|
|
||||||
gsapLoaded = true
|
gsapLoaded = true
|
||||||
})
|
})
|
||||||
.catch((e) => {
|
.catch((e) => {
|
||||||
|
|||||||
@@ -19,10 +19,15 @@ export interface DesktopImage extends HTMLImageElement {
|
|||||||
|
|
||||||
export function onMutation<T extends HTMLElement>(
|
export function onMutation<T extends HTMLElement>(
|
||||||
element: T,
|
element: T,
|
||||||
callback: (arg0: MutationRecord[], arg1: MutationObserver) => void,
|
trigger: (arg0: MutationRecord) => boolean,
|
||||||
observeOptions: MutationObserverInit = { attributes: true }
|
observeOptions: MutationObserverInit = { attributes: true }
|
||||||
): void {
|
): void {
|
||||||
new MutationObserver((mutations, observer) => {
|
new MutationObserver((mutations, observer) => {
|
||||||
callback(mutations, observer)
|
for (const mutation of mutations) {
|
||||||
|
if (trigger(mutation)) {
|
||||||
|
observer.disconnect()
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
}).observe(element, observeOptions)
|
}).observe(element, observeOptions)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ const defaultState = {
|
|||||||
trailLength: thresholds[getThresholdSessionIndex()].trailLength
|
trailLength: thresholds[getThresholdSessionIndex()].trailLength
|
||||||
}
|
}
|
||||||
|
|
||||||
export const state = new Watchable<State>(defaultState)
|
export const state = new Watchable<State>(defaultState, false)
|
||||||
export const isAnimating = new Watchable<boolean>(false)
|
export const isAnimating = new Watchable<boolean>(false)
|
||||||
export const navigateVector = new Watchable<NavVec>('none')
|
export const navigateVector = new Watchable<NavVec>('none')
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { type Power3, type gsap } from 'gsap'
|
import { type gsap } from 'gsap'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* utils
|
* utils
|
||||||
@@ -16,9 +16,9 @@ export function expand(num: number): string {
|
|||||||
return ('0000' + num.toString()).slice(-4)
|
return ('0000' + num.toString()).slice(-4)
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function loadGsap(): Promise<[typeof gsap, typeof Power3]> {
|
export async function loadGsap(): Promise<typeof gsap> {
|
||||||
const g = await import('gsap')
|
const g = await import('gsap')
|
||||||
return [g.gsap, g.Power3]
|
return g.gsap
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getThresholdSessionIndex(): number {
|
export function getThresholdSessionIndex(): number {
|
||||||
@@ -37,7 +37,11 @@ export function removeDuplicates<T>(arr: T[]): T[] {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
export class Watchable<T> {
|
export class Watchable<T> {
|
||||||
constructor(private obj: T) {}
|
constructor(
|
||||||
|
private obj: T,
|
||||||
|
private readonly lazy: boolean = true
|
||||||
|
) {}
|
||||||
|
|
||||||
private readonly watchers: Array<(arg0: T) => void> = []
|
private readonly watchers: Array<(arg0: T) => void> = []
|
||||||
|
|
||||||
get(): T {
|
get(): T {
|
||||||
@@ -45,6 +49,7 @@ export class Watchable<T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
set(e: T): void {
|
set(e: T): void {
|
||||||
|
if (e === this.obj && this.lazy) return
|
||||||
this.obj = e
|
this.obj = e
|
||||||
this.watchers.forEach((watcher) => {
|
this.watchers.forEach((watcher) => {
|
||||||
watcher(this.obj)
|
watcher(this.obj)
|
||||||
|
|||||||
@@ -64,18 +64,15 @@ export function initCollection(ijs: ImageJSON[]): void {
|
|||||||
{ passive: true }
|
{ passive: true }
|
||||||
)
|
)
|
||||||
// preload
|
// preload
|
||||||
onIntersection(img, (entries, observer) => {
|
onIntersection(img, (entry) => {
|
||||||
entries.every((entry) => {
|
// no intersection, hold
|
||||||
// no intersection, skip
|
if (entry.intersectionRatio <= 0) return false
|
||||||
if (entry.intersectionRatio <= 0) return true
|
// preload the i + 5th image, if it exists
|
||||||
// preload the i + 5th image
|
if (i + 5 < imgs.length) {
|
||||||
if (i + 5 < imgs.length) {
|
imgs[i + 5].src = imgs[i + 5].dataset.src
|
||||||
imgs[i + 5].src = imgs[i + 5].dataset.src
|
}
|
||||||
}
|
// triggered
|
||||||
// disconnect observer and return false to break the loop
|
return true
|
||||||
observer.disconnect()
|
|
||||||
return false
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { type Power3, type gsap } from 'gsap'
|
import { type gsap } from 'gsap'
|
||||||
import { type Swiper } from 'swiper'
|
import { type Swiper } from 'swiper'
|
||||||
|
|
||||||
import { container, scrollable } from '../container'
|
import { container, scrollable } from '../container'
|
||||||
@@ -17,16 +17,18 @@ import { capitalizeFirstLetter, loadSwiper, type MobileImage } from './utils'
|
|||||||
let swiperNode: HTMLDivElement
|
let swiperNode: HTMLDivElement
|
||||||
let gallery: HTMLDivElement
|
let gallery: HTMLDivElement
|
||||||
let curtain: HTMLDivElement
|
let curtain: HTMLDivElement
|
||||||
let swiper: Swiper
|
|
||||||
let lastIndex = -1
|
|
||||||
let indexDispNums: HTMLSpanElement[] = []
|
let indexDispNums: HTMLSpanElement[] = []
|
||||||
let galleryImages: MobileImage[] = []
|
let galleryImages: MobileImage[] = []
|
||||||
let collectionImages: MobileImage[] = []
|
let collectionImages: MobileImage[] = []
|
||||||
|
|
||||||
let _Swiper: typeof Swiper
|
|
||||||
let _gsap: typeof gsap
|
let _gsap: typeof gsap
|
||||||
let _Power3: typeof Power3
|
let _swiper: Swiper
|
||||||
|
|
||||||
|
/**
|
||||||
|
* state
|
||||||
|
*/
|
||||||
|
|
||||||
|
let lastIndex = -1
|
||||||
let libLoaded = false
|
let libLoaded = false
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -44,7 +46,7 @@ export function slideUp(): void {
|
|||||||
|
|
||||||
_gsap.to(gallery, {
|
_gsap.to(gallery, {
|
||||||
y: 0,
|
y: 0,
|
||||||
ease: _Power3.easeInOut,
|
ease: 'power3.inOut',
|
||||||
duration: 1,
|
duration: 1,
|
||||||
delay: 0.4
|
delay: 0.4
|
||||||
})
|
})
|
||||||
@@ -63,7 +65,7 @@ function slideDown(): void {
|
|||||||
|
|
||||||
_gsap.to(gallery, {
|
_gsap.to(gallery, {
|
||||||
y: '100%',
|
y: '100%',
|
||||||
ease: _Power3.easeInOut,
|
ease: 'power3.inOut',
|
||||||
duration: 1
|
duration: 1
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -128,17 +130,15 @@ export function initGallery(ijs: ImageJSON[]): void {
|
|||||||
() => {
|
() => {
|
||||||
loadGsap()
|
loadGsap()
|
||||||
.then((g) => {
|
.then((g) => {
|
||||||
_gsap = g[0]
|
_gsap = g
|
||||||
_Power3 = g[1]
|
|
||||||
})
|
})
|
||||||
.catch((e) => {
|
.catch((e) => {
|
||||||
console.log(e)
|
console.log(e)
|
||||||
})
|
})
|
||||||
loadSwiper()
|
loadSwiper()
|
||||||
.then((s) => {
|
.then((S) => {
|
||||||
_Swiper = s
|
_swiper = new S(swiperNode, { spaceBetween: 20 })
|
||||||
swiper = new _Swiper(swiperNode, { spaceBetween: 20 })
|
_swiper.on('slideChange', ({ realIndex }) => {
|
||||||
swiper.on('slideChange', ({ realIndex }) => {
|
|
||||||
setIndex(realIndex)
|
setIndex(realIndex)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
@@ -159,7 +159,7 @@ export function initGallery(ijs: ImageJSON[]): void {
|
|||||||
|
|
||||||
function changeSlide(slide: number): void {
|
function changeSlide(slide: number): void {
|
||||||
galleryLoadImages()
|
galleryLoadImages()
|
||||||
swiper.slideTo(slide, 0)
|
_swiper.slideTo(slide, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
function scrollToActive(): void {
|
function scrollToActive(): void {
|
||||||
@@ -237,13 +237,18 @@ function createGallery(ijs: ImageJSON[]): void {
|
|||||||
e.height = ij.hiImgH
|
e.height = ij.hiImgH
|
||||||
e.width = ij.hiImgW
|
e.width = ij.hiImgW
|
||||||
e.alt = ij.alt
|
e.alt = ij.alt
|
||||||
e.classList.add('hide')
|
e.style.opacity = '0'
|
||||||
// load event
|
// load event
|
||||||
e.addEventListener(
|
e.addEventListener(
|
||||||
'load',
|
'load',
|
||||||
() => {
|
() => {
|
||||||
e.classList.remove('hide')
|
if (state.get().index !== ij.index) {
|
||||||
l.classList.add('hide')
|
_gsap.set(e, { opacity: 1 })
|
||||||
|
_gsap.set(l, { opacity: 0 })
|
||||||
|
} else {
|
||||||
|
_gsap.to(e, { opacity: 1, delay: 0.5, duration: 0.5, ease: 'power3.out' })
|
||||||
|
_gsap.to(l, { opacity: 0, duration: 0.5, ease: 'power3.in' })
|
||||||
|
}
|
||||||
},
|
},
|
||||||
{ once: true, passive: true }
|
{ once: true, passive: true }
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -20,10 +20,15 @@ export function getRandom(min: number, max: number): number {
|
|||||||
|
|
||||||
export function onIntersection<T extends HTMLElement>(
|
export function onIntersection<T extends HTMLElement>(
|
||||||
element: T,
|
element: T,
|
||||||
callback: (arg0: IntersectionObserverEntry[], arg1: IntersectionObserver) => void
|
trigger: (arg0: IntersectionObserverEntry) => boolean
|
||||||
): void {
|
): void {
|
||||||
new IntersectionObserver((entries, observer) => {
|
new IntersectionObserver((entries, observer) => {
|
||||||
callback(entries, observer)
|
for (const entry of entries) {
|
||||||
|
if (trigger(entry)) {
|
||||||
|
observer.disconnect()
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
}).observe(element)
|
}).observe(element)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user