FEATURE: Allow pausing animated images in posts (#12795)
Co-authored-by: Jarek Radosz <jradosz@gmail.com>
This commit is contained in:
parent
1e9301d4d8
commit
c11d75da87
|
@ -0,0 +1,66 @@
|
|||
import { withPluginApi } from "discourse/lib/plugin-api";
|
||||
|
||||
let _gifClickHandlers = {};
|
||||
|
||||
export default {
|
||||
name: "animated-images-pause-on-click",
|
||||
|
||||
initialize() {
|
||||
withPluginApi("0.8.7", (api) => {
|
||||
function _cleanUp() {
|
||||
Object.values(_gifClickHandlers || {}).forEach((handler) =>
|
||||
handler.removeEventListener("click", _handleClick)
|
||||
);
|
||||
|
||||
_gifClickHandlers = {};
|
||||
}
|
||||
|
||||
function _handleClick(event) {
|
||||
const img = event.target;
|
||||
if (img && !img.previousSibling) {
|
||||
let canvas = document.createElement("canvas");
|
||||
canvas.width = img.width;
|
||||
canvas.height = img.height;
|
||||
canvas.getContext("2d").drawImage(img, 0, 0, img.width, img.height);
|
||||
canvas.setAttribute("aria-hidden", "true");
|
||||
canvas.setAttribute("role", "presentation");
|
||||
|
||||
img.parentNode.classList.add("paused-animated-image");
|
||||
img.parentNode.insertBefore(canvas, img);
|
||||
} else {
|
||||
img.previousSibling.remove();
|
||||
img.parentNode.classList.remove("paused-animated-image");
|
||||
}
|
||||
}
|
||||
|
||||
function _attachCommands(post, helper) {
|
||||
if (!helper) {
|
||||
return;
|
||||
}
|
||||
|
||||
let images = post.querySelectorAll("img.animated");
|
||||
|
||||
images.forEach((img) => {
|
||||
if (_gifClickHandlers[img.src]) {
|
||||
_gifClickHandlers[img.src].removeEventListener(
|
||||
"click",
|
||||
_handleClick
|
||||
);
|
||||
|
||||
delete _gifClickHandlers[img.src];
|
||||
}
|
||||
|
||||
_gifClickHandlers[img.src] = img;
|
||||
img.addEventListener("click", _handleClick, false);
|
||||
});
|
||||
}
|
||||
|
||||
api.decorateCookedElement(_attachCommands, {
|
||||
onlyStream: true,
|
||||
id: "animated-images-pause-on-click",
|
||||
});
|
||||
|
||||
api.cleanupStream(_cleanUp);
|
||||
});
|
||||
},
|
||||
};
|
|
@ -1233,3 +1233,19 @@ a.mention-group {
|
|||
@include ellipsis;
|
||||
}
|
||||
}
|
||||
|
||||
.paused-animated-image {
|
||||
position: relative;
|
||||
display: block;
|
||||
> canvas {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
}
|
||||
|
||||
img.animated {
|
||||
// need to keep the image hidden but clickable
|
||||
// so the user can resume animation
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -318,6 +318,12 @@ class CookedPostProcessor
|
|||
return
|
||||
end
|
||||
|
||||
upload = Upload.get_from_url(src)
|
||||
|
||||
if upload.present? && upload.animated?
|
||||
img.add_class("animated")
|
||||
end
|
||||
|
||||
return if original_width <= SiteSetting.max_image_width && original_height <= SiteSetting.max_image_height
|
||||
|
||||
user_width, user_height = [original_width, original_height] if user_width.to_i <= 0 && user_height.to_i <= 0
|
||||
|
@ -332,7 +338,6 @@ class CookedPostProcessor
|
|||
width, height = ImageSizer.resize(width, height)
|
||||
end
|
||||
|
||||
upload = Upload.get_from_url(src)
|
||||
if upload.present?
|
||||
upload.create_thumbnail!(width, height, crop: crop)
|
||||
|
||||
|
|
|
@ -973,7 +973,7 @@ describe CookedPostProcessor do
|
|||
expect(doc.css('img').first['srcset']).to_not eq(nil)
|
||||
end
|
||||
|
||||
it "does not optimize animated images" do
|
||||
it "does not optimize animated images but adds a class so animated images can be identified" do
|
||||
upload.update!(animated: true)
|
||||
post = Fabricate(:post, raw: "![image|1024x768, 50%](#{upload.short_url})")
|
||||
|
||||
|
@ -984,6 +984,7 @@ describe CookedPostProcessor do
|
|||
expect(doc.css('.lightbox-wrapper').size).to eq(1)
|
||||
expect(doc.css('img').first['src']).to include(upload.url)
|
||||
expect(doc.css('img').first['srcset']).to eq(nil)
|
||||
expect(doc.css('img.animated').size).to eq(1)
|
||||
end
|
||||
|
||||
it "optimizes images in quotes" do
|
||||
|
|
Loading…
Reference in New Issue