Created
March 15, 2024 19:41
-
-
Save kelvinpraises/cca0ff162d5d43f285fc7caebec01443 to your computer and use it in GitHub Desktop.
A circular scroll implementation of https://web.archive.org/web/20230131201043/https://imaginaryones.com/
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import React, { useState } from "react"; | |
import { motion, AnimatePresence } from "framer-motion"; | |
const items = [ | |
"Community", | |
"NFT Marketplace", | |
"Partnerships", | |
"Events", | |
"Blog", | |
]; // Add more or remove but always try to make it odd or adjust the visibility from the css below | |
const DrumComponent = () => { | |
const [centerIndex, setCenterIndex] = useState(2); // Start with the middle item or make it random | |
const handleClick = (index) => { | |
// Calculate the shortest path to bring clicked item to center | |
let offset = index - centerIndex; | |
if (Math.abs(offset) > items.length / 2) { | |
offset -= Math.sign(offset) * items.length; | |
} | |
setCenterIndex( | |
(prevIndex) => (prevIndex + offset + items.length) % items.length | |
); | |
}; | |
// This function generates the items with their positions adjusted based on the centerIndex | |
const generateItems = () => { | |
return items.map((item, index) => { | |
let position = index - centerIndex; | |
// Adjust position for circular buffer effect https://en.wikipedia.org/wiki/Circular_buffer | |
if (position < -Math.floor(items.length / 2)) { | |
position += items.length; | |
} else if (position > Math.floor(items.length / 2)) { | |
position -= items.length; | |
} | |
return ( | |
<motion.div | |
key={item} | |
initial={{ y: 50 * position, opacity: 0 }} // Adjust y to 100 or higher for a spread in load effect or a lower number for a spread out effect, 50 makes it balanced | |
animate={{ y: 50 * position, opacity: 1 }} | |
exit={{ y: 50 * position, opacity: 0 }} | |
transition={{ type: "spring", stiffness: 300, damping: 30 }} // adjust the stiffness to a lower number like 30 to see how it works | |
style={{ | |
position: "absolute", | |
top: "40%", // adjust this to position the items | |
transform: "translate(-50%, -50%)", | |
height: "50px", | |
width: "200px", | |
display: "flex", | |
justifyContent: "center", | |
alignItems: "center", | |
cursor: "pointer", | |
backgroundColor: position === 0 ? "lightgreen" : "transparent", | |
}} | |
onClick={() => handleClick(index)} | |
> | |
{item} | |
</motion.div> | |
); | |
}); | |
}; | |
return ( | |
<div style={{ position: "relative", height: "250px", overflow: "hidden" }}> | |
<AnimatePresence>{generateItems()}</AnimatePresence> | |
</div> | |
); | |
}; | |
export default DrumComponent; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Video demo
Screen.Recording.2024-03-15.at.20.45.58.mov