Prevent Image Flicker During Loading in React Component

23 Views Asked by At

I'm working on a React component that receives an imageUrl and visualizes it on a canvas. The component intentionally adds a 2-second delay to simulate loading time for large images. However, I'm facing an issue: when the imageUrl changes in the parent component and while the image is loading, the previous image disappears from the browser momentarily then the new image is shown, causing a flicker effect. The behavior I am looking for is the component keeps showing the previous image until the new image is fully loaded and the swaps them.

How can I modify my existing code to ensure that the transition between images is smooth without any flicker? Here's my current implementation:

class ImageViewer extends React.Component {
  constructor(props) {
    super(props);
    this.imagecanvas = React.createRef();
  }
  componentDidMount() {
    this.drawCanvas();
  }
  componentDidUpdate(prevProps) {
    if (prevProps.imageUrl !== this.props.imageUrl) {
      this.drawCanvas();
    }
  }
  drawCanvas() {
    const canvas = this.imagecanvas.current;
    const context = canvas.getContext('2d');
    const image = new Image();
    // Clearing previous image
    context.clearRect(0, 0, canvas.width, canvas.height);
    // Load new image
    image.onload = () => {
      setTimeout(() => {
        context.drawImage(image, 0, 0);
      }, 2000);
    };
    image.src = this.props.imageUrl;
  }
  render() {
    return (
      <canvas id="imageView" ref={this.imagecanvas}></canvas>
    );
  }
}

Any suggestions or improvements to prevent the flicker during image loading would be greatly appreciated!

I have searched the internet and found the structure provided above is very common, however, I have not seen any mention of the flickering issue and how to address it.

1

There are 1 best solutions below

0
thelonglqd On

IMO, keeping the previous image and display the new one when it's fully loaded is not a good UX because user will see the image is there but in couple of seconds it is replaced by another one and user get confused.

To handle flickering when image is loading, we have couple of solution

  • skeleton loading
  • progressive loading image

But the idea behind all these technique are the same. We need a kind of placeholder with the same size with the image being loaded to prevent flickering. In case of skeleton loading, it is just a placeholder with loading effect.

enter image description here

In The second case, instead of a loading effect, We use a blurry version of your image (which is loaded very fast) to place there first, and when your high resolution image is fully loaded, replace with the blurry one.

You could base on the code below to figure out how it works on your own code.

  {isImageLoading && (
    <div className="animate-pulse bg-slate-100 h-56 w-56 rounded-md"></div>
  )}
  <div
    className={classNames({
      hidden: isImageLoading,
    })}>
    <img
      className="w-56 h-56" // width and the height shoule be the same size with your being loaded image
      src={imageURL}
      alt="a van"
      onLoad={() => setIsImageLoading(false)} // this cb function will be triggered when your image is fully loaded
      style={{
        display: isImageLoading ? 'none' : 'block',
      }}
    />
    <div className="flex justify-between items-center">
      <p className="text-lg font-semibold">{name}</p>
      <p className="text-lg font-semibold">
        ${price}/day
      </p>
    </div>
    <Badge type={type} />
  </div>