mirror of
https://github.com/nuxt/nuxt.git
synced 2025-01-18 17:35:57 +00:00
feat(nuxt): slow down loading indicator when approaching 100% (#25119)
This commit is contained in:
parent
ecc4c8e0c5
commit
90ca0e8797
@ -33,6 +33,7 @@ You can pass custom HTML or components through the loading indicator's default s
|
||||
- `height`: Height of the loading bar, in pixels (default `3`).
|
||||
- `duration`: Duration of the loading bar, in milliseconds (default `2000`).
|
||||
- `throttle`: Throttle the appearing and hiding, in milliseconds (default `200`).
|
||||
- `estimatedProgress`: By default Nuxt will back off as it approaches 100%. You can provide a custom function to customize the progress estimation, which is a function that receives the duration of the loading bar (above) and the elapsed time. It should return a value between 0 and 100.
|
||||
|
||||
::callout
|
||||
This component is optional. :br
|
||||
@ -42,3 +43,7 @@ To achieve full customization, you can implement your own one based on [its sour
|
||||
::callout
|
||||
You can hook into the underlying indicator instance using [the `useLoadingIndicator` composable](/docs/api/composables/use-loading-indicator), which will allow you to trigger start/finish events yourself.
|
||||
::
|
||||
|
||||
::callout
|
||||
The loading indicator's speed gradually decreases after reaching a specific point controlled by `estimatedProgress`. This adjustment provides a more accurate reflection of longer page loading times and prevents the indicator from prematurely showing 100% completion.
|
||||
::
|
||||
|
@ -13,6 +13,12 @@ links:
|
||||
A composable which returns the loading state of the page. Used by [`<NuxtLoadingIndicator>`](/docs/api/components/nuxt-loading-indicator) and controllable.
|
||||
It hooks into [`page:loading:start`](/docs/api/advanced/hooks#app-hooks-runtime) and [`page:loading:end`](/docs/api/advanced/hooks#app-hooks-runtime) to change its state.
|
||||
|
||||
## Parameters
|
||||
|
||||
- `duration`: Duration of the loading bar, in milliseconds (default `2000`).
|
||||
- `throttle`: Throttle the appearing and hiding, in milliseconds (default `200`).
|
||||
- `estimatedProgress`: By default Nuxt will back off as it approaches 100%. You can provide a custom function to customize the progress estimation, which is a function that receives the duration of the loading bar (above) and the elapsed time. It should return a value between 0 and 100.
|
||||
|
||||
## Properties
|
||||
|
||||
### `isLoading`
|
||||
@ -38,3 +44,16 @@ Set the `progress` value to `100`, stop all timers and intervals then reset the
|
||||
### `clear()`
|
||||
|
||||
Used by `finish()`. Clear all timers and intervals used by the composable.
|
||||
|
||||
## Example
|
||||
|
||||
```ts
|
||||
<script setup lang="ts">
|
||||
const { progress, isLoading, start, finish, clear } = useLoadingIndicator({
|
||||
duration: 2000,
|
||||
throttle: 200,
|
||||
// This is how progress is calculated by default
|
||||
estimatedProgress: (duration, elapsed) => (2 / Math.PI * 100) * Math.atan(elapsed / duration * 100 / 50)
|
||||
})
|
||||
</script>
|
||||
```
|
||||
|
@ -19,12 +19,17 @@ export default defineComponent({
|
||||
color: {
|
||||
type: [String, Boolean],
|
||||
default: 'repeating-linear-gradient(to right,#00dc82 0%,#34cdfe 50%,#0047e1 100%)'
|
||||
}
|
||||
},
|
||||
estimatedProgress: {
|
||||
type: Function as unknown as () => (duration: number, elapsed: number) => number,
|
||||
required: false
|
||||
},
|
||||
},
|
||||
setup (props, { slots, expose }) {
|
||||
const { progress, isLoading, start, finish, clear } = useLoadingIndicator({
|
||||
duration: props.duration,
|
||||
throttle: props.throttle
|
||||
throttle: props.throttle,
|
||||
estimatedProgress: props.estimatedProgress,
|
||||
})
|
||||
|
||||
expose({
|
||||
|
@ -7,10 +7,12 @@ export type LoadingIndicatorOpts = {
|
||||
duration: number
|
||||
/** @default 200 */
|
||||
throttle: number
|
||||
}
|
||||
|
||||
function _increase (progress: Ref<number>, num: number) {
|
||||
progress.value = Math.min(100, progress.value + num)
|
||||
/**
|
||||
* You can provide a custom function to customize the progress estimation,
|
||||
* which is a function that receives the duration of the loading bar (above)
|
||||
* and the elapsed time. It should return a value between 0 and 100.
|
||||
*/
|
||||
estimatedProgress?: (duration: number, elapsed: number) => number
|
||||
}
|
||||
|
||||
function _hide (isLoading: Ref<boolean>, progress: Ref<number>) {
|
||||
@ -32,14 +34,20 @@ export type LoadingIndicator = {
|
||||
clear: () => void
|
||||
}
|
||||
|
||||
function defaultEstimatedProgress (duration: number, elapsed: number): number {
|
||||
const completionPercentage = elapsed / duration * 100
|
||||
return (2 / Math.PI * 100) * Math.atan(completionPercentage / 50)
|
||||
}
|
||||
|
||||
function createLoadingIndicator (opts: Partial<LoadingIndicatorOpts> = {}) {
|
||||
const { duration = 2000, throttle = 200 } = opts
|
||||
const getProgress = opts.estimatedProgress || defaultEstimatedProgress
|
||||
const nuxtApp = useNuxtApp()
|
||||
const progress = ref(0)
|
||||
const isLoading = ref(false)
|
||||
const step = computed(() => 10000 / duration)
|
||||
let done = false
|
||||
let rafId: number
|
||||
|
||||
let _timer: any = null
|
||||
let _throttle: any = null
|
||||
|
||||
const start = () => set(0)
|
||||
@ -54,30 +62,42 @@ function createLoadingIndicator (opts: Partial<LoadingIndicatorOpts> = {}) {
|
||||
if (throttle && import.meta.client) {
|
||||
_throttle = setTimeout(() => {
|
||||
isLoading.value = true
|
||||
_startTimer()
|
||||
_startProgress()
|
||||
}, throttle)
|
||||
} else {
|
||||
isLoading.value = true
|
||||
_startTimer()
|
||||
_startProgress()
|
||||
}
|
||||
}
|
||||
|
||||
function finish () {
|
||||
progress.value = 100
|
||||
done = true
|
||||
clear()
|
||||
_hide(isLoading, progress)
|
||||
}
|
||||
|
||||
function clear () {
|
||||
clearInterval(_timer)
|
||||
clearTimeout(_throttle)
|
||||
_timer = null
|
||||
cancelAnimationFrame(rafId)
|
||||
_throttle = null
|
||||
}
|
||||
|
||||
function _startTimer () {
|
||||
function _startProgress () {
|
||||
done = false
|
||||
let startTimeStamp: number
|
||||
|
||||
function step (timeStamp: number): void {
|
||||
if (done) { return }
|
||||
|
||||
startTimeStamp ??= timeStamp
|
||||
const elapsed = timeStamp - startTimeStamp
|
||||
progress.value = Math.max(0, Math.min(100, getProgress(duration, elapsed)))
|
||||
rafId = requestAnimationFrame(step)
|
||||
}
|
||||
|
||||
if (import.meta.client) {
|
||||
_timer = setInterval(() => { _increase(progress, step.value) }, 100)
|
||||
rafId = requestAnimationFrame(step)
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user