Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(useImage): new function (#1460)
Co-authored-by: Lucio Rubens <luciorubeens@users.noreply.github.com> Co-authored-by: Anthony Fu <anthonyfu117@hotmail.com>
- Loading branch information
1 parent
9ed2898
commit f4b37a5
Showing
7 changed files
with
158 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -196,7 +196,6 @@ | |
"shorwood", | ||
"xstevenyung", | ||
"Atinux", | ||
"thalesagapito", | ||
"thierrymichel", | ||
"LeSuisse", | ||
"tmkx", | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
import { defineComponent, h, reactive } from 'vue-demi' | ||
import { useImage } from '../useImage' | ||
import type { UseImageOptions } from '../useImage' | ||
|
||
import type { RenderableComponent } from '../types' | ||
|
||
export const UseImage = defineComponent<UseImageOptions & RenderableComponent>({ | ||
name: 'UseImage', | ||
props: [ | ||
'src', | ||
'srcset', | ||
'sizes', | ||
'as', | ||
] as unknown as undefined, | ||
setup(props, { slots }) { | ||
const data = reactive(useImage(props)) | ||
|
||
return () => { | ||
if (data.isLoading && slots.loading) | ||
return slots.loading(data) | ||
|
||
else if (data.error && slots.error) | ||
return slots.error(data.error) | ||
|
||
if (slots.default) | ||
return slots.default(data) | ||
|
||
return h(props.as || 'img', props) | ||
} | ||
}, | ||
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
<script lang="ts" setup> | ||
import { useImage } from '@vueuse/core' | ||
import { ref } from 'vue' | ||
const imageOptions = ref({ src: 'https://place.dog/300/200' }) | ||
const { isLoading, error } = useImage(imageOptions, { delay: 2000 }) | ||
const change = () => { | ||
const time = new Date().getTime() | ||
imageOptions.value.src = `https://place.dog/300/200?t=${time}` | ||
} | ||
</script> | ||
|
||
<template> | ||
<div v-if="isLoading" class="w-[300px] h-[200px] animate-pulse bg-gray-500/5 p-2"> | ||
Loading... | ||
</div> | ||
<div v-else-if="error"> | ||
Failed | ||
</div> | ||
<img v-else :src="imageOptions.src" class="w-[300px] h-[200px]"> | ||
|
||
<button @click="change"> | ||
Change | ||
</button> | ||
</template> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
--- | ||
category: Browser | ||
--- | ||
|
||
# useImage | ||
|
||
Reactive load an image in the browser, you can wait the result to display it or show a fallback. | ||
|
||
## Usage | ||
|
||
```html | ||
<script setup> | ||
import { useImage } from '@vueuse/core' | ||
const avatarUrl = 'https://place.dog/300/200' | ||
const { isLoading } = useImage({ src: avatarUrl }) | ||
</script> | ||
|
||
<template> | ||
<span v-if="isLoading">Loading</span> | ||
<img v-else :src="avatarUrl"> | ||
</template> | ||
``` | ||
|
||
## Component Usage | ||
|
||
```html | ||
<template> | ||
<UseImage src="https://place.dog/300/200"> | ||
<template #loading> | ||
Loading.. | ||
</template> | ||
|
||
<template #error> | ||
Failed | ||
</template> | ||
</UseImage> | ||
</template> | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
import type { MaybeRef } from '@vueuse/shared' | ||
|
||
import { unref, watch } from 'vue-demi' | ||
|
||
import type { AsyncStateOptions } from '../useAsyncState' | ||
import { useAsyncState } from '../useAsyncState' | ||
|
||
export interface UseImageOptions { | ||
/** Address of the resource */ | ||
src: string | ||
/** Images to use in different situations, e.g., high-resolution displays, small monitors, etc. */ | ||
srcset?: string | ||
/** Image sizes for different page layouts */ | ||
sizes?: string | ||
} | ||
|
||
async function loadImage(options: UseImageOptions): Promise<HTMLImageElement> { | ||
return new Promise((resolve, reject) => { | ||
const img = new Image() | ||
const { src, srcset, sizes } = options | ||
|
||
img.src = src | ||
if (srcset) | ||
img.srcset = srcset | ||
if (sizes) | ||
img.sizes = sizes | ||
|
||
img.onload = () => resolve(img) | ||
img.onerror = reject | ||
}) | ||
} | ||
|
||
/** | ||
* Reactive load an image in the browser, you can wait the result to display it or show a fallback. | ||
* | ||
* @see https://vueuse.org/useImage | ||
* @param options Image attributes, as used in the <img> tag | ||
* @param asyncStateOptions | ||
*/ | ||
export const useImage = <Shallow extends true>( | ||
options: MaybeRef<UseImageOptions>, | ||
asyncStateOptions: AsyncStateOptions<Shallow> = {}, | ||
) => { | ||
const state = useAsyncState<HTMLImageElement | undefined>( | ||
() => loadImage(unref(options)), | ||
undefined, | ||
{ | ||
resetOnExecute: true, | ||
...asyncStateOptions, | ||
}, | ||
) | ||
|
||
watch( | ||
() => unref(options), | ||
() => state.execute(asyncStateOptions.delay), | ||
{ deep: true }, | ||
) | ||
|
||
return state | ||
} |