react里实现左右拉伸实战

news2024/11/26 13:44:27

封装组件:

新建一个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.coloradmin.cn/o/2219720.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

比较相同机器上 redis和mysql分别单独承载的 最大连接数量

在相同的机器上&#xff0c;Redis 和 MySQL 的最大连接数量会受到硬件配置&#xff08;如 CPU、内存、网络等&#xff09;、配置参数和应用场景的影响。以下是对 Redis 和 MySQL 在单机环境下最大连接数的比较&#xff1a; Redis 最大连接数量 默认配置&#xff1a; Redis 默…

【2024最新版】网络安全学习路线-适合入门小白

首先说明&#xff0c;我是一名CTF的web手&#xff0c;这是我自己亲身学习网络安全的路线&#xff0c;希望能够帮到大家&#xff0c;我虽然不是大牛&#xff0c;但我也希望能够帮助一些网安小白找到自己学习的方向&#xff0c;后面有就业的详细安全技术要求&#xff0c;如果真想…

yolov8实例分隔

1.查看显卡型号 2.在https://en.wikipedia.org/wiki/CUDA上查看显卡算力&#xff0c;这里显卡为1650&#xff0c;算力为7.5 3.查看显卡算力对应的cuda版本 4slurm上该怎么办&#xff1f; 查看slurm上计算节点cuda版本 查看cuda版本 srun -A 2022099 -J job1 -p Gnode --…

【Echarts 实战指南】解锁动态历史曲线之谜

在工作中&#xff0c;大家是否曾遇到过这样一种需求呢&#xff1f;需获取设备最近 10 分钟的历史数据。设备实时数据每 2 秒推送一次&#xff0c;且要把历史数据曲线变成动态变化的状态。倘若设备最近 10 分钟的历史数据为 20 个点&#xff0c;那么现在每 2 秒就要将最前面的点…

Java爬虫:获取直播带货数据的实战指南

在当今数字化时代&#xff0c;直播带货已成为电商领域的新热点&#xff0c;通过直播平台展示商品并进行销售&#xff0c;有效促进了产品的曝光和销售量的提升。然而&#xff0c;如何在直播带货过程中进行数据分析和评估效果&#xff0c;成为了摆在商家面前的一个重要问题。本文…

工业相机有哪些应用场景

工业相机具有高性能、高稳定性和高可靠性的特点&#xff0c;因此在众多工业领域都有广泛的应用场景。以下是朗观视觉小编总结的一些典型的应用场景&#xff1a; 机器视觉与自动化&#xff1a; 工业相机在机器视觉系统中起着核心作用&#xff0c;用于捕捉和分析物体的图像&#…

【Linux】从多线程同步到生产者消费者模型:多线程编程实践

目录 1.线程的同步 1.1.为什么需要线程的同步&#xff1f; 2.2.条件变量的接口函数 2.生产消费模型 2.1 什么是生产消费模型 2.2.生产者消费者模型优点 2.3.为何要使用生产者消费者模型 3.基于BlockingQueue的生产者消费者模型 3.1为什么要将if判断变成while&#xff…

API的力量:解决编程技术问题的利器

在软件开发的世界里&#xff0c;编程技术问题无处不在。从数据获取到用户认证&#xff0c;从支付处理到地图服务&#xff0c;这些问题的解决方案往往需要深厚的专业知识和大量的开发时间。然而&#xff0c;应用程序编程接口&#xff08;API&#xff09;的出现&#xff0c;为开发…

架构师备考-背诵精华(系统架构设计)

软件架构风格 类型 子类型 说明 数据流风格 批处理 每个处理步骤是一个单独的程序&#xff0c;每一步必须在前一步结束后才能开始&#xff0c;而且数据必须是完整的&#xff0c;以整体的方式传递。 前面的构件处理完&#xff0c;后面构件才能处理&#xff1b;数据完整传输…

(五)若使用LQR控制小车倒立摆,该如何对小车和摆杆的动力学方程线性化?哪些变量是可以进行简化的,线性化后的状态空间方程应该怎么列写

