fix(customCursor.ts): move import statement for active to the top for better organization

fix(customCursor.ts): add return type void to onMouse function for clarity
fix(customCursor.ts): add passive option to window event listener for mousemove to improve performance
fix(stageNav.ts): move import statement for setCustomCursor to the top for better organization
fix(stageNav.ts): add return type void to handleClick function for clarity
fix(stageNav.ts): add return type void to handleKey function for clarity
fix(stageNav.ts): add passive option to overlay event listeners for click, keydown, mouseover, and focus to improve performance
fix(stageNav.ts): add passive option to window event listener for keydown to improve performance
fix(stageNav.ts): add return type void to nextImage function for clarity
fix(stageNav.ts): add return type void to prevImage function for clarity
fix(gallery.ts): move import statement for Swiper to the top for better organization
fix(gallery.ts): add return type void to slideUp function for clarity
fix(gallery.ts): add return type void to initGallery function for clarity
fix(gallery.ts): add passive option to window event listener for touchstart to improve performance
fix(gallery.ts): add return type void to changeSlide function for clarity
fix(gallery.ts): add return type void to scrollToActive function for clarity
fix(gallery.ts): add return type void to createGallery function for clarity
fix(gallery.ts): add passive option to close event listeners for click and keydown to improve performance
fix(gallery.ts): add passive option to overlay event listeners for click, keydown, mouseover, and focus to improve performance
fix(gallery.ts): add passive option to window event listener for touchstart to improve performance
fix(nav.ts): add return type void to initNav function for clarity
fix(utils.ts): add return type number to getRandom function for clarity
fix(utils.ts): add return type void to onVisible function for clarity
fix(utils.ts): add return type void to addWatcher function in Watchable class for clarity
This commit is contained in:
Sped0n
2023-11-01 23:07:21 +08:00
parent aefdaa86eb
commit a395513bd6
12 changed files with 194 additions and 99 deletions

View File

