介绍
本篇文章将会使用react实现简单拖放功能。
样例
布局侧边栏拖放
LayoutResize.js
import React, {useState} from "react";
import { Button } from "antd";
import "./LayoutResize.css";
export const LayoutResize = () => {
const [state,setState] = useState({
dragging: false,
startPageX: 0,
siderWidth: 150,
})
// 鼠标点击事件
const handleMouseDown = evt => {
setState({
...state,
dragging: true,
startPageX: evt.pageX,
});
};
// 鼠标抬起事件
const handleMouseUp = () => {
setState({
...state,
dragging: false,
});
};
// 鼠标移动事件
const handleMouseMove = evt => {
if (!state.dragging){ // 没有拖拽直接返回
return;
}
console.log('move')
let siderWidth = state.siderWidth + evt.pageX - state.startPageX;
if (siderWidth < 20 || siderWidth > 300) {
return;
}
setState({
...state,
siderWidth,
startPageX: evt.pageX,
});
};
const pxWidth = `${state.siderWidth}px`;
return (
<div className="app-layout-resize" style={{ paddingLeft: pxWidth }}>
<div className="header">Header</div>
<div className="sider" style={{ width: pxWidth }}>
Sider
</div>
<div className="content" style={{ left: pxWidth }}>
Content
</div>
<div className="footer" style={{ left: pxWidth }}>
Footer
</div>
<div
className="sider-resizer"
style={{ left: pxWidth }}
onMouseDown={handleMouseDown}
/>
{/*遮盖层,鼠标可以在整个区域移动,避免移出去失效*/}
{state.dragging && (
<div
className="resize-mask"
onMouseMove={handleMouseMove}
onMouseUp={handleMouseUp}
/>
)}
</div>
);
}
LayOutResize组件是一个实现侧边栏拖放功能得布局组件。组件由左侧的sider,右侧的header,content,header,以及透明的sider-resizer。
sider-resizer做到可以滑动,基于onMouseDown,onMouseMove,onMouseup方法实现,动态修改左侧sider的大小来实现。
LayoutResize.css
.app-layout-resize {
width: 500px;
height: 400px;
position: relative;
background-color: #eee;
text-align: center;
padding-left: 150px;
line-height: 60px;
}
.app-layout-resize .header {
border-bottom: 2px solid #fff;
}
.app-layout-resize .content {
position: absolute;
bottom: 60px;
top: 60px;
left: 0;
right: 0;
}
.app-layout-resize .sider {
width: 150px;
position: absolute;
border-right: 2px solid #fff;
top: 0;
left: 0;
bottom: 0;
}
.app-layout-resize .footer {
border-top: 2px solid #fff;
bottom: 0;
left: 150px;
right: 0;
position: absolute;
}
.app-layout-resize .sider-resizer {
position: absolute;
left: 148px;
width: 6px;
top: 0;
bottom: 0;
cursor: col-resize;
}
.app-layout-resize .resize-mask {
background: rgba(0, 0, 0, 0);
position: fixed;
left: 0;
top: 0;
right: 0;
bottom: 0;
cursor: col-resize;
}
实现效果
列表元素拖放
Dnd.js
import {useState} from "react";
import './Dnd.css'
export const Dnt = ({list}) =>{
// 组件状态
const [state, setState] = useState({
list: list,
dragging: false,
draggingIdx: null,
startPageY: 0
});
// 元素行高
const lineHeight = 42;
// 交換列表元素
const switchItem =(list, fromIdx, toIdx) =>{
const a = list[fromIdx];
const b = list[toIdx];
list[fromIdx] = b;
list[toIdx] = a;
return list
}
const handleMouseDown = (evt, idx) =>{
setState({
...state,
dragging: true,
draggingIdx: idx,
startPageY:evt.pageY
});
}
const handleMouseUp = ()=>{
setState({
...state,
dragging: false,
startPageY: null,
draggingIdx: null
});
}
const handleMouseMove = (evt)=>{
// 偏移地址
const offset = evt.pageY - state.startPageY;
// 拖拽元素
const draggingIndex = state.draggingIdx;
if ( state.draggingIdx + 1 < state.list.length && offset > lineHeight){
console.log('down')
// move down
setState({
...state,
draggingIdx: state.draggingIdx + 1,
startPageY: state.startPageY + lineHeight,
list: switchItem(state.list, draggingIndex, draggingIndex + 1)
})
return;
}else if (state.draggingIdx > 0 && offset < lineHeight * -1 ){
// move up
setState({
...state,
draggingIdx: state.draggingIdx - 1,
startPageY: state.startPageY - lineHeight,
list: switchItem(state.list, draggingIndex, draggingIndex - 1)
})
}
}
const getDraggingStyle = (idx)=> {
if (idx === state.draggingIdx){
return {
backgroundColor: "#eee",
opacity: 0.5,
};
}else{
return {}
}
}
return <div className="dnd-sample">
<ul>
{
state.list.map(
(txt,idx)=> <li style={getDraggingStyle(idx)} key = {txt} onMouseDown={(event => handleMouseDown(event, idx))} >{txt}</li>
)
}
</ul>
{state.dragging && (<div className='dnd-sample-mask' onMouseMove={(event => handleMouseMove(event))} onMouseUp={(event => handleMouseUp())}></div>)}
</div>;
}
export default Dnt;
Dnd.css
.dnd-sample ul {
display: inline-block;
margin: 0;
padding: 0;
background-color: #eee;
}
.dnd-sample li {
cursor: default;
list-style: none;
border-bottom: 1px solid #ddd;
padding: 10px;
margin: 0;
width: 300px;
background-color: #fff;
}
.dnd-sample-mask {
position: fixed;
left: 0;
right: 0;
top: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.1);
}
app.js
import './App.css';
import Dnd from "./component/dnd/Dnd";
const App = ()=> {
const list = Array.of('item1','item2','item3','item4','item5','item6','item7','item8','item9');
return <Dnd list = {list}/>
}
export default App;