Skip to content

Instantly share code, notes, and snippets.

@gragland
Last active July 23, 2019 23:19
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save gragland/7556098f208ffd233b0a906cbc569110 to your computer and use it in GitHub Desktop.
Save gragland/7556098f208ffd233b0a906cbc569110 to your computer and use it in GitHub Desktop.
import { useState, useRef } from 'react';
import { useSpring, animated } from 'react-spring';
// Displays a row of cards
// Usage of hook is within <Card> component below
function App() {
return (
<div className="container">
<div className="row">
{cards.map((card, i) => (
<div className="column">
<Card>
<div className="card-title">{card.title}</div>
<div className="card-body">{card.description}</div>
<img className="card-image" src={card.image} />
</Card>
</div>
))}
</div>
</div>
);
}
function Card({ children }) {
// We add this ref to card element and use in onMouseMove event ...
// ... to get element's offset and dimensions.
const ref = useRef();
// Keep track of whether card is hovered so we can increment ...
// ... zIndex to ensure it shows up above other cards when animation causes overlap.
const [isHovered, setHovered] = useState(false);
// The useSpring hook
const [animatedProps, setAnimatedProps] = useSpring(() => {
return {
// Array containing [rotateX, rotateY, and scale] values.
// We store under a single key (xys) instead of separate keys ...
// ... so that we can use animatedProps.xys.interpolate() to ...
// ... easily generate the css transform value below.
xys: [0, 0, 1],
// Setup physics
config: { mass: 10, tension: 400, friction: 40, precision: 0.00001 }
}
});
return (
<animated.div
ref={ref}
className="card"
onMouseEnter={() => setHovered(true)}
onMouseMove={({ clientX, clientY }) => {
// Get mouse x position within card
const x =
clientX -
(ref.current.offsetLeft -
(window.scrollX || window.pageXOffset || document.body.scrollLeft));
// Get mouse y position within card
const y =
clientY -
(ref.current.offsetTop -
(window.scrollY || window.pageYOffset || document.body.scrollTop));
// Set animated values based on mouse position and card dimensions
const dampen = 50; // Lower the number the less rotation
const xys = [
-(y - ref.current.clientHeight / 2) / dampen, // rotateX
(x - ref.current.clientWidth / 2) / dampen, // rotateY
1.07 // Scale
];
// Update values to animate to
setAnimatedProps({ xys: xys });
}}
onMouseLeave={() => {
setHovered(false);
// Set xys back to original
setAnimatedProps({ xys: [0, 0, 1] });
}}
style={{
// If hovered we want it to overlap other cards when it scales up
zIndex: isHovered ? 2 : 1,
// Interpolate function to handle css changes
transform: animatedProps.xys.interpolate(
(x, y, s) =>
`perspective(600px) rotateX(${x}deg) rotateY(${y}deg) scale(${s})`
)
}}
>
{children}
</animated.div>
);
}
@thebiltheory
Copy link

https://gist.github.com/gragland/7556098f208ffd233b0a906cbc569110#file-use-spring-example-jsx-L34
Creates an error as it doesn't return an interable.
Passing a function returning the object fixes the issue.

 const [animatedProps, setAnimatedProps] = useSpring(()=>{
    // Array containing [rotateX, rotateY, and scale] values.
    // We store under a single key (xys) instead of separate keys ...
    // ... so that we can use animatedProps.xys.interpolate() to ...
    // ... easily generate the css transform value below.
    xys: [0, 0, 1],
    // Setup physics
    config: { mass: 10, tension: 400, friction: 40, precision: 0.00001 }
  });```

@gragland
Copy link
Author

@thebiltheory Thanks for pointing out this issue. Just updated the post with your fix.

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