@@ -1,6 +1,7 @@
import { active } from './stage'
import { container } from '../container' import { container } from '../container'
import { active } from './stage'
/** /**
* variables * variables
*/ */
@@ -12,7 +13,7 @@ const cursorInner = document.createElement('div')
* main functions * main functions
*/ */
function onMouse(e: MouseEvent) { function onMouse(e: MouseEvent): void {
const x = e.clientX const x = e.clientX
const y = e.clientY const y = e.clientY
cursor.style.transform = `translate3d(${x}px, ${y}px, 0)` cursor.style.transform = `translate3d(${x}px, ${y}px, 0)`
@@ -35,7 +36,7 @@ export function initCustomCursor(): void {
// append cursor to main // append cursor to main
container.append(cursor) container.append(cursor)
// bind mousemove event to window // bind mousemove event to window
window.addEventListener('mousemove', onMouse) window.addEventListener('mousemove', onMouse, { passive: true })
// add active callback // add active callback
active.addWatcher(() => { active.addWatcher(() => {
if (active.get()) { if (active.get()) {

11
assets/ts/desktop/init.ts Normal file
View File

@@ -0,0 +1,11 @@
import { type ImageJSON } from '../resources'
import { initCustomCursor } from './customCursor'
import { initStage } from './stage'
import { initStageNav } from './stageNav'
export function initDesktop(ijs: ImageJSON[]): void {
initCustomCursor()
initStage(ijs)
initStageNav()
}

View File

@@ -1,14 +1,19 @@
import { Power3, gsap } from 'gsap' import { Power3, gsap } from 'gsap'
import { container } from '../container' import { container } from '../container'
import { ImageJSON } from '../resources' import { type ImageJSON } from '../resources'
import { incIndex, state } from '../state' import { incIndex, state } from '../state'
import { Watchable } from '../utils' import { Watchable, decrement, increment } from '../utils'
/** /**
* types * types
*/ */
export type HistoryItem = { i: number; x: number; y: number } export interface HistoryItem {
i: number
x: number
y: number
}
/** /**
* variables * variables
@@ -44,7 +49,22 @@ function getElCurrent(): HTMLImageElement {
} }
function getElNextFive(): HTMLImageElement[] { function getElNextFive(): HTMLImageElement[] {
return state.get().nextFive.map((i) => imgs[i]) const s = state.get()
const els = []
for (let i = 0; i < 5; i++) {
els.push(imgs[increment(s.index + i, s.length)])
}
return els
}
function getElPrev(): HTMLImageElement {
const s = state.get()
return imgs[increment(s.index, s.length)]
}
function getElNext(): HTMLImageElement {
const s = state.get()
return imgs[decrement(s.index, s.length)]
} }
/** /**
@@ -69,7 +89,7 @@ function onMouse(e: MouseEvent): void {
// set image position with gsap // set image position with gsap
function setPositions(): void { function setPositions(): void {
const elTrail = getElTrail() const elTrail = getElTrail()
if (!elTrail.length) return if (elTrail.length === 0) return
// preload // preload
lores(getElNextFive()) lores(getElNextFive())
@@ -98,7 +118,7 @@ function expandImage(): void {
isOpen.set(true) isOpen.set(true)
isAnimating.set(true) isAnimating.set(true)
hires([getElCurrent()]) hires([getElCurrent(), getElPrev(), getElNext()])
const tl = gsap.timeline() const tl = gsap.timeline()
// move down and hide trail inactive // move down and hide trail inactive
@@ -125,6 +145,7 @@ function expandImage(): void {
ease: Power3.easeInOut ease: Power3.easeInOut
}) })
// finished // finished
// eslint-disable-next-line @typescript-eslint/no-floating-promises
tl.then(() => { tl.then(() => {
isAnimating.set(false) isAnimating.set(false)
}) })
@@ -137,6 +158,9 @@ export function minimizeImage(): void {
isOpen.set(false) isOpen.set(false)
isAnimating.set(true) isAnimating.set(true)
lores([getElCurrent()])
lores(getElTrailInactive())
const tl = gsap.timeline() const tl = gsap.timeline()
// shrink current // shrink current
tl.to(getElCurrent(), { tl.to(getElCurrent(), {
@@ -161,6 +185,7 @@ export function minimizeImage(): void {
opacity: 1 opacity: 1
}) })
// finished // finished
// eslint-disable-next-line @typescript-eslint/no-floating-promises
tl.then(() => { tl.then(() => {
isAnimating.set(false) isAnimating.set(false)
}) })
@@ -178,13 +203,23 @@ export function initStage(ijs: ImageJSON[]): void {
// get image elements // get image elements
imgs = Array.from(stage.getElementsByTagName('img')) imgs = Array.from(stage.getElementsByTagName('img'))
// event listeners // event listeners
stage.addEventListener('click', () => expandImage()) stage.addEventListener('click', () => {
stage.addEventListener('keydown', () => expandImage()) expandImage()
window.addEventListener('mousemove', onMouse) })
stage.addEventListener('keydown', () => {
expandImage()
})
window.addEventListener('mousemove', onMouse, { passive: true })
// watchers // watchers
isOpen.addWatcher(() => active.set(isOpen.get() && !isAnimating.get())) isOpen.addWatcher(() => {
isAnimating.addWatcher(() => active.set(isOpen.get() && !isAnimating.get())) active.set(isOpen.get() && !isAnimating.get())
cordHist.addWatcher(() => setPositions()) })
isAnimating.addWatcher(() => {
active.set(isOpen.get() && !isAnimating.get())
})
cordHist.addWatcher(() => {
setPositions()
})
// preload // preload
lores(getElNextFive()) lores(getElNextFive())
} }
@@ -198,7 +233,7 @@ function createStage(ijs: ImageJSON[]): void {
const stage: HTMLDivElement = document.createElement('div') const stage: HTMLDivElement = document.createElement('div')
stage.className = 'stage' stage.className = 'stage'
// append images to container // append images to container
for (let ij of ijs) { for (const ij of ijs) {
const e = document.createElement('img') const e = document.createElement('img')
e.height = ij.loImgH e.height = ij.loImgH
e.width = ij.loImgW e.width = ij.loImgW
@@ -217,16 +252,16 @@ function createStage(ijs: ImageJSON[]): void {
function hires(imgs: HTMLImageElement[]): void { function hires(imgs: HTMLImageElement[]): void {
imgs.forEach((img) => { imgs.forEach((img) => {
img.src = img.dataset.hiUrl! img.src = img.dataset.hiUrl as string
img.height = parseInt(img.dataset.hiImgH!) img.height = parseInt(img.dataset.hiImgH as string)
img.width = parseInt(img.dataset.hiImgW!) img.width = parseInt(img.dataset.hiImgW as string)
}) })
} }
function lores(imgs: HTMLImageElement[]): void { function lores(imgs: HTMLImageElement[]): void {
imgs.forEach((img) => { imgs.forEach((img) => {
img.src = img.dataset.loUrl! img.src = img.dataset.loUrl as string
img.height = parseInt(img.dataset.loImgH!) img.height = parseInt(img.dataset.loImgH as string)
img.width = parseInt(img.dataset.loImgW!) img.width = parseInt(img.dataset.loImgW as string)
}) })
} }

View File

@@ -1,6 +1,7 @@
import { container } from '../container' import { container } from '../container'
import { decIndex, incIndex, state } from '../state' import { decIndex, incIndex, state } from '../state'
import { decrement, increment } from '../utils' import { decrement, increment } from '../utils'
import { setCustomCursor } from './customCursor' import { setCustomCursor } from './customCursor'
import { active, cordHist, isAnimating, isOpen, minimizeImage } from './stage' import { active, cordHist, isAnimating, isOpen, minimizeImage } from './stage'
@@ -20,7 +21,7 @@ const navItems = ['prev', 'close', 'next'] as const
* main functions * main functions
*/ */
function handleClick(type: NavItem) { function handleClick(type: NavItem): void {
switch (type) { switch (type) {
case 'prev': case 'prev':
prevImage() prevImage()
@@ -34,7 +35,7 @@ function handleClick(type: NavItem) {
} }
} }
function handleKey(e: KeyboardEvent) { function handleKey(e: KeyboardEvent): void {
if (isOpen.get() || isAnimating.get()) return if (isOpen.get() || isAnimating.get()) return
switch (e.key) { switch (e.key) {
case 'ArrowLeft': case 'ArrowLeft':
@@ -53,16 +54,40 @@ function handleKey(e: KeyboardEvent) {
* init * init
*/ */
export function initStageNav() { export function initStageNav(): void {
const navOverlay = document.createElement('div') const navOverlay = document.createElement('div')
navOverlay.className = 'navOverlay' navOverlay.className = 'navOverlay'
for (let navItem of navItems) { for (const navItem of navItems) {
const overlay = document.createElement('div') const overlay = document.createElement('div')
overlay.className = 'overlay' overlay.className = 'overlay'
overlay.addEventListener('click', () => handleClick(navItem)) overlay.addEventListener(
overlay.addEventListener('keydown', () => handleClick(navItem)) 'click',
overlay.addEventListener('mouseover', () => setCustomCursor(navItem)) () => {
overlay.addEventListener('focus', () => setCustomCursor(navItem)) handleClick(navItem)
},
{ passive: true }
)
overlay.addEventListener(
'keydown',
() => {
handleClick(navItem)
},
{ passive: true }
)
overlay.addEventListener(
'mouseover',
() => {
setCustomCursor(navItem)
},
{ passive: true }
)
overlay.addEventListener(
'focus',
() => {
setCustomCursor(navItem)
},
{ passive: true }
)
navOverlay.append(overlay) navOverlay.append(overlay)
} }
active.addWatcher(() => { active.addWatcher(() => {
@@ -73,14 +98,14 @@ export function initStageNav() {
} }
}) })
container.append(navOverlay) container.append(navOverlay)
window.addEventListener('keydown', handleKey) window.addEventListener('keydown', handleKey, { passive: true })
} }
/** /**
* hepler * hepler
*/ */
function nextImage() { function nextImage(): void {
if (isAnimating.get()) return if (isAnimating.get()) return
cordHist.set( cordHist.set(
cordHist.get().map((item) => { cordHist.get().map((item) => {
@@ -91,7 +116,7 @@ function nextImage() {
incIndex() incIndex()
} }
function prevImage() { function prevImage(): void {
if (isAnimating.get()) return if (isAnimating.get()) return
cordHist.set( cordHist.set(
cordHist.get().map((item) => { cordHist.get().map((item) => {

View File

@@ -1,27 +1,20 @@
import { initContainer } from './container' import { initContainer } from './container'
import { initCustomCursor } from './desktop/customCursor'
import { initStage } from './desktop/stage'
import { initStageNav } from './desktop/stageNav'
import { initCollection } from './mobile/collection'
import { initGallery } from './mobile/gallery'
import { initNav } from './nav' import { initNav } from './nav'
import { initResources } from './resources' import { initResources } from './resources'
import { initState } from './state' import { initState } from './state'
import { isMobile } from './utils' import { isMobile } from './utils'
initContainer() initContainer()
initCustomCursor()
const ijs = initResources() const ijs = initResources()
initState(ijs.length) initState(ijs.length)
initNav() initNav()
if (ijs.length > 0) { if (ijs.length > 0) {
if (!isMobile()) { if (!isMobile()) {
initStage(ijs) const d = await import('./desktop/stage')
initStageNav() d.initStage(ijs)
} else { } else {
initCollection(ijs) const m = await import('./mobile/init')
initGallery(ijs) m.initMobile(ijs)
} }
} }

View File

@@ -1,7 +1,8 @@
import { container } from '../container' import { container } from '../container'
import { ImageJSON } from '../resources' import { type ImageJSON } from '../resources'
import { getNextFive, setIndex } from '../state' import { setIndex } from '../state'
import { Watchable, getRandom, onVisible } from '../utils' import { Watchable, getRandom, onVisible } from '../utils'
import { slideUp } from './gallery' import { slideUp } from './gallery'
/** /**
@@ -42,16 +43,27 @@ export function initCollection(ijs: ImageJSON[]): void {
imgs = Array.from(collection.getElementsByTagName('img')) imgs = Array.from(collection.getElementsByTagName('img'))
// add event listeners // add event listeners
imgs.forEach((img, i) => { imgs.forEach((img, i) => {
img.addEventListener('click', () => handleClick(i)) img.addEventListener(
img.addEventListener('keydown', () => handleClick(i)) 'click',
() => {
handleClick(i)
},
{ passive: true }
)
img.addEventListener(
'keydown',
() => {
handleClick(i)
},
{ passive: true }
)
// preload // preload
onVisible(img, () => { onVisible(img, () => {
// minues one because we want to preload the current and the next 4 images for (let _i = 0; _i < 5; _i++) {
getNextFive(i - 1, imgs.length) const n = i + _i
.map((i) => imgs[i]) if (n < 0 || n > imgs.length - 1) continue
.forEach((e) => { imgs[n].src = imgs[n].dataset.src as string
e.src = e.dataset.src! }
})
}) })
}) })
} }
@@ -65,7 +77,7 @@ function createCollection(ijs: ImageJSON[]): void {
const _collection: HTMLDivElement = document.createElement('div') const _collection: HTMLDivElement = document.createElement('div')
_collection.className = 'collection' _collection.className = 'collection'
// append images to container // append images to container
for (let [i, ij] of ijs.entries()) { for (const [i, ij] of ijs.entries()) {
// random x and y // random x and y
const x = i !== 0 ? getRandom(-25, 25) : 0 const x = i !== 0 ? getRandom(-25, 25) : 0
const y = i !== 0 ? getRandom(-30, 30) : 0 const y = i !== 0 ? getRandom(-30, 30) : 0

View File

@@ -1,9 +1,11 @@
import { Power3, gsap } from 'gsap' import { Power3, gsap } from 'gsap'
import Swiper from 'swiper' import { Swiper } from 'swiper'
import { container } from '../container' import { container } from '../container'
import { ImageJSON } from '../resources' import { type ImageJSON } from '../resources'
import { setIndex, state } from '../state' import { setIndex, state } from '../state'
import { Watchable, expand } from '../utils' import { Watchable, expand } from '../utils'
import { imgs, mounted } from './collection' import { imgs, mounted } from './collection'
import { scrollable } from './scroll' import { scrollable } from './scroll'
@@ -46,7 +48,6 @@ export function slideUp(): void {
setTimeout(() => { setTimeout(() => {
scrollable.set(false) scrollable.set(false)
isAnimating.set(false) isAnimating.set(false)
console.log(swiper.activeIndex)
}, 1200) }, 1200)
} }
@@ -76,7 +77,7 @@ export function initGallery(ijs: ImageJSON[]): void {
createGallery(ijs) createGallery(ijs)
// get elements // get elements
indexDispNums = Array.from( indexDispNums = Array.from(
document.getElementsByClassName('nav').item(0)!.getElementsByClassName('num') document.getElementsByClassName('nav').item(0)?.getElementsByClassName('num') ?? []
) as HTMLSpanElement[] ) as HTMLSpanElement[]
swiperNode = document.getElementsByClassName('galleryInner').item(0) as HTMLDivElement swiperNode = document.getElementsByClassName('galleryInner').item(0) as HTMLDivElement
gallery = document.getElementsByClassName('gallery').item(0) as HTMLDivElement gallery = document.getElementsByClassName('gallery').item(0) as HTMLDivElement
@@ -100,9 +101,10 @@ export function initGallery(ijs: ImageJSON[]): void {
setIndex(realIndex) setIndex(realIndex)
}) })
}) })
// mounted // mounted
mounted.set(true) mounted.set(true)
// dynamic load
window.addEventListener('touchstart', () => {})
} }
/** /**
@@ -111,7 +113,7 @@ export function initGallery(ijs: ImageJSON[]): void {
function changeSlide(slide: number): void { function changeSlide(slide: number): void {
loadImages() loadImages()
swiper!.slideTo(slide, 0) swiper.slideTo(slide, 0)
} }
function scrollToActive(): void { function scrollToActive(): void {
@@ -151,7 +153,7 @@ function createGallery(ijs: ImageJSON[]): void {
const _swiperWrapper = document.createElement('div') const _swiperWrapper = document.createElement('div')
_swiperWrapper.className = 'swiper-wrapper' _swiperWrapper.className = 'swiper-wrapper'
// swiper slide // swiper slide
for (let ij of ijs) { for (const ij of ijs) {
const _swiperSlide = document.createElement('div') const _swiperSlide = document.createElement('div')
_swiperSlide.className = 'swiper-slide' _swiperSlide.className = 'swiper-slide'
// img // img
@@ -179,8 +181,12 @@ function createGallery(ijs: ImageJSON[]): void {
// close // close
const _close = document.createElement('div') const _close = document.createElement('div')
_close.innerText = 'Close' _close.innerText = 'Close'
_close.addEventListener('click', () => slideDown()) _close.addEventListener('click', () => {
_close.addEventListener('keydown', () => slideDown()) slideDown()
})
_close.addEventListener('keydown', () => {
slideDown()
})
// nav // nav
const _navDiv = document.createElement('div') const _navDiv = document.createElement('div')
_navDiv.className = 'nav' _navDiv.className = 'nav'
@@ -210,14 +216,14 @@ function createGallery(ijs: ImageJSON[]): void {
*/ */
function loadImages(): void { function loadImages(): void {
const activeImages = new Array() const activeImages = []
// load current, next, prev image // load current, next, prev image
activeImages.push(galleryImages[swiper.activeIndex]) activeImages.push(galleryImages[swiper.activeIndex])
activeImages.push( activeImages.push(
galleryImages[Math.min(swiper.activeIndex + 1, swiper.slides.length)] galleryImages[Math.min(swiper.activeIndex + 1, swiper.slides.length)]
) )
activeImages.push(galleryImages[Math.max(swiper.activeIndex - 1, 0)]) activeImages.push(galleryImages[Math.max(swiper.activeIndex - 1, 0)])
for (let e of activeImages) { for (const e of activeImages) {
e.src = e.dataset.src! e.src = e.dataset.src as string
} }
} }

9
assets/ts/mobile/init.ts Normal file
View File

@@ -0,0 +1,9 @@
import { type ImageJSON } from '../resources'
import { initCollection } from './collection'
import { initGallery } from './gallery'
export function initMobile(ijs: ImageJSON[]): void {
initCollection(ijs)
initGallery(ijs)
}

View File

@@ -39,13 +39,16 @@ const links = Array.from(linksDiv.getElementsByClassName('link')) as HTMLAnchorE
// current link index // current link index
const currentLinkIndex = document const currentLinkIndex = document
.getElementById('main')! .getElementById('main')
.getAttribute('currentMenuItemIndex') as string ?.getAttribute('currentMenuItemIndex') as string
// set current link // set current link
for (let [index, link] of links.entries()) { for (const [index, link] of links.entries()) {
if (index === parseInt(currentLinkIndex)) { if (index === parseInt(currentLinkIndex)) {
// set current link style
link.classList.add('current') link.classList.add('current')
// set current link title (only if not home)
if (index !== 0) document.title = link.innerText + ' | ' + document.title
} }
} }
@@ -53,14 +56,26 @@ for (let [index, link] of links.entries()) {
* init * init
*/ */
export function initNav() { export function initNav(): void {
// init threshold text // init threshold text
updateThresholdText() updateThresholdText()
// init index text // init index text
updateIndexText() updateIndexText()
// event listeners // event listeners
decButton.addEventListener('click', () => decThreshold()) decButton.addEventListener(
incButton.addEventListener('click', () => incThreshold()) 'click',
() => {
decThreshold()
},
{ passive: true }
)
incButton.addEventListener(
'click',
() => {
incThreshold()
},
{ passive: true }
)
} }
// helper // helper

View File

@@ -11,7 +11,7 @@ export interface ImageJSON {
export function initResources(): ImageJSON[] { export function initResources(): ImageJSON[] {
const imagesJson = document.getElementById('imagesSource') const imagesJson = document.getElementById('imagesSource')
if (!imagesJson) { if (imagesJson === null) {
return [] return []
} }
return JSON.parse(imagesJson.textContent as string).sort( return JSON.parse(imagesJson.textContent as string).sort(

View File

@@ -21,7 +21,6 @@ const thresholds = [
const defaultState = { const defaultState = {
index: -1, index: -1,
nextFive: new Array(), // for preload
length: 0, length: 0,
threshold: thresholds[2].threshold, threshold: thresholds[2].threshold,
trailLength: thresholds[2].trailLength trailLength: thresholds[2].trailLength
@@ -36,7 +35,6 @@ export const state = new Watchable<State>(defaultState)
export function initState(length: number): void { export function initState(length: number): void {
const s = state.get() const s = state.get()
s.length = length s.length = length
s.nextFive = getNextFive(s.index, s.length)
state.set(s) state.set(s)
state.addWatcher(() => { state.addWatcher(() => {
updateIndexText() updateIndexText()
@@ -47,21 +45,18 @@ export function initState(length: number): void {
export function setIndex(index: number): void { export function setIndex(index: number): void {
const s = state.get() const s = state.get()
s.index = index s.index = index
s.nextFive = getNextFive(s.index, s.length)
state.set(s) state.set(s)
} }
export function incIndex(): void { export function incIndex(): void {
const s = state.get() const s = state.get()
s.index = increment(s.index, s.length) s.index = increment(s.index, s.length)
s.nextFive = getNextFive(s.index, s.length)
state.set(s) state.set(s)
} }
export function decIndex(): void { export function decIndex(): void {
const s = state.get() const s = state.get()
s.index = decrement(s.index, s.length) s.index = decrement(s.index, s.length)
s.nextFive = getNextFive(s.index, s.length)
state.set(s) state.set(s)
} }
@@ -82,17 +77,9 @@ export function decThreshold(): void {
*/ */
function updateThreshold(state: State, inc: number): State { function updateThreshold(state: State, inc: number): State {
const i = thresholds.findIndex((t) => state.threshold === t.threshold) const i = thresholds.findIndex((t) => state.threshold === t.threshold) + inc
const newItems = thresholds[i + inc] // out of bounds
// out of range if (i < 0 || i >= thresholds.length) return state
if (!newItems) return state const newItems = thresholds[i]
return { ...state, ...newItems } return { ...state, ...newItems }
} }
export function getNextFive(index: number, length: number): number[] {
const five = []
for (let i = 0; i < 5; i++) {
five.push(increment(index + i, length))
}
return five
}

View File

@@ -18,14 +18,14 @@ export function isMobile(): boolean {
return window.matchMedia('(hover: none)').matches return window.matchMedia('(hover: none)').matches
} }
export function getRandom(min: number, max: number) { export function getRandom(min: number, max: number): number {
return Math.floor(Math.random() * (max - min + 1)) + min return Math.floor(Math.random() * (max - min + 1)) + min
} }
export function onVisible( export function onVisible<T extends Element>(
element: HTMLImageElement, element: T,
callback: (arg0: HTMLImageElement) => void callback: (arg0: T) => void
) { ): void {
new IntersectionObserver((entries, observer) => { new IntersectionObserver((entries, observer) => {
entries.forEach((entry) => { entries.forEach((entry) => {
if (entry.intersectionRatio > 0) { if (entry.intersectionRatio > 0) {
@@ -34,7 +34,6 @@ export function onVisible(
} }
}) })
}).observe(element) }).observe(element)
if (!callback) return new Promise((r) => (callback = r))
} }
/** /**
@@ -43,7 +42,7 @@ export function onVisible(
export class Watchable<T> { export class Watchable<T> {
constructor(private obj: T) {} constructor(private obj: T) {}
private watchers: (() => void)[] = [] private readonly watchers: Array<() => void> = []
get(): T { get(): T {
return this.obj return this.obj
@@ -51,7 +50,9 @@ export class Watchable<T> {
set(e: T): void { set(e: T): void {
this.obj = e this.obj = e
this.watchers.forEach((watcher) => watcher()) this.watchers.forEach((watcher) => {
watcher()
})
} }
addWatcher(watcher: () => void): void { addWatcher(watcher: () => void): void {