react里实现左右拉伸实战

封装组件:

我自己写的一个简单的组件,可能有bug。不想自己写,建议用第三方库实现。

新建一个resizeBox.tsx文件写上代码如下:

import React, {
    
     ReactNode, useState, useEffect, useRef } from 'react';
import styles from "./resizeBox.less";
interface ResizableBoxProps {
    
    
    /**
     * 盒子的宽度
     */
    widthNum?: number;
    /**
    * 盒子的高度
    */
    heightNum?: number;
    /**
     * 内容
     */
    content?: string | ReactNode;
    /**
     * 可以传入自己的类名
     */
    className: string;
    /** 左右是否可以拉伸 左边框拉伸 默认为true*/
    isLeftFlex: boolean;
    /** 上下是否可以拉伸  右边框拉伸 默认为true*/
    isBottomFlex: boolean;
}
const ResizableBox: React.FC<ResizableBoxProps> = ({
     
     
    widthNum,
    heightNum,
    content,
    children,
    className,
    isLeftFlex = true,
    isBottomFlex = true,
    ...props
}) => {
    
    
    const boxRef = useRef<HTMLDivElement>(null);
    // 根据优先级选择要渲染的标题
    const contentRender = content || children;
    // 定义状态变量,使用useState
    const [width, setWidth] = useState(widthNum); // 宽度
    const [height, setHeight] = useState(heightNum); // 高度
    const [startX, setStartX] = useState(0); // 鼠标点击时的起始X坐标
    const [startY, setStartY] = useState(0); // 鼠标点击时的起始Y坐标
    const [initialWidth, setInitialWidth] = useState(0); // 调整大小前的初始宽度
    const [initialHeight, setInitialHeight] = useState(0); // 调整大小前的初始高度
    const [isResizing, setIsResizing] = useState(false); // 是否正在调整大小

    useEffect(() => {
    
    
        if (boxRef?.current) {
    
    
            let boxWidth = boxRef?.current?.clientWidth;
            let boxHeight = boxRef?.current?.clientHeight;
            console.log(boxRef, boxRef?.current?.clientWidth, "890")
            setWidth(boxWidth);
            setHeight(boxHeight);
        }

    }, []);
    /**开始调整大小时的事件处理函数 高度*/
    const startResizeY = (event: any) => {
    
    
        event.preventDefault();
        const {
    
     clientX, clientY } = event;
        setStartY(clientY);
        setInitialHeight(height);
        setIsResizing(true);
        document.documentElement.addEventListener('mousemove', resizeY);
        document.documentElement.addEventListener('mouseup', stopResize);
    };

    /**开始调整大小时的事件处理函数 宽度*/
    const startResizeX = (event: any) => {
    
    
        event.preventDefault();
        const {
    
     clientX, clientY } = event;
        setStartX(clientX);
        setInitialWidth(width);
        setIsResizing(true);
        document.documentElement.addEventListener('mousemove', resizeX);
        document.documentElement.addEventListener('mouseup', stopResize);
    };
    /** 宽度调整大小 */
    const resizeX = (event: any) => {
    
    
        const {
    
     clientX } = event;
        const deltaX = clientX - startX;
        const newWidth = initialWidth + deltaX;
        setWidth(newWidth);
    };
    /**高度调整大小 */
    const resizeY = (event: any) => {
    
    
        const {
    
     clientY } = event;
        const deltaY = clientY - startY;
        const newHeight = initialHeight + deltaY;
        setHeight(newHeight);
    };
    // 停止调整大小时的事件处理函数
    const stopResize = () => {
    
    
        setIsResizing(false);
        document.documentElement.removeEventListener('mousemove', resizeX);
        document.documentElement.removeEventListener('mousemove', resizeY);
        document.documentElement.removeEventListener('mouseup', stopResize);
    };
    // useEffect用于设置鼠标样式
    useEffect(() => {
    
    
        if (isResizing) {
    
    
            // document.documentElement.style.cursor = 'nwse-resize';
        } else {
    
    
            document.documentElement.style.cursor = 'default';
        }
    }, [isResizing]);
    // 返回可调整大小的组件
    return (
        <div
            className={
    
    `${
      
      className} ${
      
      styles.resizable_box}`}
            style={
    
    {
    
     width: !isLeftFlex ? "100%" : `${
      
      width}px`, height: !isBottomFlex ? "100%" : `${
      
      height}px` }} // 使用状态变量控制宽度和高度
            ref={
    
    boxRef}
        >
            //<div className={styles.container}>
                {
    
    contentRender}
            //</div>
            {
    
    
                isLeftFlex && <div className={
    
    `${
      
      styles.button} ${
      
      styles.right_button}`} onMouseDown={
    
    startResizeX}></div>
            }
            {
    
    
                isBottomFlex && <div className={
    
    `${
      
      styles.button} ${
      
      styles.bottom_button}`} onMouseDown={
    
    startResizeY}></div>
            }

        </div>
    );
};
export default ResizableBox;

