React 拖放
介绍
开演时间
结论
介绍
如果说 UI 设计就像一顿丰盛的感恩节大餐,“拖拽”功能简直就是面包和黄油,或者火鸡,甚至是火腿。或者……咳咳……你懂的!😉 这功能很棒。这是我们网站上的理想功能。它能让用户在使用我们的应用程序时真正掌控自己的体验。
注意:这篇文章需要 7 分钟才能读完,你可能需要更长时间才能跟上。我完全理解你不想浪费时间的心情!我也理解你希望遵循正确的教程。❤️ 所以,如果你想在继续之前先看看实况,请点击这里...在台式电脑上... 本教程不适用于移动设备。
那么,不用多说,让我们开始吧。
重要信息
我想在这里介绍几个概念。如果您已经熟悉 Web API,以下是一些资源,可快速回顾dataTransfer
、dataTransfer.getData()
和dataTransfer.setData()
:
dataTransfer
dataTransfer.getData()
dataTranser.setData()
这些概念对于我个人来说很难理解,所以不用担心——我会在这个博客中详细介绍到底发生了什么。
设置
让我们从头开始。npx create-react-app your-choice-appname
在终端中输入并按回车键,创建一个 React 应用程序,“your-choice-appname” 可以随意命名这个项目。
完成后,我们来清理一下。删除它App.test.js
,然后重命名index.css
为main.css
。因为我们可以。👍
接下来,您需要确保将您的内容导入到main.css
您的内部index.js
,如下所示:
import React from 'react';
import ReactDOM from 'react-dom';
import './main.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
...
完美的!
创建板卡和卡组件
我们将遵循关注点分离规则,因此让我们在源文件夹中创建一个名为“components”的文件夹—— src/components
。
在此文件夹内,创建两个文件Card.jsx
和Board.jsx
。
这些将是props
相互接受参数的功能组件。这对于来回传输数据是必要的。
注意:事情到这里开始变得有点令人困惑了。要理解接下来会发生什么,需要先理解两者同时发生的情况Card.jsx
。Board.jsx
我会提供详尽的解释,所以请耐心等待。一旦明白了,你就会体验到我所说的“顿悟时刻”。
Board.jsx
让我们从 Board 组件的骨架开始。我们将从以下内容开始:
import React from 'react';
export default function Board(props) {
return (
<div>
</div>
)
}
在深入探讨该组件的应用逻辑之前,我们需要为 设定一些属性div
。我们需要为它赋值一个id
和一个,这就是我们要用到的。让我们修改它,让它反映出我们希望它执行的动态操作。className
props
import React from 'react';
export default function Board(props) {
return (
<div
id={props.id}
className={props.className}
>
</div>
)
}
这样做的原因是我们稍后将以这样的方式使用这个板组件:<Board id="1" className="board"></Board>
正如您所见,我们的道具将是“id”和“className”。
drop
现在我们可以开始添加函数了。我们想在面板上处理两个 React 事件。它们是onDrop
(当我们把 a 拖放card
到 this 中时board
)和(当光标将 a 拖到 a 中时,onDragOver
用于跟踪 a 的数据)。让我们将这些事件应用到我们的。card
board
div
注意:这些事件将由我们尚未创建的函数触发,但我们接下来将创建它们。
export default function Board(props) {
return (
<div
id={props.id}
className={props.className}
onDrop={drop}
onDragOver={dragOver}
>
</div>
)
}
好了,现在到了最有趣的部分!我们将创建一个名为的函数drop
,并将其放在我们的上方return()
:
export default function Board(props) {
const drop = e => {
const card_id = e.dataTransfer.getData('card_id');
const card = document.getElementById(card_id);
e.target.appendChild(card);
}
return (
<div
id={props.id}
className={props.className}
onDrop={drop}
onDragOver={dragOver}
>
</div>
)
}
“哇哦,等等!马特,这是怎么回事? ”
很高兴你问了这个问题!让我们从函数中的前两个声明开始drop
。
const card_id = e.dataTransfer.getData('card_id')
负责从 中获取数据,稍后card
我们会将其拖入 中board
。我们设置了一个“card_id”的声明,并将其设置为 this ,这样当我们adataTransfer
时,它将直接从我们的游标中获取。(抱歉,我有点重复了。我觉得如果你“明白我的意思”,那说明我解释得很好。😉)drop
card
接下来,我们将设置另一个“card”声明,该声明用于抓取card
DOM 中的元素 ID,以便将其放入board
.
最后,我们使用e.target.appendChild(card)
将我们的添加到card
(e.target
当前e.target
正在board
被card
放入的)。
拖拽
这个方法简洁明了。我们要做的是创建一个dragOver
函数,该函数接受e
一个参数,用于event
阻止 React 事件的默认行为onDragOver
。简单来说,就是阻止背部onDragOver
被拖拽card
到它被拖拽时的原始位置。为了触发board
事件,该事件必须处于启动状态,但不能处于完成状态。onDrop
const dragOver = e => {
e.preventDefault();
}
总而言之,我们希望所有卡片都能显示在页面上。为此,我们只需{ props.children }
在 之间添加div
。
您的完成的Board.jsx
组件应该如下所示:
import React from 'react';
export default function Board(props) {
const drop = e => {
const card_id = e.dataTransfer.getData('card_id');
const card = document.getElementById(card_id);
e.target.appendChild(card);
}
const dragOver = e => {
e.preventDefault();
}
return (
<div
id={props.id}
className={props.className}
onDrop={drop}
onDragOver={dragOver}
>
{ props.children }
</div>
)
}
卡片.jsx
是时候介绍我们的Card.jsx
组件了!我们将以类似的方式开始设置Board.jsx
:
import React from 'react';
export default function Card(props) {
return (
<div>
</div>
)
}
接下来,让我们在 中设置一些属性div
。除了组件中已有的id
和之外,我们还想为卡片应用一个名为 的特殊属性。为了使卡片能够,嗯……你猜对了——可拖动,我们需要将此属性设置为。className
Board.jsx
draggable
true
import React from 'react';
export default function Card(props) {
return (
<div
id={props.id}
draggable={props.draggable}
className={props.className}
>
</div>
)
}
您可能已经得出结论,我们将以类似的方式使用此组件<Board></Board>
:
<Card id="1" className="card" draggable="true">
<p>Card one</p>
</Card>
现在我们可以开始添加函数了dragStart
(用于将卡片数据移动到光标处)和dragOver
(用于防止卡片掉落到其他卡片上)。这两个函数都将由 React 事件onDragStart
和执行onDragOver
。
import React from 'react';
export default function Card(props) {
return (
<div
id={props.id}
draggable={props.draggable}
className={props.className}
onDragStart={dragStart}
onDragOver={dragOver}
>
</div>
)
}
拖动开始
好东西!现在让我们添加这些函数。就在 上方return()
,我们可以从我们的函数开始dragStart
:
const dragStart = e => {
const target = e.target;
e.dataTransfer.setData('card_id', target.id)
}
我们正在声明一个target
将被赋值给 的e.target
(e.target
也就是card
我们将要拖动的 )。接下来,我们将介绍 HTML 拖放 API 的另一个函数:e.dataTransfer.setData('card_id', target.id)
。这里发生的情况是,我们将游标中的数据设置为 ,card_id
并将我们正在拖动的卡片的 ID(target.id
)分配给此引用。
叮叮……💡 还记得e.dataTransfer.getData('card_id')
我们的Board.jsx
组件吗?card
数据在组件中设置Card.jsx
,然后Board.jsx
获取该数据……看到了吗?我告诉过你,这一切都会很顺利。😉
拖拽
我们的最后一个函数……dragOver
这个函数简短明了。我们只需要将它应用于stopPropagation
事件即可。此函数的目的是防止卡片被拖放到其他卡片上。否则,用户很快就会感到困惑!
const dragOver = e => {
e.stopPropagation();
}
最后,不要忘记添加到{ props.children }
,div
就像我们对所做的那样Board.jsx
。
好了!我们已经准备好应用这些组件了。
开演时间
进入你的项目App.js
,然后导入Card.jsx
和Board.jsx
导出src/component
。最后,我们将在网页上显示两个看板和两个卡片。你的项目App.js
应该如下所示:
import React, { Component } from 'react';
import Board from './components/Board.js';
import Card from './components/Card.js';
export default class App extends Component {
render() {
return (
<div className="App">
<main className="flexbox">
<Board id="board-1" className="board">
<Card id="1" className="card" draggable="true">
<p>Card one</p>
</Card>
<Card id="2" className="card" draggable="true">
<p>Card two</p>
</Card>
</Board>
<Board id="board-2" className="board">
<Card id="3" className="card" draggable="true">
<p>Card three</p>
</Card>
<Card id="4" className="card" draggable="true">
<p>Card four</p>
</Card>
</Board>
</main>
</div>
)
}
}
还有一件事你还需要做……在你的代码中应用一些样式,main.css
这样你就可以轻松看到你的组件了。现在这样做应该足够了:
* {
margin: 0; padding: 0; box-sizing: border-box;
}
body {
background-color: #f3f3f3;
}
.flexbox {
display: flex;
justify-content: space-between;
width: 100%;
max-width: 786px;
height: 100vh;
overflow: hidden;
margin: 0 auto;
padding: 15px;
}
.flexbox .board {
display: flex;
flex-direction: column;
width: 100%;
max-width: 300px;
background-color: #313131;
padding: 15px;
}
.flexbox .board .card {
padding: 15px 25px;
background-color: #f3f3f3;
cursor: pointer;
margin-bottom: 15px;
}
加大音量npm start
并玩弄卡片!
结论
作为开发者,我们常常会对那些看起来远比实际复杂得多的流程抱有不好的印象。对我来说,拖放功能听起来比这个方法糟糕得多。虽然还有很多地方可以改进,让体验更加稳定,但希望这能给其他开发者一个公平的评价。:) 祝大家编程愉快!
文章来源:https://dev.to/matthewpalmer9/react-drag-n-drop-47ee