gatsby-image wraps each image with a gatsby-image-wrapper div which fills 100% of the available viewport width. This wrapper div can easily be styled with CSS but there is no way to treat landscape, portrait or square images differently from each other.
What if, you wanted to have landscape images fill 80%-100% of the available width but have portrait and square images fill no more than 40-50% of the viewport width.
So ideally each gatsby-image-wrapper div gets a class added depending on its aspect ratio, which would be either; landscape
, portrait
or square
.
One way to do this could be to write some conditional statement using the aspect ratio that comes with childImageSharp:
edges {
node {
name
childImageSharp {
fluid(maxWidth: 915, quality: 90) {
aspectRatio
...GatsbyImageSharpFluid_withWebp
}
}
}
}
When I map over all my gallery images, I can grab the aspect ratio and add it to each gatsby-image-wrapper using className but it's not very useful in its raw format as the returned data for aspectRatio are numbers like 0.6666666666666666
for portrait images or 1.5003750937734435
for landscape. Having those classes mentioned above would be better to work with; landscape
, portrait
or square
.
This is how I'm getting all my gallery images from the current post, along with their aspectRatio
.
export default ({ data }) => {
return (
<Layout>
<article>
{data.allFile.edges.map(({ node }, index) => (
<div>
<Img
key={index}
className={node.childImageSharp.fluid.aspectRatio}
alt={node.name}
fluid={node.childImageSharp.fluid}
/>
<span>{node.childImageSharp.fluid.aspectRatio}</span>
</div>
))}
</article>
</Layout>
);
};
The full GraphQL query I am using is:
export const query = graphql`
query($slug: String!, $absolutePathRegex: String!) {
markdownRemark(fields: { slug: { eq: $slug } }) {
html
frontmatter {
title
date
modified
caption
description
cover {
publicURL
childImageSharp {
fluid(maxWidth: 915, quality: 90) {
...GatsbyImageSharpFluid_withWebp
}
}
}
}
fields {
slug
}
}
allFile(
filter: {
extension: { regex: "/(jpg)|(png)|(tif)|(tiff)|(webp)|(jpeg)/" }
absolutePath: { regex: $absolutePathRegex }
}
) {
edges {
node {
name
childImageSharp {
fluid(maxWidth: 915, quality: 90) {
aspectRatio
...GatsbyImageSharpFluid_withWebp
}
}
}
}
}
}
`;
There must be a simple solution to this using a conditional statement in React where you map over all your images, take the aspect ratio and then convert the raw data to the desired classes.
So instead of:
<div class="1.5003750937734435 gatsby-image-wrapper"></div>
<div class="0.6666666666666666 gatsby-image-wrapper"></div>
<div class="0.6666666666666666 gatsby-image-wrapper"></div>
<div class="1.0000000000000000 gatsby-image-wrapper"></div>
<div class="1.5003750937734435 gatsby-image-wrapper"></div>
You'd get:
<div class="landscape gatsby-image-wrapper"></div>
<div class="portrait gatsby-image-wrapper"></div>
<div class="portrait gatsby-image-wrapper"></div>
<div class="square gatsby-image-wrapper"></div>
<div class="landscape gatsby-image-wrapper"></div>
Which could then be easily styled with css.
Unless I'm missing something, aren't you already 99% there? You can write your conditional inside of the map
, or better yet, write a component that wrap <Img>
:
import React from 'react'
import Img from 'gatsby-image'
// we only care about `aspectRatio`, the rest will be passed directly to `Img`
// also take out `className` so it be merged with our generated `orientation` class name
const ImgWithOrient = ({ aspectRatio, className, ...props }) => {
let orientation
if (aspectRatio > 1) orientation = 'landscape'
if (aspectRatio < 1) orientation = 'portrait'
else orientation = 'square'
return <Img className={`${className} ${orientation}`} {...props} />
}
export default ({ data }) => {
return (
<Layout>
<article>
{data.allFile.edges.map(({ node }, index) => (
<div key={index}>
<ImgWithOrient
key={index}
aspectRatio={node.childImageSharp.fluid.aspectRatio}
className="other class name"
alt={node.name}
fluid={node.childImageSharp.fluid}
/>
<span>{node.childImageSharp.fluid.aspectRatio}</span>
</div>
))}
</article>
</Layout>
)
}
Also note that when you loop over something, you'd need to put key
in the most outer component -- in this case, the outer <div>
, instead of <Img>
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With