FEATURE: Allow pausing animated images in posts (#12795)

Co-authored-by: Jarek Radosz <jradosz@gmail.com>
This commit is contained in:
Penar Musaraj 2021-04-22 11:28:35 -04:00 committed by GitHub
parent 1e9301d4d8
commit c11d75da87
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 90 additions and 2 deletions

View File

@ -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);
});
},
};

View File

@ -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;
}
}

View File

@ -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)

View File

@ -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