Files
konva/src/DragAndDrop.ts

179 lines
5.2 KiB
TypeScript
Raw Normal View History

import type { Container } from './Container.ts';
import { Konva } from './Global.ts';
import type { Node } from './Node.ts';
import type { Vector2d } from './types.ts';
import { Util } from './Util.ts';
2019-02-23 20:54:20 -05:00
2019-01-01 15:59:27 -05:00
export const DD = {
2019-08-04 14:38:57 +07:00
get isDragging() {
2024-10-05 14:18:32 +00:00
let flag = false;
DD._dragElements.forEach((elem) => {
2019-08-12 17:31:13 +07:00
if (elem.dragStatus === 'dragging') {
2019-08-04 14:38:57 +07:00
flag = true;
}
});
return flag;
},
2019-01-01 15:59:27 -05:00
justDragged: false,
2019-08-04 14:38:57 +07:00
get node() {
// return first dragging node
2024-10-05 14:18:32 +00:00
let node: Node | undefined;
DD._dragElements.forEach((elem) => {
2019-08-04 14:38:57 +07:00
node = elem.node;
});
return node;
},
_dragElements: new Map<
number,
{
node: Node;
startPointerPos: Vector2d;
offset: Vector2d;
pointerId?: number;
2019-08-12 17:31:13 +07:00
// when we just put pointer down on a node
// it will create drag element
dragStatus: 'ready' | 'dragging' | 'stopped';
// dragStarted: boolean;
// isDragging: boolean;
// dragStopped: boolean;
2019-08-04 14:38:57 +07:00
}
>(),
2019-01-01 15:59:27 -05:00
// methods
_drag(evt) {
const nodesToFireEvents: Array<Node> = [];
2019-08-04 14:38:57 +07:00
DD._dragElements.forEach((elem, key) => {
const { node } = elem;
// we need to find pointer relative to that node
2023-08-28 09:23:57 -05:00
const stage = node.getStage()!;
2019-08-04 14:38:57 +07:00
stage.setPointersPositions(evt);
// it is possible that user call startDrag without any event
// it that case we need to detect first movable pointer and attach it into the node
if (elem.pointerId === undefined) {
elem.pointerId = Util._getFirstPointerId(evt);
}
const pos = stage._changedPointerPositions.find(
(pos) => pos.id === elem.pointerId
2019-08-04 14:38:57 +07:00
);
2019-08-05 13:54:08 +07:00
// not related pointer
2019-08-04 14:38:57 +07:00
if (!pos) {
return;
}
2019-08-12 17:31:13 +07:00
if (elem.dragStatus !== 'dragging') {
2024-10-05 14:18:32 +00:00
const dragDistance = node.dragDistance();
const distance = Math.max(
2019-08-04 14:38:57 +07:00
Math.abs(pos.x - elem.startPointerPos.x),
Math.abs(pos.y - elem.startPointerPos.y)
2019-01-01 15:59:27 -05:00
);
if (distance < dragDistance) {
return;
}
2019-09-03 09:38:19 -05:00
node.startDrag({ evt });
2019-01-01 15:59:27 -05:00
// a user can stop dragging inside `dragstart`
if (!node.isDragging()) {
return;
}
}
2019-08-04 14:38:57 +07:00
node._setDragPosition(evt, elem);
nodesToFireEvents.push(node);
});
// call dragmove only after ALL positions are changed
nodesToFireEvents.forEach((node) => {
2019-01-01 15:59:27 -05:00
node.fire(
'dragmove',
{
type: 'dragmove',
target: node,
evt: evt,
2019-01-01 15:59:27 -05:00
},
true
);
2019-08-04 14:38:57 +07:00
});
2019-01-01 15:59:27 -05:00
},
2019-08-05 13:54:08 +07:00
// dragBefore and dragAfter allows us to set correct order of events
// setup all in dragbefore, and stop dragging only after pointerup triggered.
2019-09-10 12:08:36 -05:00
_endDragBefore(evt?) {
2023-08-28 09:23:57 -05:00
const drawNodes: Array<Container> = [];
2021-05-07 07:48:13 -05:00
DD._dragElements.forEach((elem) => {
2019-08-04 14:38:57 +07:00
const { node } = elem;
// we need to find pointer relative to that node
2023-08-28 09:23:57 -05:00
const stage = node.getStage()!;
2019-09-10 12:08:36 -05:00
if (evt) {
stage.setPointersPositions(evt);
}
2019-01-01 15:59:27 -05:00
2019-08-04 14:38:57 +07:00
const pos = stage._changedPointerPositions.find(
(pos) => pos.id === elem.pointerId
2019-08-04 14:38:57 +07:00
);
2019-01-01 15:59:27 -05:00
2019-08-04 14:38:57 +07:00
// that pointer is not related
if (!pos) {
return;
}
if (elem.dragStatus === 'dragging' || elem.dragStatus === 'stopped') {
2021-05-07 07:48:13 -05:00
// if a node is stopped manually we still need to reset events:
2019-01-25 00:20:15 -05:00
DD.justDragged = true;
2021-05-08 20:22:20 -05:00
Konva._mouseListenClick = false;
Konva._touchListenClick = false;
Konva._pointerListenClick = false;
elem.dragStatus = 'stopped';
2019-01-01 15:59:27 -05:00
}
2019-03-06 22:19:32 -05:00
const drawNode =
2019-08-04 14:38:57 +07:00
elem.node.getLayer() ||
2020-11-10 08:59:20 -05:00
((elem.node instanceof Konva['Stage'] && elem.node) as any);
if (drawNode && drawNodes.indexOf(drawNode) === -1) {
drawNodes.push(drawNode);
2019-01-01 15:59:27 -05:00
}
2019-08-04 14:38:57 +07:00
});
// draw in a sync way
// because mousemove event may trigger BEFORE batch draw is called
// but as we have not hit canvas updated yet, it will trigger incorrect mouseover/mouseout events
drawNodes.forEach((drawNode) => {
drawNode.draw();
});
2019-01-01 15:59:27 -05:00
},
_endDragAfter(evt) {
2019-08-04 14:38:57 +07:00
DD._dragElements.forEach((elem, key) => {
2019-08-12 17:31:13 +07:00
if (elem.dragStatus === 'stopped') {
2019-08-04 14:38:57 +07:00
elem.node.fire(
'dragend',
{
type: 'dragend',
target: elem.node,
evt: evt,
2019-08-04 14:38:57 +07:00
},
true
);
}
if (elem.dragStatus !== 'dragging') {
2019-08-04 14:38:57 +07:00
DD._dragElements.delete(key);
}
});
},
2019-01-01 15:59:27 -05:00
};
2019-03-06 22:19:32 -05:00
if (Konva.isBrowser) {
2019-01-01 15:59:27 -05:00
window.addEventListener('mouseup', DD._endDragBefore, true);
window.addEventListener('touchend', DD._endDragBefore, true);
2024-10-21 18:11:22 -05:00
// add touchcancel to fix this: https://github.com/konvajs/konva/issues/1843
window.addEventListener('touchcancel', DD._endDragBefore, true);
window.addEventListener('pointerup', DD._endDragBefore, true);
window.addEventListener('pointercancel', DD._endDragBefore, true);
2019-01-01 15:59:27 -05:00
window.addEventListener('mousemove', DD._drag);
window.addEventListener('touchmove', DD._drag);
window.addEventListener('pointermove', DD._drag);
2019-01-01 15:59:27 -05:00
window.addEventListener('mouseup', DD._endDragAfter, false);
window.addEventListener('touchend', DD._endDragAfter, false);
2024-10-21 18:11:22 -05:00
window.addEventListener('touchcancel', DD._endDragAfter, false);
window.addEventListener('pointerup', DD._endDragAfter, false);
window.addEventListener('pointercancel', DD._endDragAfter, false);
2019-01-01 15:59:27 -05:00
}