如何设置React中Konva Stage的宽高与父div保持一致
问题与解决方案
问题说明
我在一个固定高度为500px、宽度动态且可滚动的div内放置了A4格式的div,因页面存在可伸缩侧边栏,A4 div的宽度动态变化这部分运行正常。但通过Konva库添加的Stage组件仅支持数值类型的宽高参数,无法使用百分比,尝试用Ref和状态管理实现自适应宽高未达理想效果。
解决思路与修改方案
- 将Ref绑定到A4格式的div上,而非外层容器,确保获取的是Stage父容器的实际尺寸
- 使用
ResizeObserver替代window.resize事件,更精准监听容器尺寸变化(侧边栏伸缩、窗口 resize 都能触发) - 优化状态更新逻辑,确保Stage宽高与父容器实时同步
修改后的完整代码
import { useState, useRef, useEffect, useContext } from 'react'; import { Stage, Layer, Rectangle } from 'react-konva'; import { SidebarContext } from './你的侧边栏上下文路径'; // 替换为实际路径 const Home = () => { const [stageSize, setStageSize] = useState({ width: 0, height: 0 }); const a4ContainerRef = useRef(null); // 绑定到A4格式的div const { sideBarStatus } = useContext(SidebarContext); const [rectangles, setRectangles] = useState(initialRectangles); const [selectedId, selectShape] = useState(null); useEffect(() => { const updateStageSize = () => { if (a4ContainerRef.current) { const { clientWidth, clientHeight } = a4ContainerRef.current; setStageSize({ width: clientWidth, height: clientHeight }); } }; // 初始执行一次 updateStageSize(); // 使用ResizeObserver监听容器尺寸变化 const observer = new ResizeObserver(updateStageSize); if (a4ContainerRef.current) { observer.observe(a4ContainerRef.current); } return () => { observer.disconnect(); }; }, [sideBarStatus]); // 侧边栏状态变化时强制更新 const checkDeselect = e => { const clickedOnEmpty = e.target === e.target.getStage(); if (clickedOnEmpty) { selectShape(null); } }; return ( <div style={{ height: 500, display: 'flex', width: '100%', overflow: 'auto' }}> <div ref={a4ContainerRef} style={{ border: '2px solid black', backgroundColor: 'white', position: 'relative', overflow: 'auto', width: '100%', aspectRatio: '1 / 1.4142' // A4标准比例:宽/高 = 1/√2 ≈ 1/1.4142 }} > {/* 避免初始0尺寸渲染Stage */} {stageSize.width > 0 && ( <Stage width={stageSize.width} height={stageSize.height} onMouseDown={checkDeselect} onTouchStart={checkDeselect} > <Layer> {rectangles.map((rect, i) => ( <Rectangle key={i} shapeProps={rect} isSelected={rect.id === selectedId} onSelect={() => selectShape(rect.id)} onChange={newAttrs => { const rects = [...rectangles]; rects[i] = newAttrs; setRectangles(rects); }} /> ))} </Layer> </Stage> )} </div> </div> ); }; Home.acl = { action: 'read', subject: entities.home // 修正原代码拼写错误:entitites → entities }; const initialRectangles = [ { x: 10, y: 10, width: 100, height: 100, fill: 'red', id: 'rect1' }, { x: 150, y: 150, width: 100, height: 100, fill: 'green', id: 'rect2' } ]; export default Home;
关键优化点说明
- Ref绑定目标调整:将Ref移到A4格式的div上,确保获取的是Stage直接父容器的尺寸
- ResizeObserver替代全局resize:精准监听容器自身尺寸变化,比全局事件更高效,适配侧边栏伸缩场景
- A4比例实现优化:用CSS原生
aspectRatio属性替代动态计算height,避免状态更新导致的高度闪烁 - 初始渲染防护:添加尺寸判断,避免Stage以0尺寸初始化
- 数组更新优化:用展开运算符替代
slice(),语义更清晰
内容的提问来源于stack exchange,提问作者KreaN
相关产品推荐
相关产品推荐

