您的当前位置:首页React 实现拖拽功能的示例代码

React 实现拖拽功能的示例代码

2020-11-27 来源:乌哈旅游

本文介绍了React 实现拖拽功能的示例代码,分享给大家,具体如下:

实现效果:

因为工作中会用到 JIRA 所以想实现一下相似的功能,顺便学习一下 H5 的拖拽。不支持拖拽改变顺序,感觉有点麻烦,而且没必要。感觉相关的博文好少的,大部分都是直接上代码,没有解释。

图片默认可以拖动,其他元素的拖动效果同图片。正常的 div 是不能被拖动的,鼠标点击选择后移动没有效果,需要加  draggable="true" 使得元素可以被拖动。

拖拽相关的几个事件,有被拖动元素的事件,也有拖动进入的容器元素的事件。 

被拖拽元素的事件:ondragstart,ondragend 

放置元素的事件:ondragenter、ondragover、ondragleave、ondrop 

顾名思义,不需要解释。

需要注意是  ondragover 的默认事件 Reset the current drag operation to "none". 所以想让一个元素可放置,需要重写 ondragover 

element.ondragover = event => { 
 event.preventDefault();
 // ...
}

当一个元素是可放置的,拖拽经过时鼠标会变成加号(cursor: copy;)

有一个对象  dataTransfer 可以用来存储拖拽数据。

dragEle.ondragstart = e => e.dataTransfer.setData('item', e.target.id);
拖拽开始时触发,把被拖拽元素的 id 存入  e.dataTransfer 

然后在 ondrop 的时候 可以获取到这个值 (ondragenter、ondragover、ondragleave 获取不到...)

