WordPress / gutenberg

The Block Editor project for WordPress and beyond. Plugin is available from the official repository.
https://wordpress.org/gutenberg/
Other
10.53k stars 4.21k forks source link

Image block: Consider adding a button to compress the image size. #55106

Open youknowriad opened 1 year ago

youknowriad commented 1 year ago

Users often have to use online services like https://imagecompressor.com or alternatives to optimize their images. It would be great to explore offering a button (or something like that) right in the image block to compress uploaded images.

We don't need to invent anything, there's a number of libraries that provide compression APIs like https://www.npmjs.com/package/compressorjs

Maybe we could suggest an optimal quality and allow the users to tweak the number to avoid compression quality loss.

m commented 1 year ago

More generally, given the capabilities of clients and the distributed processing power they have (vs the hosts) what can we do client-side to make this as performant as possible.

ntsekouras commented 1 year ago

So, this should be a default compression for all images, or a setting(attribute) in Image block to compress existing files or probably pass a flag to media upload, when we're in a placeholder state?

If we go with block level support, it means we should implement this to many more blocks, and also users would have to adjust the quality setting each time, for every block.

youknowriad commented 1 year ago

@ntsekouras I see it as a setting per image but not really a persistent attribute, you decide to compress the image which would update the image if already uploaded or pick a compression factor before upload.

Developing it as block support is not mandatory IMO

felixarntz commented 1 year ago

This sounds like a great idea to me, and it would tremendously help image performance to have it tightly integrated into right the UI where the user is uploading the image.

If we want to allow users to tweak the compression level per uploaded image, I would suggest that the value chosen, despite not being persistent on the block, is stored persistently as a user preference (i.e. in WordPress user options) so that the compression level that the user chooses effectively becomes the default/initial state for any subsequent images they upload.

jameskoster commented 1 year ago

There could be quite a few moving parts to consider here, and I'm not sure what the scope for the first iteration should look like, so consider this a first draft of a potential design to kick-off discussion:

Compress

In the future this pattern could theoretically scale-up to entertain multiple files and bulk compression, for example when a Gallery block is selected, or even at the document level:

Multi-compress

Details like copy and icons can be refined once we decide on a general direction.

youknowriad commented 1 year ago

@jameskoster Designs look good. Do you think this belongs to the "Advanced" section for blocks or should it be more prominent, like a button in the block toolbar or a dedicate section outside "Advanced".?

youknowriad commented 1 year ago

@tyxla @jsnajdr While a bit out of scope of the current issue, this feels like a great opportunity to explore "JS lazy loading". The compressor library could be an esm module that you load through the import map API. (Obviously not something that needs to be done in the initial implementation)

swissspidy commented 1 year ago

FWIW I built a plugin that does this kinda stuff already, using all sorts of WASM stuff. Planning on open sourcing it soon. It could easily be merged into Gutenberg.

ntsekouras commented 1 year ago

I think there are some more nuances here.

  1. If we do it in block level(ex Image block) in an already uploaded image, there would be no good way to seamlessly replace the image in all places it's being used(unless I'm missing something in the REST API). What this means is, we have to delete(?) the current image and upload a compressed one. If we delete the image, other blocks that use that image will break. If we replace that image with a new compressed one and keep the old version, we have the risk of multiple compressed images of the same source.
  2. Probably we would need to keep some meta(compression factor) on the image to avoid showing the compress button with an already compressed image with the same factor.

@swissspidy does your plugin handle the above replacement case? I'm curious because you say WASM and I expect some PHP to be there..

youknowriad commented 1 year ago

If we do it in block level(ex Image block) in an already uploaded image, there would be no good way to seamlessly replace the image in all places it's being used(unless I'm missing something in the REST API). What this means is, we have to delete(?) the current image and upload a compressed one. If we delete the image, other blocks that use that image will break. If we replace that image with a new compressed one and keep the old version, we have the risk of multiple compressed images of the same source.

Don't we have a PUT support in the media endpoint? (updating the files but keeping the same names, urls, ids)

ntsekouras commented 1 year ago

Don't we have a PUT support in the media endpoint? (updating the files but keeping the same names, urls, ids)

