简体   繁体   中英

CSS for disappearing div animation is not pausing on hover

I am working on a snackbar that appears/disappears after x amount of time, and I would like the timer to be paused when I hover over the snackbar.

However, it seems that the way I have laid this out doesn't allow this to work, with the setTimeout and the useState ...

I can't find much online about how this can be fixed that I can work out, so any help would be appreciated.

https://stackblitz.com/edit/react-dyq9mg

App.js

const App = () => {

  const [showSnack, setShowSnack] = useState(false);

  const toggle = () => {
    setShowSnack(true);
    setTimeout(() => {
      setShowSnack(false);
    }, 1000);
  }

  return (
    <div>
      <button onClick={toggle}>Toggle</button>
      <Snack show={showSnack} />
    </div>
  );
}

render(<App />, document.getElementById('root'));

Snack.js

const Snack = ({show}) => {

  return(
    <div className={`my-snack ${show ? 'show' : ''}`}>My Snackbar</div>
  );
}

index.css

.my-snack {
  position: fixed;
  width: 100%;
  height: 500px;
  background: lightgray;
  bottom: 0;
  visibility: hidden;
  cursor: pointer;
}

.my-snack.show {
  visibility: visible;
  animation: fadein 0.5s, fadeout 0.5s 9.6s;
  animation-play-state: running;
}

.my-snack:hover {
  animation-play-state: paused paused;
}

/*Animations to fade the snackbar in and out*/
@keyframes fadein {
  from {bottom: -500px;}
  to {bottom: 0;}
}

@keyframes fadeout {
  from {bottom: 0;}
  to {bottom: -500px; opacity: 0;}
}

I believe you might be conflating the CSS animation to fade in the snackbar with the showing and hiding of the snackbar itself. You could use onMouseOver and onMouseOut to pause and start the timer respectively. Of course, you'd have to pass these to the Snack component and set them on the DOM node.

  const timer = useRef(null);

  const clearSnackTimer = () => clearTimeout(timer.current);

  const startSnackTimer = () => timer.current = setTimeout(() => setShowSnack(false), 1000);

  const toggle = () => {
    setShowSnack(!showSnack);
    startSnackTimer();
  };

  return (
    <div>
      <button onClick={toggle}>Toggle</button>
      <Snack show={showSnack} onMouseOver={clearSnackTimer} onMouseOut={startSnackTimer} />
    </div>
  );

Something is still wonky with the fadeout transition, but I think this solves for the requirement to pause hiding the snackbar hiding on hover, if that is your intention.

One way you could solve this is by using the onAnimationEnd event instead of a timer. I'd recommend this approach over a timer since you'd control all the timing through css and the component would react to when the animation has finished.

<Snack
  show={showSnack}
  onAnimationEnd={(e) => {
    setShowSnack(false)
  }}
/>

const Snack = ({ show, onAnimationEnd }) => {
  return (
    <div
      className={`my-snack ${show ? 'show' : ''}`}
      onAnimationEnd={(e) => {
        if (e.animationName === 'fadeout') {
          onAnimationEnd(e)
        }
      }}>
      My Snackbar
    </div>
  )
}

One note about this specific approach (for your use case) is you have to know the animation name in the js since you're using multiple animations at once. eg (e.animationName === 'fadeout')

Example: https://stackblitz.com/edit/react-szkq7w

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM