Mouse Image Distortion
A website animation featuring an image distortion in a curved, using the sin function, React, React Three Fiber and Framer Motion
Olivier Larose
August 9, 2023
/
Beginner
/
Short
2 Ways to Make a Magnetic Buttons using React, GSAP, Framer Motion. See the difference in terms of implementation for a magnetic effect between GSAP and Framer Motion.
Live DemoSource codeVideo TutorialLet's start the project by creating a Next.js application. We can do that by running npx create-next-app@latest client
inside of a terminal.
We can delete everything in the page.js
, global.css
and page.module.css
and add our own HTML and CSS, to start with a nice blank application.
npm i sass
.npm i framer-motion
.npm i gsap
.There's a lot of questions about using GSAP vs Framer Motion, so in this tutorial, we'll see the difference between imperative and declarative animations.
page.js
page.mod...
1
'use client';2
import styles from './page.module.scss'3
4
export default function Home() {5
return (6
<main className={styles.main}>7
<p>Made with GSAP:</p>8
<div className={styles.container}>9
<svg id="Layer_2" data-name="Layer 2" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 31.5 58">10
<path d="m20.72,22.16c2.77,0,5.55.02,8.32.03.4,0,.8.02,1.2.03.07.06.14.13.21.19-.28,1.58-.56,3.16-.83,4.75-.32,1.87-.64,3.74-.99,5.76-1.37.13-2.76-.07-4.14-.04-1.36.03-2.72,0-4.2,0-.13,8.38.12,16.72.11,25.11h-11.17v-24.91H0v-10.81h9.16c.04-.39.11-.71.11-1.02-.01-1.58-.05-3.17-.06-4.75-.01-1.62-.16-3.26.02-4.85.19-1.69.64-3.35,1.52-4.86,1.36-2.33,3.28-4.06,5.58-5.4,1.39-.81,2.94-1.25,4.52-1.3C24.28-.03,27.71.02,31.15,0c.07,0,.13.05.35.14.04,3.3-.29,6.66-.18,10.11-1.13,0-2.15.03-3.17,0-1.57-.07-3.15-.06-4.65.46-1.42.49-2.46,1.4-2.89,2.95-.3,1.08-.36,2.16-.34,3.25.04,1.69.13,3.38.2,5.07.08.06.16.13.24.19Z"/>11
</svg>12
13
<svg id="Layer_2" data-name="Layer 2" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 56.13 46.08">14
<path d="m0,41.47c3.69-.34,7.11-.82,10.38-1.89,2.05-.67,4.03-1.62,5.71-3.35-.55-.18-1.03-.38-1.53-.48-2.77-.58-5.26-1.64-7-4-.72-.98-1.33-2.03-2.03-3.1,1.23-.3,2.43-.59,3.81-.93-.91-.56-1.7-1.08-2.52-1.53-2.08-1.13-3.73-2.68-4.6-4.91-.37-.95-.52-1.98-.77-2.98-.11-.43-.19-.86-.3-1.33,1.42.02,2.71.63,4.2.28-.72-1.12-1.37-2.17-2.06-3.2-1.78-2.65-2.05-5.51-1.29-8.54.23-.91.43-1.84.69-2.95.78.63,1.4,1.05,1.94,1.56,2.42,2.28,5.16,4.11,8.01,5.83,2.36,1.43,4.89,2.38,7.49,3.17,1.99.61,4.06.96,6.33.91.02-.67.06-1.3.07-1.92.05-2.38.81-4.54,1.99-6.57,1.71-2.92,4.43-4.39,7.56-5.26,1.39-.39,2.76-.3,4.22-.14,2.64.29,4.8,1.52,6.96,2.82.49.3.86.38,1.4.19,1.21-.43,2.43-.83,3.67-1.19.49-.15,1.02-.18,1.84-.31-1.11,1.79-2.05,3.3-3,4.82,1.65.08,3.16-.84,4.97-.57-.86,1.35-1.83,2.42-2.78,3.52-.78.91-1.81,1.7-2.04,2.98-.25,1.43-.63,2.83-.75,4.31-.14,1.7-.6,3.37-.96,5.05-.14.64-.37,1.25-.58,1.87-1.35,3.9-3.27,7.44-5.93,10.64-2.96,3.55-6.42,6.42-10.55,8.47-2.84,1.4-5.83,2.42-8.95,2.97-2.6.45-5.24.39-7.87.33-2.95-.07-5.78-.69-8.6-1.52-2.27-.67-4.39-1.61-6.51-2.61-.14-.07-.26-.18-.61-.44Z"/>15
</svg>16
17
<svg id="Layer_2" data-name="Layer 2" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 51.1 36.69">18
<path d="m26.22,36.56c-4.74,0-9.49,0-14.23,0-3.93,0-7.19-1.49-9.63-4.58-1.02-1.29-1.66-2.82-2.03-4.45-.28-1.25-.28-2.52-.29-3.78C.03,19.98,0,16.21,0,12.44c0-1.06,0-2.13.18-3.17.2-1.09.59-2.15,1.16-3.13C2.75,3.73,4.77,1.97,7.28.79c.85-.4,1.78-.61,2.74-.63,2.02-.04,4.04-.21,6.06-.14,7.73.27,15.47.02,23.2.14,1.97.03,3.89.43,5.62,1.33,2.23,1.16,3.98,2.81,4.91,5.25.52,1.36.76,2.72.93,4.16.56,4.65.3,9.32.27,13.98-.02,2.7-.92,5.16-2.63,7.27-1.73,2.15-3.95,3.62-6.68,4.08-1.8.31-3.64.39-5.47.43-3.34.06-6.68.02-10.02.02,0-.04,0-.07,0-.11Zm7.94-18.51c-4.62-2.62-9.12-5.17-13.62-7.71-.12-.07-.29-.07-.4-.1v16.33c4.78-2.84,9.41-5.56,14.03-8.52Z"/>19
</svg>20
21
<svg id="Layer_2" data-name="Layer 2" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 47.26 53.87">22
<path d="m34.27,18.6c0,4.82,0,9.64.03,14.47.01,2.53-.24,5.01-.67,7.5-.68,3.88-2.69,6.84-5.66,9.33-2.21,1.86-4.72,2.98-7.48,3.6-1.02.23-2.09.29-3.14.34-3.54.18-6.73-.93-9.64-2.84-2.68-1.76-4.74-4.12-6.12-7.05-.74-1.58-1.16-3.26-1.51-4.94-.2-.98,0-2.04-.02-3.06-.07-3.01,1.1-5.62,2.62-8.11,2.19-3.6,5.35-5.98,9.35-7.26,1.24-.4,2.51-.7,3.84-.66.33.01.66-.13.99-.14.83-.02,1.65,0,2.58,0v9.31c-.27,0-.55-.01-.84,0-1.06.05-2.13.05-3.18.18-2.4.29-3.92,1.83-5.15,3.74-1.77,2.78-.84,7.04,1.38,9.35,2.28,2.38,6.05,2.67,8.72,1.48.6-.27,1.18-.6,1.73-.98,1.45-1.01,2.12-2.51,2.39-4.17.62-3.94.33-7.92.36-11.89.05-7.97,0-15.95,0-23.92,0-.87,0-1.74,0-2.72,3.08-.25,6.11-.14,9.16-.19.91,7.84,5.41,12.08,13.22,13.34-.07,2.84.22,5.85-.25,8.97-4.44-.24-8.48-1.55-12.25-3.76-.11-.11-.22-.22-.33-.33-.05.13-.09.25-.14.38Z"/>23
</svg>24
</div>25
<p>Made with Framer Motion:</p>26
<div className={styles.container}>27
<svg id="Layer_2" data-name="Layer 2" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 31.5 58">28
<path d="m20.72,22.16c2.77,0,5.55.02,8.32.03.4,0,.8.02,1.2.03.07.06.14.13.21.19-.28,1.58-.56,3.16-.83,4.75-.32,1.87-.64,3.74-.99,5.76-1.37.13-2.76-.07-4.14-.04-1.36.03-2.72,0-4.2,0-.13,8.38.12,16.72.11,25.11h-11.17v-24.91H0v-10.81h9.16c.04-.39.11-.71.11-1.02-.01-1.58-.05-3.17-.06-4.75-.01-1.62-.16-3.26.02-4.85.19-1.69.64-3.35,1.52-4.86,1.36-2.33,3.28-4.06,5.58-5.4,1.39-.81,2.94-1.25,4.52-1.3C24.28-.03,27.71.02,31.15,0c.07,0,.13.05.35.14.04,3.3-.29,6.66-.18,10.11-1.13,0-2.15.03-3.17,0-1.57-.07-3.15-.06-4.65.46-1.42.49-2.46,1.4-2.89,2.95-.3,1.08-.36,2.16-.34,3.25.04,1.69.13,3.38.2,5.07.08.06.16.13.24.19Z"/>29
</svg>30
31
<svg id="Layer_2" data-name="Layer 2" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 56.13 46.08">32
<path d="m0,41.47c3.69-.34,7.11-.82,10.38-1.89,2.05-.67,4.03-1.62,5.71-3.35-.55-.18-1.03-.38-1.53-.48-2.77-.58-5.26-1.64-7-4-.72-.98-1.33-2.03-2.03-3.1,1.23-.3,2.43-.59,3.81-.93-.91-.56-1.7-1.08-2.52-1.53-2.08-1.13-3.73-2.68-4.6-4.91-.37-.95-.52-1.98-.77-2.98-.11-.43-.19-.86-.3-1.33,1.42.02,2.71.63,4.2.28-.72-1.12-1.37-2.17-2.06-3.2-1.78-2.65-2.05-5.51-1.29-8.54.23-.91.43-1.84.69-2.95.78.63,1.4,1.05,1.94,1.56,2.42,2.28,5.16,4.11,8.01,5.83,2.36,1.43,4.89,2.38,7.49,3.17,1.99.61,4.06.96,6.33.91.02-.67.06-1.3.07-1.92.05-2.38.81-4.54,1.99-6.57,1.71-2.92,4.43-4.39,7.56-5.26,1.39-.39,2.76-.3,4.22-.14,2.64.29,4.8,1.52,6.96,2.82.49.3.86.38,1.4.19,1.21-.43,2.43-.83,3.67-1.19.49-.15,1.02-.18,1.84-.31-1.11,1.79-2.05,3.3-3,4.82,1.65.08,3.16-.84,4.97-.57-.86,1.35-1.83,2.42-2.78,3.52-.78.91-1.81,1.7-2.04,2.98-.25,1.43-.63,2.83-.75,4.31-.14,1.7-.6,3.37-.96,5.05-.14.64-.37,1.25-.58,1.87-1.35,3.9-3.27,7.44-5.93,10.64-2.96,3.55-6.42,6.42-10.55,8.47-2.84,1.4-5.83,2.42-8.95,2.97-2.6.45-5.24.39-7.87.33-2.95-.07-5.78-.69-8.6-1.52-2.27-.67-4.39-1.61-6.51-2.61-.14-.07-.26-.18-.61-.44Z"/>33
</svg>34
35
<svg id="Layer_2" data-name="Layer 2" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 51.1 36.69">36
<path d="m26.22,36.56c-4.74,0-9.49,0-14.23,0-3.93,0-7.19-1.49-9.63-4.58-1.02-1.29-1.66-2.82-2.03-4.45-.28-1.25-.28-2.52-.29-3.78C.03,19.98,0,16.21,0,12.44c0-1.06,0-2.13.18-3.17.2-1.09.59-2.15,1.16-3.13C2.75,3.73,4.77,1.97,7.28.79c.85-.4,1.78-.61,2.74-.63,2.02-.04,4.04-.21,6.06-.14,7.73.27,15.47.02,23.2.14,1.97.03,3.89.43,5.62,1.33,2.23,1.16,3.98,2.81,4.91,5.25.52,1.36.76,2.72.93,4.16.56,4.65.3,9.32.27,13.98-.02,2.7-.92,5.16-2.63,7.27-1.73,2.15-3.95,3.62-6.68,4.08-1.8.31-3.64.39-5.47.43-3.34.06-6.68.02-10.02.02,0-.04,0-.07,0-.11Zm7.94-18.51c-4.62-2.62-9.12-5.17-13.62-7.71-.12-.07-.29-.07-.4-.1v16.33c4.78-2.84,9.41-5.56,14.03-8.52Z"/>37
</svg>38
39
<svg id="Layer_2" data-name="Layer 2" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 47.26 53.87">40
<path d="m34.27,18.6c0,4.82,0,9.64.03,14.47.01,2.53-.24,5.01-.67,7.5-.68,3.88-2.69,6.84-5.66,9.33-2.21,1.86-4.72,2.98-7.48,3.6-1.02.23-2.09.29-3.14.34-3.54.18-6.73-.93-9.64-2.84-2.68-1.76-4.74-4.12-6.12-7.05-.74-1.58-1.16-3.26-1.51-4.94-.2-.98,0-2.04-.02-3.06-.07-3.01,1.1-5.62,2.62-8.11,2.19-3.6,5.35-5.98,9.35-7.26,1.24-.4,2.51-.7,3.84-.66.33.01.66-.13.99-.14.83-.02,1.65,0,2.58,0v9.31c-.27,0-.55-.01-.84,0-1.06.05-2.13.05-3.18.18-2.4.29-3.92,1.83-5.15,3.74-1.77,2.78-.84,7.04,1.38,9.35,2.28,2.38,6.05,2.67,8.72,1.48.6-.27,1.18-.6,1.73-.98,1.45-1.01,2.12-2.51,2.39-4.17.62-3.94.33-7.92.36-11.89.05-7.97,0-15.95,0-23.92,0-.87,0-1.74,0-2.72,3.08-.25,6.11-.14,9.16-.19.91,7.84,5.41,12.08,13.22,13.34-.07,2.84.22,5.85-.25,8.97-4.44-.24-8.48-1.55-12.25-3.76-.11-.11-.22-.22-.33-.33-.05.13-.09.25-.14.38Z"/>41
</svg>42
</div>43
</main>44
)45
}
For the GSAP implementation, we use the quickTo method to move the cursor around. We also use the React.cloneElement()
in order to return the children with a ref attached to it.
gsap.jsx
1
import React, { useEffect, useRef } from 'react'2
import gsap from 'gsap';3
4
export default function index({children}) {5
const magnetic = useRef(null);6
7
useEffect( () => {8
const xTo = gsap.quickTo(magnetic.current, "x", {duration: 1, ease: "elastic.out(1, 0.3)"})9
const yTo = gsap.quickTo(magnetic.current, "y", {duration: 1, ease: "elastic.out(1, 0.3)"})10
11
const mouseMove = (e) => {12
const { clientX, clientY } = e;13
const {height, width, left, top} = magnetic.current.getBoundingClientRect();14
const x = clientX - (left + width/2)15
const y = clientY - (top + height/2)16
xTo(x);17
yTo(y)18
}19
20
const mouseLeave = (e) => {21
gsap.to(magnetic.current, {x: 0, duration: 1})22
gsap.to(magnetic.current, {y: 0, duration: 1})23
xTo(0);24
yTo(0)25
}26
27
magnetic.current.addEventListener("mousemove", mouseMove)28
magnetic.current.addEventListener("mouseleave", mouseLeave)29
30
return () => {31
magnetic.current.removeEventListener("mousemove", mouseMove)32
magnetic.current.removeEventListener("mouseleave", mouseLeave)33
}34
}, [])35
36
return (37
React.cloneElement(children, {ref:magnetic})38
)39
}
Then we can wrap the SVG's inside the component:
page.js
1
<MagneticGSAP>2
<svg id="Layer_2" data-name="Layer 2" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 31.5 58">3
<path d="m20.72,22.16c2.77,0,5.55.02,8.32.03.4,0,.8.02,1.2.03.07.06.14.13.21.19-.28,1.58-.56,3.16-.83,4.75-.32,1.87-.64,3.74-.99,5.76-1.37.13-2.76-.07-4.14-.04-1.36.03-2.72,0-4.2,0-.13,8.38.12,16.72.11,25.11h-11.17v-24.91H0v-10.81h9.16c.04-.39.11-.71.11-1.02-.01-1.58-.05-3.17-.06-4.75-.01-1.62-.16-3.26.02-4.85.19-1.69.64-3.35,1.52-4.86,1.36-2.33,3.28-4.06,5.58-5.4,1.39-.81,2.94-1.25,4.52-1.3C24.28-.03,27.71.02,31.15,0c.07,0,.13.05.35.14.04,3.3-.29,6.66-.18,10.11-1.13,0-2.15.03-3.17,0-1.57-.07-3.15-.06-4.65.46-1.42.49-2.46,1.4-2.89,2.95-.3,1.08-.36,2.16-.34,3.25.04,1.69.13,3.38.2,5.07.08.06.16.13.24.19Z"/>4
</svg>5
</MagneticGSAP>
For Framer Motion, I use the setState()
hook with the motion
tag from Framer Motion.
framer.jsx
1
import { useRef, useState } from 'react'2
import { motion } from 'framer-motion';3
4
export default function Framer({children}) {5
const ref = useRef(null);6
const [position, setPosition] = useState({x:0,y:0});7
8
const handleMouse = (e) => {9
const { clientX, clientY } = e;10
const {height, width, left, top} = ref.current.getBoundingClientRect();11
const middleX = clientX - (left + width/2)12
const middleY = clientY - (top + height/2)13
setPosition({x: middleX, y: middleY})14
}15
16
const reset = () => {17
setPosition({x:0, y:0})18
}19
20
const { x, y } = position;21
return (22
<motion.div23
style={{position: "relative"}}24
ref={ref}25
onMouseMove={handleMouse}26
onMouseLeave={reset}27
animate={{x, y}}28
transition={{type: "spring", stiffness: 150, damping: 15, mass: 0.1}}29
>30
{children}31
</motion.div>32
)33
}
Then we can wrap the SVG's inside the component:
page.js
1
<MagneticFramer>2
<svg id="Layer_2" data-name="Layer 2" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 31.5 58">3
<path d="m20.72,22.16c2.77,0,5.55.02,8.32.03.4,0,.8.02,1.2.03.07.06.14.13.21.19-.28,1.58-.56,3.16-.83,4.75-.32,1.87-.64,3.74-.99,5.76-1.37.13-2.76-.07-4.14-.04-1.36.03-2.72,0-4.2,0-.13,8.38.12,16.72.11,25.11h-11.17v-24.91H0v-10.81h9.16c.04-.39.11-.71.11-1.02-.01-1.58-.05-3.17-.06-4.75-.01-1.62-.16-3.26.02-4.85.19-1.69.64-3.35,1.52-4.86,1.36-2.33,3.28-4.06,5.58-5.4,1.39-.81,2.94-1.25,4.52-1.3C24.28-.03,27.71.02,31.15,0c.07,0,.13.05.35.14.04,3.3-.29,6.66-.18,10.11-1.13,0-2.15.03-3.17,0-1.57-.07-3.15-.06-4.65.46-1.42.49-2.46,1.4-2.89,2.95-.3,1.08-.36,2.16-.34,3.25.04,1.69.13,3.38.2,5.07.08.06.16.13.24.19Z"/>4
</svg>5
</MagneticFramer>
That's it for this animation!
We saw how we can create a Magnetic Effect using two distinct animation library. Hope you learned more about the difference between imperative and declarative animations.
-Oli