React实现拖拽特效

devtools/2025/1/20 0:47:28/

前言

最近,我看到一个工程师的个人网站上,采用了拖拽作品集的互动特效,既有趣又吸引眼球。经过一些研究,我发现其实借助一些现成的套件,就能轻松实现这样的效果。今天就带大家一起看看,如何通过 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 (<divref={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 (<divref={containerRef}className='drag-img__container'>{images.map((src, index) => (<motion.imgkey={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.imgkey={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.imgkey={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`,}}dragdragConstraints={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 (<divref={containerRef}className='drag-img__container'>{images.map((src, index) => (<motion.imgkey={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`,}}dragdragConstraints={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;
}

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


http://www.ppmy.cn/devtools/151960.html

相关文章

适配器模式案例

如果在这样的结构中 我们在Controller中注入&#xff0c;但我们后续需要修改Oss时&#xff0c;比如从minioService改成AliyunService时&#xff0c;需要改动的代码很多。于是我们抽象出一个FileService&#xff0c;让controller只跟fileservice耦合&#xff0c;这样我没只需要在…

网络编程 | UDP套接字通信及编程实现经验教程

1、UDP基础 传输层主要应用的协议模型有两种&#xff0c;一种是TCP协议&#xff0c;另外一种则是UDP协议。在上一篇博客文章中&#xff0c;已经对TCP协议及如何编程实现进行了详细的梳理讲解&#xff0c;在本文中&#xff0c;主要讲解与TCP一样广泛使用了另一种协议&#xff1a…

iOS - block

1. Block 的内存管理 // Block 的基本结构 struct Block_layout {void *isa; // Block 的类型信息volatile int32_t flags; // 标志位&#xff0c;包含引用计数等信息int32_t reserved; // 保留字段void (*invoke)(void *, ...); // 函数指针&#x…

一文了解汽车嵌入式软件开发Franca IDL 知识

本文主要是对 Franca IDL 的作用和设计意图进行解释说明&#xff0c;并且给出其他具有类似功能的 IDL 的对比。用实际的例子来说明核心设计理念&#xff0c;帮助理解设计意图。相比而言&#xff0c;其他 Franca IDL 文章更加注重参考手册的功能&#xff0c;本文试图探求Franca …

RPA编程实践:Electron实践开始

文章目录 前言闲话少叙&#xff0c;打开官网版本发布安装在 Windows 上安装在 macOS 上安装在 Linux (Ubuntu) 上安装 前言 上回说道&#xff0c;我们electron适合于熟悉web开发&#xff0c;但想要研发桌面应用的人。 但我觉得这个需求应该不是很多。 因为使用electron&#…

【学术会议论文投稿】Spring Boot实战:零基础打造你的Web应用新纪元

第七届人文教育与社会科学国际学术会议&#xff08;ICHESS 2024&#xff09;_艾思科蓝_学术一站式服务平台 更多学术会议请看&#xff1a;https://ais.cn/u/nuyAF3 目录 一、Spring Boot简介 1.1 Spring Boot的诞生背景 1.2 Spring Boot的核心特性 二、搭建开发环境 2.1…

03、Redis从入门到放弃 之 配置文件详解

redis常用配置文件信息&#xff1a;redis.conf redis-benchmark &#xff1a; redis压力测试 daemonize noRedis默认不是以守护进程的方式运行 port 6379指定Redis监听端口&#xff0c;默认端口为6379 bind 127.0.0.1绑定的主机地址 timeout 300客户端闲置多长时间后关闭连…

【ESP32】Arduino开发 | WiFi开发 | 基站模式 + AP扫描和AP连接例程

WiFi基站模式的详细讲解放在了ESP-IDF开发系列当中&#xff0c;点击栏目目录即可跳转找到。 1. API 1.1 初始化 wl_status_t begin(const char* ssid, const char *passphrase NULL, int32_t channel 0, const uint8_t* bssid NULL, bool connect true); ssid&#xff1…