Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: implement responsiveness with srcSet and sizes #159

Merged
merged 29 commits into from
Jul 30, 2018

Conversation

frederickfogerty
Copy link
Contributor

@frederickfogerty frederickfogerty commented Jul 26, 2018

BREAKING CHANGE:

  • srcSet behaviour has changed to use sizes + srcSets
  • type=bg has been removed
  • the following props have been removed: aggressiveLoad, component, fluid, precision, defaultHeight, defaultWidth
  • generateSrcSet has been changed to disableSrcSet

Description

This PR fundamentally changes the approach taken to responsiveness. Previously, the image (or component) was rendered with an empty image, and the size of the resulting DOM element was measured. An image was loaded with these dimensions, ensuring that the image loaded was only as large as needed. Now, this component achieves the same behaviour by leveraging new native browser features, srcSet and sizes. This allows the browser to load an appropriate size based on the expected image size, which is calculated based on sizes. This has an added benefit that images can load much quicker as the browser can load them as soon as page layout has completed, or sooner (e.g. if sizes is 100vw). Overall this change has made both the usage of the library and the internals simpler and easier to understand.

The significant amount of breaking changes makes sense due to a lot of the props no longer making sense. Background mode has also been removed as it was incompatible with the new method of responsiveness, and after discussion with @jayeb it seemed that it wouldn't be missed (based on a similar change that imgix.js made).

Picture mode still doesn't work amazingly, as there is a mis-match between what a developer would expect to set as props and what they actually have to set to make picture work as they want. This is not a problem with our library per se, but with picture pecularities. For example, if the fallback img in a <picture> has a width attribute set, all the other sources will also be locked to that size, even if they specify their own widths. Maybe this is common knowledge but it seems peculiar to me. I expect to address this and make it clearer for developers in a future PR.

This PR closes #153. This PR closes #115. This PR closes #71

Checklist

Please use the checklist that is most closely related to your PR (you only need to use one checklist, and you can skip items that aren't applicable or don't make sense):

New Feature

  • If this is a big feature with breaking changes, consider [opening an issue][issues] to discuss first. This is completely up to you, but please keep in mind that your PR might not be accepted.
  • Run unit tests to ensure all existing tests are still passing
  • Add new passing unit tests to cover the code introduced by your PR
  • Update the readme
  • Add some steps so we can test your cool new feature!
  • The PR title is in the conventional commit format: e.g. feat(<area>): added new way to do this cool thing #issue-number
  • Add your info to the contributors array in package.json!

Steps to Test

Observe changes to unit tests.

It may also be worth to set up a test project and use npm link to check default image rendering and picture rendering.

* chore(readme): fix typo in readme

* chore(readme): edit codesandbox iframes to be links instead
- generateSrcSet -> disableSrcSet
- remove aggressiveLoad
- remove precision
- add sizes top-level prop
- generateSrcSet -> disableSrcSet
- remove
- remove precision
- add sizes top-level prop
- remove empty img
- remove defaultWidth and defaultHeight
- remove fluid prop
- remove auto-sizing behaviour in favour of srcsets
- add check to ensure sizes is passed when in image mode and no width and heights have been passed
* version-8:
  fix: warn the user when no <img> passed as a child to <picture> fixes #90 (#151)

# Conflicts:
#	src/react-imgix.js
#	test/unit/react-imgix.test.js
Copy link

@jayeb jayeb left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A couple of small changes, but by and large this looks great.

The biggest issue I have is with the availWidth and availHeight constants--are they even necessary at this point? We already decided that they shouldn't be used to cap the list of generated widths at a specific size, so I'm not sure they have any use anymore.

README.md Outdated

For server rendering, `aggressiveLoad` should be used. This component renders nothing on the first render as it tries to work out what the size of the container it will be rendering in, and only loads an image at the resolution required. For server rendering this will mean no image will be rendered.
Since imgix can generate as many derivative resolutions as needed, react-imgix calculates them programmatically, using the dimensions you specify (note that the w and h params scale appropriately to maintain the correct aspect ratio). All of this information has been placed into the srcset and sizes attributes.
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe I'm missing something, but what's up with the bit about w and h scaling properly? This isn't exemplified in the above code.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Whoops, I was planning to do this but didn't get around to it. I might pull this out and put it in a separate issue

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed from PR and added to #161

