溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶(hù)服務(wù)條款》

怎么用React實(shí)現(xiàn)拖拽調(diào)整大小的組件

發(fā)布時(shí)間:2022-08-26 10:18:39 來(lái)源:億速云 閱讀:318 作者:iii 欄目:開(kāi)發(fā)技術(shù)

這篇文章主要介紹了怎么用React實(shí)現(xiàn)拖拽調(diào)整大小的組件的相關(guān)知識(shí),內(nèi)容詳細(xì)易懂,操作簡(jiǎn)單快捷,具有一定借鑒價(jià)值,相信大家閱讀完這篇怎么用React實(shí)現(xiàn)拖拽調(diào)整大小的組件文章都會(huì)有所收獲,下面我們一起來(lái)看看吧。

一、實(shí)現(xiàn)流程

1.使用React.cloneElement加強(qiáng)包裹組件,在包裹的組件設(shè)置絕對(duì)定位,并在組件內(nèi)加上四個(gè)可調(diào)整大小的拖動(dòng)條,在點(diǎn)擊拖動(dòng)條并進(jìn)行拖動(dòng)時(shí)會(huì)改變DragBox的大小,如下:

2.使用:

<DragBox dragAble={true} minWidth={350} minHeight={184} edgeDistance={[10, 10, 10, 10]} dragCallback={this.dragCallback} >
    {/* 使用DragBox拖動(dòng)組件包裹需要調(diào)整大小的盒子 */}
   <div style={{ top: 100 + 'px', left: 100 + 'px', width: 350, height: 184, backgroundColor: "white" }}>
        <div style={{ backgroundColor: "yellow", width: "100%", height: 30 }}></div>
        <div style={{ backgroundColor: "green", width: "100%", height: 30 }}></div>
        <div style={{ backgroundColor: "blue", width: "100%", height: 30 }}></div>
    </div>
</DragBox>

二、代碼

DragBox組件

import React, { Component, Fragment } from 'react';
import styles from "./DragBox.less";

/**
 * 拖拽的公共組件
 * 接收參數(shù):
 *      dragAble:是否開(kāi)啟拖拽
 *      minWidth:最小調(diào)整寬度
 *      minHeight:最小調(diào)整高度
 *      edgeDistance:數(shù)組,拖拽盒子里瀏覽器上下左右邊緣的距離,如果小于這個(gè)距離就不會(huì)再進(jìn)行調(diào)整寬高
 *      dragCallback:拖拽回調(diào)
 * 
 * 使用:
 *      在DragBox組件放需要實(shí)現(xiàn)拖拽的div,DragBox組件內(nèi)會(huì)設(shè)置position:absolute(React.cloneElement)
 */
class DragBox extends Component {
    constructor(props) {
        super(props);
        // 父組件盒子
        this.containerRef = React.createRef();
        // 是否開(kāi)啟尺寸修改
        this.reSizeAble = false;
        // 鼠標(biāo)按下時(shí)的坐標(biāo),并在修改尺寸時(shí)保存上一個(gè)鼠標(biāo)的位置
        this.clientX, this.clientY;
        // 鼠標(biāo)按下時(shí)的位置,使用n、s、w、e表示
        this.direction = "";
        // 拖拽盒子里瀏覽器上下左右邊緣的距離,如果小于這個(gè)距離就不會(huì)再進(jìn)行調(diào)整寬高
        this.edgeTopDistance = props.edgeDistance[0] || 10;
        this.edgeBottomDistance = props.edgeDistance[1] || 10;
        this.edgeLeftDistance = props.edgeDistance[2] || 10;
        this.edgeRightDistance = props.edgeDistance[3] || 10;
    }

    componentDidMount(){
        // body監(jiān)聽(tīng)移動(dòng)事件
        document.body.addEventListener('mousemove', this.move);
        // 鼠標(biāo)松開(kāi)事件
        document.body.addEventListener('mouseup', this.up);
    }

    /**
     * 清除調(diào)整寬高的監(jiān)聽(tīng)
     */
    clearEventListener() {
        document.body.removeEventListener('mousemove', this.move);
        document.body.removeEventListener('mouseup', this.up);
    }

    componentWillUnmount() {
        this.clearEventListener();
    }

    /**
     * 鼠標(biāo)松開(kāi)時(shí)結(jié)束尺寸修改
     */
    up = () => {
        this.reSizeAble = false;
        this.direction = "";
    }

    /**
     * 鼠標(biāo)按下時(shí)開(kāi)啟尺寸修改
     * @param {*} e 
     * @param {String} direction 記錄點(diǎn)擊上下左右哪個(gè)盒子的標(biāo)識(shí)
     */
    down = (e, direction) => {
        this.direction = direction;
        this.reSizeAble = true;
        this.clientX = e.clientX;
        this.clientY = e.clientY;
    }

    /**
     * 鼠標(biāo)按下事件 監(jiān)聽(tīng)鼠標(biāo)移動(dòng),修改父節(jié)dom位置
     * @param {DocumentEvent} e 事件參數(shù)
     * @param {Boolean} changeLeft 是否需要調(diào)整left
     * @param {Boolean} changeTop 是否需要調(diào)整top
     * @param {Number} delta 調(diào)整位置的距離差
     */
    changeLeftAndTop = (event, changeLeft, changeTop, delta) => {
        let ww = document.documentElement.clientWidth;
        let wh = window.innerHeight;

        if (event.clientY < 0 || event.clientX < 0 || event.clientY > wh || event.clientX > ww) {
            return false;
        }
        if (changeLeft) { 
            this.containerRef.current.style.left = Math.max(this.containerRef.current.offsetLeft + delta, this.edgeLeftDistance) + 'px'; 
        }
        if (changeTop) { 
            this.containerRef.current.style.top = Math.max(this.containerRef.current.offsetTop + delta, this.edgeTopDistance) + 'px'; 
        }
    }

    /**
     * 鼠標(biāo)移動(dòng)事件
     * @param {*} e 
     */
    move = (e) => {
        // 當(dāng)開(kāi)啟尺寸修改時(shí),鼠標(biāo)移動(dòng)會(huì)修改div尺寸
        if (this.reSizeAble) {
            let finalValue;
            // 鼠標(biāo)按下的位置在上部,修改高度
            if (this.direction === "top") {
                // 1.距離上邊緣10 不修改
                // 2.因?yàn)榘粗敳啃薷母叨葧?huì)修改top、height,所以需要判斷e.clientY是否在offsetTop和this.clientY之間(此時(shí)說(shuō)明處于往上移動(dòng)且鼠標(biāo)位置在盒子上邊緣之下),不應(yīng)該移動(dòng)和調(diào)整盒子寬高
                if (e.clientY <= this.edgeTopDistance || (this.containerRef.current.offsetTop < e.clientY && e.clientY  < this.clientY)){ 
                    this.clientY = e.clientY;
                    return; 
                }
                finalValue = Math.max(this.props.minHeight, this.containerRef.current.offsetHeight + (this.clientY - e.clientY));
                // 移動(dòng)的距離,如果移動(dòng)的距離不為0需要調(diào)整高度和top
                let delta = this.containerRef.current.offsetHeight - finalValue;
                if(delta !== 0){
                    this.changeLeftAndTop(e, false, true, delta); 
                    this.containerRef.current.style.height = finalValue + "px";
                }
                this.clientY = e.clientY;
            } else if (this.direction === "bottom") {// 鼠標(biāo)按下的位置在底部,修改高度
                // 1.距離下邊緣10 不修改
                // 2.判斷e.clientY是否處于往下移動(dòng)且鼠標(biāo)位置在盒子下邊緣之上,不應(yīng)該調(diào)整盒子寬高
                if (window.innerHeight - e.clientY <= this.edgeBottomDistance || (this.containerRef.current.offsetTop + this.containerRef.current.offsetHeight > e.clientY && e.clientY  > this.clientY)) { 
                    this.clientY = e.clientY;
                    return; 
                }
                finalValue = Math.max(this.props.minHeight, this.containerRef.current.offsetHeight + (e.clientY - this.clientY));
                this.containerRef.current.style.height = finalValue + "px";
                this.clientY = e.clientY;
            } else if (this.direction === "right") { // 鼠標(biāo)按下的位置在右邊,修改寬度 
                // 1.距離右邊緣10 不修改
                // 2.判斷e.clientY是否處于往右移動(dòng)且鼠標(biāo)位置在盒子右邊緣之左,不應(yīng)該調(diào)整盒子寬高
                if (document.documentElement.clientWidth - e.clientX <= this.edgeRightDistance || (this.containerRef.current.offsetLeft + this.containerRef.current.offsetWidth > e.clientX && e.clientX  > this.clientX)) { 
                    this.clientX = e.clientX;
                    return;
                }
                // 最小為UI設(shè)計(jì)this.props.minWidth,最大為 改邊距離屏幕邊緣-10,其他同此
                let value = this.containerRef.current.offsetWidth + (e.clientX - this.clientX);
                finalValue = step(value, this.props.minWidth, document.body.clientWidth - this.edgeRightDistance - this.containerRef.current.offsetLeft);
                this.containerRef.current.style.width = finalValue + "px";
                this.clientX = e.clientX;
            } else if (this.direction === "left") {// 鼠標(biāo)按下的位置在左邊,修改寬度
                // 1.距離左邊緣10 不修改
                // 2.因?yàn)榘粗敳啃薷母叨葧?huì)修改left、height,所以需要判斷e.clientY是否在offsetLeft和this.clientY之間(此時(shí)說(shuō)明處于往左移動(dòng)且鼠標(biāo)位置在盒子左邊緣之左),不應(yīng)該移動(dòng)和調(diào)整盒子寬高
                if (e.clientX <= this.edgeLeftDistance || (this.containerRef.current.offsetLeft < e.clientX && e.clientX  < this.clientX)) { 
                    this.clientX = e.clientX;
                    return; 
                }
                let value = this.containerRef.current.offsetWidth + (this.clientX - e.clientX);
                finalValue = step(value, this.props.minWidth, this.containerRef.current.offsetWidth - this.edgeLeftDistance + this.containerRef.current.offsetLeft);
                // 移動(dòng)的距離,如果移動(dòng)的距離不為0需要調(diào)整寬度和left
                let delta = this.containerRef.current.offsetWidth - finalValue;
                if(delta !== 0){
                    // 需要修改位置,直接修改寬度只會(huì)向右增加
                    this.changeLeftAndTop(e, true, false, delta); 
                    this.containerRef.current.style.width = finalValue + "px";
                }
                this.clientX = e.clientX;
            }
            this.props.dragCallback && this.props.dragCallback(this.direction, finalValue);
        }
    }

