You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何设置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;

关键优化点说明

  1. Ref绑定目标调整:将Ref移到A4格式的div上,确保获取的是Stage直接父容器的尺寸
  2. ResizeObserver替代全局resize:精准监听容器自身尺寸变化,比全局事件更高效,适配侧边栏伸缩场景
  3. A4比例实现优化:用CSS原生aspectRatio属性替代动态计算height,避免状态更新导致的高度闪烁
  4. 初始渲染防护:添加尺寸判断,避免Stage以0尺寸初始化
  5. 数组更新优化:用展开运算符替代slice(),语义更清晰

内容的提问来源于stack exchange,提问作者KreaN

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.23 20:24:51