NEXT.JS X FRAMER MOTION
Add Stunning Animation in NextJS | Framer Motion
NextJS adalah framework static yang memiliki performa tinggi. Tapi bukan karena Static, dia terlihat membosankan.
Framer Motion adalah sebuah API yang bisa direferensikan untuk memberikan animasi motion pada aplikasi yang sedang kita buat.
Framer Motion menyediakan Ready-to-go animation and gesture controls yang memudahkan kita untuk membuat animasi yang dinamis. Motion API nya telah ter-built in di dalam package itu sendiri. Tetapi tersedia dengan mudah sebagai paket terpisah yang dapat kita install melalui npm / yarn .
Apa yang akan kita buat ?
Kali ini kita akan membuat Pop up pemilihan weapon menggunakan fitur AnimateSharedLayout dari Framer Motion. Saya juga akan menaruh project ini di Github repo jika teman-teman ingin melihatnya.
Framer Motion Example Github repo
Jika teman-teman mendapat masalah pada kode karena ada kesalahan saat menulis kode tersebut. Teman-teman bisa melihat code yang sudah saya push ke repo github saya untuk dijadikan sebagai referensinya.
STEP 01: Menginstall package Framer Motion di Next.JS
Kita akan menginstal package/dependecy Framer Motion yang akan kita gunakan pada tutorial kali ini.
Setelah berhasil menjalankan app Next.JS di develpment server, kita install Framer Motion:
yarn add framer-motion
#ataunpm i framer-motion
Setelah berhasil melakukan instalasi package-nya. Restart development server-nya, dan kita akan memulai pembuatan app-nya!
STEP 02: Konfigurasi file index.js, main.module.css, & globals.css
Kita akan membutuhkan styling pada tampilan app kita. Disini kita akan menggunakan Built-in css module dari Next.JS. Pertama kita buat dulu file main.module.css di folder styles/
Lalu Copy-Paste kan code css dibawah ini :
.App {
font-family: sans-serif;
text-align: center;
}
.wrapper {
list-style: none;
margin: 0;
padding: 0;
display: flex;
flex-direction: row;
flex-wrap: wrap;
width: 280px;
height: 280px;
}
.item {
display: flex;
width: 100px;
height: 100px;
padding: 10px;
justify-content: center;
align-items: center;
border-radius: 50%;
margin: 20px;
position: relative;
cursor: pointer;
flex-shrink: 0;
}
.item img {
width: 80%;
height: 80%;
}
.item:nth-child(3) {
padding-left: 6%;
}
.outline {
position: absolute;
top: -20px;
left: -20px;
right: -20px;
bottom: -20px;
border: 10px solid white;
border-radius: 50%;}
Setelah konfigurasi main.module.css telah diselesaikan, ubah style body di file globals.css
html,
body {
padding: 0;
margin: 0;
font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen,
Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif;
background-repeat: no-repeat;
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;}
Pertama-tama, kita akan menghapus function component Home bawaan dari Next.JS. Lalu ubah isi file index.js tersebut seperti dibawah ini:
import styles from '../styles/main.module.css';
export default function Home() {
return (
);
}
const weapons = [
{
image: "https://i.ibb.co/L0hKRZq/red-weapon.png",
color: "#ff0055"
},
{
image: "https://i.ibb.co/KwMkhWg/green-weapon.png",
color: "#22cc88"
},
{
image: "https://i.ibb.co/WnNYmrG/blue-weapon.png",
color: "#0099ff"
},
{
image: "https://i.ibb.co/HB9B3FJ/yellow-weapon.png",
color: "#ffaa00"
}];
Pada kode yang telah kita tulis di atas, kita telah membuat sebuah function component Home baru yang akan kita isi dengan kode yang akan kita tulis berikutnya. Lalu ada variabel weapons yang akan kita gunakan sebagai object data pada tutorial kali ini.
STEP 03: Membuat list Weapon
Setelah membuat variable weapon yang berisi data-data object, kita akan menggunakannya untuk menampilkan list weapon menggunakan method map()
pada component Home kita:
<ul className={styles.wrapper}>
{weapons.map((weapon) => (
<Item
key={weapon.color}
color={weapon.color}
image={weapon.image}
/>
))}</ul>
Setelah menulis kode diatas, kita telah memecah data array menjadi object yang bisa kita gunakan untuk menampilkan list weapon kita. Kita mengoper data yang telah dipecah untuk digunakan di funcional component Item yang akan kita buat dibawah ini:
function Item({ color, image }) {
return (
<li
className={styles.item}
style={{ backgroundColor: color }}
>
<img src={image} alt={color} />
</li>
);}
Setelah menulis kode diatas, kita telah berhasil menampilkan data list weapon kita. Seperti contoh dibawah ini:
STEP 04: Framer Motion is Here | END
Sejauh ini kita telah berhasil menampilkan list weapon yang telah kita buat kedalam layar browser. Tapi, tampilannya masih terlihat membosankan karena belum terdapat efek animasi dan gesture animation dari tampilan tersebut.
Oleh karena itu, kita akan membutuhkan bantuan dari Framer Motion untuk menambahkan efek animasi & gesture animation agar tampilannya bisa lebih interaktif & user friendly.
Pada Studi Kasus kali ini, kita akan menggunakan 2 function dari Framer Motion, yaitu motion, & AnimateSharedLayout. Import pada bagian paling atas index.js:
import { useState } from "react";import { motion, AnimateSharedLayout } from "framer-motion";
Setelah itu, kita akan membungkus <ul>…</ul>
pada component Home menggunakan function AnimateSharedLayout. seperti contoh dibawah ini:
export default function Home() {
const [selected, setSelected] = useState(weapons[0]);
return (
<AnimateSharedLayout>
<ul className={styles.wrapper}>
{weapons.map((weapon) => (
<Item
key={weapon.color}
color={weapon.color}
image={weapon.image}
isSelected={selected === weapon.color}
onClick ={() => setSelected(weapon.color)}
/>
))}
</ul>
</AnimateSharedLayout>
);}
Kita juga membuat statebaru, yaitu selected
, agar saat item dipilih, border animasi-nya akan menyesuaikan warna yang telah dipilih.
Kita telah membungkus tag <ul>
dengan AnimateSharedLayout. Fungsi / Kegunaan dari function AnimateSharedLayout adalah untuk memberitahu Framer Motion bahwa tag <ul>
tersebut adalah layout dari animasi yang akan kita buat.
Dalam <Item … />
, kita juga telah menambahkan event onClick()
yang akan mengubah state selected menjadi apa yang user pilih. Dan data dari state tersebut akan dioper ke props <Item… isSelected/>
.
Jika ada kesulitan atau kesalahan program saat menulis kode. saya akan memberikan github repo yang berisi project kali ini sebagai referensi kalian.
Framer Motion Example Github repo
Setelah menyelesaikan kode seperti diatas, kita akan memberikan mengubah kode pada component Item kita yang telah menerima props baru, yaitu isSelected
& onClick
.
function Item({ color, isSelected, image, onClick }) {
return (
<motion.li
className={styles.item}
whileTap={{
borderRadius: "20%",
transition: { delay: 0.05, duration: 0.3 }
}}
whileHover={{ scale: 1.1 }}
onClick={onClick}
style={{ backgroundColor: color }}
>
<img src={image} alt={color} />
{isSelected && (
<motion.div
layoutId={styles.outline}
className={styles.outline}
initial={false}
animate={{ borderColor: color }}
transition={spring}
/>
)}
</motion.li>
);}
Seperti kode diatas, kita telah menambahkan props baru. Dan kita juga menambahkan function motion pada tag <li>
kita. karena pada tag <li>
inilah, kita akan memberikan animasi pada tampilan switch kita. Kita juga sudah menambahkan baris baru, yaitu
{isSelected && (
<motion.div
layoutId={styles.outline}
className={styles.outline}
initial={false}
animate={{ borderColor: color }}
/>)}
Maksud kode diatas adalah, jika propsisSelected == true
/ ada valuenya, maka segera tampilkan kode yang ada. Pada kasus kali ini adalah tag <div>
. Kita juga melakukan hal yang sama sperti saat menambahkan function motion pada tag <li>
kita sebelumnya.
tag <div>
disini adalah border / penunjuk pilihan si user, karena kita ingin memberikan dia animasi pada saat user memilih list weapon. Oleh karena itu kita menambahkan function motion tag <div>
kita.
Setelah function motion ditambah pada tag tujuannya, maka tag tersebut akan memiliki attribute props yang tidak dimiliki oleh tag-tag biasa
Seperti pada penjelasan singkat diatas. pada kode yang sudah kita tulis, terdapat attribute props seperti initial
, animate
, whileHover
, & whileTap
.
- initial = state/kodisi style sebelum component terender di browser.
- animate = state/kondisi style saat component terender di browser.
- whileTap = kondisi style saat tag motion tersebut di tekan/tap.
- whileHover = kondisi style saat cursor mengambang/hover diatas tag motion.
Masih banyak lagi props yang bisa digunakan agar tampilan animasi nya bisa lebih interaktif lagi.
Setelah Sejauh ini, kita berhasil menambahkan animasi & user gesture pada tampilan kita sebelumnya 🎉. Seperti inilah hasil akhirnya :
Untuk membuat animasi-nya lebih cantik lagi, kita bisa memanfaatkan fitur transition untuk membuat efek bounce yang telah disediakan oleh function motion. Pertama, kita buat dahulu variable module style object nya.
const spring = {
type: "spring",
stiffness: 500,
damping: 30
};
Setelah itu, kita berikan variable berisi style module tersebut di attribute props transition pada tag <motion.div>
{isSelected && (
<motion.div
...
transition={spring}
/>
)}
Maka efek Bounce nya akan ter-apply👏.
Berikut ini adalah Full Source Code yang telah selesai.
import React, { useState } from "react";
import { motion, AnimateSharedLayout } from "framer-motion";
import styles from '../styles/main.module.css';
export default function App() {
const [selected, setSelected] = useState(weapons[0]);
return (
<AnimateSharedLayout>
<ul className={styles.wrapper}>
{weapons.map((weapon) => (
<Item
key={weapon.color}
color={weapon.color}
image={weapon.image}
isSelected={selected === weapon.color}
onClick={() => setSelected(weapon.color)}
/>
))}
</ul>
</AnimateSharedLayout>
);
}
function Item({ color, isSelected, image, onClick }) {
return (
<motion.li
className={styles.item}
whileTap={{
borderRadius: "20%",
transition: { delay: 0.05, duration: 0.3 }
}}
whileHover={{ scale: 1.1 }}
onClick={onClick}
style={{ backgroundColor: color }}
>
<img src={image} alt={color} />
{isSelected && (
<motion.div
layoutId={styles.outline}
className={styles.outline}
initial={false}
animate={{ borderColor: color }}
transition={spring}
/>
)}
</motion.li>
);
}
const weapons= [
{
image: "https://i.ibb.co/L0hKRZq/red-weapon.png",
color: "#ff0055"
},
{
image: "https://i.ibb.co/KwMkhWg/green-weapon.png",
color: "#22cc88"
},
{
image: "https://i.ibb.co/WnNYmrG/blue-weapon.png",
color: "#0099ff"
},
{
image: "https://i.ibb.co/HB9B3FJ/yellow-weapon.png",
color: "#ffaa00"
}
];
const spring = {
type: "spring",
stiffness: 500,
damping: 30};
Selanjutnya kita akan membahas tentang Page Transition di NextJS menggunakan Framer Motion 👋.
BONUS!
Dibawah ini adalah Tutorial Singkat Tentang Cara Mengupload Image Dari React Hooks Ke Laravel 8 Hanya Dalam 10 Menit