Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions packages/gatsby-image/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -361,6 +361,17 @@ You will need to add it in your graphql query as is shown in the following snipp
[gatsby-plugin-sharp](/packages/gatsby-plugin-sharp) supports many additional arguments for transforming your images like
`quality`, `sizeByPixelDensity`, `pngCompressionLevel`, `cropFocus`, `greyscale` and many more. See its documentation for more.

## Image rootMargin override

rootMargin is a property that tells the observer how far from the viewport the image should start loading. More information on this property is available on [MDN](https://developer.mozilla.org/en-US/docs/Web/API/IntersectionObserver/rootMargin)

When using the `<Img />` component, the default rootMargin is 200px, you can override this default by passing a rootMargin prop.

```javascript
<Img rootMargin="20px" />
<Img rootMargin={20} />
```

## Some other stuff to be aware of

- If you want to set `display: none;` on a component using a `fixed` prop,
Expand Down
1 change: 1 addition & 0 deletions packages/gatsby-image/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ interface GatsbyImageProps {
imgStyle?: object
placeholderStyle?: object
backgroundColor?: string | boolean
rootMargin?: string | number
onLoad?: () => void
onStartLoad?: (param: { wasCached: boolean }) => void
onError?: (event: any) => void
Expand Down
100 changes: 91 additions & 9 deletions packages/gatsby-image/src/__tests__/__snapshots__/index.js.snap
Original file line number Diff line number Diff line change
@@ -1,20 +1,102 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`<Image /> should accept a number for rootMargin 1`] = `
<div>
<div
class="fixedImage gatsby-image-wrapper"
style="position: relative; overflow: hidden; display: inline; width: 100px; height: 100px;"
>
<div
style="width: 100px; opacity: 1; height: 100px;"
title="Title for the image"
/>
<img
alt=""
class="placeholder"
src="string_of_base64"
style="position: absolute; top: 0px; left: 0px; width: 100%; height: 100%; opacity: 1; color: red;"
title="Title for the image"
/>
<picture>
<source
srcset="some srcSetWebp"
type="image/webp"
/>
<img
alt="Alt text for the image"
crossorigin="anonymous"
height="100"
itemprop="item-prop-for-the-image"
src="test_image.jpg"
srcset="some srcSet"
style="position: absolute; top: 0px; left: 0px; width: 100%; height: 100%; opacity: 0;"
title="Title for the image"
width="100"
/>
</picture>
<noscript>
&lt;picture&gt;&lt;source type='image/webp' srcset="some srcSetWebp" /&gt;&lt;img width="100" height="100" srcset="some srcSet" src="test_image.jpg" alt="Alt text for the image" title="Title for the image" style="position:absolute;top:0;left:0;opacity:1;width:100%;height:100%;object-fit:cover;object-position:center"/&gt;&lt;/picture&gt;
</noscript>
</div>
</div>
`;

exports[`<Image /> should accept a string for rootMargin 1`] = `
<div>
<div
class="fixedImage gatsby-image-wrapper"
style="position: relative; overflow: hidden; display: inline; width: 100px; height: 100px;"
>
<div
style="width: 100px; opacity: 1; height: 100px;"
title="Title for the image"
/>
<img
alt=""
class="placeholder"
src="string_of_base64"
style="position: absolute; top: 0px; left: 0px; width: 100%; height: 100%; opacity: 1; color: red;"
title="Title for the image"
/>
<picture>
<source
srcset="some srcSetWebp"
type="image/webp"
/>
<img
alt="Alt text for the image"
crossorigin="anonymous"
height="100"
itemprop="item-prop-for-the-image"
src="test_image.jpg"
srcset="some srcSet"
style="position: absolute; top: 0px; left: 0px; width: 100%; height: 100%; opacity: 0;"
title="Title for the image"
width="100"
/>
</picture>
<noscript>
&lt;picture&gt;&lt;source type='image/webp' srcset="some srcSetWebp" /&gt;&lt;img width="100" height="100" srcset="some srcSet" src="test_image.jpg" alt="Alt text for the image" title="Title for the image" style="position:absolute;top:0;left:0;opacity:1;width:100%;height:100%;object-fit:cover;object-position:center"/&gt;&lt;/picture&gt;
</noscript>
</div>
</div>
`;

exports[`<Image /> should have a transition-delay of 1sec 1`] = `
<div>
<div
class="fixedImage gatsby-image-wrapper"
style="position: relative; overflow: hidden; display: inline; width: 100px; height: 100px;"
>
<div
style="background-color: lightgray; width: 100px; opacity: 1; height: 100px; transition-delay: 1000ms;"
style="width: 100px; opacity: 1; height: 100px;"
title="Title for the image"
/>
<img
alt=""
class="placeholder"
src="string_of_base64"
style="position: absolute; top: 0px; left: 0px; width: 100%; height: 100%; object-fit: cover; object-position: center; opacity: 1; transition-delay: 1000ms; color: red;"
style="position: absolute; top: 0px; left: 0px; width: 100%; height: 100%; opacity: 1; color: red;"
title="Title for the image"
/>
<picture>
Expand All @@ -29,7 +111,7 @@ exports[`<Image /> should have a transition-delay of 1sec 1`] = `
itemprop="item-prop-for-the-image"
src="test_image.jpg"
srcset="some srcSet"
style="position: absolute; top: 0px; left: 0px; width: 100%; height: 100%; object-fit: cover; object-position: center; opacity: 0; transition: opacity 1000ms;"
style="position: absolute; top: 0px; left: 0px; width: 100%; height: 100%; opacity: 0;"
title="Title for the image"
width="100"
/>
Expand All @@ -48,14 +130,14 @@ exports[`<Image /> should render fixed size images 1`] = `
style="position: relative; overflow: hidden; display: inline; width: 100px; height: 100px;"
>
<div
style="background-color: lightgray; width: 100px; opacity: 1; height: 100px; transition-delay: 500ms;"
style="width: 100px; opacity: 1; height: 100px;"
title="Title for the image"
/>
<img
alt=""
class="placeholder"
src="string_of_base64"
style="position: absolute; top: 0px; left: 0px; width: 100%; height: 100%; object-fit: cover; object-position: center; opacity: 1; transition-delay: 500ms; color: red;"
style="position: absolute; top: 0px; left: 0px; width: 100%; height: 100%; opacity: 1; color: red;"
title="Title for the image"
/>
<picture>
Expand All @@ -70,7 +152,7 @@ exports[`<Image /> should render fixed size images 1`] = `
itemprop="item-prop-for-the-image"
src="test_image.jpg"
srcset="some srcSet"
style="position: absolute; top: 0px; left: 0px; width: 100%; height: 100%; object-fit: cover; object-position: center; opacity: 0; transition: opacity 500ms;"
style="position: absolute; top: 0px; left: 0px; width: 100%; height: 100%; opacity: 0;"
title="Title for the image"
width="100"
/>
Expand All @@ -92,14 +174,14 @@ exports[`<Image /> should render fluid images 1`] = `
style="width: 100%; padding-bottom: 66.66666666666667%;"
/>
<div
style="background-color: lightgray; position: absolute; top: 0px; bottom: 0px; opacity: 1; right: 0px; left: 0px; transition-delay: 500ms;"
style="position: absolute; top: 0px; bottom: 0px; opacity: 1; right: 0px; left: 0px;"
title="Title for the image"
/>
<img
alt=""
class="placeholder"
src="string_of_base64"
style="position: absolute; top: 0px; left: 0px; width: 100%; height: 100%; object-fit: cover; object-position: center; opacity: 1; transition-delay: 500ms; color: red;"
style="position: absolute; top: 0px; left: 0px; width: 100%; height: 100%; opacity: 1; color: red;"
title="Title for the image"
/>
<picture>
Expand All @@ -115,7 +197,7 @@ exports[`<Image /> should render fluid images 1`] = `
sizes="(max-width: 600px) 100vw, 600px"
src="test_image.jpg"
srcset="some srcSet"
style="position: absolute; top: 0px; left: 0px; width: 100%; height: 100%; object-fit: cover; object-position: center; opacity: 0; transition: opacity 500ms;"
style="position: absolute; top: 0px; left: 0px; width: 100%; height: 100%; opacity: 0;"
title="Title for the image"
/>
</picture>
Expand Down
14 changes: 12 additions & 2 deletions packages/gatsby-image/src/__tests__/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -79,13 +79,13 @@ describe(`<Image />`, () => {
)
// No Intersection Observer in JSDOM, so placeholder img will be visible (opacity 1) by default
expect(placeholderImageTag.getAttribute(`style`)).toEqual(
`position: absolute; top: 0px; left: 0px; width: 100%; height: 100%; object-fit: cover; object-position: center; opacity: 1; transition-delay: 500ms; color: red;`
`position: absolute; top: 0px; left: 0px; width: 100%; height: 100%; opacity: 1; color: red;`
)
expect(placeholderImageTag.getAttribute(`class`)).toEqual(`placeholder`)
})

