-
Notifications
You must be signed in to change notification settings - Fork 10.3k
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
[gatsby-image] Placeholder flicker/transition when image is cached by browser #25942
Comments
Caching observations regarding
|
For For images that are not using Notes about `Cache-Control` response header (not likely of value to most)Additional observation, if For this observation, the response headers were slightly different. Additional fields were in the response header, Sometimes these are 200-OK responses, and always if visiting the page/url via new tab if the cached resource has become stale, seems soft reload/refresh does something different, where I guess if the URL hasn't changed for the active page, I get the better caching behavior. A hard reload adds to request headers Similar to experiences with the "Performance" profiling tab in dev tools, where I noticed some behavior changing, I had found the browser was using cached response headers with soft reloads that should have been different due to revalidation when stale, perhaps because the resource was a 304, it did not update the response headers and those were cached with the resource. Noticed that the addition of Another difference with hard vs soft reload is the image is always returning a 200-OK response, including once stale and validating for an update, additionally, I have tested with a replacement image of different dimensions in the static file location, in soft refreshes where validation request is sent, the image resizes to fit the width/height of the image wrapper that has the original image placeholder base64, then once the validation response is returned, it will show the image with it's proper proportions cropped with object-fit, memory/disk caches skip the distorted step and hard reload doesn't have the distorted render either during revalidation. The brief distorted image did happen to render on hard reload "Slow 3G" when the image actually had changed, provided a cached resource was available. Possibly related to not returning a Another update, while hard reload with "Slow 3G" resulted in no temporary distorted image like a soft/normal reload, which also seems to mean no Used Created a custom network throttle that only ups latency to around 5 seconds. Soft reload reveals the distorted image until the pending request returns a response(TTFB delay), while hard reload skipped the distorted image showing the base64 placeholder until response returns. These were both testing reloads after cache was stale for Image distortion is conforming to the image components width/height attributes(fixed image type), with Noted that soft reloads with the increased latency didn't show a distorted image if other assets such as js and document caches had expired. Then reduced latency and that brought the behaviour back up again, seems consistent between soft/hard reloads then. For all I know this distorted image stage is some optimization for lazy loaded images and could be ignoring cache-control expectations when cache is stale and should revalidate. https://engineering.fb.com/web/this-browser-tweak-saved-60-of-requests-to-facebook/
Although Chrome made changes to that behaviour (and soft/hard reload appears to have been available for a long time prior), so I'm just going to assume reloads aren't particularly reliable way to test cache-control/revalidation.. The article also links to Chrome and Firefox announcements from back in 2017 related to the FB cache revalidation optimizations. Then there's also the mention of browser caches not being reliable/consistent here: https://github.com/web-platform-tests/wpt/tree/master/fetch/http-cache Some inconsistencies across implementations can be noted here: Chrome, Opera(Blink based), Epiphany(webkit based, similar to Safari afaik) all seem to behave similar with the |
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. |
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 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 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:
|
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. 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. Thanks for being a part of the Gatsby community! 💪💜 |
The old |
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
timhagn/gatsby-background-image#92
#18858
#12254 (comment)
#12836
#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.The text was updated successfully, but these errors were encountered: