跳到主要内容

简单的拖拽区域

dragBoundFunc 在每次拖拽移动时被调用,你返回什么坐标,节点就落在哪里。

用法​

使用Konva为了限制拖放的形状的移动, 我们可以使用dragBoundsFunc属性,这是一个用户定义的函数。
这个函数可以用来约束 在各种方向的拖放运动,如约束运动 水平地,垂直地,对角地或径向地,或甚至约束节点 保持在盒子,圆圈或任何其他路径的内部。

说明:拖放水平文本,并观察它只能 水平移动。 拖放垂直文本,并观察它只能垂直移动。

<!DOCTYPE html>
<html>
<head>
<script src="https://unpkg.com/konva@10/konva.min.js"></script>
<meta charset="utf-8">
<title>Konva Simple Drag Bounds Demo</title>
<style>
body {
margin: 0;
padding: 0;
overflow: hidden;
background-color: #F0F0F0;
}
</style>
</head>
<body>
<div id="container"></div>
<script>
var width = window.innerWidth;
var height = window.innerHeight;

var stage = new Konva.Stage({
container: 'container',
width: width,
height: height
});

var layer = new Konva.Layer();

var rectHeight = 50;
var rectWidth = 100;
var rectY = (stage.getHeight() - rectHeight) / 2;

var hbox = new Konva.Text({
x: 20,
y: 70,
fontSize: 24,
fontFamily: 'Calibri',
text: 'horizontal',
fill: 'black',
padding: 15,
draggable: true,
dragBoundFunc: function(pos) {
return {
x: pos.x,
y: this.getAbsolutePosition().y
}
}
});

var vbox = new Konva.Text({
x: 150,
y: 70,
draggable: true,
fontSize: 24,
fontFamily: 'Calibri',
text: 'vertical',
fill: 'black',
padding: 15,
draggable: true,
dragBoundFunc: function(pos) {
return {
x: this.getAbsolutePosition().x,
y: pos.y
}
}
});

layer.add(hbox);
layer.add(vbox);
// add the layer to the stage
stage.add(layer);
</script>

</body>
</html>

常见问题​

回调里的坐标是相对什么的?​

是绝对坐标,相对舞台左上角,不是相对父容器。

这一点在节点位于有变换的分组内时尤其重要——分组做了平移或缩放之后, 节点的 x/y 与它的绝对位置完全不同。回调里拿到的和返回的都必须是绝对坐标, 不要混入节点自身的坐标值。

需要在两套坐标之间换算时用 node.getAbsoluteTransform()。

能在回调里阻止拖拽发生吗?​

不能。dragBoundFunc 只决定节点落在哪里,它无法取消这次拖拽——返回原位置只是让节点看起来不动,拖拽状态仍在进行。

要真正中止,在 dragstart 或 dragmove 里调 node.stopDrag()。 两者的差别与 Transformer 的 stopTransform 完全类似:约束是「不让它动」,中止是「结束这次交互」。

只想限制一个方向怎么写?​

返回时把另一个方向固定成起始值:

node.dragBoundFunc(function (pos) {
return { x: pos.x, y: this.absolutePosition().y };
});

注意这里用了 function 而不是箭头函数——回调里的 this 指向被拖节点, 用箭头函数会拿不到。这是个很容易踩的坑。

性能提示​

dragBoundFunc 每帧都会被调用。取整、比较、简单算术没有问题, 但以下几件事会让拖拽明显发涩:

在回调里调 getClientRect()。它会遍历子节点重新计算包围盒, 分组节点上尤其贵。需要包围盒就在 dragstart 时算一次存起来。

在回调里遍历图层找参考对象。做吸附时很容易这么写。正确做法是 dragstart 时收集候选坐标存成数组,回调里只做比较。

在回调里读取 DOM。任何 getBoundingClientRect、offsetWidth 都会强制 浏览器重排,在每帧回调里是灾难性的。