Skip to content

Instantly share code, notes, and snippets.

@tylerpaige
Last active June 7, 2022 21:29
Show Gist options
  • Save tylerpaige/e6bca4212753cfd0ae2a2b0fb4e193b4 to your computer and use it in GitHub Desktop.
Save tylerpaige/e6bca4212753cfd0ae2a2b0fb4e193b4 to your computer and use it in GitHub Desktop.
import React from 'react'
import { GatsbyImage } from 'gatsby-plugin-image'
/*
Example:
<GatsbyImageFromStrapi
image={image}
transformations={[getCropTransformation('4:5')]}
layoutsByBreakpoint={{
xs: 0.5,
sm: 0.33,
lg: 0.25
}}
/>
*/
export const GatsbyImageFromStrapi = ({
image,
transformations = [],
layoutsByBreakpoint = undefined,
...props
}) => {
const parsedImage = parseCloudinaryUrlFromStrapiImage(image)
// If the transformations include a change to the aspect ratio,
// we want to use that instead of the ratio we parsed from Strapi
if (transformations.some((t) => t.includes('ar_'))) {
const cropTransfomation = transformations.find((t) => t.includes('ar_'))
const [width, height] = cropTransfomation.split('ar_').pop().split(':')
parsedImage.aspectRatio = parseInt(width) / parseInt(height)
}
const sources = getSourcesFromStrapiImage(
parsedImage,
transformations,
layoutsByBreakpoint
)
const thumbnailSource = {
src: transformStrapiImage(parsedImage, [
...transformations,
getResizeTransformation(100),
]),
type: parsedImage.mime,
size: '100w',
}
const gatsbyImageData = {}
gatsbyImageData.layout = 'fullWidth'
gatsbyImageData.backgroundColor = '#f8e8e8'
gatsbyImageData.width = parsedImage.aspectRatio
gatsbyImageData.height = 1
gatsbyImageData.images = {}
gatsbyImageData.images.fallback = {}
gatsbyImageData.images.fallback.src = thumbnailSource.src
gatsbyImageData.images.fallback.srcSet = `${thumbnailSource.src} 100w`
gatsbyImageData.images.fallback.sizes = '100w'
gatsbyImageData.images.sources = sources.map((source) => {
return {
srcSet: source.src,
media: source.media,
}
})
return (
<GatsbyImage
image={gatsbyImageData}
alt={image.alternativeText}
{...props}
/>
)
}
export const breakpoints = {
sm: 400,
md: 800,
lg: 1600,
xl: 3200
}
export const breakpointsArr = Object.entries(breakpoints);
export const parseCloudinaryUrlFromStrapiImage = (image) => {
const publicID = image.hash
let baseURL = image.url.substring(0, image.url.indexOf(publicID))
if (!baseURL.endsWith('upload/')) {
baseURL = baseURL.substring(
0,
baseURL.indexOf('upload/') + 'upload/'.length
)
}
const aspectRatio = image.width / image.height
return {
aspectRatio,
baseURL,
publicID,
mime: image.mime,
alternativeText: image.alternativeText,
}
}
export const transformStrapiImage = (image, transformations) => {
const { publicID, baseURL } =
image.publicID && image.baseURL
? image
: parseCloudinaryUrlFromStrapiImage(image)
const transformationStr = transformations.join('/')
return `${baseURL}${transformationStr}/${publicID}`
}
export const getResizeTransformation = (width) => {
return `c_scale,w_${width}`
}
export const getCropTransformation = (ratio) => {
return `c_crop,ar_${ratio}`
}
export const resizeStrapiImage = (image, width) => {
return transformStrapiImage(image, [getResizeTransformation(width)])
}
export const getLayoutMultiplierForBreakpoint = (breakpoint, layouts = {}) => {
// A shorthand way to use this is to pass in the multiplier itself
if (typeof layouts === "number") {
return layouts;
}
const defaultSize = 1;
switch (breakpoint) {
case "xl":
return layouts.xl || layouts.lg || layouts.md || layouts.sm || defaultSize
case "lg":
return layouts.lg || layouts.md || layouts.sm || defaultSize
case "md":
return layouts.md || layouts.sm || defaultSize
case "sm":
return layouts.sm || defaultSize
default:
return defaultSize
}
}
export const getSourcesFromStrapiImage = (
image,
transformations = [],
layoutsByBreakpoint = undefined
) => {
return breakpointsArr.map(([breakpoint, minimumViewportWidth]) => {
const layoutMultiplier = getLayoutMultiplierForBreakpoint(breakpoint, layoutsByBreakpoint);
const renderedWidth = minimumViewportWidth * layoutMultiplier;
const widthToRequest = renderedWidth * 2;
const src = transformStrapiImage(image, [
...transformations,
getResizeTransformation(widthToRequest),
])
const media = breakpoint === "sm" ? "" : `(min-width: ${minimumViewportWidth}px)`;
return {
// the url
src,
// the media query clause
media,
// the mimetype
type: image.mime,
// how wide the image asset itself is
naturalWidth: widthToRequest,
// (approximately) how wide the image will be on the page
renderedWidth: renderedWidth
}
}).reverse();
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment