拖动器是一种控制器对象,用于协调拖动可拖动对象以响应用户互动。
在极少数情况下,您可能需要实现自定义拖动手柄,因为您可能不需要对协调拖动进行太多自定义。scroll-options 插件会实现自定义拖动器,因为它想要在工作区边缘添加滚动,从而更改像素坐标转换为工作区坐标的方式。
职责
拖动操作时,拖动者有以下几项责任:
- 对可拖动对象调用拖动方法。
- 计算可拖动对象应在工作区坐标中移动到的位置。
- 对任何悬停的拖动目标调用拖动目标方法。
实现
如需创建自定义拖动器,您必须实现 IDragger
接口。
class MyDragger implements IDragger {
// Takes in the draggable being dragged and the workspace the drag
// is occurring in.
constructor(draggable, workspace);
}
您还可以对内置的 Blockly.dragging.Dragger
进行子类化,该类已处理基本职责。
开始拖动
onDragStart
方法会初始化拖动操作。它应存储执行拖动操作所需的所有数据。它还应对正在拖动的可拖动对象调用 startDrag
。
onDragStart(e) {
this.startLoc = this.draggable.getRelativeToSurfaceXY();
this.draggable.startDrag(e);
}
拖动
onDrag
方法会执行拖动操作。它应根据 totalDelta
(以像素坐标给出)计算可拖动对象的新工作区位置。
它还应更新当前悬停在其上的所有拖动目标。
- 应始终先调用
wouldDelete
,然后再调用拖动目标上的其他钩子。 - 应始终先对旧拖动目标调用
onDragExit
,然后再对新拖动目标调用onDragEnter
。 - 在拖动目标首次悬停时,应在
onDragEnter
之后调用onDragOver
;在拖动目标仍处于悬停状态的每次onDrag
调用中,也应调用onDragOver
。
onDrag(e, totalDelta) {
// Update the draggable location.
const delta = this.pixelsToWorkspaceUnits(totalDelta);
const newLoc = Coordinate.sum(this.startLoc, delta);
this.draggable.drag(newLoc, e);
// Call wouldDeleteDraggable.
if (isDeletable(this.draggable)) {
this.draggable.setDeleteStyle(
// Checks that the drag target is an `IDeleteArea` and calls `wouldDelete`
// on it.
this.wouldDeleteDraggable(e, this.draggable),
);
}
const newDragTarget = this.workspace.getDragTarget(e);
if (this.dragTarget !== newDragTarget) {
// Call `onDragExit` then `onDragEnter`.
this.dragTarget?.onDragExit(this.draggable);
newDragTarget?.onDragEnter(this.draggable);
}
// Always call `onDragOver`
newDragTarget?.onDragOver(this.draggable);
this.dragTarget = newDragTarget;
}
结束拖动
onEndDrag
方法会结束拖动。它应通知可拖动对象拖动已结束,以及任何悬停的拖动目标已放下可拖动对象。如果拖动目标是删除区域,则还应处置可拖动对象。
- 应始终先调用
onDrop
,然后再调用其他方法。 - 如果拖动目标阻止拖动,则应调用
revertDrag
。 - 应在还原拖动操作后,但在处置之前调用
endDrag
。 - 如果拖动目标是删除区域,则应调用
dispose
。
onDragEnd(e) {
// Call `onDrop` first.
const dragTarget = this.workspace.getDragTarget(e);
if (dragTarget) {
this.dragTarget?.onDrop(this.draggable);
}
// Then revert the drag (if applicable).
if (this.shouldReturnToStart(e, this.draggable)) {
this.draggable.revertDrag();
}
// Then end the drag.
this.draggable.endDrag(e);
// Then delete the draggable (if applicable).
if (
isDeletable(this.draggable) &&
this.wouldDeleteDraggable(e, this.draggable)
) {
this.draggable.dispose();
}
}
注册
您需要注册拖动类,以便在检测到拖动操作时创建该类。
// Note that the type is BLOCK_DRAGGER even though draggers drag more than
// blocks. The name is for backwards compatibility.
Blockly.registry.register(registry.Type.BLOCK_DRAGGER, 'MY_DRAGGER', MyDragger);
用法
实现自定义拖动器后,您可以通过将其传递给注入配置结构体来使用它。
const myWorkspace = Blockly.inject('blocklyDiv', {
plugins: {
// Note that we pass this to blockDragger even though draggers drag more
// than blocks. The name is for backwards compatibility.
blockDragger: MyDragger,
},
});