No and in general I think WP REST API only uses POST for updates(doesn't really matter in our case though). I looked a bit at the core attachment controller now and it doesn't seem to handle such a case internally.

youknowriad commented 1 year ago

We do have a "put" (update media) endpoint https://developer.wordpress.org/rest-api/reference/media/#update-a-media-item It's not clear from the docs whether it allows updating the file itself or just the other properties, if it's not possible, It would be a good addition IMO.

swissspidy commented 1 year ago

@ntsekouras Yes there‘s also a lot of PHP of course.

You don‘t want to override or delete the original image to prevent data loss and in case the user wants to revert the changes. So you need to upload a new one, mark it as compressed/optimized, and add a way to connect/link it with the original. My solution does all that. Let me prepare a demo for you all this week.

adamsilverstein commented 1 year ago

We don't need to invent anything, there's a number of libraries that provide compression APIs like npmjs.com/package/compressorjs

Also worth looking at https://github.com/kleisauke/wasm-vips (maybe that is what @swissspidy's plugin already leverages?)

m commented 1 year ago

It's worth also making sure we say how big the image is, and then we can show the before and after in size and quality.

swissspidy commented 1 year ago

So I wasn't planning on making this public until it was more published, but feel free to check out my media experiments repository here: https://github.com/swissspidy/media-experiments

The readme contains more information about the things I've built already and what I've planned on adding, like bulk optimization. I should be able to get to that soon though.

In the meantime, feel free to reach out to me on Slack if you have any questions.

Here are some demo videos:

Demos **Optimize (compress) an existing image/video** https://github.com/swissspidy/media-experiments/assets/841956/af783b3d-e7f4-425f-8de7-23a63dc2b9f9 **Preferences to select preferred image format etc.** Media preferences modal in the block editor **Automatic Poster Generation** https://github.com/swissspidy/media-experiments/assets/841956/ba082f49-145e-441e-89c5-a63acdd039bd **Preview image generation for PDFs** https://github.com/swissspidy/media-experiments/assets/841956/f26b4f7d-f60b-4177-b8d0-b79d12c8863b **Converting GIFs to Videos** https://github.com/swissspidy/media-experiments/assets/841956/df3343e5-6e49-44da-bb45-9f6f43f92665 **Mute videos by removing audio channel completely** https://github.com/swissspidy/media-experiments/assets/841956/2a9b566f-58ec-4256-b820-c7443fc53301 **Record yourself via webcam & upload the video** https://github.com/swissspidy/media-experiments/assets/841956/dd4ad5ed-8374-48f2-8223-9ed856b590b4 **Improved media placeholders** Block sidebar controls showing BlurHash and dominant color of a video
youknowriad commented 1 year ago

@ntsekouras @swissspidy This is really cool, how do you think we can move forward now? Can we transform this to a Gutenberg PR or something?

swissspidy commented 1 year ago

Probably best to start by discussing this more in the #core-editor chat, as there is a lot to unpack and many things to be considered. My plugin barely scratches the surface of what can be done and goes way beyond what has been originally proposed in this issue here.

In my experience (I have been working with this kind of stuff for multiple years now), something like compressorjs (which just uses canvas.toBlob() is not nearly enough to accomodate all requirements and browser inconsistencies (e.g. Safari doesn't support quality or image/webp).

I would love if my plugin ends up in Gutenberg eventually, but it's not quite there yet. I suggest checking it out a bit more closely and giving it a try yourself.

ntsekouras commented 1 year ago

@swissspidy your plugin seems to do lots of cool stuff 🚀 . Do you think it would be possible to extract something minimal to include in GB and iterate? For example just the image compression without file type conversion etc..

jameskoster commented 1 year ago

Do you think this belongs to the "Advanced" section for blocks or should it be more prominent, like a button in the block toolbar or a dedicate section outside "Advanced".?

I suggest the Advanced panel because it's the only one that exists for all blocks that might use this affordance. This makes the placement consistent across.

I'd welcome more thoughts, but if the plan is to have a global default 'quality' setting for all uploaded media, then I don't know that this needs to be more prominent. To me it feels like something you'd generally set-and-forget, and the tool in the Advanced panel caters to the edge cases.

It might be useful to have a filterable threshold value, and alert the user somehow (potentially something like https://github.com/WordPress/gutenberg/issues/41747) if an image breaches it, similar to the color contrast warnings.

Including a preview with size details in the modal (as per @swissspidy's plugin) would be neat. Especially if it can update dynamically as you adjust the quality value.