diff --git a/docs/content/2.usage/2.nuxt-picture.md b/docs/content/2.usage/2.nuxt-picture.md index 5b4c5fe3a..2d4bf5ec9 100644 --- a/docs/content/2.usage/2.nuxt-picture.md +++ b/docs/content/2.usage/2.nuxt-picture.md @@ -1,5 +1,5 @@ --- -title: '' +title: "" description: Discover how to use and configure the Nuxt Picture component. --- @@ -17,13 +17,34 @@ See props supported by[``](/usage/nuxt-img#props) ### `format` -Format on pictures can be used to serve images in multiple formats. A legacy format will be generated automatically. So in the example below avif, webp and png would be generated. They will be added in the same order they are added to the format attribute. +You can use this option to configure the format of your images. The available formats are webp, avif, jpeg, jpg, png, and gif. The order of the formats is important, as the first format that is supported by the browser will be used. You can pass multiple values like ['avif', 'webp'] or as a comma-separated string like 'avif,webp'. + +A legacy format will be generated automatically. They will be added in the order of the formats array, the legacy format will be added last. ```html - +// generates avif, webp and png + + +// generates avif, webp and jepg + + +// generates wepb and png + ``` -Available formats are `webp`, `avif`, `jpeg`, `jpg`, `png` and `gif`. If the format is not specified, it will respect the default image format. +If the format is not specified, it will respect the default image format. ### `legacyFormat` diff --git a/playground/pages/picture.vue b/playground/pages/picture.vue index 64d32cfc4..4607b47a8 100644 --- a/playground/pages/picture.vue +++ b/playground/pages/picture.vue @@ -1,6 +1,6 @@ diff --git a/src/runtime/components/_base.ts b/src/runtime/components/_base.ts index fd6c9235d..4cd8b688f 100644 --- a/src/runtime/components/_base.ts +++ b/src/runtime/components/_base.ts @@ -43,6 +43,11 @@ export const baseImageProps = { } } +export const basePictureProps = { + ...baseImageProps, + format: { type: [String, Array] as unknown as () => string | string[], default: undefined } +} + export interface BaseImageAttrs { width?: number height?: number @@ -66,6 +71,10 @@ export interface BaseImageModifiers { [key: string]: any } +export interface BasePictureModifiers extends Omit { + format?: string | string[]; +} + export const useBaseImage = (props: ExtractPropTypes) => { const options = computed(() => { return { @@ -109,3 +118,47 @@ export const useBaseImage = (props: ExtractPropTypes) => modifiers } } + +export const useBasePicture = (props: ExtractPropTypes) => { + const options = computed(() => { + return { + provider: props.provider, + preset: props.preset + } + }) + + const attrs = computed(() => { + return { + width: parseSize(props.width), + height: parseSize(props.height), + alt: props.alt, + referrerpolicy: props.referrerpolicy, + usemap: props.usemap, + longdesc: props.longdesc, + ismap: props.ismap, + crossorigin: props.crossorigin === true ? 'anonymous' : props.crossorigin || undefined, + loading: props.loading, + decoding: props.decoding + } + }) + + const $img = useImage() + + const modifiers = computed(() => { + return { + ...props.modifiers, + width: parseSize(props.width), + height: parseSize(props.height), + format: props.format, + quality: props.quality || $img.options.quality, + background: props.background, + fit: props.fit + } + }) + + return { + options, + attrs, + modifiers + } +} diff --git a/src/runtime/components/nuxt-picture.ts b/src/runtime/components/nuxt-picture.ts index 213994821..9a0cece6d 100644 --- a/src/runtime/components/nuxt-picture.ts +++ b/src/runtime/components/nuxt-picture.ts @@ -1,11 +1,12 @@ import { h, defineComponent, ref, computed, onMounted } from 'vue' import { prerenderStaticImages } from '../utils/prerender' -import { useBaseImage, baseImageProps } from './_base' +import { baseImageProps, useBasePicture } from './_base' import { useImage, useHead, useNuxtApp } from '#imports' import { getFileExtension } from '#image' export const pictureProps = { ...baseImageProps, + format: { type: [String, Array], default: undefined }, legacyFormat: { type: String, default: null }, imgAttrs: { type: Object, default: null } } @@ -16,7 +17,7 @@ export default defineComponent({ emits: ['load'], setup: (props, ctx) => { const $img = useImage() - const _base = useBaseImage(props) + const _base = useBasePicture(props) const originalFormat = computed(() => getFileExtension(props.src)) const isTransparent = computed(() => ['png', 'webp', 'gif', 'svg'].includes(originalFormat.value)) @@ -28,7 +29,7 @@ export default defineComponent({ type Source = { srcset?: string, src?: string, type?: string, sizes?: string } const sources = computed(() => { - const formats = props.format?.split(',') || (originalFormat.value === 'svg' ? ['svg'] : ($img.options.format?.length ? [...$img.options.format] : ['webp'])) + const formats = (Array.isArray(props.format) ? props.format : props.format?.split(',') || (originalFormat.value === 'svg' ? ['svg'] : ($img.options.format?.length ? [...$img.options.format] : ['webp']))) as string[] if (formats[0] === 'svg') { return [{ src: props.src }] } @@ -44,7 +45,6 @@ export default defineComponent({ const { srcset, sizes, src } = $img.getSizes(props.src!, { ..._base.options.value, sizes: props.sizes || $img.options.screens, - densities: props.densities, modifiers: { ..._base.modifiers.value, format } }) diff --git a/test/unit/picture.test.ts b/test/unit/picture.test.ts index 1932ddecf..5c77db210 100644 --- a/test/unit/picture.test.ts +++ b/test/unit/picture.test.ts @@ -78,6 +78,7 @@ describe('Renders simple image', () => { } }) expect(img.find('source[type="image/avif"]').exists()).toBe(true) + expect(img.findAll('source').length).toBe(1) expect(img.find('img').exists()).toBe(true) }) @@ -92,6 +93,7 @@ describe('Renders simple image', () => { }) expect(img.find('source[type="image/avif"]').exists()).toBe(true) expect(img.find('source[type="image/webp"]').exists()).toBe(true) + expect(img.findAll('source').length).toBe(2) expect(img.find('img').exists()).toBe(true) }) @@ -106,6 +108,22 @@ describe('Renders simple image', () => { }) expect(img.find('source[type="image/avif"]').exists()).toBe(true) expect(img.find('source[type="image/gif"]').exists()).toBe(true) + expect(img.findAll('source').length).toBe(2) + expect(img.find('img').exists()).toBe(true) + }) + + it('checks that multiple formats can also be parsed as array', () => { + const img = mount(NuxtPicture, { + propsData: { + width: 200, + height: 200, + format: ['avif', 'webp'], + src + } + }) + expect(img.find('source[type="image/avif"]').exists()).toBe(true) + expect(img.find('source[type="image/webp"]').exists()).toBe(true) + expect(img.findAll('source').length).toBe(2) expect(img.find('img').exists()).toBe(true) })