<div id='root'></div>
* {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
  user-select: none;
  /* overflow: hidden; */
}

body {
  background-color: #b5abb6;
  overscroll-behavior-y: contain;
}

#container {
  width: 400px;
  height: 100vh;
  position: absolute;
  box-shadow: 0px 0px 25px -8px rgba(53, 53, 53, 0.82);
  overflow: hidden;
}

.pageContainer {
  position: absolute;
  width: 100%;
  height: 100%;
}

.page {
  width: 100%;
  height: 100%;
  position: absolute;
  font-family: "Oswald", sans-serif;
  text-align: center;
  font-size: 37px;
  font-weight: bold;
  flex-direction: column;
  display: flex;
  justify-content: space-between;
  padding: 0vh 13px;
  padding-top: 7vh;
}

#page0 {
  background: rgb(255, 255, 255);
}

#page1 {
  background-image: linear-gradient(to right, #670185 0%, #430053 100%);
}

#page2 {
  background-image: linear-gradient(to right, #ff2c2c 0%, #b50031 100%);
}

#root {
  display: flex;
  justify-content: center;
}

svg {
  position: absolute;
  height: 100%;
}

button {
  position: absolute;
  width: 40px;
  height: 40px;
  border-radius: 50%;
  font-family: "Oswald", sans-serif;
  background: transparent;
  color: #fff;
  border: 1px solid rgba(255, 255, 255, 0.4);
}

button:focus {
  outline: 0;
}

img {
  width: 43vh;
	margin-left: 15px;
}

.header {
  display: flex;
  justify-content: space-between;
  color: rgb(255, 255, 255);
  font-family: "Roboto", sans-serif;
  font-size: 19px;
  padding: 0px 31px;
}

#header0 {
  color: #000;
}

#header1 {
  color: rgb(255, 255, 255);
}

#header2 {
  color: #fff;
}

.skip {
  font-weight: 400;
}

#content {
  display: flex;
  flex-direction: column;
  padding: 0px 48px;
  height: 30vh;
}

.contentL1 {
  color: rgb(0, 0, 0);
  font-family: monospace;
  font-weight: 100;
  text-align: left;
  letter-spacing: -2px;
  line-height: 18px;
  opacity: 0.3;
}

.contentL2 {
  color: #000;
  font-family: "Oswald", sans-serif;
  margin-bottom: 8px;
  text-align: left;
}

.contentL3 {
  color: #000;
  font-family: "Roboto", sans-serif;
  text-align: left;
  font-size: 15px;
  font-weight: 100;
  word-spacing: 2px;
  letter-spacing: 1px;
  opacity: 0.5;
}

.text0 {
  color: #000;
}

.text1 {
  color: #fd5888;
}

.text2 {
  color: #fff;
}

.button0 {
  color: #000;
  border: 1px solid rgba(0, 0, 0, 0.4);
}

.button1 {
  color: #fff;
  border: 1px solid rgba(255, 255, 255, 0.4);
}

.button2 {
  color: #fff;
  border: 1px solid rgba(255, 255, 255, 0.4);
}