src/constants.js Outdated
@@ -0,0 +1,6 @@
const CONSTANTS = {
availWidth: window.screen.availWidth,
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can't find good data about browser support for this. Have we verified that window.screen is available on all browsers we support?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I realise now this isn't used anymore anyway so I'll just remove it.


const roundToNearest = (size, precision) =>
precision * Math.ceil(size / precision);
const NODE_ENV = process.env.NODE_ENV;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Interesting. How does this end up manifesting when this code is run in-browser?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is an extremely common pattern which is used everywhere in React. It is usually defined by webpack plugin or webpack itself.

if (props.crop) crop = props.crop;

let fit = false;
if (entropy) fit = "crop";
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this should be if (entropy || faces)?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated

let srcSet;

if (disableSrcSet) {
srcSet = src;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm... the disabledSrcSet option doesn't actually disabled srcset. Is that bad? Maybe there's no good reason not to just set it anyway as we're doing here, but if the option says "don't set it" the user expectation might be that it doesn't get set. I don't feel strongly either way, but it's food for thought.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll be honest, it isn't very clear, by just reading this section of the code, what the actual behaviour is. I'll explain:

option says "don't set it" the user expectation might be that it doesn't get set

This is the actual behaviour. This value is not automatically applied to rendered element. For an img, if disableSrcSet is set then srcSet is not applied. For a source, if srcSet is set then the srcSet attribute is simply the source, without any px or w generations applied. For source this is desired as srcSet is required in this case.

Does this make more sense? Is there a way we can improve the code?

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yea, I'm on board now 👍

const addFallbackSrc = srcSet => srcSet.concat(`, ${src}`);
const _srcSet = targetWidths.map(buildSrcSetPair).join(", ");
const _srcSetWithFallback = addFallbackSrc(_srcSet);
srcSet = _srcSetWithFallback;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems like a really convoluted way of doing this. How about just doing this?

srcSet = targetWidths.map(buildSrcSetPair).concat([src]).join(", ");

Copy link
Contributor Author

@frederickfogerty frederickfogerty Jul 26, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My aim with _srcSetWithFallback was to make clear what the added src was for. Would this work?

const addFallbackSrc = srcSet => srcSet.concat(src);
const _srcSet = targetWidths.map(buildSrcSetPair);
const _srcSetWithFallback = addFallbackSrc(_srcSet);
srcSet = _srcSetWithFallback.join(', ');

or even

const addFallbackSrc = srcSet => srcSet.concat(src);
srcSet = addFallbackSrc(targetWidths.map(buildSrcSetPair)).join(', ');

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think either one of those is an improvement over the code as it's currently written, yea.

});
}

export default targetWidths();
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is an excellent update to the crusty version found in imgix.js, thank you. At some point in the near future I'd love to pull this out into its own repo with a JSON distributable a la https://github.com/imgix/imgix-url-params.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you. That sounds like a great idea.


import CONSTANTS from "./constants";

const _screen = {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this constant even used?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, I forgot to remove it when I did the update.

@frederickfogerty
Copy link
Contributor Author

Thanks for the review @jayeb. You're dead right with the constants - I've removed them.

@frederickfogerty
Copy link
Contributor Author

Okay @jayeb I think I've covered everything discussed here - are you able to give this a quick review?

@frederickfogerty frederickfogerty merged commit fa68df6 into version-8 Jul 30, 2018
@frederickfogerty frederickfogerty deleted the fred/use-srcsets branch July 30, 2018 19:48
@jayeb jayeb mentioned this pull request Jul 30, 2018
9 tasks
frederickfogerty added a commit that referenced this pull request Aug 15, 2018
* version-8:
  docs: add link to fluid example and fix some links
  docs: add example about fitting to the size of the container
  docs: add ref information
  feat: refactor picture and source behaviour into different components (#163)
  feat: reduce props API surface area (#162)
  feat: implement responsiveness with srcSet and sizes (#159)
  fix: warn the user when no <img> passed as a child to <picture> fixes #90 (#151)

# Conflicts:
#	README.md
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants