索引
UI
库提供 ht.ui.DragHelper
静态类处理组件内部或者组件之间通过拖拽交换数据的需求;这个类提供的静态函数如下:
function doDrag(view, data, image, offsetX, offsetY)
开始拖拽组件view
被拖拽的组件,ht.ui.View
类型data
拖拽数据,Object
类型(可选)image
拖拽图片,可以是 HTMLImageElement
或 HTMLCanvasElement
,也可以是 HT
的矢量对象(可选)offsetX
图片左上角相对于鼠标在水平方向偏移量(可选)offsetY
图片左上角相对于鼠标在垂直方向偏移量(可选)function isDragging(view)
判断组件是否正在拖拽view
组件实例,ht.ui.View
类型(可选)function cancelDrag(event)
取消拖拽过程event
事件对象(可选)function acceptDragDrop(targetView, cursor)
接受拖拽targetView
接受拖拽的组件cursor
拖拽提示图标,可以是 HTMLImageElement
或 HTMLCanvasElement
,也可以是 HT
的矢量对象(可选)拖拽操作一般涉及的两个组件:被拖拽的组件和接受拖拽的组件;被拖拽组件和接受拖拽组件在拖拽过程中都会派发出一些事件,其中接受拖拽的组件的事件列举如下:
dragEnter
,拖拽过程中鼠标进入组件范围时派发,事件对象格式:{source: source, target: target, data: data, kind: 'dragEnter', nativeEvent: event}
dragExit
,拖拽过程中鼠标移出组件范围时派发,事件对象格式:{source: source, target: target, data: data, kind: 'dragEnter', nativeEvent: event}
dragMove
,拖拽过程中鼠标在组件范围内移动时派发,事件对象格式:{source: source, target: target, data: data, kind: 'dragMove', nativeEvent: event}
dragCanceled
,拖拽操作取消(如按 ESC
键)时派发,事件对象格式:{source: source, target: target, data: data, kind: 'dragCanceled', nativeEvent: event}
dragCompleted
,拖拽操作完成时派发(在接受拖拽的组件上松开鼠标),事件对象格式:{source: source, target: target, data: data, kind: 'dragCompleted', nativeEvent: event}
被拖拽组件可派发两种事件:
dragCanceled
,拖拽操作取消(如按 ESC
键)时派发,事件对象格式:{source: source, target: target, data: data, kind: 'dragCanceled', nativeEvent: event}
dragCompleted
,拖拽操作完成时派发(在接受拖拽的组件上松开鼠标),事件对象格式:{source: source, target: target, data: data, kind: 'dragCompleted', nativeEvent: event}
一个完整的拖拽流程如下:
doDrag
启动拖拽dragEnter
事件,调用 acceptDragDrop
接受拖拽dragCompleted
事件,处理拖拽数据接下来我们看一个例子,页面里两个 VBoxLayout
,左侧容器中有十个按钮,右侧容器是空的,拖拽 API
允许用户将左侧容器中的按钮拖拽到右侧容器中:
首先在左侧容器上监听鼠标按下事件,启动拖拽
// 按下鼠标时, 启动子组件的拖拽
vBox1.on('d:mousedown', startDrag);
function startDrag(e) {
var view = ht.Default.getViewAt(e);
if (view instanceof ht.ui.Button) {
// 启动拖拽,调用 toImage 生成拖拽图片
// 如果不想自己创建图片,可以用简单的方式:view.getRootCanvas(),因为大部分组件的内容都是直接绘制在
// canvas 上的,所以把 canvas 当作图片交给 DragHelper 也是可以的
DragHelper.doDrag(view, {}, toImage(view, view.getText()), -view.getWidth() / 2, -view.getHeight() / 2);
}
}
在右侧容器上监听拖拽相关事件:
function handleDragEvents(e) {
if (e.kind === 'dragEnter') {
var target = e.target;
// 接受拖拽数据
if (target instanceof ht.ui.VBoxLayout)
DragHelper.acceptDragDrop(target);
}
else if (e.kind === 'dragMove') {
// 计算鼠标所在的插入位置,并且在容器上绘制插入提示线
var targetView = e.target;
delete targetView._insertPosition;
delete targetView._hintRect;
var point = targetView.getContentPoint(e.nativeEvent);
var children = targetView.getChildren();
var childrenSize = children.size();
for (var i = childrenSize - 1; i > -1; i--) {
var child = children.get(i);
if (point.y > child.getY() + child.getHeight()) {
targetView._hintRect = {
x: 5,
y: child.getY() + child.getHeight() + targetView.getContentTop() + 2,
width: targetView.getWidth() - 10,
height: 2
};
targetView._insertPosition = i + 1;
break;
}
}
if (!targetView._hintRect) {
targetView._hintRect = {
x: 5,
y: targetView.getContentTop() + 2,
width: targetView.getWidth() - 10,
height: 2
};
targetView._insertPosition = 0;
}
targetView.iv();
}
else if (e.kind === 'dragCompleted') {
// 拖拽结束,处理拖拽数据
var source = e.source,
target = e.target;
if (source !== target) {
target.addView(source, {
marginTop: 5,
marginLeft: 5,
marginRight: 5,
width: 'match_parent',
height: 'wrap_content'
}, target._insertPosition);
delete target._hintRect;
delete target._insertPosition;
}
}
else if (e.kind === 'dragExit' || e.kind === 'dragCanceled') {
// 拖拽取消或鼠标移出组件范围时清空状态
var target = e.target;
delete target._hintRect;
delete target._insertPosition;
target.iv();
}
}