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 {
cursor: pointer;
}
.hide {
display: none;
}

View File

@@ -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;
}
}
}

View File

@@ -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)
}
}

View File

@@ -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'
const isClose = index === 1
// close
if (isClose) {
overlay.addEventListener(
'click',
() => {
handleClick(navItem)
handleCloseClick(navItem)
},
{ passive: true }
)
overlay.addEventListener(
'keydown',
() => {
handleClick(navItem)
handleCloseClick(navItem)
},
{ passive: true }
)
overlay.addEventListener(
'mouseover',
() => {
setCustomCursor(navItem)
handleCloseHover(navItem)
},
{ passive: true }
)
overlay.addEventListener(
'focus',
() => {
setCustomCursor(navItem)
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)
}

View File

@@ -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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,6 +1,6 @@
{
"name": "bridget",
"version": "v0.0.4",
"version": "v1.0.0",
"description": "bridget theme source file",
"packageManager": "pnpm@8.10.2",
"private": true,
@@ -9,7 +9,7 @@
"lint": "eslint . --fix && prettier --write .",
"lint:check": "eslint . && prettier . --check",
"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",
"rollup:build": "rollup -c --environment BUILD:production",
"rollup:server": "rollup -c --watch --environment BUILD:production",

File diff suppressed because one or more lines are too long