封装组件:
我自己写的一个简单的组件,可能有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;// 定义状态变量,使用useStateconst [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 (<divclassName={`${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;// 定义状态变量,使用useStateconst [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 (<divclassName={`${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;