Springy

A sprigy, bouncy and non-boring way to display images on a webstie.

Try dragging an image around:)

Gist

Images displayed here inlude logos to some of my apps, some medicore 3D concepts of all things Starship and the very first images i created using DALLE-2.

Code

npm i framer-motion @popmotion/popcorn
/components/lab/Springy/spring.js
"use client";
import React, { useState,useRef } from "react";
import { motion,useMotionValue,useSpring,useTransform, } from "framer-motion";
import { distance } from "@popmotion/popcorn";
import Image from "next/image";
import { springimgs } from "./images";
const grid = [[0, 1, 2, 3], [4, 5, 6, 7], [8, 9, 10, 11], [12, 13, 14, 15]];
const size = 60;
const gap = 10;
const Square = ({ active, setActive, colIndex, rowIndex, x, y }) => {
const isDragging = colIndex === active.col && rowIndex === active.row;
const diagonalIndex = (360 / 6) * (colIndex + rowIndex);
const d = distance({ x: active.col, y: active.row },{ x: colIndex, y: rowIndex });
const springConfig = {stiffness: Math.max(700 - d * 120, 0),damping: 20 + d * 5};
const dx = useSpring(x, springConfig);
const dy = useSpring(y, springConfig);
return (
<motion.div
drag
dragConstraints={{ left: 0, right: 0, top: 0, bottom: 0 }}
dragTransition={{ bounceStiffness: 500, bounceDamping: 20 }}
dragElastic={1}
onDragStart={() => setActive({ row: rowIndex, col: colIndex })}
style={{
background: 'hsla(calc(var(--base-hue) + diagonalIndex), 80%, 60%, 1)',
width: size,
height: size,
top: rowIndex * (size + gap),
left: colIndex * (size + gap),
position: "absolute",
// borderRadius: "50%",
x: isDragging ? x : dx,
y: isDragging ? y : dy,
zIndex: isDragging ? 1 : 0
}}
className="overflow-hidden"
>
<Image src={springimgs[colIndex * 4 + rowIndex]} className="w-full h-auto pointer-events-none"></Image>
</motion.div>
);
};
const Spring = () => {
const [active, setActive] = useState({ row: 0, col: 0 });
const x = useMotionValue(0);
const y = useMotionValue(0);
return (
<div className="flex items-center place-content-center place-items-center justify-center text-center">
<motion.div transition={{ duration: 10, loop: Infinity, ease: "linear" }} style={{ width: "100%", height: "100%" }}>
<motion.div style={{display: "flex",width: (size + gap) * 4 - gap,height: (size + gap) * 4 - gap,transform: "translate(0%, 0%)",perspective: 500, position: "relative"}}>
{grid.map((row, rowIndex) =>
row.map((_item, colIndex) => (
<Square x={x} y={y} active={active} setActive={setActive} rowIndex={rowIndex} colIndex={colIndex} key={rowIndex + colIndex}/>
))
)}
</motion.div>
</motion.div>
</div>
);
}
export default Spring

The code for this component is here. Some libraries used are:

  • Next.js
  • framer-motion
  • @popmotion/popcorn (for distance)
  • tailwindcss
  • Inspired by theframer-motionuseSpringdocs lol.