Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Next.js Image- how to maintain aspect ratio and add letterboxes when needed

Tags:

css

next.js

I have an area of my Next.js app which is the selected photo of a photo gallery so it has to stay fixed in size as people flip through the selected image or a photo is loading. I have a responsive layout but if really pressed, I'd say that this pixel area is 566px*425px.

I'm confused about how to actually do this. Here's the closest I've been able to get it, but the problem is that I get overflow of the image when the aspect ratio exceeds 566x425 and for images that have an aspect ratio below 566x425 it will stretch it in the Y direct. What I really want is to have a fixed box and then if the aspect ratios differ from the max size, you'll see letterboxes either along the sides or on the top and bottom.

           <div
            style={{
              position: 'relative',
              width: '566px',
              height: '425px',
            }}
          >
            <Image
              src={currCommit.image.url}
              alt="Current Image"
              layout={'fill'}
              objectFit="cover"
            />
          </div>
like image 597
Coherent Avatar asked Mar 28 '21 20:03

Coherent


People also ask

How do you retain aspect ratio?

In the HTML, put the player <iframe> in a <div> container. In the CSS for the <div>, add a percentage value for padding-bottom and set the position to relative, this will maintain the aspect ratio of the container. The value of the padding determines the aspect ratio. ie 56.25% = 16:9.

How do I add aspect ratio to an image?

To create an exact aspect ratio, divide the height by the width. For example: 2:3 aspect ratio: 3 ÷ 2 = 1.5, so you'd drag the slider to 150. 3:2 aspect ratio: 2 ÷ 3 = .


2 Answers

Ooh I got it! The key was to set the parent div to a fixed size and relative and then set the Image to a layout of fill and an objectFit of contain. The only downside to this approach is I need to set media queries so it will scale for smaller sizes.

<div className="relative item-detail">
  <Image src={currCommit.image.url} alt="Current Image" layout={'fill'} objectFit={'contain'} />
</div>

Then in the css I set:

.item-detail {
  width: 300px;
  height: 225px;
}
like image 51
Coherent Avatar answered Oct 21 '22 10:10

Coherent


there's better solution i think, NextImage have callback property onLoadingComplete:

A callback function that is invoked once the image is completely loaded and the placeholder has been removed.

The onLoadingComplete function accepts one parameter, an object with the following properties: naturalWidth, naturalHeight

you can use the natural properties to set image ratio without loosing NextImage's layout functionality like this:

const NaturalImage = (props: ImageProps) => {
  const [ratio, setRatio] = useState(16/9) // default to 16:9

  return (
    <NextImage
      {...props}
      // set the dimension (affected by layout)
      width={200}
      height={200 / ratio}
      layout="fixed" // you can use "responsive", "fill" or the default "intrinsic"
      onLoadingComplete={({ naturalWidth, naturalHeight }) => 
        setRatio(naturalWidth / naturalHeight)
      }
    />
  )
}

the only downside is the aspect ratio applied only after image loaded, so the placeholder using the default ratio (in this case 16:9 - common), and this can cause CLS

like image 31
bayu Avatar answered Oct 21 '22 10:10

bayu