    render() {
        // 四個(gè)紅色盒子 用于鼠標(biāo)移動(dòng)到上面按下進(jìn)行拖動(dòng)
        const children = (
            <Fragment key={"alphaBar"}>
                <div key={1} className={styles.alphaTopBar} onMouseDown={(e) => this.down(e, "top")}></div>
                <div key={2} className={styles.alphaBottomBar} onMouseDown={(e) => this.down(e, "bottom")}></div>
                <div key={3} className={styles.alphaLeftBar} onMouseDown={(e) => this.down(e, "left")}></div>
                <div key={4} className={styles.alphaRightBar} onMouseDown={(e) => this.down(e, "right")}></div>
            </Fragment>
        );

        // 給傳進(jìn)來(lái)的children進(jìn)行加強(qiáng):添加position:"absolute",添加四個(gè)用于拖動(dòng)的透明盒子
        const childrenProps = this.props.children.props;

        const cloneReactElement = React.cloneElement(
            this.props.children,
            {
                style: {
                    // 復(fù)用原來(lái)的樣式
                    ...childrenProps.style,
                    // 添加position:"absolute"
                    position: "absolute"
                },
                ref: this.containerRef
            },
            // 復(fù)用children,添加四個(gè)用于拖動(dòng)的紅色盒子
            [childrenProps.children, children]
        );

        return (
            <Fragment>
                {
                    cloneReactElement
                }
            </Fragment>
        );
    }
}

/**
 * 取最大和最小值之間的值
 * @param {*} value 
 * @param {*} min 
 * @param {*} max 
 * @returns 
 */
function step(value, min, max) {
    if (value < min) {
        return min;
    } else if (value > max) {
        return max;
    } else {
        return value;
    }
}

export default DragBox;

### DragBox組件拖動(dòng)條的樣式

.alphaTopBar{
    position: absolute;
    width: 100%;
    height: 8px;
    top: -5px;
    left: 0;
    background-color: red;
    cursor: row-resize;
  }
  .alphaBottomBar{
    position: absolute;
    width: 100%;
    height: 8px;
    bottom: -5px;
    left: 0;
    background-color: red;
    cursor: row-resize;
  }
  .alphaLeftBar{
    position: absolute;
    width: 8px;
    height: 100%;
    top: 0;
    left: -5px;
    background-color: red;
    cursor: col-resize;
  }
  .alphaRightBar{
    position: absolute;
    width: 8px;
    height: 100%;
    top: 0;
    right: -5px;
    background-color: red;
    cursor: col-resize;
  }

關(guān)于“怎么用React實(shí)現(xiàn)拖拽調(diào)整大小的組件”這篇文章的內(nèi)容就介紹到這里,感謝各位的閱讀!相信大家對(duì)“怎么用React實(shí)現(xiàn)拖拽調(diào)整大小的組件”知識(shí)都有一定的了解,大家如果還想學(xué)習(xí)更多知識(shí),歡迎關(guān)注億速云行業(yè)資訊頻道。

向AI問(wèn)一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI