asset-utils icon indicating copy to clipboard operation
asset-utils copied to clipboard

[getImageDimensions] Account for cropping

Open hdoro opened this issue 3 years ago • 4 comments

Hey @rexxars 👋

I caught a bug in that stems from dimensions calculated wrongly with getImageDimensions if an image has a crop. Basically, if an image's _id includes 2304x1400 but its crop results in something like 400x400, getImageDimensions returns width: 2304, height: 1400, aspectRatio: 2304/1400, where the aspectRatio is actually 1. This makes the padding hack in front-ends unpredictable and broken.

AFAIK, it's doable to get the proper values if an image object with hotspot/crop is provided through some simple-ish math, the question is whether this should be considered by getImageDimensions or if we need a new API. I'm thinking a new property cropped?: { width: number; height: number; aspectRatio: number } would be the best approach as modifying the original width and height would break existing code and adding a new API could get more confusing and laborious.

Any strong opinions? Should I write a PR or is this something we're not looking for?

hdoro avatar Mar 11 '21 19:03 hdoro

Hey Henrique - thanks for filing this!

I suppose we could add a method that takes a SanityImageObjectStub as the only valid input - getImageDimensionsWithCrop, or similar?

Alternatively a second argument to the getImageDimensions (useCrop?: boolean), perhaps?

rexxars avatar Mar 11 '21 22:03 rexxars

Thanks for the quick answer 😄

A potential issue with having SanityImageObjectStub as the only valid input is that if you process images that could be resolved or not you'd have to manually set the systems for dealing with that. Particularly in the Sanity.io site this is a very common situation but maybe it's an edge case we don't need to care about?

Anywho, happy to go with what you think best, you're light-years ahead of me here.

I'll try to work on the PR for this late next week :)

hdoro avatar Mar 12 '21 19:03 hdoro

Hello. 3 years later, I chime in. Any snippet/PR @hdoro?

With Google penalizing CLS, it's more important than ever to know beforehand the dimensions of the image that we want to display. Unfortunately getImageDimensions from @sanity/asset-utils only gives the original dimensions.

thobas-dnvgl avatar May 10 '24 08:05 thobas-dnvgl

Here you go @thobas-dnvgl :

import type { SanityImageObject } from '@sanity/image-url/lib/types/types'

export function getImageDimensions(
  image: SanityImageObject,
): { width: number; height: number; aspectRatio: number } | undefined {
  if (!image?.asset?._ref) {
    return
  }

  // example asset._ref:
  // image-7558c4a4d73dac0398c18b7fa2c69825882e6210-366x96-png
  // When splitting by '-' we can extract the dimensions, id and extension
  const dimensions = image.asset._ref.split('-')[2]
  const [width, height] = dimensions.split('x').map(Number)

  if (!width || !height || Number.isNaN(width) || Number.isNaN(height)) {
    return
  }

  if (image.crop) {
    const croppedWidth =
      width * (1 - (image.crop?.right || 0) - (image.crop?.left || 0))
    const croppedHeight =
      height * (1 - (image.crop?.top || 0) - (image.crop?.bottom || 0))
    return {
      width: croppedWidth,
      height: croppedHeight,
      aspectRatio: croppedWidth / croppedHeight,
    }
  }

  return {
    width,
    height,
    aspectRatio: width / height,
  }
}

hdoro avatar May 10 '24 12:05 hdoro