实现类似LeetCode的可拖拽分隔边框问题求助
解决LeetCode风格可拖拽分隔条的问题
问题根源分析
你的代码存在两个核心问题:
- 拖拽延迟:用
useState实时更新位置,React状态更新是异步且会触发重渲染,频繁触发就会导致UI跟不上鼠标移动 - 光标异常:未设置拖拽过程中的光标样式,且事件绑定/移除逻辑存在引用不匹配的问题
改进后的实现代码
import { useEffect, useState, useRef } from 'react'; const SplitPane = () => { const [divPosition, setDivPosition] = useState(window.innerWidth - 700); const dividerRef = useRef<HTMLDivElement>(null); // 保存初始偏移量,避免鼠标位置和分隔条位置错位 const startOffsetRef = useRef(0); useEffect(() => { const divider = dividerRef.current; if (!divider) return; const handleMouseDown = (e: MouseEvent) => { // 只允许点击分隔条左侧6px区域触发拖拽 if ((e.target as HTMLElement).offsetLeft > 6) return; e.preventDefault(); // 记录鼠标相对于分隔条左侧的偏移 startOffsetRef.current = e.clientX - divider.getBoundingClientRect().left; // 设置全局光标 document.body.style.cursor = 'col-resize'; document.addEventListener('mousemove', handleMouseMove); document.addEventListener('mouseup', handleMouseUp); }; const handleMouseMove = (e: MouseEvent) => { // 直接操作DOM更新位置,跳过React状态更新的异步延迟 const newPosition = e.clientX - startOffsetRef.current; // 加边界限制,避免容器被拖到不可用尺寸 const clampedPosition = Math.max(200, Math.min(window.innerWidth - 200, newPosition)); divider.style.left = `${clampedPosition}px`; }; const handleMouseUp = () => { // 拖拽结束后再更新状态保存位置 setDivPosition(parseInt(divider.style.left)); document.body.style.cursor = ''; document.removeEventListener('mousemove', handleMouseMove); document.removeEventListener('mouseup', handleMouseUp); }; divider.addEventListener('mousedown', handleMouseDown); return () => { divider.removeEventListener('mousedown', handleMouseDown); }; }, []); return ( <div style={{ display: 'flex', height: '100vh', position: 'relative' }}> <div style={{ width: `${divPosition}px`, backgroundColor: '#f5f5f5', padding: '16px' }}> 左侧容器 </div> <div ref={dividerRef} style={{ position: 'absolute', left: `${divPosition}px`, width: '12px', height: '100%', cursor: 'col-resize', backgroundColor: '#e0e0e0', display: 'flex', alignItems: 'center', justifyContent: 'center', }} > {/* 可添加视觉提示 */} <div style={{ width: '2px', height: '40px', backgroundColor: '#999' }} /> </div> <div style={{ flex: 1, padding: '16px' }}> 右侧容器 </div> </div> ); }; export default SplitPane;
关键改进点
- 消除拖拽延迟:拖拽过程中直接操作DOM修改分隔条位置,仅在拖拽结束后更新React状态保存最终位置,彻底规避异步更新的延迟问题
- 修复光标异常:给分隔条默认设置
cursor: col-resize,拖拽时给document.body同步设置相同光标,结束后恢复默认样式 - 修正事件逻辑:将事件提取为独立函数,保证添加和移除的是同一个引用,避免内存泄漏
- 位置校正:记录鼠标点击时分隔条的偏移量,防止拖拽时光标与分隔条位置错位
- 边界限制:添加最小/最大宽度限制,避免容器被拖到不可用尺寸
相关学习资源
- MDN文档:
MouseEvent、Element.getBoundingClientRect()、CSScursor属性 - React官方文档:
useRef的使用、useEffect的清理机制 - 前端交互模式:双面板拖拽调整的通用实现思路
内容的提问来源于stack exchange,提问作者Alijonov
相关产品推荐
相关产品推荐

