封装组件:
新建一个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;
}
参数名 | 类型 | 描述 |
---|---|---|
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组件使用。我只是提供一个思路。
盒子带滚动条就需要 动态加上或减去滚动的高度
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;