timhagn / gatsby-background-image

Lazy-loading React (multi)background-image component with optional support for the blur-up effect.
MIT License
253 stars 49 forks source link

Compatibility with Gatsby 3 & workaround (☂️ Umbrella Issue) #141

Open YannickMol opened 3 years ago

YannickMol commented 3 years ago

Hi Team,

As the new version of gatsby-image is coming up in the form of the gatsby-plugin-image plugin, I was wondering whether this will be reflected in a new version of the gatsby-background-image plugin. When using the new API and gatsbyImageData in a BackgroundImage component, it gives the following error: Warning: Failed prop type: Invalid prop "fluid" supplied to "BackgroundImage"..

Thanks, Yannick

timhagn commented 3 years ago

Hi Yannick,

wished I were a team, but I'm flying solo on gbi ; ). Already looked into the new plugin, see #132, but didn't look into its Types. Will try to rectify this week - or feel free to open a PR : ).

Best,

Tim.

timhagn commented 3 years ago

Hi @YannickMol,

just looked into it & have to say: Uffz. Quite another paradigm of handling images oO. ~~Trying to implement a converter function for the new gatsbyImageData Type, but couldn't replicate your error... Would you be able to give me some sources or (even better) a reproduction repo?~~ Scratch that, the aspectRatio was missing, as the gatsbyImageData Type directly gives width & height instead of it.

Best,

Tim.

timhagn commented 3 years ago

Hi @YannickMol, again.

As written above, I just worked on a converter function. It isn't completely finished & I'm gonna try adding it to gbi in the upcoming week, but should you want to try it, have a look below : ).


import React from 'react'
import { graphql, useStaticQuery } from 'gatsby'
import { GatsbyImage, getImage } from "gatsby-plugin-image"
import BackgroundImage from "gatsby-background-image"

// ------------------ COPY FROM HERE -------------------------------------------
// Helper functions.
const getBgImageType = imageData => imageData.layout === 'fixed' ? 'fixed' : 'fluid'
const getAspectRatio = imageData => imageData.width / imageData.height
const getPlaceholder = imageData => {
  if (imageData.placeholder) {
    return imageData.placeholder.fallback.includes(`base64`) ?
      { base64: imageData.placeholder.fallback }
      : { tracedSvg: imageData.placeholder.fallback }
  }
  return {}
}

/**
 * Tries to Backport the new `gatsbyImageData` type to the classic `fluid` / `fixed` form.
 *
 * @param imageData   {object}    The image data to convert.
 * @returns {{}}
 */
const convertToBgImage = imageData => {
  if (imageData && imageData.layout) {
    const returnBgObject = {}
    const bgType = getBgImageType(imageData)
    const aspectRatio = getAspectRatio(imageData)
    const placeholder = getPlaceholder(imageData)
    returnBgObject[bgType] = {
      ...imageData.images.fallback,
      ...placeholder,
      aspectRatio,
    }
    return returnBgObject
  }
  return {}
}
// ------------------ TO HERE --------------------------------------------------

const GpiTest = () => {
  const {placeholderImage, oldImage} = useStaticQuery(
    graphql`
      query {
        placeholderImage: file(relativePath: { eq: "gatsby-astronaut.png" }) {
          childImageSharp {
            gatsbyImageData(
              width: 200
              placeholder: BLURRED
              formats: [AUTO, WEBP, AVIF]
            )
          }
        }
      }
    `
  )
  const image = getImage(placeholderImage)

  // Use like this:
  const bgImage = convertToBgImage(image)

  return (
    <BackgroundImage
      Tag="section"
      // Spread bgImage into BackgroundImage:
      {...bgImage}
      preserveStackingContext
    >
      <div style={{minHeight: 1000, minWidth: 1000}}>
        <GatsbyImage image={image} alt={"testimage"}/>
      </div>
    </BackgroundImage>
  )
}
export default GpiTest

WebP (& AVIF) handling is currently missing (& for the latter I have to adapt gbi), but at least at my place it worked like the above. Have phun : )!

Best,

Tim.

P.S.: With this function you could use the new type with the "old" GatsbyImage as well, should you want to ; ).

github-actions[bot] commented 3 years ago

Hi there! As @timhagn momentarily is the main contributor to this package, this issue has been automatically marked as stale because it has not had any recent activity. It will be closed if no further activity occurs, though we're open to suggestions on how to get more maintainers! Thank you for your contributions : )!

rburgst commented 3 years ago

I have created a simple wrapper that can be used as a 1:1 replacement for <BackgroundImage> until its fully compatible. Note that I only use fluid images. So you might need to tweak a little bit.

// Helper functions.
import BackgroundImage, { IBackgroundImageProps, IFixedObject, IFluidObject } from 'gatsby-background-image'
import { getImage } from 'gatsby-plugin-image'
import { IGatsbyImageData } from 'gatsby-plugin-image/dist/src/components/gatsby-image.browser'
import React, { CSSProperties } from 'react'
import { FunctionComponent } from 'react'

const getBgImageType = (imageData: IGatsbyImageData) => (imageData.layout === 'fixed' ? 'fixed' : 'fluid')
const getAspectRatio = (imageData: IGatsbyImageData) => imageData.width / imageData.height
const getPlaceholder = (imageData: IGatsbyImageData) => {
  if (imageData.placeholder) {
    return imageData.placeholder!.fallback?.includes(`base64`)
      ? { base64: imageData.placeholder!.fallback }
      : { tracedSVG: imageData.placeholder!.fallback }
  }
  return {}
}

