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:
Spedon
2024-01-20 23:34:13 +08:00
committed by GitHub
parent 8c6b38bb49
commit 9fa1b718b8
20 changed files with 200 additions and 65 deletions

View File

@@ -17,3 +17,7 @@ a,
button { button {
cursor: pointer; cursor: pointer;
} }
.hide {
display: none;
}

View File

@@ -27,6 +27,18 @@
height: 100%; height: 100%;
object-fit: contain; object-fit: contain;
} }
.loadingText {
position: absolute;
top: 50%;
left: 50%;
transform: translate3d(-50%, -50%, 0);
}
.slideContainer {
position: relative;
display: inline-block;
}
} }
} }

View File

@@ -25,6 +25,7 @@ export const cordHist = new Watchable<HistoryItem[]>([])
export const isOpen = new Watchable<boolean>(false) export const isOpen = new Watchable<boolean>(false)
export const isAnimating = new Watchable<boolean>(false) export const isAnimating = new Watchable<boolean>(false)
export const active = new Watchable<boolean>(false) export const active = new Watchable<boolean>(false)
export const isLoading = new Watchable<boolean>(false)
let _gsap: typeof gsap let _gsap: typeof gsap
let _Power3: typeof Power3 let _Power3: typeof Power3
@@ -114,9 +115,13 @@ function setPositions(): void {
if (isOpen.get()) { if (isOpen.get()) {
lores(getElTrail()) 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(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) isOpen.set(true)
isAnimating.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() const tl = _gsap.timeline()
// move down and hide trail inactive // move down and hide trail inactive
@@ -292,3 +301,27 @@ function lores(imgs: HTMLImageElement[]): void {
img.width = parseInt(img.dataset.loImgW as string) 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)
}
}

View File

@@ -3,7 +3,14 @@ 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,
isLoading,
isOpen,
minimizeImage
} from './stage'
/** /**
* types * types
@@ -21,6 +28,8 @@ const navItems = [
mainDiv.getAttribute('closeText') as string, mainDiv.getAttribute('closeText') as string,
mainDiv.getAttribute('nextText') as string mainDiv.getAttribute('nextText') as string
] as const ] as const
const loadingText = (mainDiv.getAttribute('loadingText') as string) + '...'
let loadedText = ''
/** /**
* main functions * main functions
@@ -56,39 +65,80 @@ function handleKey(e: KeyboardEvent): void {
*/ */
export function initStageNav(): void { export function initStageNav(): void {
// isLoading
isLoading.addWatcher((o) => {
if (o) setCustomCursor(loadingText)
else setCustomCursor(loadedText)
})
// navOverlay
const navOverlay = document.createElement('div') const navOverlay = document.createElement('div')
navOverlay.className = 'navOverlay' navOverlay.className = 'navOverlay'
for (const navItem of navItems) { for (const [index, navItem] of navItems.entries()) {
const overlay = document.createElement('div') const overlay = document.createElement('div')
overlay.className = 'overlay' overlay.className = 'overlay'
overlay.addEventListener( const isClose = index === 1
'click', // close
() => { if (isClose) {
handleClick(navItem) overlay.addEventListener(
}, 'click',
{ passive: true } () => {
) handleCloseClick(navItem)
overlay.addEventListener( },
'keydown', { passive: true }
() => { )
handleClick(navItem) overlay.addEventListener(
}, 'keydown',
{ passive: true } () => {
) handleCloseClick(navItem)
overlay.addEventListener( },
'mouseover', { passive: true }
() => { )
setCustomCursor(navItem) overlay.addEventListener(
}, 'mouseover',
{ passive: true } () => {
) handleCloseHover(navItem)
overlay.addEventListener( },
'focus', { passive: true }
() => { )
setCustomCursor(navItem) overlay.addEventListener(
}, 'focus',
{ passive: true } () => {
) 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) navOverlay.append(overlay)
} }
active.addWatcher(() => { active.addWatcher(() => {
@@ -127,3 +177,23 @@ function prevImage(): void {
decIndex() 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)
}

View File

@@ -196,14 +196,34 @@ function createGallery(ijs: ImageJSON[]): void {
for (const 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'
// loading indicator
const l = document.createElement('div')
l.className = 'loadingText'
l.innerText =
(document.getElementById('main')?.getAttribute('loadingText') as string) + '...'
// img // img
const e = document.createElement('img') const e = document.createElement('img')
e.dataset.src = ij.hiUrl e.dataset.src = ij.hiUrl
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')
// 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 // append
_swiperSlide.append(e) p.append(e)
p.append(l)
_swiperSlide.append(p)
_swiperWrapper.append(_swiperSlide) _swiperWrapper.append(_swiperSlide)
} }
// swiper node // swiper node

View File

@@ -8,3 +8,5 @@ other = "schließen"
other = "schwelle" other = "schwelle"
[404] [404]
other = "seite nicht gefunden" other = "seite nicht gefunden"
[loading]
other = "lade"

View File

@@ -8,3 +8,5 @@ other = "close"
other = "threshold" other = "threshold"
[404] [404]
other = "page not found" other = "page not found"
[loading]
other = "loading"

View File

@@ -8,3 +8,5 @@ other = "cerrar"
other = "umbral" other = "umbral"
[404] [404]
other = "página no encontrada" other = "página no encontrada"
[loading]
other = "cargando"

View File

@@ -8,3 +8,5 @@ other = "fermer"
other = "seuil" other = "seuil"
[404] [404]
other = "page non trouvée" other = "page non trouvée"
[loading]
other = "chargement"

View File

@@ -8,3 +8,5 @@ other = "chiudi"
other = "soglia" other = "soglia"
[404] [404]
other = "pagina non trovata" other = "pagina non trovata"
[loading]
other = "caricamento"

View File

@@ -8,3 +8,5 @@ other = "閉じる"
other = "しきい値" other = "しきい値"
[404] [404]
other = "ページが見つかりません" other = "ページが見つかりません"
[loading]
other = "読み込み中"

View File

@@ -8,3 +8,5 @@ other = "닫기"
other = "임계값" other = "임계값"
[404] [404]
other = "페이지를 찾을 수 없습니다" other = "페이지를 찾을 수 없습니다"
[loading]
other = "로딩중"

View File

@@ -8,3 +8,5 @@ other = "关闭"
other = "阈值" other = "阈值"
[404] [404]
other = "页面不存在" other = "页面不存在"
[loading]
other = "加载中"

View File

@@ -8,3 +8,5 @@ other = "關閉"
other = "閾值" other = "閾值"
[404] [404]
other = "找不到頁面" other = "找不到頁面"
[loading]
other = "載入中"

View File

@@ -8,3 +8,5 @@ other = "關閉"
other = "閾值" other = "閾值"
[404] [404]
other = "找不到頁面" other = "找不到頁面"
[loading]
other = "載入中"

View File

@@ -8,3 +8,5 @@ other = "关闭"
other = "阈值" other = "阈值"
[404] [404]
other = "页面不存在" other = "页面不存在"
[loading]
other = "加载中"

View File

@@ -8,3 +8,5 @@ other = "關閉"
other = "閾值" other = "閾值"
[404] [404]
other = "找不到頁面" other = "找不到頁面"
[loading]
other = "載入中"

View File

@@ -3,6 +3,7 @@
document.getElementById("main").setAttribute("nextText", "{{- i18n "next" -}}") document.getElementById("main").setAttribute("nextText", "{{- i18n "next" -}}")
document.getElementById("main").setAttribute("prevText", "{{- i18n "prev" -}}") document.getElementById("main").setAttribute("prevText", "{{- i18n "prev" -}}")
document.getElementById("main").setAttribute("closeText", "{{- i18n "close" -}}") document.getElementById("main").setAttribute("closeText", "{{- i18n "close" -}}")
document.getElementById("main").setAttribute("loadingText", "{{- i18n "loading" -}}")
</script> </script>
<div class="container"> <div class="container">
{{- partial "nav.html" . -}} {{- partial "nav.html" . -}}

View File

@@ -1,6 +1,6 @@
{ {
"name": "bridget", "name": "bridget",
"version": "v0.0.4", "version": "v1.0.0",
"description": "bridget theme source file", "description": "bridget theme source file",
"packageManager": "pnpm@8.10.2", "packageManager": "pnpm@8.10.2",
"private": true, "private": true,
@@ -9,7 +9,7 @@
"lint": "eslint . --fix && prettier --write .", "lint": "eslint . --fix && prettier --write .",
"lint:check": "eslint . && prettier . --check", "lint:check": "eslint . && prettier . --check",
"dev": "run-p rollup:dev hugo:dev", "dev": "run-p rollup:dev hugo:dev",
"build": "rm -f ./static/bundled/js/* && run-s rollup:build hugo:build && yes | cp -rf ./exampleSite/public/css/* ./static/bundled/css", "build": "rm -f ./static/bundled/js/* && run-s rollup:build hugo:build && yes | cp -rf ./exampleSite/public/css/*.css ./static/bundled/css",
"server": "run-p rollup:server hugo:server", "server": "run-p rollup:server hugo:server",
"rollup:build": "rollup -c --environment BUILD:production", "rollup:build": "rollup -c --environment BUILD:production",
"rollup:server": "rollup -c --watch --environment BUILD:production", "rollup:server": "rollup -c --watch --environment BUILD:production",

File diff suppressed because one or more lines are too long