写在前面&#xff1a; 关于lqr控制的讲解&#xff0c;可以观看如下三个视频&#xff1a; 2. LQR数学公式理解_哔哩哔哩_bilibili 如何感性地理解LQR控制&#xff1f;_哔哩哔哩_bilibili LQR简介与使用_哔哩哔哩_bilibili 正文&#xff1a; 在之前系列的文章中我们已经得出…

scala 抽象类

理解抽象类 抽象的定义 定义一个抽象类 &#xff1a;abstract class A {} idea实例 抽象类重写 idea实例 练习 1.abstract2.错3.abstract class A{}4.对

Redis应用高频面试题

Redis 作为一个高性能的分布式缓存系统,广泛应用于后端开发中,因此在后端研发面试中,关于 Redis 的问题十分常见。 本文整理了30个常见的 Redis 面试题目,涵盖了 Redis 的源码、数据结构、原理、集群模式等方面的知识,并附上简要的回答,帮助大家更好地准备相关的面试。 …

web前端--html 5---qq注册

<!DOCTYPE html> <html lang"en"> <head> <meta charset"UTF-8"> <meta name"viewport" content"widthdevice-width, initial-scale1.0"> <title>qq注册</title> <link rel"impo…

图像识别解决方案

图像识别解决方案是一种基于人工智能技术的图像处理和识别方法&#xff0c;能够实现对图像内容的自动分析和理解。以下是朗观视觉小编对图像识别解决方案的详细阐述&#xff1a; 一、技术原理 图像识别解决方案的核心原理是机器学习算法和深度学习网络。通过收集大量的图像数据…

nnUnet 大模型学习笔记(续):3d_fullres 模型的推理、切片推理、计算dice系数

目录 1. 前言 2. 更改epochs 3. 推理 3.1 nnUNet_predict 3.2 切成小的nii gz文件推理 切片代码 融合代码 3.3 可视化展示 3.4 评估指标 参考 1. 前言 训练了一天半&#xff0c;终于跑完了。。。。 训练的模型在这可以免费下载&#xff1a; 基于nnUnet3d-fullres训…

深⼊理解指针(2)

目录 1. 数组名的理解 2. 使⽤指针访问数组 3. ⼀维数组传参的本质 4. ⼆级指针 5. 指针数组 6. 指针数组模拟⼆维数组 1. 数组名的理解 我们在使⽤指针访问数组的内容时&#xff0c;有这样的代码&#xff1a; int arr[10] {1,2,3,4,5,6,7,8,9,10}; int *p &arr[…

rancher安装并快速部署k8s 管理集群工具

主机准备 准备4台主机 3台用于k8s集群 &#xff0c;1台用于rancher 每台服务器新增配置文件 vi etc/sysctl.confnet.ipv4.ip_forward 1 刷新生效 sysctl –p 安装docker 安装的时候可以去github上检索rancher看看最新版本适配那个版本的docker&#xff0c;这里安装23.0.1…

Linux 线程概念及线程控制

1.线程与进程的关系 执行流&#xff08;Execution Flow&#xff09;通常指的是程序执行过程中的控制路径&#xff0c;它描述了程序从开始到结束的指令执行顺序。例如我们要有两个执行流来分别进行加法和减法的运算&#xff0c;我们可以通过使用 fork 函数来创建子进程&#xf…

智慧商城项目2-登录模块

登录页静态布局 1.先重置默认样式 找到styles/common.less文件,没有就新建 // 重置默认样式 * {margin: 0;padding: 0;box-sizing: border-box;}// 文字溢出省略号.text-ellipsis-2 {overflow: hidden;-webkit-line-clamp: 2;text-overflow: ellipsis;display: -webkit-box;-…

CentOS7安装RabbitMQ-3.13.7、修改端口号

本文安装版本&#xff1a; Erlang&#xff1a;26.0 官网下载地址 Erlang RabbitMQ&#xff1a;3.13.7 官网下载地址 RabbitMQ RabbitMQ和Erlang对应关系查看&#xff1a;https://www.rabbitmq.com/which-erlang.html 注&#xff1a;安装erlang之前先安装下依赖文件&#xff0…