
本文详解如何在 konva.js 中实现真正响应式的 canvas,通过统一缩放(uniform scaling)与舞台定位,确保图形在窗口缩放时不失真、不拉伸,并始终居中可见。
本文详解如何在 konva.js 中实现真正响应式的 canvas,通过统一缩放(uniform scaling)与舞台定位,确保图形在窗口缩放时不失真、不拉伸,并始终居中可见。
在使用 Konva.js 构建响应式游戏或可视化应用时,一个常见误区是直接按容器宽高同比例缩放舞台(scaleX ≠ scaleY),导致圆形变椭圆、矩形变形——这正是原代码中 scaleX = containerWidth / sceneWidth 与 scaleY = containerHeight / sceneHeight 独立计算所引发的失真问题。
核心原则:必须使用统一缩放因子(uniform scale)
为保持形状原始比例,应取宽高缩放比中的较小值:
const scale = Math.min(containerWidth / sceneWidth, containerHeight / sceneHeight);
该策略确保内容在任意容器尺寸下均能完整显示(“内切”适配),避免裁剪或拉伸。
关键改进点:
-
固定逻辑坐标系:所有图形位置(如
x,y,radius,width)应基于预设的虚拟画布尺寸(如sceneWidth = 1000,sceneHeight = 1000)定义,而非依赖stage.width()/height()动态计算——否则缩放后坐标基准混乱; -
舞台尺寸设为容器物理尺寸:
stage.width(containerWidth)和stage.height(containerHeight),使渲染区域填满父容器; -
通过
stage.scale()统一缩放内容,再用stage.position()居中补偿缩放后的空白区域:const x = (containerWidth - sceneWidth * scale) / 2; const y = (containerHeight - sceneHeight * scale) / 2; stage.position({ x, y }); -
调用
stage.batchDraw()强制重绘,保证 resize 后视觉即时更新。
✅ 完整修正代码如下:
<meta charset="utf-8"><title>Konva Responsive Canvas Demo</title><style>
body {
margin: 0; padding: 0; overflow: hidden;
background-color: #f0f0f0; height: 100vh;
}
#stage-parent {
width: 100%; height: 100vh;
}
</style><div id="stage-parent">
<div id="container"></div>
</div>
<script src="https://unpkg.com/konva@9/konva.min.js"></script><script>
const sceneWidth = 1000;
const sceneHeight = 1000;
const stage = new Konva.Stage({
container: 'container',
width: sceneWidth,
height: sceneHeight
});
const layer = new Konva.Layer();
stage.add(layer);
// 所有坐标均基于 sceneWidth/sceneHeight 定义(逻辑坐标)
const circle = new Konva.Circle({
x: sceneWidth / 2,
y: sceneHeight / 2,
radius: 50,
fill: 'red'
});
layer.add(circle);
const rect = new Konva.Rect({
x: sceneWidth - 100,
y: sceneHeight - 100,
width: 100,
height: 100,
fill: 'green'
});
layer.add(rect);
function fitStageIntoParentContainer() {
const container = document.getElementById('stage-parent');
const containerWidth = container.offsetWidth;
const containerHeight = container.offsetHeight;
const scale = Math.min(
containerWidth / sceneWidth,
containerHeight / sceneHeight
);
const scaledWidth = sceneWidth * scale;
const scaledHeight = sceneHeight * scale;
// 舞台占据整个容器物理像素
stage.width(containerWidth);
stage.height(containerHeight);
// 内容按 uniform scale 缩放
stage.scale({ x: scale, y: scale });
// 居中定位缩放后的内容
stage.position({
x: (containerWidth - scaledWidth) / 2,
y: (containerHeight - scaledHeight) / 2
});
stage.batchDraw(); // 必须触发重绘
}
fitStageIntoParentContainer();
window.addEventListener('resize', fitStageIntoParentContainer);
</script>
⚠️ 注意事项:
- 若需“填充式”适配(允许部分内容被裁剪),可将
Math.min改为Math.max,但需配合overflow: hidden及额外边界校验; - 所有后续添加的图形(如用户交互生成)也必须使用逻辑坐标(基于
sceneWidth/sceneHeight),不可混用stage.width()动态值; - 在高 DPI 设备上,如需高清渲染,应结合
window.devicePixelRatio调整stage的pixelRatio属性,此为进阶优化项。
掌握统一缩放 + 逻辑坐标 + 容器对齐三要素,即可构建稳定、专业、真正响应式的 Konva.js 应用。











