filepond-plugin-image-preview icon indicating copy to clipboard operation
filepond-plugin-image-preview copied to clipboard

Image randomly blurred/upscaled (due to expand animation?)

Open justinas55 opened this issue 3 years ago • 14 comments

Describe the bug Dropping an image to filepond sometimes renders it blurred/upscaled (container is bigger than image resized for preview) and sometimes it is working correctly.

filepond good

filepond blurred

To reproduce:

  • open https://pqina.github.io/filepond-plugin-image-preview/
  • drop larger image file (~1000x1000 but any size seems to cause some kind of distortion)
  • Expected: preview image is clear and detailed in relation to container size
  • Actual: preview image is randomly upscaled making the preview image blurred/pixelated
  • GIF in action: https://ibb.co/7vdDyPK

Context

  • Google Chrome Version 92.0.4515.107 (Official Build) (64-bit)
  • Windows 10 64bit
  • https://pqina.github.io/filepond-plugin-image-preview/
  • Tested @ 2021-07-23 11:16 UTC
  • Script version: FilePondPluginImagePreview 4.6.7 (from page source)

Investigations/Solutions It seems like container height is captured in the start of animation and is much less than it expands to. So image is resized to small image, but then container expands - at least that's my guess.

In https://github.com/pqina/filepond-plugin-image-preview/blob/1484606b564ef9e705a7f738e1d5beba16351856/src/js/view/createImageWrapperView.js#L298 . If I change that to: var previewContainerWidth = root.element.clientWidth; var previewContainerHeight = root.element.clientHeight; then it seems to work correctly, but didn't have the time to properly validate this solution, yet.

Also using fixed height option imagePreviewHeight fix this problem as well.

P.S. Really grateful for this project, it's a very nice plugin!

justinas55 avatar Jul 23 '21 11:07 justinas55

Wow that's a weird bug. Thank you so much for the detailed report, will look into this as soon as possible.

rikschennink avatar Jul 29 '21 07:07 rikschennink

I tried to produce this with a big jpeg and png but I'm not able to. Tested on MacOS chrome 92 and Windows 10 Chrome 92. (Tested on Firefox and Safari as well just to be sure)

FilePond calculates sizes in a requestAnimationFrame loop so there are no reflows during animations, while re-requesting clientWidth and clientHeight will get the correct size it shouldn't be needed as it should be calculated before.

Will leave this open though as it looks like something is up.

rikschennink avatar Aug 02 '21 07:08 rikschennink

Did a couple more tests and tracked it down to screen refresh rate:

  • I use 100 Hz screen refresh rate, if I switch to 60 Hz - problem disappears.
  • Also tried Microsoft Edge Version 92.0.902.62 (Official build) (64-bit) where problem is same as in Chrome
  • Works in Firefox 88.0.1 (64-bit) without the problem even @ 100 Hz.
  • Tried enabling/disabling hardware acceleration and switching off all extensions, resetting default theme, but was still reproducing in Chrome @ 100 Hz.

I tried to screenshot page when hitting breakpoint in previewImageLoaded in both refresh rates, but to my eyes I don't see anything obvious between 60 Hz and 100 Hz views, just that root.rect.element.height is too small @ 100 Hz:

filepond-preview-debug2-60hz filepond-preview-debug2

justinas55 avatar Aug 02 '21 09:08 justinas55

calling requestAnimationFrame two times in a row before calling drawImage does help, but so does adding setTimeout(..., 10). my guess:

  • setting panel height (https://github.com/pqina/filepond/blob/b559b56cd8d6e82429646ec719df39415e7b8944/src/js/app/view/item.js#L144) happens after requestAnimationFrame (https://github.com/pqina/filepond-plugin-image-preview/blob/1484606b564ef9e705a7f738e1d5beba16351856/src/js/index.js#L172) - verified this with logging, and then it is down to timing if panel is resized at the time of drawPreview or not. in the case 60 Hz requestAnimationFrame takes a bit longer - and that usually works, but in 100hz the time is not enough and panel is still not resized.

Just didn't find how root.height = ... results in actual element's height being set, but probably the fix would be to ensure that requestAnimationFrame(() => drawPreview...) is called after panel's height is set.

justinas55 avatar Aug 02 '21 14:08 justinas55

Thank you so much for this info, I'm going to try and find a way to reproduce this and will get back to you.

rikschennink avatar Aug 04 '21 07:08 rikschennink

The issue is that I don't have a way to test this / reproduce the problem.

Is there any way to simulate 100hz in chrome? Firefox has a layout.frame_rate setting.

rikschennink avatar Sep 10 '21 06:09 rikschennink

I googled a bit, but didn't find such an option. But if you have any ideas/commits/PRs I can test it.

justinas55 avatar Sep 10 '21 13:09 justinas55

Can you try replacing

https://github.com/pqina/filepond-plugin-image-preview/blob/master/src/js/index.js#L172

with a setTimeout(/* */, 0)

I'm thinking maybe because of the high frame rate it runs before the height has been applied?

rikschennink avatar Sep 13 '21 08:09 rikschennink

When I changed those lines to

requestAnimationFrame(function() {
				  setTimeout(function () {
					root.dispatch('DID_FINISH_CALCULATE_PREVIEWSIZE', {
					  id: props.id
					});
				  }, 20);
              });

It seems to be stable now, but using 0ms or 10ms delays still resulted in same artifact.

Another one that works and seems a bit cleaner to me:

requestAnimationFrame(function() {
				  requestAnimationFrame(function () {
					root.dispatch('DID_FINISH_CALCULATE_PREVIEWSIZE', {
					  id: props.id
					});
				  });
              });

justinas55 avatar Sep 13 '21 09:09 justinas55

can you remove the wrapping requestAnimationFrame ?

rikschennink avatar Sep 13 '21 09:09 rikschennink

Still reproduces with this:

if (root.ref.shouldDrawPreview) {
              // queue till next frame so we're sure the height has been applied this forces the draw image call inside the wrapper view to use the correct height
				setTimeout(function () {
					root.dispatch('DID_FINISH_CALCULATE_PREVIEWSIZE', {
					  id: props.id
					});
				  }, 0);
              root.ref.shouldDrawPreview = false;
            }

justinas55 avatar Sep 13 '21 09:09 justinas55

Alright, thanks for the quick test, so nested requestAnimationFrame seems to do the trick.

rikschennink avatar Sep 13 '21 09:09 rikschennink

Published 4.6.10 🤞

rikschennink avatar Sep 13 '21 09:09 rikschennink

Wow, thanks!

justinas55 avatar Sep 13 '21 10:09 justinas55