birkir / prime

✨Open Source GraphQL CMS
https://docs.primecms.app
MIT License
1.72k stars 112 forks source link

Feature: On-demand AssetTransformations #207

Open intellix opened 5 years ago

intellix commented 5 years ago

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:

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:

And a pre-made solution posted at the top of it:

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:

Screenshot 2019-05-21 14 24 55

What are your thoughts on replacing/deprecating up-front crops and doing them on the fly?

birkir commented 5 years ago

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:

  1. User configured crop regions on a field level (id: landscape, width: 200, height: 100)
  2. Publisher configured position and zoom in crop (id: landscape, x: -40, y: -100, width: 220, height: 150)
  3. And then: GraphQL configured image transformation on the output with inherited publisher defined crop (if available)
intellix commented 5 years ago

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 :)