There have been a couple of cases where people expressed confusion with the current resize API.
The original "fluent" API was partly influenced by that of the gm module, the idea being that having a similar-ish API would make migration between the two modules easier. (The gm module builds an ordered list of flags to pass to the the underlying convert command line utility so it was always a bit of a leaky abstraction anyway.)
That was almost five years ago and I've wanted to improve this API for a while, especially as there is now a generation of developers who have never had to work with command line image processing tools.
The resize function currently has three signatures (that will remain):
resize(width)
resize(width, height) // supports null width for auto-scaling
resize(width, height, options)
This proposal is for an additional signature:
resize(options)
where options is an object with one or more of these properties:
canvascropembedfastShrinkOnLoad (existing property)heightkernel (existing property)widthwithoutEnlargementThe following example usage of the proposed API would become possible.
NOTE: this proposed API has been updated - see https://github.com/lovell/sharp/issues/1135#issuecomment-423770094
resize({ width }) // equivalent to resize(width)
resize({ height }) // equivalent to resize(null, height)
resize({ height, width }) // resize(width, height)
resize({ crop: 'east', height, width }) // resize(width, height).crop('east')
resize({ embed: 'west', height, width }) // resize(width, height).embed('west')
resize({ canvas: 'max', height, width }) // resize(width, height).max()
resize({ canvas: 'min', height, width }) // resize(width, height).min()
resize({ canvas: 'ignoreAspectRatio', height, width }) // resize(width, height).ignoreAspectRatio()
resize({ height, width, withoutEnlargement: true }) // resize(width, height).withoutEnlargement()
resize({ canvas: 'max', height, width, withoutEnlargement: true }) // resize(width, height).max().withoutEnlargement()
REPEATED NOTE: this proposed API has been updated - see https://github.com/lovell/sharp/issues/1135#issuecomment-423770094
The max(), withoutEnlargement() etc. options-as-functions would be deprecated and (eventually) removed. This was the approach previously taken to move the no-longer-present progressive() function to become options of the jpeg() and png() output-selecting functions.
Constructive thoughts and feedback, as always, welcome.
Update: mention existing fastShrinkOnLoad and kernel options that will remain.
Update: consider moving operations that can result in an image of a different size, e.g. extend and trim, to the resize.js file (and therefore the resize docs).
Thanks for this explanation. I like the proposed signatures for these functions and I think it will reduce the learning curve for those of us in the "generation" that never used CLI-based image processing tools.
One question I asked in #1127 that I'm not sure this addresses is this: at what point is the result of the "fluent" API actually executed? For example, if I want to chain two different resize functions together, I want the first one to execute and then the second one to be executed on the result of the first one. Is that currently possible? And if not, is there a proposal for accomplishing something like this?
I like it. This indeed makes it a bit easier to learn. Here are some further ideas:
TL;DR: Align with CSS terminology where possible
resize({ width, height, fit: 'inside' }) // Like resize({ width, height, canvas: 'max' }) above
resize({ width, height, fit: 'outside' }) // Like resize({ width, height, canvas: 'min' }) above
resize({ width, height, fit: 'fill' }) // Like resize({ width, height, canvas: 'ignoreAspectRatio' }) above
resize({ width, height, fit: 'cover', position: 'bottom' }) // Like resize({ width, height, crop: 'south' }) above
resize({ width, height, fit: 'contain' } }) // Like resize({ width, height, embed: 'center' }) above
canvas to fit (with 'min' renamed to 'outside' and 'max' to 'inside')crop and embed to cover and contain to align with how it is called in CSS (background-size and object-fit properties)top, bottom, etc. to align with CSS (object-position property)crop and embed with canvas (now fit) because crop is basically canvas: 'min' with what is sticking out cut off. So, combining all of them makes sense because they're doing similar things and cannot be used at the same time.Possible explanation for the documentation: The width and height properties describe a box. The fit property specifies how the image is sized and placed relative to this box. Possible values for fit:
'inside': Preserving aspect ratio, resize the image to be as large as possible while ensuring that it stays inside the box.'outside': Preserving aspect ratio, resize the image to be as small as possible while ensuring its dimensions are greater than the box.'fill': Ignore the aspect ratio and stretch the image so that it fits the box exactly.'cover': Preserving aspect ratio, resize the image to be as small as possible while ensuring its dimensions are greater than the box and crop the parts outside the box. By default the image is centered, meaning an equal amount is cut off on both sides. For more control you can use the position property.'contain': Preserving aspect ratio, resize the image to be as large as possible while ensuring that it stays inside the box and add extra space around to fit the box. By default the image is centered, meaning extra space is added equally on both sides. For more control you can use the position property.@MajorBreakfast I've finally got round to thinking about your proposal and really like the CSS-inspired fit and position property naming to aid understanding (plus be "different enough" to aid API migration).
Based on this, the proposed examples would become:
resize({ width }) // equivalent to resize(width)
resize({ height }) // equivalent to resize(null, height)
resize({ height, width }) // resize(width, height)
resize({ position: 'right', height, width }) // resize(width, height).crop('east')
resize({ fit: 'contain', position: 'left bottom', height, width }) // resize(width, height).embed('southwest')
resize({ fit: 'inside', height, width }) // resize(width, height).max()
resize({ fit: 'outside', height, width }) // resize(width, height).min()
resize({ fit: 'fill', height, width }) // resize(width, height).ignoreAspectRatio()
resize({ height, width, withoutEnlargement: true }) // resize(width, height).withoutEnlargement()
resize({ fit: 'inside', height, width, withoutEnlargement: true }) // resize(width, height).max().withoutEnlargement()
The CSS positions (e.g. left top) would map to their equivalent gravities (compass points e.g. northeast) and both approaches would be permissible.
We'd still need the withoutEnlargement as a separate option as I don't think there's a CSS equivalent (object-fit: scale-down is close but not quite the same).
It's a slightly bigger API change than I was thinking but if we're going to break the API we may as well try to get it right second time ;)
Perhaps a codemod could be produced to help once an API is settled upon?
Commit c3274e4 makes this big but worthwhile change, which will be included in v0.21.0.
The resize docs also have more examples. Having all the options together rather than being spread over multiple functions, plus leaning on some CSS naming standards, should hopefully make it all much easier to understand.
(The background() function will be moving to become a resize({ background }) option as part of the work for #1392.)
sharp v0.21.0 is now available with this change.
Complete documentation for all the resize options is at http://sharp.pixelplumbing.com/en/stable/api-resize/#resize
Could we add a bit more color to the semantic differences these options, especially cover and inside?
Also, from my reading of the above, fit: "outside" will mean the resulting image is larger than the given width and height (to maintain the existing aspect ratio), is that correct?
Cover and contain both enlarge images, so how do they interact with withoutEnlargement: true?
Would these options would be clearer if they each had a drawing, like this?

@mceachen A PR to improve the docs with images, like the one you've provided, would be very welcome thank you!
If it's unclear from the docs and my comments above, cover is the default behaviour when both width and height are provided and is what used to be called crop; inside is what used to be max.
withoutEnlargement has always, I think/hope, been independent of the "fit".
I realise this is quite a big change, hence the deprecation route, but it seems like the concepts of cover and contain are now becoming relatively well understood.
Hi everyone!
I try to migrate to v0.21.0, but didn't really get what would be the equivalent of old:
resize(width, height).crop()
will it be this one?:
resize({ height, width, position: 'center' })
Thanks for such an amazing module
@PazzaVlad When providing both a width and height the default behaviour remains the same; previously this was .crop() and now it is { fit: 'cover' }.
The following are all functionally equivalent:
resize(width, height);
resize(width, height).crop();
resize(width, height).crop('center');
resize(width, height, { fit: 'cover' });
resize(width, height, { fit: 'cover', position: 'center' });
resize(width, height, { position: 'center' });
Most helpful comment
sharp v0.21.0 is now available with this change.
Complete documentation for all the
resizeoptions is at http://sharp.pixelplumbing.com/en/stable/api-resize/#resize