Skip to content

Instantly share code, notes, and snippets.

@scott1028
Created April 30, 2023 04:47
Show Gist options
  • Save scott1028/df3143f815aa4bbdd1f235f1a9b6d6cb to your computer and use it in GitHub Desktop.
Save scott1028/df3143f815aa4bbdd1f235f1a9b6d6cb to your computer and use it in GitHub Desktop.
WIP: POC: JS Scrollbar Wheel or Moving animation smoonthly by requestAnimationFrame
<pre>
scroll smoonthly: https://stackoverflow.com/questions/47011055/smooth-vertical-scrolling-on-mouse-wheel-in-vanilla-javascript
</pre>
<hr />
<div id="root"></div>
import React, { useRef, useState } from "https://esm.sh/react@18";
import ReactDOM from "https://esm.sh/react-dom@18";
// const moveStep = dom?.offsetWidth || 0; // N pixel, per second
const moveStep = 100;
const App = () => {
const [container, setContainer] = useState(null);
const [dom, setDom] = useState(null);
const [targetOffset, setTargetOffset] = useState("");
const isActiveRef = useRef(false);
const lastEscapedTimeRef = useRef(null);
const startTimeRef = useRef(null);
const reset = () => {
dom.style.left = "";
dom.textContent = "";
isActiveRef.current = false;
// setTargetOffset(`${container.offsetWidth / 5}`);
setTargetOffset("600");
};
const animationHandler = (escapedTime) => {
const targetOffsetWidth = +targetOffset || container.offsetWidth;
const delta = escapedTime - (lastEscapedTimeRef.current ?? escapedTime);
lastEscapedTimeRef.current = escapedTime;
const domLeft = parseFloat(dom.style.left || 0);
// console.debug("debug:", [
// delta,
// escapedTime,
// startTimeRef.current,
// escapedTime - startTimeRef.current,
// (escapedTime - startTimeRef.current) / 1000
// ]);
// NOTE: add Math.pow to make moving position smoothly
let step = (delta / 1000) * moveStep;
// step = Math.pow(moveStep, (escapedTime - startTimeRef.current) / 1000);
const nextDomLeft = Math.min(
domLeft + step,
targetOffsetWidth - dom.offsetWidth
);
dom.style.left = `${nextDomLeft}px`;
dom.textContent = `${parseInt(nextDomLeft)}px`;
if (nextDomLeft + dom.offsetWidth >= targetOffsetWidth) {
return false;
}
return true;
};
const move = (escapedTime) => {
// pre-handler
if (!isActiveRef.current) {
return;
}
if (!startTimeRef.current) {
startTimeRef.current = escapedTime;
}
// Main
const isNext = animationHandler(escapedTime);
if (!isNext) {
console.debug("animation done");
console.timeEnd("go");
isActiveRef.current = !isActiveRef.current;
// setTargetOffset((state) => {
// const nextValue = Math.min(
// +targetOffset + container.offsetWidth / 5,
// container.offsetWidth - dom.offsetWidth
// );
// console.debug("nextValue:", [nextValue]);
// return `${nextValue}`;
// });
return;
}
// post-handler
requestAnimationFrame(move);
};
const toggle = () => {
isActiveRef.current = !isActiveRef.current;
if (isActiveRef.current) {
dom.textContent = "";
startTimeRef.current = null;
lastEscapedTimeRef.current = null;
console.time("go");
requestAnimationFrame(move);
}
};
return (
<div>
<h1>The Button has a ref</h1>
<input
onChange={(e) => setTargetOffset(e.target.value)}
value={targetOffset}
/>
<button onClick={toggle} className="button">
Start
</button>
<button onClick={reset} className="button">
Reset
</button>
<hr />
<div
className="container"
ref={(element) => {
console.debug("test ref:", element);
if (!element || container) return;
setContainer(element);
// setTargetOffset(`${element.offsetWidth / 5}`);
setTargetOffset("600");
}}
>
<div className="box" ref={setDom} />
</div>
</div>
);
};
ReactDOM.render(<App />, document.getElementById("root"));
.container {
background: lightyellow;
width: 600px;
height: 400px;
position: relative;
}
.box {
background: lightblue;
width: 50px;
height: 100px;
position: absolute;
}

WIP: POC: JS Scrollbar Wheel or Moving animation smoonthly by requestAnimationFrame

A Pen by Scott on CodePen.

License.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment