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

如何使用react-konva为基础图像编辑器制作响应式画布

React-Konva 实现响应式画布并保持内容比例

要实现画布随父容器/窗口动态调整且保持内容比例,核心是根据容器尺寸实时计算适配缩放比例,让画布内容始终按原始比例完整显示。以下是具体实现方案:

步骤1:完善Canvas上下文的尺寸监听与计算逻辑

修改canvas-context.tsx,添加容器尺寸监听和缩放比例计算:

import { useState, useEffect, useRef, createContext, useContext } from 'react';

const CanvasContext = createContext<any>(null);

export const CanvasProvider = ({ children }: { children: React.ReactNode }) => {
  const stageRef = useRef<any>(null);
  const containerRef = useRef<HTMLDivElement>(null);
  // 自定义原始画布尺寸,所有内容基于此尺寸绘制
  const [dimensions, setDimensions] = useState({
    originalWidth: 1920,
    originalHeight: 1080,
    scale: 1,
    containerWidth: 0,
    containerHeight: 0,
  });
  const [selectedId, setSelectedId] = useState<string | null>(null);

  // 计算适配的缩放比例
  const calculateScale = () => {
    if (!containerRef.current) return;
    const { width: containerWidth, height: containerHeight } = containerRef.current.getBoundingClientRect();
    const { originalWidth, originalHeight } = dimensions;
    
    const canvasRatio = originalWidth / originalHeight;
    const containerRatio = containerWidth / containerHeight;

    // 根据比例关系确定缩放基准
    let scale = 1;
    if (containerRatio > canvasRatio) {
      // 容器更宽,以高度为基准缩放
      scale = containerHeight / originalHeight;
    } else {
      // 容器更高,以宽度为基准缩放
      scale = containerWidth / originalWidth;
    }

    setDimensions(prev => ({
      ...prev,
      containerWidth,
      containerHeight,
      scale,
    }));
  };

  useEffect(() => {
    calculateScale();
    
    // 监听容器尺寸变化
    const observer = new ResizeObserver(calculateScale);
    containerRef.current && observer.observe(containerRef.current);
    
    // 监听窗口resize,兼容容器随窗口变化的场景
    window.addEventListener('resize', calculateScale);

    return () => {
      containerRef.current && observer.unobserve(containerRef.current);
      window.removeEventListener('resize', calculateScale);
    };
  }, []);

  return (
    <CanvasContext.Provider value={{
      stageRef,
      containerRef,
      dimensions,
      setSelectedId,
      selectedId,
    }}>
      {children}
    </CanvasContext.Provider>
  );
};

export const useCanvas = () => useContext(CanvasContext);

步骤2:修改Canvas组件适配尺寸与缩放

更新Canvas组件,让Stage使用容器尺寸,同时处理内容居中偏移:

import { Layer, Rect, Stage } from "react-konva";
import { useCanvas } from "./canvas-context";
import { ShapeRenderer } from "./shape-renderer";

const Canvas = () => {
    const { setSelectedId, stageRef, containerRef, dimensions } = useCanvas();
    const { originalWidth, originalHeight, scale, containerWidth, containerHeight } = dimensions;

    const deselectShape = (e: any) => {
        if (e.target === e.target.getStage()) setSelectedId(null);
    };

    // 计算居中偏移,转换为Konva原始坐标系的偏移值
    const offsetX = (containerWidth - originalWidth * scale) / (2 * scale);
    const offsetY = (containerHeight - originalHeight * scale) / (2 * scale);

    return (
        <div
            ref={containerRef}
            id="canvas-container"
            className="relative flex h-full w-full flex-col items-center justify-center overflow-hidden"
        >
            <Stage
                id="konva-container"
                ref={stageRef}
                width={containerWidth}
                height={containerHeight}
                onMouseDown={deselectShape}
                onTouchStart={deselectShape}
                scaleX={scale}
                scaleY={scale}
                x={offsetX}
                y={offsetY}
            >
                <Layer>
                    <Rect
                        x={0}
                        y={0}
                        width={originalWidth}
                        height={originalHeight}
                        fill="black"
                        listening={false}
                    />
                    <ShapeRenderer />
                </Layer>
            </Stage>
        </div>
    );
};

export default Canvas;

关键要点说明

  • 原始尺寸定义:originalWidth和originalHeight是画布内容的基准尺寸,所有图形/图像都基于此尺寸绘制,确保比例统一。
  • 缩放逻辑:通过对比容器与画布的宽高比,选择以宽度或高度为基准缩放,保证内容完整显示且比例不变。
  • 居中处理:计算偏移量并设置Stage的x/y属性,让画布内容在容器中居中显示,偏移值需除以缩放比例转换为Konva原始坐标系的数值。
  • 精准监听:使用ResizeObserver直接监听容器尺寸变化,比window.resize更精准,适配容器因布局调整而变化的场景。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.15 12:58:20