mirror of
https://github.com/withastro/astro.git
synced 2025-03-17 23:11:29 -05:00
feat: add responsive support to picture component
This commit is contained in:
parent
fd68ea9771
commit
8e2379c0c1
3 changed files with 122 additions and 17 deletions
|
@ -23,14 +23,16 @@ if (typeof props.height === 'string') {
|
|||
props.height = parseInt(props.height);
|
||||
}
|
||||
|
||||
const { experimentalResponsiveImages } = imageConfig;
|
||||
|
||||
const layoutClassMap = {
|
||||
fixed: 'aim-fi',
|
||||
responsive: 'aim-re',
|
||||
'full-width': '',
|
||||
};
|
||||
|
||||
if (experimentalResponsiveImages) {
|
||||
const layout = props.layout ?? imageConfig.experimentalLayout ?? 'none';
|
||||
const useResponsive = imageConfig.experimentalResponsiveImages && layout !== 'none';
|
||||
|
||||
if (useResponsive) {
|
||||
// Apply defaults from imageConfig if not provided
|
||||
props.layout ??= imageConfig.experimentalLayout;
|
||||
props.fit ??= imageConfig.experimentalObjectFit ?? 'cover';
|
||||
|
@ -56,12 +58,12 @@ const { style = '', class: className, ...attrs } = { ...additionalAttributes, ..
|
|||
---
|
||||
|
||||
{
|
||||
experimentalResponsiveImages && props.layout ? (
|
||||
useResponsive ? (
|
||||
<img
|
||||
src={image.src}
|
||||
{...attrs}
|
||||
{style}
|
||||
class={`${layoutClassMap[props.layout ?? imageConfig.experimentalLayout] ?? ''} aim ${className ?? ''}`.trim()}
|
||||
class={`${layoutClassMap[layout] ?? ''} aim ${className ?? ''}`.trim()}
|
||||
/>
|
||||
) : (
|
||||
<img src={image.src} {...additionalAttributes} {...image.attributes} class={className} />
|
||||
|
@ -69,13 +71,12 @@ const { style = '', class: className, ...attrs } = { ...additionalAttributes, ..
|
|||
}
|
||||
|
||||
<style
|
||||
define:vars={experimentalResponsiveImages &&
|
||||
props.layout && {
|
||||
w: image.attributes.width ?? props.width ?? image.options.width,
|
||||
h: image.attributes.height ?? props.height ?? image.options.height,
|
||||
fit: cssFitValues.includes(props.fit) && props.fit,
|
||||
pos: props.position,
|
||||
}}
|
||||
define:vars={useResponsive && {
|
||||
w: image.attributes.width ?? props.width ?? image.options.width,
|
||||
h: image.attributes.height ?? props.height ?? image.options.height,
|
||||
fit: cssFitValues.includes(props.fit ?? '') && props.fit,
|
||||
pos: props.position,
|
||||
}}
|
||||
>
|
||||
/* Shared by all Astro images */
|
||||
.aim {
|
||||
|
|
|
@ -1,9 +1,13 @@
|
|||
---
|
||||
import { type LocalImageProps, type RemoteImageProps, getImage } from 'astro:assets';
|
||||
import { type LocalImageProps, type RemoteImageProps, getImage, imageConfig } from 'astro:assets';
|
||||
import * as mime from 'mrmime';
|
||||
import { isESMImportedImage, resolveSrc } from '../dist/assets/utils/imageKind';
|
||||
import { AstroError, AstroErrorData } from '../dist/core/errors/index.js';
|
||||
import type { GetImageResult, ImageOutputFormat } from '../dist/types/public/index.js';
|
||||
import type {
|
||||
GetImageResult,
|
||||
ImageOutputFormat,
|
||||
UnresolvedImageTransform,
|
||||
} from '../dist/types/public/index.js';
|
||||
import type { HTMLAttributes } from '../types';
|
||||
|
||||
type Props = (LocalImageProps | RemoteImageProps) & {
|
||||
|
@ -37,6 +41,23 @@ if (scopedStyleClass) {
|
|||
pictureAttributes.class = scopedStyleClass;
|
||||
}
|
||||
}
|
||||
|
||||
const layoutClassMap = {
|
||||
fixed: 'aim-fi',
|
||||
responsive: 'aim-re',
|
||||
'full-width': '',
|
||||
};
|
||||
|
||||
const layout = props.layout ?? imageConfig.experimentalLayout ?? 'none';
|
||||
const useResponsive = imageConfig.experimentalResponsiveImages && layout !== 'none';
|
||||
|
||||
if (useResponsive) {
|
||||
// Apply defaults from imageConfig if not provided
|
||||
props.layout ??= imageConfig.experimentalLayout;
|
||||
props.fit ??= imageConfig.experimentalObjectFit ?? 'cover';
|
||||
props.position ??= imageConfig.experimentalObjectPosition ?? 'center';
|
||||
}
|
||||
|
||||
for (const key in props) {
|
||||
if (key.startsWith('data-astro-cid')) {
|
||||
pictureAttributes[key] = props[key];
|
||||
|
@ -53,7 +74,7 @@ const optimizedImages: GetImageResult[] = await Promise.all(
|
|||
format: format,
|
||||
widths: props.widths,
|
||||
densities: props.densities,
|
||||
}),
|
||||
} as UnresolvedImageTransform),
|
||||
),
|
||||
);
|
||||
|
||||
|
@ -71,7 +92,7 @@ const fallbackImage = await getImage({
|
|||
format: resultFallbackFormat,
|
||||
widths: props.widths,
|
||||
densities: props.densities,
|
||||
});
|
||||
} as UnresolvedImageTransform);
|
||||
|
||||
const imgAdditionalAttributes: HTMLAttributes<'img'> = {};
|
||||
const sourceAdditionalAttributes: HTMLAttributes<'source'> = {};
|
||||
|
@ -84,6 +105,16 @@ if (props.sizes) {
|
|||
if (fallbackImage.srcSet.values.length > 0) {
|
||||
imgAdditionalAttributes.srcset = fallbackImage.srcSet.attribute;
|
||||
}
|
||||
const cssFitValues = ['fill', 'contain', 'cover', 'scale-down'];
|
||||
|
||||
const {
|
||||
class: className,
|
||||
style,
|
||||
...attrs
|
||||
} = {
|
||||
...imgAdditionalAttributes,
|
||||
...fallbackImage.attributes,
|
||||
};
|
||||
|
||||
if (import.meta.env.DEV) {
|
||||
imgAdditionalAttributes['data-image-component'] = 'true';
|
||||
|
@ -106,5 +137,43 @@ if (import.meta.env.DEV) {
|
|||
);
|
||||
})
|
||||
}
|
||||
<img src={fallbackImage.src} {...imgAdditionalAttributes} {...fallbackImage.attributes} />
|
||||
{
|
||||
useResponsive ? (
|
||||
<img
|
||||
src={fallbackImage.src}
|
||||
{style}
|
||||
{...attrs}
|
||||
class={`${layoutClassMap[layout] ?? ''} aim ${className ?? ''}`.trim()}
|
||||
/>
|
||||
) : (
|
||||
<img src={fallbackImage.src} {...attrs} {style} class={className} />
|
||||
)
|
||||
}
|
||||
</picture>
|
||||
<style
|
||||
define:vars={useResponsive && {
|
||||
w: fallbackImage.attributes.width ?? props.width ?? fallbackImage.options.width,
|
||||
h: fallbackImage.attributes.height ?? props.height ?? fallbackImage.options.height,
|
||||
fit: cssFitValues.includes(props.fit ?? '') && props.fit,
|
||||
pos: props.position,
|
||||
}}
|
||||
>
|
||||
/* Shared by all Astro images */
|
||||
.aim {
|
||||
width: 100%;
|
||||
height: auto;
|
||||
object-fit: var(--fit);
|
||||
object-position: var(--pos);
|
||||
aspect-ratio: var(--w) / var(--h);
|
||||
}
|
||||
/* Styles for responsive layout */
|
||||
.aim-re {
|
||||
max-width: calc(var(--w) * 1px);
|
||||
max-height: calc(var(--h) * 1px);
|
||||
}
|
||||
/* Styles for fixed layout */
|
||||
.aim-fi {
|
||||
width: calc(var(--w) * 1px);
|
||||
height: calc(var(--h) * 1px);
|
||||
}
|
||||
</style>
|
||||
|
|
35
packages/astro/test/fixtures/core-image-layout/src/pages/picture.astro
vendored
Normal file
35
packages/astro/test/fixtures/core-image-layout/src/pages/picture.astro
vendored
Normal file
|
@ -0,0 +1,35 @@
|
|||
---
|
||||
import { Picture } from "astro:assets";
|
||||
import myImage from "../assets/penguin.jpg";
|
||||
---
|
||||
|
||||
<div id="picture-density-2-format">
|
||||
<Picture src={myImage} width={Math.round(myImage.width / 2)} alt="A penguin" formats={['avif', 'webp']} />
|
||||
</div>
|
||||
|
||||
<div id="picture-widths">
|
||||
<Picture src={myImage} width={Math.round(myImage.width / 2)} alt="A penguin" widths={[myImage.width]} sizes="(max-width: 448px) 400px, (max-width: 810px) 750px, 1050px" />
|
||||
</div>
|
||||
|
||||
<div id="picture-fallback">
|
||||
<Picture src={myImage} fallbackFormat="jpeg" alt="A penguin" />
|
||||
</div>
|
||||
|
||||
<div id="picture-attributes">
|
||||
<Picture src={myImage} fallbackFormat="jpeg" alt="A penguin" class="img-comp" pictureAttributes={{ class: 'picture-comp' }} />
|
||||
</div>
|
||||
|
||||
<div id="picture-mime-types">
|
||||
<Picture alt="A penguin" src={myImage} formats={['jpg', 'jpeg', 'png', 'avif', 'webp']} />
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.img-comp {
|
||||
border: 5px solid blue;
|
||||
}
|
||||
|
||||
.picture-comp {
|
||||
border: 5px solid red;
|
||||
display: inline-block;
|
||||
}
|
||||
</style>
|
Loading…
Add table
Reference in a new issue