<!DOCTYPE html>
<html lang="ru">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=">
  <title>Button effect</title>
</head>
<body>
  <div id="app"></div>
</body>
</html>
html,
body {
  margin: 0;
}

*,
*::before,
*::after {
  box-sizing: border-box;
}

.container {
  position: relative;
  display: flex;
  justify-content: center;
  align-items: center;
  height: 100vh;
  overflow: hidden;
}

.btn {
  -webkit-appearance: none;
  cursor: pointer;
  position: relative;
  overflow: hidden;
  padding: 16px 32px;
  border: none;
  border-radius: 3px;
  color: #222;
  font-weight: bold;
  background-color: #eee;
  transform: scale(1);
  transition: background-color .3s ease, transform .2s ease;
  
  &:hover {
    transform: scale(1.075);
    background-color: #ddd;
  }
  
  &:active {
    transform: scale(1);
  }
  
  &:focus {
    outline: none;
  }
}

.page-line {
  display: block;
  position: absolute;
  z-index: -1;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  background: #000;
  transform: translateY(-100%);
  animation: slide-in-out 1000ms ease;
}

@keyframes slide-in-out {
  0% {
    transform: translateY(-100%);
}
  100% {
    transform: translateY(100%);
}
}
View Compiled
const { CSSTransition, TransitionGroup } = ReactTransitionGroup;

const useOverlayLines = () => {
  const [lines, setLines] = React.useState([]);
  const linesLastKey = React.useRef(1);
  const addLine = React.useCallback(
    (key) => {
      setLines((oldLines) => [...oldLines, linesLastKey.current++]);
    },
    [setLines, linesLastKey]
  );

  const removeLine = React.useCallback(
    (key) => {
      setLines((oldLines) => {
        let index = oldLines.indexOf(key);

        if (index > -1) {
          const newLines = [...oldLines];
          newLines.splice(index, 1);
          return newLines;
        }
        return oldLines;
      });
    },
    [setLines]
  );

  return {
    lines,
    addLine,
    removeLine,
  };
};

const OverlayLinesAnimation = ({ lines, onRemoveLine = () => {} }) => {
  return (
    <TransitionGroup>
      {lines &&
        lines.map((key) => (
          <CSSTransition
            key={key}
            timeout={500}
            onEntered={() => onRemoveLine(key)}
            unmountOnExit
          >
            <div className="page-line"></div>
          </CSSTransition>
        ))}
    </TransitionGroup>
  );
};

const Button = ({ children, ...otherProps }) => {
  return (
    <button type="button" className="btn" {...otherProps}>
      {children}
    </button>
  );
};

const App = () => {
  const { lines, addLine, removeLine } = useOverlayLines();

  return (
    <div className="container">
      <OverlayLinesAnimation lines={lines} onRemoveLine={removeLine} />
      <Button onClick={addLine}>Кнопка</Button>
    </div>
  );
};

ReactDOM.render(<App />, document.getElementById("app"));
View Compiled

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

  1. https://cdnjs.cloudflare.com/ajax/libs/react/16.13.1/umd/react.production.min.js
  2. https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.13.1/umd/react-dom.production.min.js
  3. https://unpkg.com/react-transition-group/dist/react-transition-group.js