Skip to content

Instantly share code, notes, and snippets.

@sjonner
Created July 7, 2020 09:48
Show Gist options
  • Save sjonner/dc0d6143576778f311859c6bb1bf5b50 to your computer and use it in GitHub Desktop.
Save sjonner/dc0d6143576778f311859c6bb1bf5b50 to your computer and use it in GitHub Desktop.
Liquid Swipe
<div id='root'></div>
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 > 160 || 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
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.13.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.13.1/umd/react-dom.production.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/@react-spring/web@9.0.0-rc.3/index.umd.js"></script>
<script src="https://odintsov.me/static/libs/react-use-gesture.js"></script>
* {
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%;
}
}
<link href="https://fonts.googleapis.com/css2?family=Roboto:wght@400;900&amp;display=swap" rel="stylesheet" />
<link href="https://fonts.googleapis.com/css2?family=Oswald:wght@469&amp;display=swap" rel="stylesheet" />
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment