跳到主要内容

保持宽高比

缩放图片、图标这类内容时通常不希望变形。Transformer 默认就帮你锁住了宽高比。

用法​

keepRatio 的默认值就是 true,不需要手动开启。想允许自由变形才需要显式关掉:

const tr = new Konva.Transformer({
nodes: [rect],
keepRatio: false,
});

无论设成哪个值,按住 SHIFT 拖动都会临时反转当前行为。

/**
* select_and_transform/Keep_Ratio.html
*/
<!DOCTYPE html>
<html>
<head>
<script src="https://unpkg.com/konva@10/konva.min.js"></script>
<meta charset="utf-8">
<title>Konva Keep Ratio Demo</title>
<style>
body {
margin: 0;
padding: 0;
overflow: hidden;
background-color: #F0F0F0;
}
</style>
</head>
<body>
<div id="container"></div>
<script>
var stage = new Konva.Stage({
container: 'container',
width: window.innerWidth,
height: window.innerHeight
});
var layer = new Konva.Layer();
stage.add(layer);

var locked = new Konva.Rect({
x: 60, y: 80, width: 120, height: 90,
fill: '#4078c0', stroke: 'black', strokeWidth: 2, draggable: true
});
layer.add(locked);
// keepRatio 默认就是 true,这里显式写出来是为了和右边对照
layer.add(new Konva.Transformer({ nodes: [locked], keepRatio: true }));

var free = new Konva.Rect({
x: 280, y: 80, width: 120, height: 90,
fill: '#c04040', stroke: 'black', strokeWidth: 2, draggable: true
});
layer.add(free);
layer.add(new Konva.Transformer({ nodes: [free], keepRatio: false }));

layer.add(new Konva.Text({
x: 20, y: 20, fontSize: 14, fill: '#333',
text: '左:keepRatio 为 true,拖角保持宽高比(按住 SHIFT 临时解除)\n右:keepRatio 为 false,拖角可自由变形'
}));
</script>

</body>
</html>

常见问题​

我没设置 keepRatio,为什么拖角不能自由变形?​

因为它的默认值就是 true。很多人以为这是个需要主动开启的功能,结果反过来—— 想要自由变形的场景才需要显式设 false。

临时想自由拉一下,按住 SHIFT 即可,不用改代码。

keepRatio 为什么对边锚点不起作用?​

边锚点(middle-left、top-center 等)本来就是单方向拉伸,「保持比例」在这里 没有意义,所以 keepRatio 只作用于四个角锚点。

如果你希望完全禁止非等比缩放,光设 keepRatio 不够,还要用 enabledAnchors 把边锚点去掉:

enabledAnchors: ['top-left', 'top-right', 'bottom-left', 'bottom-right']

等比缩放时以哪个方向为准?​

以拖动距离较大的那个方向为准,另一个方向按原比例跟随。

这意味着斜向拖动时的实际缩放量可能小于鼠标位移——向右下拖动时, 宽度增加的幅度会被高度方向的约束拉回来。这是等比缩放的正常表现,不是掉帧。

与其他方案的取舍​

也可以关掉 keepRatio,改在 boundBoxFunc 里自己按比例修正 newBox。 看起来只是几行算术,但要额外处理一件事:SHIFT 键临时反转。

用户按住 SHIFT 时期望的是「切换到另一种模式」,自己实现就要监听键盘状态并在 boundBoxFunc 里分支。keepRatio 已经把这套交互做好了,自己重做纯属重复劳动。

真正需要自定义比例逻辑的场景很少——例如「宽度必须是高度的整数倍」这类约束, 那时才值得走 boundBoxFunc。