前端網頁動效
-作品集拖曳效果怎麼做?放在個人網站超吸睛!
前陣子看到一個工程師的個人網站做了這種拖曳作品集的特效,覺得非常有趣且吸睛,研究一下發現其實可以用一些套件快速做到這樣的效果,馬上看看是怎麼做的吧!
前置準備 - 安裝 React + Framer Motion
今天我們會用 React + Framer Motion 來製作這個特效。
可以先用 vite 來快速建置 React 的開發環境:
npm create vite@latest my-react-app -- --template react
執行完成後就依序執行以下指令,分別是:
cd my-react-app
: 移動到my-react-app
資料夾npm install
: 安裝相關依賴npm run dev
: 執行這個 app
cd my-react-app
npm install
npm run dev
接著打開 http://localhost:5173
就會看到這個畫面
並且也可以看到你的資料夾結構,如果只是想練習這個特效,直接改 App.jsx
就好
下一步就是安裝 Framer Motion,直接在終端機打上以下指令就好:
npm install motion
引入圖片 & 製作 container
接著我們可以將圖片存在 ./public/drag-img/
這個資料夾,並利用陣列的方式儲存圖片的位置,這樣等一下直接用 Array.map
的方式就可以渲染出所有的圖片。
const images = [
'/drag-img/image-1.png',
'/drag-img/image-2.png',
'/drag-img/image-3.png',
'/drag-img/image-4.png',
'/drag-img/image-5.png',
'/drag-img/image-6.png',
'/drag-img/image-7.png',
'/drag-img/image-8.png',
'/drag-img/image-9.png',
'/drag-img/image-10.png',
'/drag-img/image-11.png',
'/drag-img/image-12.png',
'/drag-img/image-13.png',
'/drag-img/image-14.png',
];
接著製作等等要放圖片的容器,這邊用 useRef
來儲存 container,因為等等要抓到 container 的寬高,讓圖片能夠保持在這個 container 裡面。
export default function DragImg() {
const containerRef = useRef(null);
return (
<div
ref={containerRef}
className='drag-img__container'
>
{/* ... */}
</div>
);
}
並稍微調整 container 的 style
.drag-img__container {
position: relative;
width: 100vw;
height: 100vh;
overflow: hidden;
background: #f0f0f0;
}
渲染圖片 & 隨機圖片位置
利用剛剛的 images 陣列來渲染出所有的圖片,注意這邊是用 motion.img
的 tag,這是 framer motion 的用法,這樣等等才可以使用 framer motion 的功能。
export default function DragImg() {
const containerRef = useRef(null);
return (
<div
ref={containerRef}
className='drag-img__container'
>
{images.map((src, index) => (
<motion.img
key={index}
src={src}
className='drag-img__img'
alt={`Image ${index + 1}`}
/>
))}
</div>
);
}
.drag-img__img {
width: 200px;
aspect-ratio: 4/3;
object-fit: contain;
padding: 4px;
position: absolute;
background: rgba(255, 255, 255, 0.2);
border-radius: 6px;
box-shadow: 0 0 2px rgba(0, 0, 0, 0.1);
cursor: grab;
}
稍微調整圖片的寬度、比例,並讓他的 position
是 absolute
,其他就是一些小裝飾,例如 padding
、shadow
等等,現在所有的圖片都會在右上角,因為我們還沒調整他們的位置
接著可以利用 JavaScript 來隨機圖片的位置,順便隨機旋轉的角度,讓他有種散落在整個 container 的感覺。
{images.map((src, index) => (
<motion.img
key={index}
src={src}
className='drag-img__img'
alt={`Image ${index + 1}`}
style={{
top: `${Math.random() * (window.innerHeight - 200)}px`,
left: `${Math.random() * (window.innerWidth - 150)}px`,
rotate: `${Math.random() * 40 - 20}deg`,
}}
/>
))}
製作拖曳效果
接著是重頭戲,我們要利用 Framer Motion 來製作拖曳效果,其實非常簡單,因為 Framer Motion 已經有內建 drag 的屬性,所以我們只要在 motion.img
加上 drag 屬性就完成了~
{images.map((src, index) => (
<motion.img
// ...
drag
dragConstraints={containerRef}
whileDrag={{ scale: 1.1, rotate: 0 }}
/>
))}
這邊加上 dragConstraints
讓他只能在 container 裡面拖移,不會跑到外面。並利用 whileDrag
來控制拖曳時的 style。
完整程式碼
到這邊就完全搞定了,其實非常簡單!以下附上全部的程式碼:
import { useRef } from 'react';
import { motion } from 'framer-motion';
const images = [
'/drag-img/image-1.png',
'/drag-img/image-2.png',
'/drag-img/image-3.png',
'/drag-img/image-4.png',
'/drag-img/image-5.png',
'/drag-img/image-6.png',
'/drag-img/image-7.png',
'/drag-img/image-8.png',
'/drag-img/image-9.png',
'/drag-img/image-10.png',
'/drag-img/image-11.png',
'/drag-img/image-12.png',
'/drag-img/image-13.png',
'/drag-img/image-14.png',
];
export default function DragImg() {
const containerRef = useRef(null);
return (
<div
ref={containerRef}
className='drag-img__container'
>
{images.map((src, index) => (
<motion.img
key={index}
src={src}
className='drag-img__img'
alt={`Image ${index + 1}`}
style={{
top: `${Math.random() * (window.innerHeight - 200)}px`,
left: `${Math.random() * (window.innerWidth - 150)}px`,
rotate: `${Math.random() * 40 - 20}deg`,
}}
drag
dragConstraints={containerRef}
whileDrag={{ scale: 1.1, rotate: 0 }}
/>
))}
</div>
);
}
.drag-img__container {
position: relative;
width: 100vw;
height: 100vh;
overflow: hidden;
background: #f0f0f0;
}
.drag-img__img {
width: 200px;
aspect-ratio: 4/3;
object-fit: contain;
padding: 4px;
position: absolute;
background: rgba(255, 255, 255, 0.2);
border-radius: 6px;
box-shadow: 0 0 2px rgba(0, 0, 0, 0.1);
cursor: grab;
}
參考連結
Vite : Getting Started | Vite
React : React
Framer Motion : Quick start | Motion for React (prev Framer Motion)