-
Notifications
You must be signed in to change notification settings - Fork 49
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
AccessKit Disable GIFs: Implement poster-based and css-based pausing #1727
base: master
Are you sure you want to change the base?
AccessKit Disable GIFs: Implement poster-based and css-based pausing #1727
Conversation
b7da0db
to
48c04d8
Compare
48c04d8
to
c4ccd24
Compare
Ah, here's an interesting oddity: The WebCodecs API We actually have—to an extent—the ability to choose whether we fetch a webp or gif in many cases, because gifv is served as either depending on the fetch headers. I committed What I don't know is how exactly caching works in this case, and thus whether there's a benefit to ensuring that we fetch the same data twice or whether we ought to either prioritize the gif via q-weighting or just omit the header. In general, like in the "speed up XKit Rewritten's initialization on page load" project, it's possible to get quite into the weeds eking out every last frame of performance, but particularly because this PR already makes pausing instant for the vast majority of GIFs the user will see, I certainly don't think it's worth additional effort. But I did test it, so I'll note it. (somewhat relevant: https://developer.mozilla.org/en-US/docs/Web/HTTP/Content_negotiation/List_of_default_Accept_values#values_for_an_image) |
This comment was marked as outdated.
This comment was marked as outdated.
d0cce45
to
7b9e44c
Compare
7b9e44c
to
3f7bdde
Compare
0a92bde
to
b6a9b4d
Compare
Now this one is fascinating. In Safari, this fails to show the image behind the poster when the user hovers and invalidates the rule: const hovered = `:is(:hover > *, [${hoverContainerAttribute}]:hover *)`; img:has(~ [${posterAttribute}]):not(${hovered}) {
visibility: hidden !important;
} Moving the attribute to the animated image instead of the poster* and changing out that - img:has(~ [${posterAttribute}]):not(${hovered}) {
+ img:has(~ [${posterAttribute}]:not(${hovered})) {
visibility: hidden !important;
} This is thus almost certainly a bug in Safari's CSS invalidation logic around the Maybe at some point I'll make a minimal demo of this and submit it to... something, I dunno. *ideally we don't do this, as on mobile devices Tumblr's code sometimes deletes the animated image and keeps the poster, as noted in the previous now-outdated comment. |
b6a9b4d
to
31290d6
Compare
Description
This implements, depending on how you count, two or three new techniques to pause animated images. This yields:
This enables future PRs to implement:
Supersedes #1681 and #1709 (they are effectively combined, for non-poster GIFs) and #1707 (included, for with-poster GIFs).
Technical Details
Images with poster elements are paused by showing the poster element and hiding the image. Other elements are paused with CSS overrides (
content
for images,background-image
for elements with it set to a gif) set to a blob URL created by fetching the image source and pausing it.Non-animated images are ignored (unless they have posters, or have an animation flag but only have one frame, or the Firefox version is between 121 and 132).
details
Currently, AccessKit pauses animated images by waiting until they load, and inserting canvas elements with paused versions of their contents into the DOM that cover them when they are not hovered. It "pauses" elements with animated images as part of their background-image property by replacing the background image with a solid color.
When this code finds an animated* image beneath the "poster" element provided by Tumblr on GIFs in posts, it applies CSS that shows the poster instead of the image unless the stack is hovered. This is fast, efficient, and allows future development to use a simple tweak to delay the animated image download, if desired.
Notes: *Tumblr always provides "poster" elements on GIFs, so like the current code does this will erroneously add hoverable GIF labels to non-animated GIF images.
When this code finds a possibly-animated image without a "poster," including webp images, it will download the image source, detect whether it's actually animated (if supported), create a blob URL containing a paused version of the image, and apply CSS that sets the image's
content
property to the new URL unless it's hovered. This enables only-if-animated webp image pausing, and using CSS instead of a canvas element means that future development can target more elements where the canvas would break layout (e.g. animated blog headers).Notes: Animated image detection requires the WebCodecs API, and thus requires Chromium or Firefox 133+. This is feature detected, and fallback
createImageBitmap
-based code that assumes .gif/.webp images are animated but otherwise fully works is included.When this code finds an element with a possibly-animated image url as part of its
background-image
property, it will create a paused image url as just described, and apply CSS that replaces the url in the image'sbackground-image
property unless it's hovered. This pauses elements we previously background-overrode like the tag page banner (including its gradient!), and means that future development can target even more elements that use this property.Notes: This uses a fairly complex regular expression. Elements are processed faster in Chromium or Firefox 133+, often pausing long before they finish downloading, because source URLs are available immediately and the WebCodecs API supports streaming.
This currently never calls
URL.revokeObjectURL
. Some rudimentary investigation appears to reveal that blob URLs are stored on-disk (so I don't think you can out-of-memory your browser with this) until the page is hard-navigated.Here's (iirc) every technique I considered (diagram courtesy of https://mermaid.js.org/intro/):
details
We can insert paused content as:
background-image
and certain image elements like headers (dom layout issues; possibly solvable).content
/background-image
replacement with a blob URL. Highly compatible; a bit slow/expensive (blob URL creation is async and uses storage/memory).We can create paused content via:
background-image
and doesn't help distinguish animated/non-animated webp.The vast majority of elements we care about are GIFs with posters, so we want an efficient method for those.
The download and css code paths are necessary to cover everything, but we definitely want to avoid downloading in high volume, so we want at least one more path. I picked use poster because I like the delay-load feature and it's efficient and simple, and declined to add anything else to reduce the LOC count; there are other combinations with minor upsides, but imho they're mostly worse.
diagram source:
misc notes
small load speed improvement for non-poster animated images:
Testing steps