I am using useDrag hook of react-use-gesture to achieve the following animation (switching between chapters) but it does not work on touch devices without touch-action: none;
.
I have scrollable content, if I set touch-action:none
the scroll doesn't work on touch device and if I set touch-action: unset
the scroll works but the animation doesn't work.
Any idea on how can I get the following animation working on touch devices? Currently the animation works on non-touch devices.
import clamp from "lodash-es/clamp";
import React, { useRef, useState } from "react";
import { animated, useSprings } from "react-spring";
import { useDrag } from "react-use-gesture";
import chapters from "./chapters";
import "./styles.css";
export default function App() {
const index = useRef(0);
const [chapterScrollState, setChapterScrollState] = useState(
chapters.map(() => -1)
); // -1 => top, 0 => between, 1 => bottom
const [props, set] = useSprings(chapters.length, (i) => ({
y: i * window.innerHeight,
scale: 1,
display: "block"
}));
const bind = useDrag(
({
active,
movement: [mx, my],
direction: [xDir, yDir],
distance,
cancel
}) => {
if (chapterScrollState[index.current] === yDir) {
cancel();
}
if (active && distance > window.innerHeight / 2)
cancel(
(index.current = clamp(
index.current + (yDir > 0 ? -1 : 1),
0,
chapters.length - 1
))
);
set((i) => {
if (i < index.current - 1 || i > index.current + 1)
return { display: "none" };
const y = (i - index.current) * window.innerHeight + (active ? my : 0);
const scale = active ? 1 - distance / window.innerHeight / 2 : 1;
return { y, scale, display: "block" };
});
}
);
const onScroll = (e, i) => {
let scrollState = 0;
if (
Math.round(e.target.scrollTop) ===
e.target.scrollHeight - e.target.offsetHeight
) {
// bottom
scrollState = 1;
} else if (e.target.scrollTop === 0) {
// top
scrollState = -1;
}
setChapterScrollState((prev) => {
let newArr = [...prev];
newArr[i] = scrollState;
return newArr;
});
};
return props.map(({ y, display, scale }, i) => {
return (
<animated.div {...bind()} key={i} style={{ display, y }}>
<animated.div
className="animatedChapter"
onScroll={(e) => onScroll(e, i)}
style={{
scale
}}
>
<div className="chapter">{chapters[i].content}</div>
</animated.div>
</animated.div>
);
});
}
* {
box-sizing: border-box;
}
html,
body {
overscroll-behavior-y: contain;
margin: 0;
padding: 0;
height: 100%;
width: 100%;
user-select: none;
font-family: -apple-system, BlinkMacSystemFont, avenir next, avenir,
helvetica neue, helvetica, ubuntu, roboto, noto, segoe ui, arial, sans-serif;
position: fixed;
overflow: hidden;
}
#root {
position: fixed;
overflow: hidden;
width: 100%;
height: 100%;
}
#root > div {
position: absolute;
width: 100vw;
height: 100vh;
will-change: transform;
}
#root > div > div {
overflow-y: auto;
touch-action: none;
background-size: cover;
background-repeat: no-repeat;
background-position: center center;
width: 100%;
height: 100%;
will-change: transform;
box-shadow: 0 62.5px 125px -25px rgba(50, 50, 73, 0.5),
0 37.5px 75px -37.5px rgba(0, 0, 0, 0.6);
}
.chapter {
padding: 30px;
}
CodeSandbox Link
Animation GIF
question from:
https://stackoverflow.com/questions/65868378/cant-scroll-on-touch-devices-when-having-scrollable-content-react-use-gesture