mirror of
https://github.com/Sped0n/bridget.git
synced 2026-04-14 10:09:31 -07:00
feat: add loading indicator for desktop and mobile (#244)
* feat: add new CSS rule for hiding elements - Add a new CSS rule for the class "hide" with a display property set to "none". * feat: refactor image loading and navigation logic on desktop - Add a line of code in `assets/ts/desktop/stage.ts` to reset the `src` of `elc` and add a class `hide` to it - Add a line of code in `assets/ts/desktop/stage.ts` to call the `loader` function with `elc` as an argument - Add a function `loader` in `assets/ts/desktop/stage.ts` to handle image loading and error events - Modify the `initStageNav` function in `assets/ts/desktop/stageNav.ts` to watch the `isLoading` state and set custom cursor accordingly - Modify the `initStageNav` function in `assets/ts/desktop/stageNav.ts` to handle close click events by calling the `handleClick` function and setting `isLoading` state to false - Modify the `initStageNav` function in `assets/ts/desktop/stageNav.ts` to handle previous/next click events by calling the `handleClick` function only if `isLoading` is false - Modify the `initStageNav` function in `assets/ts/desktop/stageNav.ts` to handle previous/next hover events by setting `loadedText` and updating custom cursor depending on `isLoading` state * feat: refactor createGallery function and enhance loading functionality on mobile - Add a loading text element to the gallery.scss file - Add a loading indicator to the createGallery function in gallery.ts - Modify the createGallery function in gallery.ts to hide the loading text element on image load - Move the image element append logic to the parent container in the createGallery function in gallery.ts * feat: update translations and add new loading translation in i18n files * chore: remove css source map * chore: modify build command to ignore css source map * refactor: remove unnecessary style * fix: fix desktop cursor text transition bug
This commit is contained in:
@@ -17,3 +17,7 @@ a,
|
||||
button {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.hide {
|
||||
display: none;
|
||||
}
|
||||
|
||||
@@ -27,6 +27,18 @@
|
||||
height: 100%;
|
||||
object-fit: contain;
|
||||
}
|
||||
|
||||
.loadingText {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate3d(-50%, -50%, 0);
|
||||
}
|
||||
|
||||
.slideContainer {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -25,6 +25,7 @@ export const cordHist = new Watchable<HistoryItem[]>([])
|
||||
export const isOpen = new Watchable<boolean>(false)
|
||||
export const isAnimating = new Watchable<boolean>(false)
|
||||
export const active = new Watchable<boolean>(false)
|
||||
export const isLoading = new Watchable<boolean>(false)
|
||||
|
||||
let _gsap: typeof gsap
|
||||
let _Power3: typeof Power3
|
||||
@@ -114,9 +115,13 @@ function setPositions(): void {
|
||||
|
||||
if (isOpen.get()) {
|
||||
lores(getElTrail())
|
||||
hires([getElCurrent(), getElPrev(), getElNext()])
|
||||
const elc = getElCurrent()
|
||||
elc.src = '' // reset src to ensure we only display hires images
|
||||
elc.classList.add('hide')
|
||||
hires([elc, getElPrev(), getElNext()])
|
||||
_gsap.set(imgs, { opacity: 0 })
|
||||
_gsap.set(getElCurrent(), { opacity: 1, x: 0, y: 0, scale: 1 })
|
||||
_gsap.set(elc, { opacity: 1, x: 0, y: 0, scale: 1 })
|
||||
loader(elc)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -127,7 +132,11 @@ function expandImage(): void {
|
||||
isOpen.set(true)
|
||||
isAnimating.set(true)
|
||||
|
||||
hires([getElCurrent(), getElPrev(), getElNext()])
|
||||
const elc = getElCurrent()
|
||||
// don't clear src here because we want a better transition
|
||||
|
||||
hires([elc, getElPrev(), getElNext()])
|
||||
loader(elc)
|
||||
|
||||
const tl = _gsap.timeline()
|
||||
// move down and hide trail inactive
|
||||
@@ -292,3 +301,27 @@ function lores(imgs: HTMLImageElement[]): void {
|
||||
img.width = parseInt(img.dataset.loImgW as string)
|
||||
})
|
||||
}
|
||||
|
||||
function loader(e: HTMLImageElement): void {
|
||||
if (!e.complete) {
|
||||
isLoading.set(true)
|
||||
e.addEventListener(
|
||||
'load',
|
||||
() => {
|
||||
isLoading.set(false)
|
||||
e.classList.remove('hide')
|
||||
},
|
||||
{ once: true, passive: true }
|
||||
)
|
||||
e.addEventListener(
|
||||
'error',
|
||||
() => {
|
||||
isLoading.set(false)
|
||||
},
|
||||
{ once: true, passive: true }
|
||||
)
|
||||
} else {
|
||||
e.classList.remove('hide')
|
||||
isLoading.set(false)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,14 @@ import { decIndex, incIndex, state } from '../state'
|
||||
import { decrement, increment } from '../utils'
|
||||
|
||||
import { setCustomCursor } from './customCursor'
|
||||
import { active, cordHist, isAnimating, isOpen, minimizeImage } from './stage'
|
||||
import {
|
||||
active,
|
||||
cordHist,
|
||||
isAnimating,
|
||||
isLoading,
|
||||
isOpen,
|
||||
minimizeImage
|
||||
} from './stage'
|
||||
|
||||
/**
|
||||
* types
|
||||
@@ -21,6 +28,8 @@ const navItems = [
|
||||
mainDiv.getAttribute('closeText') as string,
|
||||
mainDiv.getAttribute('nextText') as string
|
||||
] as const
|
||||
const loadingText = (mainDiv.getAttribute('loadingText') as string) + '...'
|
||||
let loadedText = ''
|
||||
|
||||
/**
|
||||
* main functions
|
||||
@@ -56,39 +65,80 @@ function handleKey(e: KeyboardEvent): void {
|
||||
*/
|
||||
|
||||
export function initStageNav(): void {
|
||||
// isLoading
|
||||
isLoading.addWatcher((o) => {
|
||||
if (o) setCustomCursor(loadingText)
|
||||
else setCustomCursor(loadedText)
|
||||
})
|
||||
// navOverlay
|
||||
const navOverlay = document.createElement('div')
|
||||
navOverlay.className = 'navOverlay'
|
||||
for (const navItem of navItems) {
|
||||
for (const [index, navItem] of navItems.entries()) {
|
||||
const overlay = document.createElement('div')
|
||||
overlay.className = 'overlay'
|
||||
overlay.addEventListener(
|
||||
'click',
|
||||
() => {
|
||||
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 }
|
||||
)
|
||||
const isClose = index === 1
|
||||
// close
|
||||
if (isClose) {
|
||||
overlay.addEventListener(
|
||||
'click',
|
||||
() => {
|
||||
handleCloseClick(navItem)
|
||||
},
|
||||
{ passive: true }
|
||||
)
|
||||
overlay.addEventListener(
|
||||
'keydown',
|
||||
() => {
|
||||
handleCloseClick(navItem)
|
||||
},
|
||||
{ passive: true }
|
||||
)
|
||||
overlay.addEventListener(
|
||||
'mouseover',
|
||||
() => {
|
||||
handleCloseHover(navItem)
|
||||
},
|
||||
{ passive: true }
|
||||
)
|
||||
overlay.addEventListener(
|
||||
'focus',
|
||||
() => {
|
||||
handleCloseHover(navItem)
|
||||
},
|
||||
{ passive: true }
|
||||
)
|
||||
}
|
||||
// prev and next
|
||||
else {
|
||||
overlay.addEventListener(
|
||||
'click',
|
||||
() => {
|
||||
handlePNClick(navItem)
|
||||
},
|
||||
{ passive: true }
|
||||
)
|
||||
overlay.addEventListener(
|
||||
'keydown',
|
||||
() => {
|
||||
handlePNClick(navItem)
|
||||
},
|
||||
{ passive: true }
|
||||
)
|
||||
overlay.addEventListener(
|
||||
'mouseover',
|
||||
() => {
|
||||
handlePNHover(navItem)
|
||||
},
|
||||
{ passive: true }
|
||||
)
|
||||
overlay.addEventListener(
|
||||
'focus',
|
||||
() => {
|
||||
handlePNHover(navItem)
|
||||
},
|
||||
{ passive: true }
|
||||
)
|
||||
}
|
||||
navOverlay.append(overlay)
|
||||
}
|
||||
active.addWatcher(() => {
|
||||
@@ -127,3 +177,23 @@ function prevImage(): void {
|
||||
|
||||
decIndex()
|
||||
}
|
||||
|
||||
function handleCloseClick(navItem: NavItem): void {
|
||||
handleClick(navItem)
|
||||
isLoading.set(false)
|
||||
}
|
||||
|
||||
function handleCloseHover(navItem: NavItem): void {
|
||||
loadedText = navItem
|
||||
setCustomCursor(navItem)
|
||||
}
|
||||
|
||||
function handlePNClick(navItem: NavItem): void {
|
||||
if (!isLoading.get()) handleClick(navItem)
|
||||
}
|
||||
|
||||
function handlePNHover(navItem: NavItem): void {
|
||||
loadedText = navItem
|
||||
if (isLoading.get()) setCustomCursor(loadingText)
|
||||
else setCustomCursor(navItem)
|
||||
}
|
||||
|
||||
@@ -196,14 +196,34 @@ function createGallery(ijs: ImageJSON[]): void {
|
||||
for (const ij of ijs) {
|
||||
const _swiperSlide = document.createElement('div')
|
||||
_swiperSlide.className = 'swiper-slide'
|
||||
// loading indicator
|
||||
const l = document.createElement('div')
|
||||
l.className = 'loadingText'
|
||||
l.innerText =
|
||||
(document.getElementById('main')?.getAttribute('loadingText') as string) + '...'
|
||||
// img
|
||||
const e = document.createElement('img')
|
||||
e.dataset.src = ij.hiUrl
|
||||
e.height = ij.hiImgH
|
||||
e.width = ij.hiImgW
|
||||
e.alt = ij.alt
|
||||
e.classList.add('hide')
|
||||
// load event
|
||||
e.addEventListener(
|
||||
'load',
|
||||
() => {
|
||||
e.classList.remove('hide')
|
||||
l.classList.add('hide')
|
||||
},
|
||||
{ once: true, passive: true }
|
||||
)
|
||||
// parent container
|
||||
const p = document.createElement('div')
|
||||
p.className = 'slideContainer'
|
||||
// append
|
||||
_swiperSlide.append(e)
|
||||
p.append(e)
|
||||
p.append(l)
|
||||
_swiperSlide.append(p)
|
||||
_swiperWrapper.append(_swiperSlide)
|
||||
}
|
||||
// swiper node
|
||||
|
||||
Reference in New Issue
Block a user