Closed polarathene closed 3 years ago
imgcurrentSrc
and imgCached
use<img>
if a cached copy can be displayed in the meantimeWhen max-age expires and the browser(Chrome in this case) makes a request to check if resource has been modified, on Slow 3G throttled network, TTFB is 2 seconds long, despite this, img.currentSrc
was valid prior and the image was rendered before the response(TTFB) was returned, where the handleImageLoaded()
method is triggered or img.complete===true
. The browser chose to serve the local cached copy without waiting.
img.currentSrc
will be set depending on cache layerWhen switching quickly between image variants for an art directed image (with my active PR), sometimes the img.currentSrc
is empty, while max-age
would not have expired. It pulls it from disk-cache has no TTFB delay due to no network request made("200 OK" response, not "304 not modified"), seems to be reliable for memory-cache or when making a network request though. If cache is disabled, img.currentSrc
will be empty as expected and the proper image will not transition from placeholder until handleImageLoaded()
fires.
img.currentSrc
may indicate a locally cached image, but the rendered cached image may be replaced if the response differsFinal test was to wait for max-age
to expire and swap the image for a different image large enough to take a while on Slow 3G to retrieve. img.currentSrc
returned the URL, and as expected showed the cached resource prior to waiting on an image response. Once the first byte of the response arrived, the new image I had replaced on disk began to load in..
img.currentSrc
is a valid solution when the resource data at a given URI is immutableThis does mean it's technically incorrect in this situation.. especially since imgCached
being set would remove the placeholder and immediately reveal the img element, showing the progressive image download(either pixelated, slowly improving clarity as more pixels arrive, or the top to bottom scanlines). That would normally be a problem, but since image URIs are meant to be unique, the browser should never run into this situation where it has a different image than the one it's previously cached for that URI.
Therefore, this approach is still deemed valid, but should probably include a developer note for any maintainers to be aware of this behaviour and expectation/assumption being made with resource URIs provided to gatsby-image
.
For isCritical
images, those exist in the DOM prior to React, thus img.currentSrc
is always set for these, img.complete
appears to be reliable instead and has been used in componentDidMount()
which worked until my hydration PR changed the call order with handleRef()
.
For images that are not using isCritical
, img.complete
also seems valid for when the memory-cache is used, but not when performing a network request(due to TTFB delay, whereas img.currentSrc
is valid), or disk-cache which also doesn't work for img.currentSrc
either. Network throttle doesn't appear to influence that, nor the DOM load event relative to the image resource timing.
Chrome, Opera(Blink based), Epiphany(webkit based, similar to Safari afaik) all seem to behave similar with the currentSrc
attribute having something to indicate the image is cached, but Firefox does not mimic that and always appears to be false(at this point in time when queried). So currentSrc
won't work with firefox.
I've the same problem showing a Logo on the top left side. Every time I click on a page the Logo flickers. This is uncommon. I used useStaticQuery with childImageSharp.fixed.
This is uncommon.
What is uncommon? The flickering of the low quality placeholder is reproducible, just need to refresh/reload or visit the page via url. It should only happen once when you visit the page, then the rest of the session it should be in the gatsby-image
internal cache, which should prevent that visual appearing until the next visit of the site.
If you want to avoid it, since the logo is probably quite small, just avoid the base64 placeholder, if you look up the graphql sharp API that mentions using childImageSharp
fragment, there is another one with _nobase64
, or on the image object data you pass to gatsby-image
fixed prop, you can set the base64
field to ""
(empty string) that should work too I think.
For larger images where you want the placeholder while the real picture downloads, it's more of an issue.
@polarathene thank you for the answer, it helped. It was the blur up effect that I found uncommon but that's because I'm new to Gatsby. I should have read the documentation further. It says:
If you don’t want to use the blur-up effect, choose the fragment with noBase64 at the end.
Hiya!
This issue has gone quiet. Spooky quiet. 👻
We get a lot of issues, so we currently close issues after 60 days of inactivity. It’s been at least 20 days since the last update here. If we missed this issue or if you want to keep it open, please reply here. You can also add the label "not stale" to keep this issue open! As a friendly reminder: the best way to see this issue, or any other, fixed is to open a Pull Request. Check out gatsby.dev/contribute for more information about opening PRs, triaging issues, and contributing!
Thanks for being a part of the Gatsby community! 💪💜
Not stale.
Hiya!
This issue has gone quiet. Spooky quiet. 👻
We get a lot of issues, so we currently close issues after 60 days of inactivity. It’s been at least 20 days since the last update here. If we missed this issue or if you want to keep it open, please reply here. As a friendly reminder: the best way to see this issue, or any other, fixed is to open a Pull Request. Check out gatsby.dev/contribute for more information about opening PRs, triaging issues, and contributing!
Thanks for being a part of the Gatsby community! 💪💜
The old gatsby-image
is deprecated now, if you still see this same behavior in gatsby-plugin-image
please open a new issue with a minimal reproduction. Thanks!
Description
Using a placeholder such as base64 is nice to have for first retrieval of assets. However when they are cached this results in:
gatsby-image
cache:It's a minor UX issue, but it would be nice to smooth these out.
Examples
https://i.imgur.com/rRq7BGW.mp4
Related
https://github.com/timhagn/gatsby-background-image/issues/92 https://github.com/gatsbyjs/gatsby/issues/18858 https://github.com/gatsbyjs/gatsby/issues/12254#issuecomment-471278211
https://github.com/gatsbyjs/gatsby/issues/12836
https://github.com/gatsbyjs/gatsby/pull/14158
Discussion
I can put together a PR resolving these as best as possible if desirable.
Hydration flicker
Can be resolved by adding keyframe CSS in a
<style>
element. This allows for having the placeholder opacity at 0, and toggling it to 1 viaanimation
rule.Drawback is it adds to page weight (although compression may optimize that away), the more images there are each adding that same element and CSS. Unless we place this in the HTML base within the
<head>
instead, then it can be toggled viaclassName
.A delay would need to be around 200-300ms minimum(based on 200ms until
gatsby-image
code executed fromperformance.now()
and 10-100ms for a cached response to update state to render), so a flicker would still be visible, it would just be similar to an externally linked<img>
flashing in over whatever the background was, instead of a potentially large upscaled20px
base64 pixelated looking image before the cached image renders. ThebackgroundColor
placeholder, especially with an appropriate colour can reduce that "flash" / flicker appearance.Presently CSS can be used to add a blur filter and reduce the low quality pixelated flicker impact, but still may not be desirable. Likewise, in this case a user could provide the keyframe CSS externally, so long as they have a reliable
className
to target(not presently supported).Internally, this does not stop the fade transition. There are two cases to handle for this:
imgCached
withinhandleRef()
can be used, this leverages the imagecurrentSrc
to know if the browser has the image in cache without always needing to make a network request. When it is a falsy value, the browser will always log a network request, pulling from disk-cache or querying the server if the asset has changed delaying the assignment ofcurrentSrc
, thus even if the asset is cached locally it may not be set reliably whenhandleRef()
fires.imgCached
, usingperformance.now()
only once withingatsby-image
module, all instances can reference when it was first available since the page loaded. A value of<500ms
would be a good indication that the page has been previously loaded before, the browser should reliably have access to the cache if the image to load is consistent, probably not reliable if the viewport dimensions have changed that a different image source would be selected.The keyframe CSS can be used after hydration as well and removed once
imgLoaded
fires. Doing so would momentarily defer the placeholder visibility, if a cached image is not quickly retrievable. On throttledFast 3G
if the server needs to be queried if content has changed due toCache-Control
header expiring, the latency imposes ~500ms(Chrome to localgatsby serve
or Caddy server) and 2sec forSlow 3G
to return a304
(~128 bytes response). The higher the latency, the less likely the user would witness this flickering issue, so this approach is still valid.TL;DR
The
performance.now()
fallback is probably too much of an assumption to include, users would need to wait until v3 withgatsby-image
modularized into a composable component to implement this approach if acceptable for them.imgCached
is still worthwhile even if it fails sometimes (would not break anything).I am not expecting the
animation
CSS with keyframe in<script>
being part of thegatsby-image
component by default would be welcomed, especially since it should be possible for a user to configure for, like they would an improved blur-up via CSS filter. TheclassName
to target however would be required.Intersection Observer
Without the CSS keyframes approach, I don't see this feature being possible to skip the initial placeholder rendered frame with page transitions. That frame in my measurements was lasting ~40ms regardless of network throttled speed, when the resource was cached.
Hiding the placeholder via the CSS
animation
opacity toggle works, but effectively transfers the flicker issue to those loading the resource over network, however, this would only be visible AFAIK during initial page loads where hydration is involved, making it far less likely to be encountered.[blank] -> placeholder while retrieving JS -> hydration -> [blank] -> placeholder while retrieving image -> image
Native Lazy Loading
Unlike Intersection Observer instances, these have
isVisible
as true and only hide visibility of the image element until it's loaded. UsingimgCached
here works well too.