/**
 * Tries to Backport the new `gatsbyImageData` type to the classic `fluid` / `fixed` form.
 *
 * @param imageData   {object}    The image data to convert.
 * @returns {{}}
 */
export function convertToBgImage(imageData: IGatsbyImageData): Partial<IBackgroundImageProps> {
  if (imageData && imageData.layout) {
    const returnBgObject: Partial<IBackgroundImageProps> = {}
    const bgType = getBgImageType(imageData)
    const aspectRatio = getAspectRatio(imageData)
    const placeholder = getPlaceholder(imageData)
    // @ts-ignore
    returnBgObject[bgType] = {
      ...imageData.images.fallback,
      ...placeholder,
      aspectRatio,
    }
    return returnBgObject
  }
  return {}
}

export interface IBgImageProps {
  fluid?: IGatsbyImageData
  className?: string
  onClick?: (e: Event) => void
  tabIndex?: number
  fadeIn?: boolean
  id?: string
  style?: CSSProperties
  role?: string
  preserveStackingContext?: boolean
}

/**
 * This is a temporary stopgap solution until `<BackgroundImage>` natively supports `gatsby-plugin-image`,
 * see [https://github.com/timhagn/gatsby-background-image/issues/141](https://github.com/timhagn/gatsby-background-image/issues/141).
 * @param {React.PropsWithChildren<IBgImageProps>} props
 * @return {JSX.Element}
 * @constructor
 */
export const BgImage: FunctionComponent<IBgImageProps> = (props) => {
  const { fluid, children, ...args } = props
  if (fluid) {
    const image = getImage(fluid)
    const bgImage = image && convertToBgImage(image)
    return (
      <BackgroundImage {...bgImage} {...args}>
        {children}
      </BackgroundImage>
    )
  } else {
    return <div>{children}</div>
  }
}

Depending on which other features you need, you might need to add them to IBgImageProps

joernroeder commented 3 years ago

@timhagn @rburgst The getPlaceholder helper needs to return the traced svg as { tracedSVG: 'svg...' } not tracedSvg.

mwskwong commented 3 years ago

Currently, this workaround is very basic. I'm planning to extend this to support webp and art direction as well.

@timhagn @rburgst, I'm trying to also support avif as well. The original fluid object has srcWebp and srcSetWebp fields, I imagine if I specify srcAvif and srcSetAvif, they will be simply ignored?

timhagn commented 3 years ago

Hi @all!

Wow, thanks for the effort to all of you! Great enhancement of my meager backport @rburgst : )! And you're right @matthewkwong2, right now srcAvif and srcSetAvif would simply be ignored right now, but I'm just working on it : )!

Best,

Tim.

timhagn commented 3 years ago

Hi again @all,

if you head over to the gbimage-bridge branch & package, you may see the proceedings of the new adapter and may of course add your knowledge and code : )!

Best,

Tim.

timhagn commented 3 years ago

Hi again @all!

Uffz, heavy lifting. It's alread working with WebP (see g3bitest ; ). Nearly there, just a few more steps:

Hope to finish it tomorrow, but any help welcome in the meantime (see here) : )!

Best,

Tim.

timhagn commented 3 years ago

And once more with feeling: Hi @all!

Just published the first version of gbimage-bridge & new versions of gbi(-es5). In combination it now has:

Have a look at gbimage-bridge, grab gbi@1.5.0 & give it a spin : )!

Best,

Tim.

P.S.: The resulting image from the conversion could even be used with classic gatsby-image for whomever can't update right now, so thanks for giving me a push to create this ; )!

mwskwong commented 3 years ago

@timhagn It's a bit weird you decided to publish it as a separated package. I thought there will be a new major release of the original one.

timhagn commented 3 years ago

Hi @matthewkwong2,

it was way faster to write the bridge & gain more experience with the new format than going through all the code and every important moving part to rip our fluid / fixed & replace it with IGatsbyImageData ^^.

Next step (apart from trying to fix more issues % ) is a rewrite, as the current code has become quite a behemoth through "organic growth" that everything should be revisited. Especially looking at the LCP-Score & suchlike ; ).

Best,

Tim.

JStumpp commented 3 years ago

Hi @timhagn,

thanks a lot, it works, but there is one major issue, the images all seem to load twice. Even in your demo repository you can see it in the network: https://github.com/timhagn/g3bitest

image

Any ideas?

mwskwong commented 3 years ago

Hi @timhagn,

thanks a lot, it works, but there is one major issue, the images all seem to load twice. Even in your demo repository you can see it in the network: https://github.com/timhagn/g3bitest

image

Any ideas?

I notice the same thing. While this issue also happens on gatsby-plugin-image as well.

timhagn commented 3 years ago

hmmz Would have to invest a little more, but I guess this has to do with #125. The bug I opened in the Gatsby repo for it just got closed -.- And when you say:

While this issue also happens on gatsby-plugin-image as well.

we might perhaps give them a shout, or what do you think?

mwskwong commented 3 years ago

Already done it in https://github.com/gatsbyjs/gatsby/issues/30272

pkuczynski commented 3 years ago

@timhagn I know you are the only person working on this, but do you have any ETA when this will be available without the gbimage-bridge? No pressure of course...

mwskwong commented 3 years ago

Already done it in gatsbyjs/gatsby#30272

This issue seems to be fixed with the latest release of gatsby-plugin-image and gatsby-transformer-sharp. Guess it is not our fault after all. It would be great if someone else can verify that.