Как использовать компонент изображения в Next.js с неизвестной шириной и высотой
Начиная с версии 10, Next.js поставляется со встроенным компонентом, который обеспечивает функцию оптимизации изображения и адаптивного изменения размера изображения. Мне он очень нравится, и я использую его на своем веб-сайте для изображений фиксированного размера. Согласно официальной документации, ширина и высота являются обязательными реквизитами, если только они не используются.
layout=fill
.
Теперь я хотел бы использовать компонент, даже если заранее не знаю ширину и высоту. У меня есть ресурсы блога, которые поступают с CMS, где точный размер изображения неизвестен. В этом случае, могу ли я использовать
Image
компонент?
Любая помощь будет оценена по достоинству.
13 ответов
Да, вы можете использовать его с
layout=fill
вариант, который вы упомянули.
В этом случае вам нужно будет установить «соотношение сторон» для ваших изображений.
<div style={{ position: "relative", width: "100%", paddingBottom: "20%" }} >
<Image
alt="Image Alt"
src="/image.jpg"
layout="fill"
objectFit="contain" // Scale your image down to fit into the container
/>
</div>
Вы можете использовать
objectFit="cover"
тоже, и это увеличит ваше изображение, чтобы заполнить весь контейнер.
Обратной стороной этого подхода является то, что он будет прикреплен к
width
вы указываете, это может быть относительное, например "100%" или абсолютное, например, "10rem", но нет никакого способа установить автоматическую ширину и высоту в зависимости от размера вашего изображения, или, по крайней мере, пока нет.
У меня тоже была такая же проблема: я хотел использовать
Image
from next/image в кирпичной кладке
ImageList
из МУИ ...
Во всяком случае, это трюк, который я использовал, чтобы получить размер моего изображения и, таким образом, обработать размер контейнера:
import React, { FC, useState } from 'react';
import styled from 'styled-components';
import Image from 'next/image';
interface Props {
src: string;
}
export const MasonryItem: FC<Props> = ({ src }) => {
const [paddingTop, setPaddingTop] = useState('0');
return (
<Container style={{ paddingTop }}>
<Image
src={src}
layout="fill"
objectFit="contain"
onLoad={({ target }) => {
const { naturalWidth, naturalHeight } = target as HTMLImageElement;
setPaddingTop(`calc(100% / (${naturalWidth} / ${naturalHeight})`);
}}
/>
</Container>
);
};
const Container = styled.div`
position: relative;
`;
Идея: получить размер изображения при его загрузке, вычислить соотношение и использовать его, чтобы контейнер заполнил требуемое пространство с помощью padding-top. Таким образом, независимо от размера вашего изображения, контейнер будет соответствовать ему.
Примечание. Я не использовал ширину или высоту контейнера, потому что они мне не нужны в моем проекте, но, возможно, вам придется установить один из них для вашего проекта.
Поскольку я все еще начинаю с React, NextJS и машинописного текста, возможно, решение может быть красивее, если у кого-то есть идея его улучшить, я буду рад его прочитать!
Если вы хотите сохранить соотношение исходного изображения, вы можете сделать это следующим образом:
<div
style={{
width: 150,
height: 75,
position: "relative",
}}>
<Image
src={"/images/logos/" + merger.companyLogoUrl}
alt={merger.company}
layout="fill"
objectFit="contain"
/>
Изображение будет заполнено настолько, насколько позволяет контейнер. Поэтому, если вы знаете, что ваши изображения шире, чем длинные, например, вы можете просто установить ширину контейнера, а затем установить высоту контейнера больше, чем прогнозируемая высота изображения.
Так, например, если вы работаете с широкими изображениями, вы можете установить ширину контейнера равной 150 пикселей, тогда, пока высота контейнера больше высоты изображения, изображение будет отображаться на высоте исходного соотношения.
ничего не помогло мне из вышеперечисленных вариантов, но я пробовал решить проблему другим способом, у меня работает:
const [imageSize, setSmageSize] = useState({
width: 1,
height: 1
});
<Image
src={imgPath}
layout="responsive"
objectFit="contain"
alt={alt}
priority={true}
onLoadingComplete={target => {
setSmageSize({
width: target.naturalWidth,
height: target.naturalHeight
});
}}
width={imageSize.width}
height={imageSize.height}
/>
Я написал сообщение в блоге, в котором подробно описал, как получить размер удаленных изображений, а также использовать SSG. Как использовать компонент изображения в Next.js с неизвестной шириной и высотой
import Image from "next/image";
import probe from "probe-image-size";
export const getStaticProps = async () => {
const images = [
{ url: "https://i.imgur.com/uCxsmmg.png" },
{ url: "https://i.imgur.com/r4IgKkX.jpeg" },
{ url: "https://i.imgur.com/dAyge0Y.jpeg" }
];
const imagesWithSizes = await Promise.all(
images.map(async (image) => {
const imageWithSize = image;
imageWithSize.size = await probe(image.url);
return imageWithSize;
})
);
return {
props: {
images: imagesWithSizes
}
};
};
//...
export default function IndexPage({ images }) {
return (
<>
{images?.map((image) => (
<Image
key={image.url}
src={image.url}
width={image.size.width}
height={image.size.height}
/>
))}
</>
);
}
Я много раз сталкивался с проблемой, когда мне хотелось, чтобы изображение масштабировалось пропорционально, чтобы заполнить ширину экрана. В этом случае вы знаете, что хотитеwidth: 100%
и это похоже наheight: auto
будет автоматически масштабироваться, но это не работает. Ключевым моментом является то, что вам нужно установить правильные настройки для изображения, которое вы используете. Вы можете сделать это в CSS. Например:
<div
style={{
position: "relative",
width: "100%",
aspect-ratio: 16/9
}}
>
<Image
src="/path/to/image.png"
layout="fill"
/>
</div>
Обратите внимание: если вы установитеaspect-ratio
неправильно для изображения, тогда оно не будет заполнять ширину или высоту, как ожидалось.
Вот мое решение, которое соответствует родительскому контейнеру. Также заботится о том, является ли изображение вертикальным или горизонтальным. Ширина задается динамически.
Однако обязательно измените максимальную высоту!
import { StaticImageData } from "next/image";
import Image from "next/image";
import styled from "styled-components";
interface Props {
image: StaticImageData;
altText: string;
}
const ImageDisplay = ({ image, altText }: Props) => {
return (
<ImageContainer image={image}>
{image.width > image.height ? (
<Image src={image} className={"imgHorizontal"} alt={altText} />
) : (
<Image src={image} className={"imgVertical"} alt={altText} />
)}
</ImageContainer>
);
};
const ImageContainer = styled.div<{ image: StaticImageData }>`
display: flex;
justify-content: center;
width: 100%;
max-height: 1000px;
${(props) => props.image.width < props.image.height && `height: ${props.image.height}px;`}
.imgVertical {
object-fit: contain;
width: auto;
height: 100%;
}
.imgHorizontal {
object-fit: contain;
width: 100%;
height: auto;
}
`;
export default ImageDisplay;
В большинстве случаев вы хотите использовать изображение, сохраняя соотношение сторон и устанавливая пользовательское свойство ширины. Вот решение, которое я нашел. Вы должны использовать статически импортированные изображения в следующем проекте. В приведенном ниже коде я использовал tailwindCSS для стилизации.
import React from 'react';
import Image from 'next/image';
import mintHeroImg from '../public/images/mint-hero.png';
export default function Lic() {
return (
<div className="w-[100px] relative">
<Image src={mintHeroImg} alt="mint-hero" />
</div>
);
}
Это отобразит изображение шириной 100 пикселей, а высота определяется соотношением сторон изображения. Относительное положение является существенным свойством .
Иногда вы хотите иметь изображения разных размеров, например, маленькое, среднее и большое. Мы передаем свойство компоненту и на основе этого свойства используем разные стили. В таком случае,
layout="fill"
это удобно.
при заполнении изображение будет растягиваться как по ширине, так и по высоте до размеров родительского элемента, при условии, что элемент является относительным
const Card = (props) => {
const { imgUrl, size } = props;
const classMap = {
large: styles.lgItem,
medium: styles.mdItem,
small: styles.smItem,
};
return (
<div className={classMap[size]}>
<Image src={imgUrl} alt="image" layout="fill" />
</div>
);
};
Вы пишете стиль css для каждого случая. Важно то, что вы должны иметь
position:relative
. Например:
.smItem {
position: relative;
width: 300px;
min-width: 300px;
height: 170px;
min-height: 170px;
}
.mdItem {
position: relative;
width: 158px;
min-width: 158px;
height: 280px;
min-height: 280px;
}
.lgItem {
position: relative;
width: 218px;
min-width: 218px;
height: 434px;
min-height: 434px;
}
@Oleski ответ, который вы дали, помог мне придумать следующее:
Моя цель — поместить любое изображение, независимо от его размера, в квадратный контейнер без корректировки воспринимаемого соотношения сторон изображения.
onLoadingComplete={(target: any) => {
const aspectRatio = target.naturalWidth / target.naturalHeight
if (target.naturalHeight > target.naturalWidth) {
setImageSize({
height: containerWidth / 2,
width: containerWidth / 2 * aspectRatio
})
} else {
setImageSize({
width: target.naturalWidth,
height: target.naturalHeight,
});
}
}}
Лучший способ сделать это — использовать обычный HTML-тег img. На мой взгляд, компонент изображения Next.js — это провал. Нам нужно добавить дополнительный код и обработку для простых изображений. В идеале ширина и высота должны быть необязательными.
<Image src={thumbnail || ""}
alt="post-thumbnail"
layout="responsive"
objectFit="cover"
width={6}
height={4}
/>
width = {6} height = {4} - это соотношение
Пример: квадрат => ширина ={1} высота ={1}
Для Next 12 и CSS для попутного ветра.
Вот что у меня есть для автоматической высоты и ширины, масштабированных до 100% родительского div:
<div>
<Image
sizes="(max-width: 100%)"
src={srcImageImport}
alt="My Image" />
</div>