# TaskDrag **Repository Path**: weijing0905/task-drag ## Basic Information - **Project Name**: TaskDrag - **Description**: 基于react完成的一个任务拖动小应用 - **Primary Language**: JavaScript - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2021-11-20 - **Last Updated**: 2022-04-14 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # 一、项目简要说明 使用React框架,采用react-router-dom的路由方式,完成的一个任务拖拽小应用。其中使用的是函数组件,并使用useState、useEffect来对一些状态进行管理。 克隆到本地npm i,然后npm start即可启动 # 二、我的实现步骤 ### 1、初始化项目 npx create-react-app name npm install --save react-router-dom (这里我安装后改成了5.2.0的版本) 启动项目 npm start, 在默认3000端口访问 ![输入图片说明](https://images.gitee.com/uploads/images/2021/1120/203814_d3a69078_8890834.png "Snipaste_2021-11-20_20-37-49.png") ### 2、配置路由 在App.js文件中 ```jsx
``` ### 3、start页面 ![输入图片说明](https://images.gitee.com/uploads/images/2021/1121/161901_4ac6eb0c_8890834.png "屏幕截图.png") ```jsx
Start App
``` 点击按钮进入/home页面 ### 4、home页面(主要部分) ![输入图片说明](https://images.gitee.com/uploads/images/2021/1121/161948_120467f8_8890834.png "屏幕截图.png") ###功能说明 (1)点击加号新增任务 (2)鼠标拖动可以挪动任务到不同区域 —— 采用任务中心点计算 (3)鼠标落在任务区域,任务右上角出现删除按钮,点击可以删除任务 ### 思路总结 一个完整的功能都是由小的部分逐渐拼凑到一起组成的,所以解耦很重要。 #### (1)页面结构 ![输入图片说明](README.assets/205538_151ae75b_8890834.png "屏幕截图.png") 对应也应该有三个数组类型的状态 ```javascript const [prepare, setPrepare] = useState(['task1', 'task2']); const [learning, setLearning] = useState([]); const [complete, setComplete] = useState([]); ``` 在三个板块都是列表渲染 ```jsx
{ prepare.map((item, index) => { return (
{item} +
) }) }
``` #### (2)添加任务 点击添加按钮的时候,创建一个input节点,监听输入,失去焦点时判断输入的内容是否为空,为空则取消本次添加,不为空则添加;如果当前没有完成添加无法进行下一次添加。 新添加的任务,所以要更新state ```javascript let res = [...prepare]; res.push(now);//now是刚刚输入的内容 setPrepare(res); ``` 完成修改,并将创建的输入框清除element.remove(); #### (3)任务拖动 使用useEffect钩子 ```javascript useEffect(() => { let task = document.querySelectorAll("#task"); task.forEach((task) => { task.addEventListener("mousedown", mouseDown, true); }) }, [prepare, learning, complete]) //这三个状态发生变化的时候,函数会执行 ``` 做任务拖动的时候,需要注意,窗口的大小是会改变的,所以我们可以获得三个分类板块在当前窗口的位置,再对任务拖动的时候进行判断是否进入了板块的范围 我需要知道的知识有 - 获得元素在当前窗口的位置 - 相对定位 - top、left设置 - clientX、clientY - offsetX、offsetY - 获得 任务盒子的中心 在当前窗口的坐标 - 判断盒子中心坐标 在鼠标抬起的时候是否在三个板块的范围中 - 如果在,进行三个状态的更改,移出的状态要删除元素,移入的地方要新增元素 - 如果不在,则回到最初的位置,设置top、left属性为0 具体代码备注很详细了 #### (4)删除 删除按钮是使用绝对定位来写 ```css .cha{ width: 16px; height: 16px; background-color: rgb(165, 65, 65); display: none; position: absolute; top:4px; left:146px; border-radius: 50%; color: white; text-align: center; line-height:13px ; transform: rotate(45deg); border:none; cursor: default; } ``` 并且当鼠标停留在对应的任务上时,显示按钮 ```css #task:hover>.cha{ display: block; } ``` 在监听的时候,使用事件代理的方式处理拖动和删除功能 ```javascript const handleDelete = (type, taskName) => {//接收传过来的任务来源——代办、进行中、已完成,以及任务名 if (type === "content_P") { let newP = [...prepare]; let index = newP.indexOf(taskName); newP.splice(index, 1); setPrepare(newP); } if (type === "content_L") { let newL = [...learning]; let index = newL.indexOf(taskName); newL.splice(index, 1); setLearning(newL); } if (type === "content_C") { let newC = [...complete]; let index = newC.indexOf(taskName); newC.splice(index, 1); setComplete(newC); } } const mouseDown = (event) => {//事件代理 event.stopPropagation(); if (event.target.tagName.toLowerCase() === 'span') {//如果是点击删除 //... handleDelete(type,taskName); } else { //处理拖动事件 } } ```