当前位置: 首页 > article >正文

react里实现左右拉伸实战

封装组件:

新建一个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: `${width}px`, height: `${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;
}
参数名类型描述
widthNumnumber (可选)盒子的宽度
heightNumnumber (可选)盒子的高度
content`stringReactNode` (可选)
classNamestring (可选)自定义的类名
isLeftFlexboolean (可选)左右是否可以拉伸,左边框拉伸,默认为 true
isBottomFlexboolean (可选)上下是否可以拉伸,底边框拉伸,默认为 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组件使用。我只是提供一个思路。

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

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: `${width}px`, height: `${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;


http://www.kler.cn/news/355680.html

相关文章:

  • YOLO11改进 | 注意力机制 | 添加双重注意力机制 DoubleAttention【附代码+小白必备】
  • 86.#include预处理命令(1)
  • 【最新华为OD机试E卷-支持在线评测】VLAN资源池(100分)多语言题解-(Python/C/JavaScript/Java/Cpp)
  • C# 实操高并发分布式缓存解决方案
  • Git中Update和Pull的区别
  • H.264 编码参数优化策略
  • 时序数据库 TDengine 支持集成开源的物联网平台 ThingsBoard
  • 计算机前沿技术-人工智能算法-大语言模型-最新研究进展-2024-10-17
  • 【计算机网络 - 基础问题】每日 3 题(四十八)
  • 简单说说 spring是如何实现AOP的(源码分析)
  • try increasing the minimum deployment target IOS
  • Trimble三维激光扫描开启工业元宇宙的安全“智造”之路-沪敖3D
  • 【PyTorch][chapter30][transformer-3]
  • Apache SeaTunnel 介绍
  • 用 Git Stash 临时保存修改,轻松切换任务!
  • 安科瑞智慧能源管理系统EMS3.0在浙江某能源集团有限公司的应用
  • Web3与传统互联网的区别
  • mqtt单次订阅多个主题
  • LeetCode146. LRU 缓存(2024秋季每日一题 37)
  • Centos7 安装升级最新版Redis7.4.1