prime
prime copied to clipboard
Feature: On-demand AssetTransformations
So I was just playing with the Cloudinary integration and it's pretty good that you can define "crops" up front and edit them directly into Cloudinary. It seems like they also already support being able to do it on demand as you can edit the URL manually afterwards.
Couple of thoughts:
- Users of the API aren't able to see which crops are available as it's just a
String
type - Users of the API being able to specify exactly the dimensions/crops they need. On-demand would allow them to define exactly what they need and is like GraphQL for images.
"I'm creating a list of blog posts and need
120x100
rather than 1mb 1,200x1000 images on the homepage"
Coming from GraphCMS there are AssetTransformations which allow to do them on the fly (provided by Filestack https://www.filestack.com/products/file-upload/):
{
assets(first: 1) {
id
createdAt
url(transformation: {
image: {
resize: {
width: 100,
height: 100,
fit: clip
}
}
document: {
output: {
format: png
}
}
})
}
}
Which creates a URL like: https://media.graphcms.com/output=format:png/resize=fit:clip,height:100,width:100/y1UgaYKCQ4FfJDfnaie3
We're using AWS and plan to store stuff in S3. Having a quick google I found a blog post with a tutorial for basic resizing on the fly:
- https://aws.amazon.com/blogs/compute/resize-images-on-the-fly-with-amazon-s3-aws-lambda-and-amazon-api-gateway
And a pre-made solution posted at the top of it:
- https://aws.amazon.com/solutions/serverless-image-handler/
Personally I never needed the format changing on the fly but I guess JPG -> WEBP is a nice optimisation and comes out of the box with Thumbor (used in the above solution). Thumbor usage docs: https://github.com/thumbor/thumbor/wiki/Usage
Here's a library for dynamically building Thumbor URLs in TypeScript that was recently maintained: https://github.com/MCeddy/ThumborUrlBuilderTs
The GraphCMS GQL types/inputs/enums for Assets and AssetTransformation looks something like:
type Asset {
id: ID!
createdAt: DateTime!
updatedAt: DateTime!
handle: String!
fileName: String!
width: Float
height: Float
size: Float
mimeType: String
""" Get the url for the asset with provided transformations applied. """
url(transformation: AssetTransformationInput): String!
}
input AssetTransformationInput {
image: ImageTransformationInput
document: DocumentTransformationInput
""" Pass true if you want to validate the passed transformation parameters """
validateOptions: Boolean = false
}
""" Transformations for Images """
input ImageTransformationInput {
"" Resizes the image """
resize: ImageResizeInput
}
input ImageResizeInput {
""" The width in pixels to resize the image to. The value must be an integer from 1 to 10000. """
width: Int
""" The height in pixels to resize the image to. The value must be an integer from 1 to 10000. """
height: Int
""" The default value for the fit parameter is fit:clip. """
fit: ImageFit
}
""" Transformations for Documents """
input DocumentTransformationInput {
""" Changes the output for the file. """
output: DocumentOutputInput
}
input DocumentOutputInput {
""" Transforms a document into a desired file type. See this matrix for format support: """
format: DocumentFileTypes
}
enum ImageFit {
""" Resizes the image to fit within the specified parameters without distorting, cropping, or changing the aspect ratio. """
clip
""" Resizes the image to fit the specified parameters exactly by removing any parts of the image that don't fit within the boundaries. """
crop
""" Resizes the image to fit the specified parameters exactly by scaling the image to the desired size. The aspect ratio of the image is not respected and the image can be distorted using this method. """
scale
""" Resizes the image to fit within the parameters, but as opposed to 'fit:clip' will not scale the image if the image is smaller than the output size. """
max
}
enum DocumentFileTypes {
jpg
odp
ods
odt
png
svg
txt
webp
docx
html
pdf
doc
xlsx
xls
pptx
ppt
}
Also, a general cost which can be compared to Cloudinary from here https://docs.aws.amazon.com/solutions/latest/serverless-image-handler/overview.html:
What are your thoughts on replacing/deprecating up-front crops and doing them on the fly?
Yes, this is something I always wanted to do, have Prime output URL (and metadata) for an image based a image transformation properties set via GraphQL.
However, I will not remove up-front crops as a feature. This is very important for publishers to be able to fit an image into different crop sizes for perfect display in different viewports/usage on the front-end.
So what about:
- User configured crop regions on a field level (id: landscape, width: 200, height: 100)
- Publisher configured position and zoom in crop (id: landscape, x: -40, y: -100, width: 220, height: 150)
- And then: GraphQL configured image transformation on the output with inherited publisher defined crop (if available)
Sounds good to me. I guess I would have achieved perfectly cropped images by defining multiple images and width/height validators:
{
posts {
title
content
imageXS
imageMD
imageLG
}
}
I guess with crops up-front it looks like:
{
posts {
title
content
imageXS: image(crop: "xs")
imageMD: image(crop: "md")
imageLG: image(crop: "lg")
}
}
As long as I can transform on the fly I'm happy :)