reactjs - 如何在 React 中创建可调整大小的组件
问题描述
正如标题中所说。我想创建一个 React 组件,它可以让我通过拖动来调整其宽度 - 就像 Windows 操作系统中的窗口一样。实际上处理这个问题的最佳方法是什么?
编辑
我包括我目前对此事的处理方法:
首先,我在容器的右上角放置了一个“拖动器”元素。当我在该元素上按下鼠标时,我想创建一个 mousemove 事件侦听器,它将修改相对于光标的 X 坐标相对于容器边缘的初始 X 位置的 containerWidth。我已经让那个事件监听器在按住鼠标按钮后触发并记录我的坐标,但不幸的是,由于某种原因,在鼠标未按下(mouseUp 事件)后事件没有被删除,这不是我想要的。任何建议表示赞赏,还有关于我将来可能期望与该主题相关的一些问题的建议。谢谢。
type Props = MasterProps & LinkStateToProps & LinkDispatchToProps;
const Test3 = (Props: Props) => {
const [containerWidth, setContainerWidth] = React.useState(640)
const [isBeingStretched, setIsBeingStretched] = React.useState(false);
const masterRef = React.useRef(null);
const logMousePosition = React.useCallback((event:MouseEvent)=>{
console.log(event.clientX);
},[])
const handleMouseDown=()=>{
document.addEventListener('mousemove', logMousePosition);
masterRef.current.addEventListener('mouseup', ()=>{
document.removeEventListener('mouseup', logMousePosition)
})
}
const handleMouseUp = () => {
document.removeEventListener('mousemove', logMousePosition);
}
return (
<div className="master-wrapper" ref={masterRef}>
<div className="stretchable-div" style={{ width: `${containerWidth}px` }}>
<div className="dragger-wrapper">
<h2>This is supposed to change width</h2>
<div className="dragger"
id="dragger"
onMouseDown={handleMouseDown}
onMouseUp={handleMouseUp}/>
</div>
</div>
</div>
);
}
export default connect(mapStateToProps, mapDispatchToProps)(Test3);
解决方案
我以前从来没有做过这样的事情,所以我决定试一试,结果用 React 状态管理实现它非常简单。如果您是 React 新手,我可以理解为什么您可能不知道从哪里开始,这没关系,尽管在我完成我的解决方案之前需要注意两点:
- 诸如
document.getElementById
or之类的语句document.addEventListener
将不再按预期运行。使用 React,您正在操作一个虚拟 DOM,它会为您更新实际的 DOM,您应该尽可能让它这样做。 - 用来
refs
绕过这个事实是不好的做法。它们的行为方式可能与上述陈述类似,但这不是它们的预期用例。阅读文档中有关ref
.
这是我演示的 JSX 部分的样子:
return (
<div className="container" onMouseMove={resizeFrame} onMouseUp={stopResize}>
<div className="box" style={boxStyle}>
<button className="dragger" onMouseDown={startResize}>
Size Me
</button>
</div>
</div>
);
我们将需要三个不同的事件 -onMouseDown
和onMouseMove
-onMouseUp
来跟踪调整大小的不同阶段。您已经在自己的代码中做到了这一点。在 React 中,我们将所有这些声明为元素本身的属性,尽管它们实际上并不是内联函数。React 在后台将它们添加为我们的事件监听器。
const [drag, setDrag] = useState({
active: false,
x: "",
y: ""
});
const startResize = e => {
setDrag({
active: true,
x: e.clientX,
y: e.clientY
});
};
我们将使用一些状态来跟踪正在进行的调整大小。我将所有内容压缩为一个对象以避免膨胀并使其更具可读性,尽管如果您有其他类似useEffect
或useMemo
依赖于该状态的钩子,这并不总是理想的。第一个事件只是保存用户鼠标的初始 x 和 y 位置,并将 active 设置为 true 以供下一个事件参考。
const [dims, setDims] = useState({
w: 200,
h: 200
});
const resizeFrame = e => {
const { active, x, y } = drag;
if (active) {
const xDiff = Math.abs(x - e.clientX);
const yDiff = Math.abs(y - e.clientY);
const newW = x > e.clientX ? dims.w - xDiff : dims.w + xDiff;
const newH = y > e.clientY ? dims.h + yDiff : dims.h - yDiff;
setDrag({ ...drag, x: e.clientX, y: e.clientY });
setDims({ w: newW, h: newH });
}
};
第二个状态将初始化,然后随着元素值的变化更新元素的尺寸。这可以使用您想要的任何度量,尽管它必须与某些 CSS 属性相关。
该resizeFrame
函数执行以下操作:
- 通过解构赋值使属性
drag
易于使用。这将使代码更具可读性和更易于键入。 - 检查调整大小是否处于活动状态。
onMouseMove
将为鼠标在相关元素上移动的每个像素触发,因此我们要确保它被适当地调节。 - 用于
Math.abs()
获取当前鼠标位置和保存的鼠标位置之间的值差,为正整数。这将使我们不必进行第二轮条件语句。 - 根据新的鼠标位置是大于还是小于任一轴上的先前位置,使用 turnary 语句从尺寸中添加或减去差异。
- 使用新值设置状态,使用扩展运算符
...
保留不相关的部分drag
。
const stopResize = e => {
setDrag({ ...drag, active: false });
};
const boxStyle = {
width: `${dims.x}px`,
height: `${dims.y}px`
};
然后,一旦用户完成,我们只需将拖动状态的活动设置为 false。JS 样式对象被传递给带有状态变量的元素,以便自动处理。
这是带有完成效果的代码框。
这样做的一个缺点是它基本上要求您将该mouseMove
事件侦听器分配给最大的站点容器,因为在调整大小期间鼠标不会停留在框的范围内。如果您想拥有多个具有相同功能的元素,这可能是一个问题,尽管没有什么是您无法通过良好的状态管理解决的。您可能可以对此进行微调,以使鼠标始终停留在拖动元素上,尽管这需要更复杂的实现。
推荐阅读
- spring-boot - 限制创建没有主体的会话
- autohotkey - 为什么我的 ImageSearch 命令不起作用?
- javascript - MongoDB Bulk Insert Operation instead of For-Loop
- jquery - jquery调用按钮单击事件不起作用
- react-native - 如何在 react-native 中将 base64 数据存储到设备内部/外部存储?
- java - 从资源目录中读取的 Java JKS 文件密钥库异常
- apache-spark - 读入火花时将字符串列转换为时间戳
- puppeteer - Puppeteer 的流类型定义阻止使用默认导出?
- java - 在Java中每X秒发送一次UDP消息
- java - 使用 SingleColumnValueFilter 在 hbase 过滤器中不比较 int 值