Switching between images to avoid "jumping" behaviour

Viewed 204

I am trying to imitate the behaviour of a checkbox by switching custom checkbox images depending on the condition (true or false). I get a very annoying visual bug where as I set state to the truthy or falsy condition the images depending on it and the box does a little flickr before switching, here's my code:

import React from 'react';
import boxCheckedIcon from '../../../../assets/SmallBoxChecked.svg';
import boxUncheckedIcon from '../../../../assets/SmallBoxUnchecked.svg';


class Example extends React.Component {

    constructor(props) {
        super(props);
        this.state = {
            condition: false,
        }
    }


    handleSelect = () => {
        this.setState(prevState => ({
            condition: !prevState.condition
        }));
    }


    renderCheckbox = condition => {
        return condition ? <img
            id="checkedCheck"
            src={boxCheckedIcon}
            alt="checked check box"
        /> : <img
                id="uncheckedCheck"
                src={boxUncheckedIcon}
                alt="unchecked check box"
            />
    };

    render() {
        const { condition } = this.state;

        return (
            <div onClick={() => handleSelect()}>
                {renderCheckbox(condition)}
            </div>
        );
    }
}

any idea how to make the transition between the two images smoother? CSS, React or otherwise..

4 Answers

The most correct would be to move your images to a CSS-sprite and switch it background-position by an additional class

render() {
  const { condition } = this.state;

  return (
    <div
      className={classNames(
        'checkbox__icon',
        condition && 'is-checked',
      )}
      onClick={this.handleSelect}
    />
  );
}

In the example, I use the lib https://www.npmjs.com/package/classnames

Problem is that every time you render different img tag, browser will fetch/download images again. Without using any font libraries, you can avoid this situation by rendering both images on the DOM and hiding/showing them based on condition:

renderCheckbox = (condition) => {
  return (
    <>
      <img
        heigth="40"
        width="40"
        id="checkedCheck"
        src={boxCheckedIcon}
        alt="checked check box"
        className={condition ? "" : "hidden"}
      />
      <img
        heigth="40"
        width="40"
        id="uncheckedCheck"
        src={boxUncheckedIcon}
        alt="unchecked check box"
        className={condition ? "hidden" : ""}
      />
    </>
  );
};

and in your style.css file you can add hidden class:

.hidden {
  display: none;
}

take a look at this codesandbox.

If you're using Create React App, you can use svg components instead of img tag.

import { ReactComponent as Checked } from '../assets/checked.svg'
import { ReactComponent as Unchecked } from '../assets/unchecked.svg'

// Use it like this 
return condition ? <Checked/> : <Unchecked/>

Try specifying the dimensions on the image. Missing image dimensions could cause reflows because the browser has to load the image to see its dimensions and it will temporarily collapse the area used by the image causing everything below the image to jump around.

Related