React 实现拖放功能

介绍

        本篇文章将会使用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;

使用效果

  • 15
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值