@media only screen and (max-width: 500px) {
  #container {
    width: 100%;
  }
}
const { useState, useEffect } = React;
const { useSpring, animated, interpolate } = ReactSpring;
const { useDrag } = ReactUseGesture;
const rootElement = document.getElementById("root");
const height = window.innerHeight;
const width = window.innerWidth;
let w = 400;
if (width <= 500) {
  w = width;
}
const content = [
  {
    contentL1: "Online",
    contentL2: "GAMBLING",
    contentL3:
      "Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia.",
    src:
      "https://github.com/Cuberto/liquid-swipe/blob/master/Example/liquid-swipe/Images.xcassets/firstPageImage.imageset/firstPageImage@3x.png?raw=true",
  },
  {
    contentL1: "For",
    contentL2: "GAMERS",
    contentL3:
      "Temporibus autem aut officiis debitis aut rerum necessitatibus.",
    src:
      "https://raw.githubusercontent.com/Cuberto/liquid-swipe/master/Example/liquid-swipe/Images.xcassets/secondPageImage.imageset/secondPageImage%403x.png",
  },
  {
    contentL1: "Online",
    contentL2: "GAMBLING",
    contentL3:
      "Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia.",
    src:
     "https://github.com/Cuberto/liquid-swipe/blob/master/Example/liquid-swipe/Images.xcassets/firstPageImage.imageset/firstPageImage@3x.png?raw=true",
  },
];
const keyMap = {
  0: 1,
  1: 2,
  2: 0,
};
const getPath = (y, x, width) => {
  const anchorDistance = 200 + x * 0.5;
  const curviness = anchorDistance - 60;
  return `M0, 
    ${height} 
    H0V0h${width}v 
    ${y - anchorDistance} 
    c0, 
    ${curviness} 
    , 
   ${x} 
    , 
    ${curviness} 
    , 
   ${x} 
    , 
    ${anchorDistance} 
    S${width}, 
    ${y} 
    ,${width}, 
    ${y + anchorDistance * 2}
    V
    ${height}
    z`;
};
const Page = ({ data, index, setActive, gone = false }) => {
  const [isGone, setGone] = useState(gone);
  const [isMove, setMove] = useState(false);
  const { contentL1, contentL2, contentL3, src } = data;
  const [{ posX, posY }, setPos] = useSpring(() => ({
    posX: -50,
    posY: height * 0.72 - 20,
    config: {
      mass: 3,
    },
  }));
  const [{ d }, setDvalue] = useSpring(() => ({
    d: gone ? getPath(0, 0, w) : getPath(height * 0.72, 0, 0),
    config: {
      mass: 3,
    },
    onRest: () => {
      if (isGone) {
        setDvalue(getPath(0, 0, w));
      }
    },
  }));
  useEffect(() => {
    if (!gone) {
      setDvalue({
        d: getPath(height * 0.72, 48, 5),
      });
      setTimeout(() => {
        setPos({
          posX: 7,
        });
      }, 100);
    }
  }, [gone]);

  const bind = useDrag(({ down, movement: [mx], xy: [, my], vxvy: [vx] }) => {
    if (!isGone) {
      if (down && isMove) {
        setDvalue({
          d: getPath(my, mx + 60, 10),
        });
        setPos({
          posX: mx + 20,
          posY: my - 20,
        });
        if (mx > 200 || vx > 3) {
          setDvalue({
            d: getPath(my, -50, w),
          });
          setGone(true);
          setTimeout(() => {
            setDvalue({
              d: getPath(my, 0, w),
            });
            setActive(index);
          }, 240);
        }
      } else {
        setDvalue({
          d: getPath(height * 0.72, 48, 5),
        });
        setPos({
          posX: 7,
          posY: height * 0.72 - 20,
        });
      }
    }
  });
  return (
    <div id={`pageContainer${index}`} className="pageContainer" {...bind()}>
      <svg version="1.1" id="blob" xmlns="http://www.w3.org/2000/svg">
        <clipPath id={`clipping${index}`}>
          <animated.path id={`blob-path${index}`} d={d} />
        </clipPath>
      </svg>
      <div
        id={`page${index}`}
        className="page"
        style={{
          clipPath: `url(#clipping${index})`,
          WebkitClipPath: `url(#clipping${index})`,
        }}
      >
        <div id={`header${index}`} className="header">
          <div>GameCoin</div>
          <div className={`skip text${index}`}>SKIP</div>
        </div>
        <img alt={`img${index}`} src={src} />
        <div id="content">
          <div className={`contentL1 text${index}`}>{contentL1}</div>
          <div className={`contentL2 text${index}`}>{contentL2}</div>
          <div className={`contentL3 text${index}`}>{contentL3}</div>
        </div>
      </div>
      <animated.button
        className={`button${index}`}
        onMouseDown={() => {
          setMove(true);
        }}
        onMouseUp={() => {
          setMove(false);
        }}
        onTouchStart={() => {
          setMove(true);
        }}
        onTouchEnd={() => {
          setMove(false);
        }}
        style={{
          opacity: posX.interpolate({
            range: [0, 100],
            output: [1, 0],
          }),
          transform: interpolate(
            [
              posX.interpolate((x) => `translateX(${x}px)`),
              posY.interpolate((y) => `translateY(${y}px)`),
            ],
            (translateX, translateY) => `${translateX} ${translateY}`
          ),
        }}
      >
        {">"}
      </animated.button>
    </div>
  );
};
const App = () => {
  const [isActive, setActive] = useState(0);
  const [elm, setElm] = useState([
    <Page
      key={0}
      data={content[0]}
      index={0}
      setActive={setActive}
      gone={true}
    />,
  ]);

  useEffect(() => {
    const key = keyMap[isActive];
    if (elm.length === 2) {
      setTimeout(() => {
        setElm([
          ...elm.slice(1, 3),
          <Page
            key={key}
            data={content[key]}
            index={key}
            setActive={setActive}
          />,
        ]);
      }, 600);
    } else {
      setElm([
        ...elm,
        <Page
          key={key}
          data={content[key]}
          index={key}
          setActive={setActive}
        />,
      ]);
    }
  }, [isActive]);
  return (
    <>
      <div id="container">{elm}</div>
    </>
  );
};


ReactDOM.render(
    <App />,
  rootElement
);
View Compiled

External CSS

  1. https://fonts.googleapis.com/css2?family=Roboto:wght@400;900&amp;display=swap
  2. https://fonts.googleapis.com/css2?family=Oswald:wght@469&amp;display=swap

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://codepen.io/pizza3/pen/xxXjJLV.js
  4. https://codepen.io/pizza3/pen/gOGzjRv.js