putEle.ondrop = function(e) {
 let id = e.dataTransfer.getData('item');
 // ...
}
简单的应用:
<!DOCTYPE html>
<html lang="zh">
<head>
 <meta charset="UTF-8">
 <meta name="viewport" content="width=device-width, initial-scale=1.0">
 <meta http-equiv="X-UA-Compatible" content="ie=edge">
 <title>Document</title>
 <style>
 .wrapper {display: flex;border: 1px solid orangered;padding: 10px;}
 .col {border: 1px solid #808080;height: 500px;width: 200px;margin: 0 10px;padding: 10px;}
 .item {border: 1px solid #808080;margin: 5px 0;}
 </style>
</head>
<body>
 <div class="wrapper">
 <div class="col1 col">
 <div class="item" id="item1" draggable="true">item1</div>
 <div class="item" id="item2" draggable="true">item2</div>
 <div class="item" id="item3" draggable="true">item3</div>
 </div>
 <div class="col2 col"></div>
 <div class="col3 col"></div>
 <div class="col4 col"></div>
 </div>
 <script>
 let cols = document.getElementsByClassName('col');
 for (let col of cols) {
 col.ondragenter = e => { 
 console.log('放置元素 ondragenter', '<' + e.dataTransfer.getData('item') + '>'); 
 }
 col.ondragover = e => {
 e.preventDefault();
 console.log('放置元素 ondragover', '<' + e.dataTransfer.getData('item') + '>');
 }
 col.ondragleave = e => { 
 console.log('放置元素 ondragleave', '<' + e.dataTransfer.getData('item') + '>'); 
 }
 col.ondrop = function(e) {
 console.log('放置元素 ondrop', '<' + e.dataTransfer.getData('item') + '>');
 this.append(document.getElementById(e.dataTransfer.getData('item')));
 }
 }
 let items = document.getElementsByClassName('item');
 for (let item of items) {
 item.ondragstart = e => {
 console.log('拖拽元素 ondragstart');
 e.dataTransfer.setData('item', e.target.id);
 }
 item.ondragend = e => {
 console.log('拖拽元素 ondragend');
 }
 }
 </script>
</body>
</html>
文章开头部分的 React 写的 demo
<!DOCTYPE html>
<html>
 <head>
 <meta charset="utf-8">
 <title></title>
 <script src="https://unpkg.com/react@16/umd/react.development.js"></script>
 <script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
 <script src="https://unpkg.com/babel-standalone@6.15.0/babel.min.js"></script>
 <style>
 .item {
 border: 1px solid #1da921;
 width: 180px;
 border-radius: 5px;
 box-shadow: 0 0 5px 0 #b3b3b3;
 margin: 5px auto;
 background: #fff;
 }
 .item.active {
 border-style: dashed;
 }
 .item-header {
 font-size: 12px;
 color: #9e9e9e;
 padding: 3px 5px;
 }
 .item-main {
 padding: 5px;
 font-size: 14px;
 color: #424242;
 height: 36px;
 overflow: hidden;
 text-overflow: ellipsis;
 display: -webkit-box;
 -webkit-box-orient: vertical;
 -webkit-line-clamp: 2;
 }
 .item-header-point {
 background: #ccc;
 float: right;
 padding: 0 4px;
 min-width: 10px;
 text-align: center;
 color: #fff;
 border-radius: 50%;
 }
 .col {
 border: 1px solid #d2d2d2;
 flex-grow: 1;
 width: 180px;
 height: 100%;
 margin: 0 2px;
 background: #eee;
 flex-grow: 1;
 display: flex;
 flex-direction: column;
 }
 .col-header {
 height: 40px;
 line-height: 40px;
 background: #1DA921;
 color: #fff;
 text-align: center;
 }
 .col-main {
 overflow: auto;
 flex-grow: 1;
 }
 .col-main.active {
 background: #00ad23;
 opacity: 0.1;
 }
 .task-wrapper {
 display: flex;
 height: 400px;
 width: 700px;
 }
 </style>
 </head>
 <body>
 <div id="app"></div>
 <script type="text/babel">
 const STATUS_TODO = 'STATUS_TODO';
 const STATUS_DOING = 'STATUS_DOING';
 const STATUS_DONE = 'STATUS_DONE';
 
 const STATUS_CODE = {
 STATUS_TODO: '待处理',
 STATUS_DOING: '进行中',
 STATUS_DONE: '已完成'
 }
 let tasks = [{
 id: 0,
 status: STATUS_TODO,
 title: '每周七天阅读五次,每次阅读完要做100字的读书笔记',
 username: '小夏',
 point: 10
 }, {
 id: 1,
 status: STATUS_TODO,
 title: '每周七天健身4次,每次健身时间需要大于20分钟',
 username: '橘子🍊',
 point: 5
 }, {
 id: 2,
 status: STATUS_TODO,
 title: '单词*100',
 username: '┑( ̄Д  ̄)┍',
 point: 2
 }, {
 id: 3,
 status: STATUS_TODO,
 title: '单词*150',
 username: '┑( ̄Д  ̄)┍',
 point: 2
 }, {
 id: 4,
 status: STATUS_TODO,
 title: '单词*200',
 username: '┑( ̄Д  ̄)┍',
 point: 2
 }, {
 id: 5,
 status: STATUS_TODO,
 title: '单词*250',
 username: '┑( ̄Д  ̄)┍',
 point: 2
 }]
 
 class TaskItem extends React.Component {
 handleDragStart = (e) => {
 this.props.onDragStart(this.props.id);
 }
 render() {
 let { id, title, point, username, active, onDragEnd } = this.props;
 return (
 <div 
 onDragStart={this.handleDragStart}
 onDragEnd={onDragEnd}
 id={`item-${id}`} 
 className={'item' + (active ? ' active' : '')}
 draggable="true"
 >
 <header className="item-header">
 <span className="item-header-username">{username}</span>
 <span className="item-header-point">{point}</span>
 </header>
 <main className="item-main">{title}</main>
 </div>
 );
 }
 }
 
 class TaskCol extends React.Component {
 state = {
 in: false
 }
 handleDragEnter = (e) => {
 e.preventDefault();
 if (this.props.canDragIn) {
 this.setState({
 in: true
 })
 }
 }
 handleDragLeave = (e) => {
 e.preventDefault();
 if (this.props.canDragIn) {
 this.setState({
 in: false
 })
 }
 }
 handleDrop = (e) => {
 e.preventDefault();
 this.props.dragTo(this.props.status);
 this.setState({
 in: false
 })
 }
 render() {
 let { status, children } = this.props;
 return (
 <div 
 id={`col-${status}`} 
 className={'col'}
 onDragEnter={this.handleDragEnter}
 onDragLeave={this.handleDragLeave}
 onDragOver={this.handleDragEnter}
 onDrop={this.handleDrop}
 draggable="true"
 >
 <header className="col-header">
 {STATUS_CODE[status]}
 </header>
 <main className={'col-main' + (this.state.in ? ' active' : '')}>
 {children}
 </main>
 </div>
 );
 }
 }
 
 class App extends React.Component {
 state = {
 tasks: tasks,
 activeId: null
 }
 /**
 * 传入被拖拽任务项的 id
 */
 onDragStart = (id) => {
 this.setState({
 activeId: id
 })
 }
 
 dragTo = (status) => {
 let { tasks, activeId} = this.state;
 let task = tasks[activeId];
 if (task.status !== status) {
 task.status = status;
 this.setState({
 tasks: tasks
 })
 }
 this.cancelSelect();
 }
 
 cancelSelect = () => {
 this.setState({
 activeId: null
 })
 }
 
 render() {
 let { tasks, activeId } = this.state;
 let { onDragStart, onDragEnd, cancelSelect } = this;
 return (
 <div className="task-wrapper">
 {
 Object.keys(STATUS_CODE).map(status => 
 <TaskCol 
 status={status} 
 key={status} 
 dragTo={this.dragTo}
 canDragIn={activeId != null && tasks[activeId].status !== status}>
 { tasks.filter(t => t.status === status).map(t => 
 <TaskItem
 key={t.id}
 active={t.id === activeId}
 id={t.id}
 title={t.title} 
 point={t.point} 
 username={t.username}
 onDragStart={onDragStart}
 onDragEnd={cancelSelect}
 />)
 }
 </TaskCol>
 )
 }
 </div>
 )
 }
 }
 
 ReactDOM.render(
 <App />,
 document.getElementById('app')
 );
 </script>
 </body>
</html>
显示全文