refactor: reduce amount of createEffect and improve imports (#284)

* refactor: update import syntax

* feat: add GalleryImage component for simplicity

* refactor: replace createEffect in GalleryNav with createMemo

* refactor: refactor Gallery component logic and improve imports
This commit is contained in:
Spedon
2024-02-23 15:59:32 +08:00
committed by GitHub
parent 875113448b
commit e081e139fc
10 changed files with 115 additions and 100 deletions

View File

@@ -1,6 +1,6 @@
import { createSignal, onCleanup, onMount, type Accessor, type JSX } from 'solid-js'
export function CustomCursor(props: {
export default function CustomCursor(props: {
children?: JSX.Element
active: Accessor<boolean>
cursorText: Accessor<string>

View File

@@ -4,10 +4,10 @@ import { Show, createMemo, createSignal, type JSX } from 'solid-js'
import type { ImageJSON } from '../resources'
import type { Vector } from '../utils'
import { CustomCursor } from './customCursor'
import { Nav } from './nav'
import { Stage } from './stage'
import { StageNav } from './stageNav'
import CustomCursor from './customCursor'
import Nav from './nav'
import Stage from './stage'
import StageNav from './stageNav'
/**
* interfaces and types

View File

@@ -51,7 +51,7 @@ function updateIndexText(indexValue: string, indexLength: string): void {
* Nav component
*/
export function Nav(): null {
export default function Nav(): null {
const [state, { incThreshold, decThreshold }] = useState()
createEffect(() => {

View File

@@ -90,7 +90,7 @@ function onMutation<T extends HTMLElement>(
* Stage component
*/
export function Stage(props: {
export default function Stage(props: {
ijs: ImageJSON[]
setIsLoading: Setter<boolean>
isOpen: Accessor<boolean>

View File

@@ -5,7 +5,7 @@ import { decrement, increment, type Vector } from '../utils'
import type { HistoryItem } from './layout'
export function StageNav(props: {
export default function StageNav(props: {
children?: JSX.Element
prevText: string
closeText: string

View File

@@ -31,7 +31,7 @@ function onIntersection<T extends HTMLElement>(
}).observe(element)
}
export function Collection(props: {
export default function Collection(props: {
children?: JSX.Element
ijs: ImageJSON[]
isAnimating: Accessor<boolean>

View File

@@ -1,13 +1,16 @@
import { type gsap } from 'gsap'
import {
createEffect,
createSignal,
For,
on,
onMount,
Show,
type Accessor,
type JSX,
type Setter
} from 'solid-js'
import { createStore } from 'solid-js/store'
import { type Swiper } from 'swiper'
import invariant from 'tiny-invariant'
@@ -15,8 +18,8 @@ import { type ImageJSON } from '../resources'
import { useState } from '../state'
import { loadGsap, type Vector } from '../utils'
import { capitalizeFirstLetter, GalleryNav } from './galleryNav'
import type { MobileImage } from './layout'
import GalleryImage from './galleryImage'
import GalleryNav, { capitalizeFirstLetter } from './galleryNav'
function removeDuplicates<T>(arr: T[]): T[] {
if (arr.length < 2) return arr // optimization
@@ -28,7 +31,7 @@ async function loadSwiper(): Promise<typeof Swiper> {
return s.Swiper
}
export function Gallery(props: {
export default function Gallery(props: {
children?: JSX.Element
ijs: ImageJSON[]
closeText: string
@@ -43,10 +46,6 @@ export function Gallery(props: {
let _gsap: typeof gsap
let _swiper: Swiper
// eslint-disable-next-line solid/reactivity
const imgs: MobileImage[] = Array<MobileImage>(props.ijs.length)
// eslint-disable-next-line solid/reactivity
const loadingDivs: HTMLDivElement[] = Array<HTMLDivElement>(props.ijs.length)
let curtain: HTMLDivElement | undefined
let gallery: HTMLDivElement | undefined
let galleryInner: HTMLDivElement | undefined
@@ -56,16 +55,18 @@ export function Gallery(props: {
// states
let lastIndex = -1
let libLoaded = false
let mounted = false
let navigateVector: Vector = 'none'
const [state, { setIndex }] = useState()
const [libLoaded, setLibLoaded] = createSignal(false)
// eslint-disable-next-line solid/reactivity
const [loads, setLoads] = createStore(Array<boolean>(props.ijs.length).fill(false))
// helper functions
const slideUp: () => void = () => {
// isAnimating is prechecked in isOpen effect
if (!libLoaded || !mounted) return
if (!libLoaded() || !mounted) return
props.setIsAnimating(true)
invariant(curtain, 'curtain is not defined')
@@ -133,11 +134,7 @@ export function Gallery(props: {
activeImagesIndex = [currentIndex, nextIndex, prevIndex]
break
}
removeDuplicates(activeImagesIndex).forEach((i) => {
const e = imgs[i]
if (e.src === e.dataset.src) return // already loaded
e.src = e.dataset.src
})
setLoads(removeDuplicates(activeImagesIndex), true)
}
const changeSlide: (slide: number) => void = (slide) => {
@@ -149,22 +146,6 @@ export function Gallery(props: {
// effects
onMount(() => {
imgs.forEach((img, i) => {
const loadingDiv = loadingDivs[i]
img.addEventListener(
'load',
() => {
if (state().index !== parseInt(img.dataset.index)) {
_gsap.set(img, { opacity: 1 })
_gsap.set(loadingDiv, { opacity: 0 })
} else {
_gsap.to(img, { opacity: 1, delay: 0.5, duration: 0.5, ease: 'power3.out' })
_gsap.to(loadingDiv, { opacity: 0, duration: 0.5, ease: 'power3.in' })
}
},
{ once: true, passive: true }
)
})
window.addEventListener(
'touchstart',
() => {
@@ -186,7 +167,7 @@ export function Gallery(props: {
.catch((e) => {
console.log(e)
})
libLoaded = true
setLibLoaded(true)
},
{ once: true, passive: true }
)
@@ -234,26 +215,19 @@ export function Gallery(props: {
<div ref={gallery} class="gallery">
<div ref={galleryInner} class="galleryInner">
<div class="swiper-wrapper">
<For each={props.ijs}>
{(ij, i) => (
<div class="swiper-slide">
<div class="slideContainer">
<img
ref={imgs[i()]}
height={ij.hiImgH}
width={ij.hiImgW}
data-src={ij.hiUrl}
data-index={ij.index}
alt={ij.alt}
style={{ opacity: 0 }}
<Show when={libLoaded()}>
<For each={props.ijs}>
{(ij, i) => (
<div class="swiper-slide">
<GalleryImage
load={loads[i()]}
ij={ij}
loadingText={_loadingText}
/>
<div ref={loadingDivs[i()]} class="loadingText">
{_loadingText}
</div>
</div>
</div>
)}
</For>
)}
</For>
</Show>
</div>
</div>
<GalleryNav

View File

@@ -0,0 +1,69 @@
import { onMount, type JSX } from 'solid-js'
import invariant from 'tiny-invariant'
import type { ImageJSON } from '../resources'
import { useState } from '../state'
import { loadGsap } from '../utils'
export default function GalleryImage(props: {
children?: JSX.Element
load: boolean
ij: ImageJSON
loadingText: string
}): JSX.Element {
let img: HTMLImageElement | undefined
let loadingDiv: HTMLDivElement | undefined
let _gsap: typeof gsap
const [state] = useState()
onMount(() => {
loadGsap()
.then((g) => {
_gsap = g
})
.catch((e) => {
console.log(e)
})
img?.addEventListener(
'load',
() => {
invariant(img, 'ref must be defined')
invariant(loadingDiv, 'loadingDiv must be defined')
if (state().index !== props.ij.index) {
_gsap.set(img, { opacity: 1 })
_gsap.set(loadingDiv, { opacity: 0 })
} else {
_gsap.to(img, {
opacity: 1,
delay: 0.5,
duration: 0.5,
ease: 'power3.out'
})
_gsap.to(loadingDiv, { opacity: 0, duration: 0.5, ease: 'power3.in' })
}
},
{ once: true, passive: true }
)
})
return (
<>
<div class="slideContainer">
<img
ref={img}
{...(props.load && { src: props.ij.hiUrl })}
height={props.ij.hiImgH}
width={props.ij.hiImgW}
data-src={props.ij.hiUrl}
alt={props.ij.alt}
style={{ opacity: 0 }}
/>
<div ref={loadingDiv} class="loadingText">
{props.loadingText}
</div>
</div>
</>
)
}

View File

@@ -1,4 +1,4 @@
import { createEffect, on, type Accessor, type JSX, type Setter } from 'solid-js'
import { createMemo, type Accessor, type JSX, type Setter } from 'solid-js'
import { useState } from '../state'
import { expand } from '../utils'
@@ -7,63 +7,35 @@ export function capitalizeFirstLetter(str: string): string {
return str.charAt(0).toUpperCase() + str.slice(1)
}
export function GalleryNav(props: {
export default function GalleryNav(props: {
children?: JSX.Element
closeText: string
isAnimating: Accessor<boolean>
setIsOpen: Setter<boolean>
}): JSX.Element {
// variables
const indexNums: HTMLSpanElement[] = Array<HTMLSpanElement>(8)
// states
const [state] = useState()
const stateLength = state().length
// helper functions
const updateIndexText: () => void = () => {
const indexValue: string = expand(state().index + 1)
const indexLength: string = expand(stateLength)
indexNums.forEach((e: HTMLSpanElement, i: number) => {
if (i < 4) {
e.innerText = indexValue[i]
} else {
e.innerText = indexLength[i - 4]
}
})
}
const indexValue = createMemo(() => expand(state().index + 1))
const indexLength = createMemo(() => expand(state().length))
const onClick: () => void = () => {
if (props.isAnimating()) return
props.setIsOpen(false)
}
// effects
createEffect(
on(
() => {
state()
},
() => {
updateIndexText()
},
{ defer: true }
)
)
return (
<>
<div class="nav">
<div>
<span ref={indexNums[0]} class="num" />
<span ref={indexNums[1]} class="num" />
<span ref={indexNums[2]} class="num" />
<span ref={indexNums[3]} class="num" />
<span class="num">{indexValue()[0]}</span>
<span class="num">{indexValue()[1]}</span>
<span class="num">{indexValue()[2]}</span>
<span class="num">{indexValue()[3]}</span>
<span>/</span>
<span ref={indexNums[4]} class="num" />
<span ref={indexNums[5]} class="num" />
<span ref={indexNums[6]} class="num" />
<span ref={indexNums[7]} class="num" />
<span class="num">{indexLength()[0]}</span>
<span class="num">{indexLength()[1]}</span>
<span class="num">{indexLength()[2]}</span>
<span class="num">{indexLength()[3]}</span>
</div>
<div onClick={onClick} onKeyDown={onClick}>
{capitalizeFirstLetter(props.closeText)}

View File

@@ -2,8 +2,8 @@ import { Show, createSignal, type JSX, type Setter } from 'solid-js'
import type { ImageJSON } from '../resources'
import { Collection } from './collection'
import { Gallery } from './gallery'
import Collection from './collection'
import Gallery from './gallery'
/**
* interfaces