如何使用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
相关产品推荐
相关产品推荐

