Skip to content

Commit 081c04c

Browse files
committed
replace canvas with css content; pause remaining things
1 parent 43c2b75 commit 081c04c

File tree

1 file changed

+36
-69
lines changed

1 file changed

+36
-69
lines changed

src/features/accesskit/disable_gifs.js

Lines changed: 36 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@ import { buildStyle, postSelector } from '../../utils/interface.js';
55
import { getPreferences } from '../../utils/preferences.js';
66
import { memoize } from '../../utils/memoize.js';
77

8-
const canvasClass = 'xkit-paused-gif-placeholder';
98
const pausedPosterAttribute = 'data-paused-gif-use-poster';
9+
const pausedContentVar = '--xkit-paused-gif-content';
1010
const pausedBackgroundImageVar = '--xkit-paused-gif-background-image';
1111
const hoverContainerAttribute = 'data-paused-gif-hover-container';
1212
const labelAttribute = 'data-paused-gif-label';
@@ -16,7 +16,6 @@ const containerClass = 'xkit-paused-gif-container';
1616
let loadingMode;
1717

1818
const hovered = `:is(:hover, [${hoverContainerAttribute}]:hover *)`;
19-
const parentHovered = `:is(:hover > *, [${hoverContainerAttribute}]:hover *)`;
2019

2120
export const styleElement = buildStyle(`
2221
[${labelAttribute}]::after {
@@ -46,16 +45,17 @@ export const styleElement = buildStyle(`
4645
transform: translateY(-50%);
4746
}
4847
49-
.${canvasClass} {
50-
position: absolute;
51-
visibility: visible;
48+
[${labelAttribute}]${hovered}::after,
49+
[${pausedPosterAttribute}]:not(${hovered}) > div > ${keyToCss('knightRiderLoader')} {
50+
display: none;
51+
}
5252
53-
background-color: rgb(var(--white));
53+
${keyToCss('blogCard')} ${keyToCss('headerImage')}${keyToCss('small')}[${labelAttribute}]::after {
54+
font-size: 0.8rem;
55+
top: calc(140px - 1em - 2.2ch);
5456
}
5557
56-
.${canvasClass}${parentHovered},
57-
[${labelAttribute}]${hovered}::after,
58-
[${pausedPosterAttribute}]:not(${hovered}) > div > ${keyToCss('knightRiderLoader')} {
58+
img:is([${pausedPosterAttribute}], [style*="${pausedContentVar}"]):not(${hovered}) ~ div > ${keyToCss('knightRiderLoader')} {
5959
display: none;
6060
}
6161
${keyToCss('background')}[${labelAttribute}]::after {
@@ -73,6 +73,9 @@ ${keyToCss('background')}[${labelAttribute}]::after {
7373
display: none;
7474
}
7575
76+
img[style*="${pausedContentVar}"]:not(${hovered}) {
77+
content: var(${pausedContentVar});
78+
}
7679
[style*="${pausedBackgroundImageVar}"]:not(${hovered}) {
7780
background-image: var(${pausedBackgroundImageVar}) !important;
7881
}
@@ -94,27 +97,6 @@ const addLabel = (element, inside = false) => {
9497
}
9598
};
9699

97-
/**
98-
* Fetches the selected image and tests if it is animated. On older browsers without ImageDecoder
99-
* support, GIF images are assumed to be animated and WebP images are assumed to not be animated.
100-
*/
101-
const isAnimated = memoize(async sourceUrl => {
102-
const response = await fetch(sourceUrl, { headers: { Accept: 'image/webp,*/*' } });
103-
const contentType = response.headers.get('Content-Type');
104-
105-
if (typeof ImageDecoder === 'function' && await ImageDecoder.isTypeSupported(contentType)) {
106-
const decoder = new ImageDecoder({
107-
type: contentType,
108-
data: response.body,
109-
preferAnimation: true
110-
});
111-
await decoder.decode();
112-
return decoder.tracks.selectedTrack.animated;
113-
} else {
114-
return !sourceUrl.endsWith('.webp');
115-
}
116-
});
117-
118100
/**
119101
* Fetches the selected image, tests if it is animated, and returns a blob URL with the paused image
120102
* if it is. This is a small memory leak, as the resulting blob URL will never be revoked; avoid
@@ -154,53 +136,29 @@ const createPausedUrlIfAnimated = memoize(async sourceUrl => {
154136
return URL.createObjectURL(blob);
155137
});
156138

157-
const pauseGif = async function (gifElement) {
158-
if (gifElement.currentSrc.endsWith('.webp') && !(await isAnimated(gifElement.currentSrc))) return;
159-
160-
const image = new Image();
161-
image.src = gifElement.currentSrc;
162-
image.onload = () => {
163-
if (gifElement.parentNode && gifElement.parentNode.querySelector(`.${canvasClass}`) === null) {
164-
const canvas = document.createElement('canvas');
165-
canvas.width = image.naturalWidth;
166-
canvas.height = image.naturalHeight;
167-
canvas.className = gifElement.className;
168-
canvas.classList.add(canvasClass);
169-
canvas.setAttribute('style', gifElement.getAttribute('style'));
170-
canvas.getContext('2d').drawImage(image, 0, 0);
171-
gifElement.after(canvas);
172-
addLabel(gifElement);
173-
174-
gifElement.closest(keyToCss(
175-
'imgLink' // trending tag: https://www.tumblr.com/explore/trending
176-
))?.setAttribute(hoverFixAttribute, '');
177-
}
178-
};
179-
};
180-
181139
const processGifs = function (gifElements) {
182-
gifElements.forEach(gifElement => {
140+
gifElements.forEach(async gifElement => {
183141
if (gifElement.closest(`${keyToCss('avatarImage', 'subAvatarImage')}, .block-editor-writing-flow`)) return;
184-
const pausedGifElements = [...gifElement.parentNode.querySelectorAll(`.${canvasClass}`)];
185-
if (pausedGifElements.length) {
186-
gifElement.after(...pausedGifElements);
187-
return;
188-
}
189-
190142
gifElement.decoding = 'sync';
191143

192144
const posterElement = gifElement.parentElement.querySelector(keyToCss('poster'));
193145
if (posterElement) {
194146
gifElement.parentElement.setAttribute(pausedPosterAttribute, loadingMode);
195-
addLabel(posterElement);
196-
return;
197-
}
198-
199-
if (gifElement.complete && gifElement.currentSrc) {
200-
pauseGif(gifElement);
201147
} else {
202-
gifElement.onload = () => pauseGif(gifElement);
148+
const sourceUrl = gifElement.currentSrc ||
149+
await new Promise(resolve => gifElement.addEventListener('load', () => resolve(gifElement.currentSrc), { once: true }));
150+
151+
const pausedUrl = await createPausedUrlIfAnimated(sourceUrl);
152+
if (!pausedUrl) return;
153+
154+
gifElement.style.setProperty(pausedContentVar, `url(${pausedUrl})`);
203155
}
156+
addLabel(gifElement);
157+
158+
gifElement.closest(keyToCss(
159+
'albumImage', // post audio element
160+
'imgLink' // trending tag: https://www.tumblr.com/explore/trending
161+
))?.setAttribute(hoverFixAttribute, '');
204162
});
205163
};
206164

@@ -263,10 +221,18 @@ export const main = async function () {
263221
${
264222
'figure' // post image/imageset; recommended blog carousel entry; blog view sidebar "more like this"; post in grid view; blog card modal post entry
265223
},
224+
${
225+
'main.labs' // labs settings header: https://www.tumblr.com/settings/labs
226+
},
266227
${keyToCss(
267228
'linkCard', // post link element
229+
'albumImage', // post audio element
230+
'messageImage', // direct message attached image
231+
'messagePost', // direct message linked post
268232
'typeaheadRow', // modal search dropdown entry
269233
'tagImage', // search page sidebar related tags, recommended tag carousel entry: https://www.tumblr.com/search/gif, https://www.tumblr.com/explore/recommended-for-you
234+
'headerBanner', // blog view header
235+
'headerImage', // modal blog card header, activity page "biggest fans" header
270236
'topPost', // activity page top post
271237
'colorfulListItemWrapper', // trending tag: https://www.tumblr.com/explore/trending
272238
'takeoverBanner' // advertisement
@@ -314,11 +280,12 @@ export const clean = async function () {
314280
wrapper.replaceWith(...wrapper.children)
315281
);
316282

317-
$(`.${canvasClass}`).remove();
318283
$(`[${labelAttribute}]`).removeAttr(labelAttribute);
319284
$(`[${pausedPosterAttribute}]`).removeAttr(pausedPosterAttribute);
320285
$(`[${hoverContainerAttribute}]`).removeAttr(hoverContainerAttribute);
321286
$(`[${hoverFixAttribute}]`).removeAttr(hoverFixAttribute);
287+
[...document.querySelectorAll(`img[style*="${pausedContentVar}"]`)]
288+
.forEach(element => element.style.removeProperty(pausedContentVar));
322289
[...document.querySelectorAll(`[style*="${pausedBackgroundImageVar}"]`)]
323290
.forEach(element => element.style.removeProperty(pausedBackgroundImageVar));
324291
};

0 commit comments

Comments
 (0)