it(`should have a transition-delay of 1sec`, () => {
const component = setup(false, { durationFadeIn: `1000` })
const component = setup(false, { durationFadeIn: 1000 })
expect(component).toMatchSnapshot()
})

Expand All @@ -101,4 +101,14 @@ describe(`<Image />`, () => {
expect(onLoadMock).toHaveBeenCalledTimes(1)
expect(onErrorMock).toHaveBeenCalledTimes(1)
})

it(`should accept a number for rootMargin`, () => {
const component = setup(false, { rootMargin: 20 })
expect(component).toMatchSnapshot()
})

it(`should accept a string for rootMargin`, () => {
const component = setup(false, { rootMargin: `20px` })
expect(component).toMatchSnapshot()
})
})
16 changes: 11 additions & 5 deletions packages/gatsby-image/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ const activateCacheForImage = props => {
let io
const listeners = new WeakMap()

function getIO() {
function getIO(rootMargin) {
if (
typeof io === `undefined` &&
typeof window !== `undefined` &&
Expand All @@ -62,15 +62,15 @@ function getIO() {
}
})
},
{ rootMargin: `200px` }
{ rootMargin }
)
}

return io
}

const listenToIntersections = (el, cb) => {
const observer = getIO()
const listenToIntersections = (el, rootMargin, cb) => {
const observer = getIO(rootMargin)

if (observer) {
observer.observe(el)
Expand Down Expand Up @@ -208,7 +208,11 @@ class Image extends React.Component {

handleRef(ref) {
if (this.state.IOSupported && ref) {
this.cleanUpListeners = listenToIntersections(ref, () => {
const rootMargin =
typeof this.props.rootMargin === `number`
? `${this.props.rootMargin}px`
: this.props.rootMargin
this.cleanUpListeners = listenToIntersections(ref, rootMargin, () => {
const imageInCache = inImageCache(this.props)
if (
!this.state.isVisible &&
Expand Down Expand Up @@ -480,6 +484,7 @@ Image.defaultProps = {
durationFadeIn: 500,
alt: ``,
Tag: `div`,
rootMargin: `200px`,
}

const fixedObject = PropTypes.shape({
Expand Down Expand Up @@ -510,6 +515,7 @@ Image.propTypes = {
fixed: fixedObject,
fluid: fluidObject,
fadeIn: PropTypes.bool,
rootMargin: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
durationFadeIn: PropTypes.number,
title: PropTypes.string,
alt: PropTypes.string,
Expand Down