新建一个resizeBox.less:

这里我用的是style Module 如果你不用这个请自行转换语法。只需要把resizeBox.tsx里的styles.去掉。并且直接引入less即可。

.resizable_box {
    
    
    position: relative;
}

.container {
    
    
    width: 100%;
    height: 100%;
}

// .button {
    
    
//     width: 8px;
//     height: 8px;
//     background-color: #f00;
//     /* cursor: pointer; */
//     position: absolute;
// }

.button {
    
    
    // width: 2px;
    // height: 100%;
    background: none;
    position: absolute;
}

.right_button {
    
    
    width: 2px;
    height: 100%;
    right: -2px;
    top: 0;
    // top: 50%;
    // transform: translateY(-50%);
    cursor: e-resize;
    background: blue;
}

.bottom_button {
    
    
    width: 100%;
    height: 2px;
    bottom: -2px;
    // left: 50%;
    // transform: translateX(-50%);
    cursor: n-resize;
    background: blue;
}

组件使用文档:

interface ResizableBoxProps {
    
    
    /**
     * 盒子的宽度
     */
    widthNum?: number;
    /**
    * 盒子的高度
    */
    heightNum?: number;
    /**
     * 内容
     */
    content?: string | ReactNode;
    /**
     * 可以传入自己的类名
     */
    className?: string;
    /** 左右是否可以拉伸 左边框拉伸 默认为true*/
    isLeftFlex?: boolean;
    /** 上下是否可以拉伸  右边框拉伸 默认为true*/
    isBottomFlex?: boolean;
}
参数名 类型 描述
widthNum number (可选) 盒子的宽度
heightNum number (可选) 盒子的高度
content `string ReactNode` (可选)
className string (可选) 自定义的类名
isLeftFlex boolean (可选) 左右是否可以拉伸,左边框拉伸,默认为 true
isBottomFlex boolean (可选) 上下是否可以拉伸,底边框拉伸,默认为 true

实际用法:

左右拉伸,左边拉伸右边跟着变动:

这里使用了flex巧妙的实现了这个效果,左边div设置一个宽度,右边的flex:1即可。

左右设置width

import React, {
    
     useEffect, useState, useRef } from "react";
import styles from "./index.less";
import ResizeBox from "./resizeBox";
const EtfManager: React.FC = () => {
    
    
    // const myRef = useRef(null);
    return (
        <div style={
    
    {
    
     marginTop: 40, background: "#fff", height: 800, display: "flex",flexDirection: "row"}}>
            <ResizeBox className={
    
    styles.left}>
                <div>

                </div>
            </ResizeBox>

            <div className={
    
    styles.right}>

            </div>
        </div>
    );
};
export default EtfManager;

index.less:

.left {
    
    
    width: 40%;
    background: red;
    height: 100%;
}

.right {
    
    
    flex: 1;
    width: 200px;
    background: green;
    height: 100%;
}

效果图如下:
鼠标放到蓝色的线上即可拖动。
左右效果图

上下拉伸,上边拉伸下边跟着变动:

上下设置height,且 flex-direction:column 设置纵向布局。

import React, {
    
     useEffect, useState, useRef } from "react";
import styles from "./index.less";
import ResizeBox from "./resizeBox";
const EtfManager: React.FC = () => {
    
    
    // const myRef = useRef(null);
    return (
        <div style={
    
    {
    
     marginTop: 40, background: "#fff", height: 800, display: "flex",flexDirection: "column"}}>
            <ResizeBox className={
    
    styles.left}>
                <div>

                </div>
            </ResizeBox>

            <div className={
    
    styles.right}>

            </div>
        </div>
    );
};
export default EtfManager;

index.less:

.left{
    
    
    width:100%;
    background: red;
    height: 50%;
  }
  .right{
    
    
    flex: 1;
    background: green;
    width: 100%;
  }

上下效果图

其他用法 可以自行拓展和嵌套 resizeBox组件使用:

其他用法可以自行拓展组件和嵌套resizeBox组件使用。我只是提供一个思路。
比如 左右拉伸 和上下拉伸组合:

import React, {
    
     useEffect, useState, useRef } from "react";
