2025-08-20 11:27:20 +12:00
|
|
|
import type { Container } from './Container.ts';
|
2025-08-20 09:39:20 +12:00
|
|
|
import { Konva } from './Global.ts';
|
2025-08-20 11:27:20 +12:00
|
|
|
import type { Node } from './Node.ts';
|
|
|
|
import type { Vector2d } from './types.ts';
|
2025-08-20 09:39:20 +12:00
|
|
|
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;
|
2020-06-18 16:41:54 -05:00
|
|
|
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;
|
2020-06-18 16:41:54 -05:00
|
|
|
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) {
|
2020-06-18 16:41:54 -05:00
|
|
|
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(
|
2020-06-18 16:41:54 -05:00
|
|
|
(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);
|
2020-06-18 16:41:54 -05:00
|
|
|
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,
|
2020-06-18 16:41:54 -05:00
|
|
|
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(
|
2020-06-18 16:41:54 -05:00
|
|
|
(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;
|
|
|
|
}
|
|
|
|
|
2019-11-12 15:10:36 -05:00
|
|
|
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;
|
2019-09-05 13:01:20 -05:00
|
|
|
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);
|
2022-08-05 10:57:36 -05:00
|
|
|
|
|
|
|
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
|
|
|
});
|
2022-08-05 10:57:36 -05: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,
|
2020-06-18 16:41:54 -05:00
|
|
|
evt: evt,
|
2019-08-04 14:38:57 +07:00
|
|
|
},
|
|
|
|
true
|
|
|
|
);
|
2019-09-05 13:01:20 -05:00
|
|
|
}
|
|
|
|
if (elem.dragStatus !== 'dragging') {
|
2019-08-04 14:38:57 +07:00
|
|
|
DD._dragElements.delete(key);
|
|
|
|
}
|
|
|
|
});
|
2020-06-18 16:41:54 -05:00
|
|
|
},
|
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);
|
2025-08-24 10:37:23 +00:00
|
|
|
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);
|
2025-08-24 10:37:23 +00:00
|
|
|
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);
|
2025-08-24 10:37:23 +00:00
|
|
|
window.addEventListener('pointerup', DD._endDragAfter, false);
|
|
|
|
window.addEventListener('pointercancel', DD._endDragAfter, false);
|
2019-01-01 15:59:27 -05:00
|
|
|
}
|