React实现拖拽特效

前言

最近,我看到一个工程师的个人网站上,采用了拖拽作品集的互动特效,既有趣又吸引眼球。经过一些研究,我发现其实借助一些现成的套件,就能轻松实现这样的效果。今天就带大家一起看看,如何通过 Framer Motion 来制作这个特效吧!

安装 React + Framer Motion

我将使用 ReactFramer Motion 来实现这个特效。首先,用 Vite 来快速搭建一个 React 开发环境:

复制代码
npm create vite@latest my-react-app -- --template react

执行完成后就依序执行以下指令,分别是:

  1. cd my-react-app : 移动到 my-react-app 资料夹

  2. npm install : 安装相关依赖

  3. npm run dev : 执行

    cd my-react-app
    npm install
    npm run dev

打开浏览器,访问 http://localhost:5173,你应该会看到一个基础的 React 应用如下图。

并且也可以看到你的资料夹结构,如果只是想练习这个特效,直接改 App.jsx 就好

下一步就是安装 Framer Motion,直接在终端机打上以下指令就好:

复制代码
npm install framer-motion

引入图片 & 创建容器

我们将图片保存在 ./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 来引用容器,以便后面获取容器的宽高。

复制代码
export default function DragImg() {
  const containerRef = useRef(null);

  return (
    <div
      ref={containerRef}
      className='drag-img__container'
    >
       {/* 图片渲染 */}
    </div>
  );
}

接下来,稍微修改一下style样式

复制代码
.drag-img__container {
  position: relative;
  width: 100vw;
  height: 100vh;
  overflow: hidden;
  background: #f0f0f0;
}

渲染图片 & 随机位置

通过刚才定义的 images 数组来渲染所有图片。这里使用的是 motion.img 标签,这样才能使用 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
    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 }}  // 拖拽时的样式调整
  />
))}

这里,我们添加了 dragConstraints,使图片只能在容器内拖动,不会超出边界。同时,使用 whileDrag 来调整拖拽时图片的缩放和旋转效果。

完整代码示例

到此为止就完全搞定了,其实非常简单!以下附上全部的代码:

复制代码
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;
}

通过以上步骤就成功创建了一个可以拖拽的图片展示特效。操作非常简单,而且效果十分酷炫!快来试试看吧!

相关推荐
Van_Moonlight18 分钟前
RN for OpenHarmony 实战 TodoList 项目:任务完成进度条
javascript·开源·harmonyos
Van_Moonlight26 分钟前
RN for OpenHarmony 实战 TodoList 项目:深色浅色主题切换
javascript·开源·harmonyos
小贵子的博客28 分钟前
Ant Design Vue <a-table>
前端·javascript·vue.js·anti-design-vue
天天进步201531 分钟前
【Nanobrowser源码分析4】交互篇: 从指令到动作:模拟点击、滚动与输入的底层实现
开发语言·javascript·ecmascript
console.log('npc')38 分钟前
vue2中子组件父组件的修改参数
开发语言·前端·javascript
Van_captain1 小时前
rn_for_openharmony常用组件_Chip纸片
javascript·开源·harmonyos
奋斗吧程序媛1 小时前
vue3 Study(1)
前端·javascript·vue.js
QQ12958455041 小时前
ThingsBoard - APP首页修改为手工选择组织
前端·javascript·物联网·iot
whyfail1 小时前
前端数据存储新选择:IndexedDB与Dexie.js技术指南
前端·javascript·数据库
椰果uu1 小时前
vue-virtual-scroller-虚拟滚动列表:渲染不定高度长列表+可控跳转
前端·javascript·typescript·vue