import styles from "./index.less";
import ResizeBox from "./resizeBox";
const EtfManager: React.FC = () => {
    
    
    // const myRef = useRef(null);
    return (

        <div style={
    
    {
    
     display: "flex", marginTop: 40, background: "#fff", height: 800, width: "100%" }}>
            <ResizeBox className={
    
    styles.vv}>
                <ResizeBox className={
    
    styles.left} isLeftFlex={
    
    false}>
                    <div>

                    </div>
                </ResizeBox>

                <div className={
    
    styles.right}>

                </div>
            </ResizeBox>
            <div style={
    
    {
    
     flex: 1 }}>888</div>

        </div>
    );
};
export default EtfManager;

less:

.left {
    
    
    width: 100%;
    background: red;
    height: 50%;
}

.right {
    
    
    flex: 1;
    background: green;
    width: 100%;
}

.vv {
    
    
    display: flex;
    flex-direction: column;
    width: 30%;
    border: 1px solid yellow;
}

左边上下拉,黄色的线左右拉。
在这里插入图片描述

盒子带滚动条就需要 动态加上或减去滚动的高度

resizeBox.tsx使用以下代码即可:




import React, {
    
     ReactNode, useState, useEffect, useRef } from 'react';
import styles from "./resizeBox.less";
interface ResizableBoxProps {
    
    
    /**
     * 盒子的宽度
     */
    widthNum?: number;
    /**
    * 盒子的高度
    */
    heightNum?: number;
    /**
     * 内容
     */
    content?: string | ReactNode;
    /**
     * 可以传入自己的类名
     */
    className?: string;
    /** 左右是否可以拉伸 左边框拉伸 默认为true*/
    isLeftFlex?: boolean;
    /** 上下是否可以拉伸  右边框拉伸 默认为true*/
    isBottomFlex?: boolean;
}
const ResizableBox: React.FC<ResizableBoxProps> = ({
     
     
    widthNum,
    heightNum,
    content,
    children,
    className,
    isLeftFlex = true,
    isBottomFlex = true,
    ...props
}) => {
    
    
    const boxRef = useRef<HTMLDivElement>(null);
    // 根据优先级选择要渲染的标题
    const contentRender = content || children;
    // 定义状态变量,使用useState
    const [width, setWidth] = useState(widthNum); // 宽度
    const [height, setHeight] = useState(heightNum); // 高度
    const [startX, setStartX] = useState(0); // 鼠标点击时的起始X坐标
    const [startY, setStartY] = useState(0); // 鼠标点击时的起始Y坐标
    const [initialWidth, setInitialWidth] = useState(0); // 调整大小前的初始宽度
    const [initialHeight, setInitialHeight] = useState(0); // 调整大小前的初始高度
    const [isResizing, setIsResizing] = useState(false); // 是否正在调整大小
    const [xMoveing, setXMoveing] = useState(false); // 是否正在调整大小
    const getScrollTop = () => {
    
    
        var scrollTop = 0;
        if (typeof window.pageYOffset === "number") {
    
    
            // 支持 pageYOffset 属性(IE9+,最新浏览器)
            scrollTop = window.pageYOffset;
        } else if (
            document.documentElement &&
            document.documentElement.scrollTop
        ) {
    
    
            // 支持 document.documentElement.scrollTop 属性(IE8+)
            scrollTop = document.documentElement.scrollTop;
        } else if (document.body && document.body.scrollTop) {
    
    
            // 支持 document.body.scrollTop 属性(IE6, IE7)
            scrollTop = document.body.scrollTop;
        }
        return scrollTop;
    };
    const getScrollLeft = () => {
    
    
        let scrollLeft = 0;
        if (typeof window.pageXOffset === "number") {
    
    
            // 支持 pageXOffset 属性(IE9+,最新浏览器)
            scrollLeft = window.pageXOffset;
        } else if (
            document.documentElement &&
            document.documentElement.scrollLeft
        ) {
    
    
            // 支持 document.documentElement.scrollLeft 属性(IE8+)
            scrollLeft = document.documentElement.scrollLeft;
        } else if (document.body && document.body.scrollLeft) {
    
    
            // 支持 document.body.scrollLeft 属性(IE6, IE7)
            scrollLeft = document.body.scrollLeft;
        }
        return scrollLeft;
    };
    // 使用示例
    useEffect(() => {
    
    
        if (boxRef?.current) {
    
    
            let boxWidth = boxRef?.current?.clientWidth;
            let boxHeight = boxRef?.current?.clientHeight;
            // 获取元素位置信息
            let boxClientRect = boxRef?.current?.getBoundingClientRect();
            let boxLeft = boxClientRect?.left;
            let boxTop = boxClientRect?.top;
            setWidth(boxWidth);
            setHeight(boxHeight);
            setStartX(boxLeft);
            setStartY(boxTop);
        }
        const getScrollTop = () => {
    
    
            var scrollTop = 0;
            if (typeof window.pageYOffset === "number") {
    
    
                // 支持 pageYOffset 属性(IE9+,最新浏览器)
                scrollTop = window.pageYOffset;
            } else if (
                document.documentElement &&
                document.documentElement.scrollTop
            ) {
    
    
                // 支持 document.documentElement.scrollTop 属性(IE8+)
                scrollTop = document.documentElement.scrollTop;
            } else if (document.body && document.body.scrollTop) {
    
    
                // 支持 document.body.scrollTop 属性(IE6, IE7)
                scrollTop = document.body.scrollTop;
            }
            return scrollTop;
        };
    }, []);
    /**开始调整大小时的事件处理函数 高度*/
    const startResizeY = (event: any) => {
    
    
        event.preventDefault();
        const {
    
     clientX, clientY } = event;
        setStartY(clientY + getScrollTop());
        setInitialHeight(height);
        setIsResizing(true);
        document.documentElement.addEventListener('mousemove', resizeY);
        document.documentElement.addEventListener('mouseup', stopResize);
    };


    /**开始调整大小时的事件处理函数 宽度*/
    const startResizeX = (event: any) => {
    
    
        event.preventDefault();
        const {
    
     clientX, clientY } = event;
        setStartX(clientX + getScrollLeft());
        setInitialWidth(width);
        setIsResizing(true);
        document.documentElement.addEventListener('mousemove', resizeX);
        document.documentElement.addEventListener('mouseup', stopResize);
    };
    /** 宽度调整大小 */
    const resizeX = (event: any) => {
    
    
        const {
    
     clientX } = event;
        const deltaX = clientX - startX;
        const newWidth = initialWidth + deltaX + getScrollLeft();
        setWidth(newWidth);
        setXMoveing(true);
    };
    /**高度调整大小 */
    const resizeY = (event: any) => {
    
    
        const {
    
     clientY } = event;
        const deltaY = clientY - startY;
        const newHeight = initialHeight + deltaY + getScrollTop();
        console.log(newHeight, getScrollTop(), initialHeight, "newHeight");
        setHeight(newHeight);
    };
    // 停止调整大小时的事件处理函数
    const stopResize = () => {
    
    
        setIsResizing(false);
        document.documentElement.removeEventListener('mousemove', resizeX);
        document.documentElement.removeEventListener('mousemove', resizeY);
        document.documentElement.removeEventListener('mouseup', stopResize);
    };
    // useEffect用于设置鼠标样式
    useEffect(() => {
    
    
        if (isResizing) {
    
    
            // document.documentElement.style.cursor = 'nwse-resize';
        } else {
    
    
            document.documentElement.style.cursor = 'default';
        }
    }, [isResizing]);
    // 返回可调整大小的组件
    const xMove = (event: any) => {
    
    
        if (isResizing) {
    
    
            event.preventDefault();
            const {
    
     clientX, clientY } = event;
            setStartX(clientX);
            setInitialWidth(width);
            resizeX(event);
        }
    }
    return (
        <div
            className={
    
    `${
      
      className} ${
      
      styles.resizable_box}`}
            style={
    
    {
    
     width: !isLeftFlex ? "100%" : `${
      
      width}px`, height: !isBottomFlex ? "100%" : `${
      
      height}px` }} // 使用状态变量控制宽度和高度
            ref={
    
    boxRef}
        >
            <div className={
    
    styles.container}>
                {
    
    contentRender}
            </div>
            {
    
    
                isLeftFlex && <div className={
    
    `${
      
      styles.button} ${
      
      styles.right_button}`} onMouseDown={
    
    startResizeX}></div>
            }
            {
    
    
                isBottomFlex && <div className={
    
    `${
      
      styles.button} ${
      
      styles.bottom_button}`} onMouseDown={
    
    startResizeY}></div>
            }

        </div>
    );
};
export default ResizableBox;

优化和改进:

因为 拖动的框不明显可以 给其加个 hover效果,鼠标移到.bottom_button、.right_button 加个hover效果即可。
或者 cursor 可以改成col-resize、row-resize

猜你喜欢

转载自blog.csdn.net/weixin_44058725/article/details/143054072