konva
konva copied to clipboard
[feat] Allow disable DD
Currently DD always attached to document in module load phase, that dissalow force isBrowser = false for some reason (when there are a virtual events):
https://github.com/konvajs/konva/blob/3219008dfa1de8cfdbfeeb444f38264878df033e/src/DragAndDrop.ts#L152
and there are not ways to detach it from Konva without hacks.
Maybe can be expose a method for detaching/attaching back? Like:
DD.attach = () => {
if (!Konva.isBrowser || DD._attached) return;
DD._attached = true;
window.addEventListener('mouseup', DD._endDragBefore, true);
window.addEventListener('touchend', DD._endDragBefore, true);
window.addEventListener('mousemove', DD._drag);
window.addEventListener('touchmove', DD._drag);
window.addEventListener('mouseup', DD._endDragAfter, false);
window.addEventListener('touchend', DD._endDragAfter, false);
}
DD.detach = () => {
if (!Konva.isBrowser || !DD._attached) return;
DD._attached = false;
window.removeEventListener('mouseup', DD._endDragBefore, true);
window.removeEventListener('touchend', DD._endDragBefore, true);
window.removeEventListener('mousemove', DD._drag);
window.removeEventListener('touchmove', DD._drag);
window.removeEventListener('mouseup', DD._endDragAfter, false);
window.removeEventListener('touchend', DD._endDragAfter, false);
}
Why do you need it? What is your use with virtual events?
We not use a canvas as canvas, we use it as image source and have more that one canvas instances as panels, BUT we have a main canvas for webgl context that capture events for lookup. Syntetic events is proxed to each stage instance. Our current code is:
import Konva from 'konva';
import { RootState } from 'react-ogl';
import { createRef, MutableRefObject } from 'react';
export const PROXED_EVENTS = ['pointermove', 'pointerdown', 'pointerup', 'pointerover', 'pointerout'] as const;
(Konva as any).isBrowser = false;
/**
* Forward simulated events to canvas scope to specific stage
*/
export const forwardEvents = (event: PointerEvent | WheelEvent, stage: Konva.Stage) => {
if (!event || !stage) {
return;
}
const { type } = event;
const syntEvent = {
type,
clientX: event.clientX,
clientY: event.clientY,
// for wheel
deltaX: (event as any).deltaX ?? 0,
deltaY: (event as any).deltaY ?? 0,
};
if (type === 'pointerup') {
Konva.DD._endDragBefore(syntEvent);
}
if (type === 'pointermove') {
Konva.DD._drag(syntEvent);
}
// fire as mouse when pointer
type.includes('pointer') &&
stage[`_${type}`]?.({
...syntEvent,
type: type.replace('pointer', 'mouse'),
});
// fire as pointer
stage[`_${type}`]?.(syntEvent);
// fire drag after AFTER up event
if (type === 'pointerup') {
Konva.DD._endDragAfter(syntEvent);
}
};
export const attachEventProxy = (context: EventTarget, stage: Konva.Stage) => {
const scope = (event) => forwardEvents(event, stage);
PROXED_EVENTS.forEach((type) => context.addEventListener(type, scope));
return () => PROXED_EVENTS.forEach((type) => context.removeEventListener(type, scope));
};
/**
* Path Konva for use in OGL state and XR, because RAF must be from rendere
* Allow to use OffscrenCanvas wehn possible and disable DD
*/
export const patchKonvaJS = (state: RootState) => {
if (Konva.Util['@__pathed__']) {
return;
}
let konvaQueue: Array<(time: number) => void> = [];
/**
* Use Konva path, raf sould be requiested in valid phase
*/
Konva.Util.requestAnimFrame = (callback: any) => konvaQueue.push(callback);
Konva.Util['@__pathed__'] = true;
const refCallback: MutableRefObject<any> = createRef();
refCallback.current = (_state, time) => {
// update konva frames
if (konvaQueue.length) {
const q = konvaQueue;
konvaQueue = [];
q.forEach((c) => c && c(time));
}
};
// and register
state.subscribe(refCallback);
// try to use offscreen canvas
// eslint-disable-next-line no-restricted-globals
if ((self as any).OffscreenCanvas) {
Konva.Util.createCanvasElement = () => {
// eslint-disable-next-line no-restricted-globals
const canvas = new (self as any).OffscreenCanvas(1, 1);
canvas.style = {};
return canvas;
};
}
// allso detach events
// becuase it will corrupt state
{
const { DD } = Konva;
window.removeEventListener('mouseup', DD._endDragBefore, true);
window.removeEventListener('touchend', DD._endDragBefore, true);
window.removeEventListener('mousemove', DD._drag);
window.removeEventListener('touchmove', DD._drag);
window.removeEventListener('mouseup', DD._endDragAfter, false);
window.removeEventListener('touchend', DD._endDragAfter, false);
}
};
So, like this: each panel is new stage + layer for independent update, and each stage can has draggables. Panelles is curved, placed in 3D space, this is why need to proxy drag events from synthetic events, that created from raycast result on specific plane. 2D plan in FPS 3D space. Where mouse events used for camera movement and conflicts with drag events (because drag listen same context)
Well, I am not sure about that use case. I see this in the code:
// allso detach events // becuase it will corrupt state
What will it corrupt?
Is it possible for you to create a tiny demo that can reproduce the issue? Can you use current API and just manually unsubscribe as you do in the code?
Well, I am not sure about that use case. I see this in the code:
// allso detach events // becuase it will corrupt state
What will it corrupt?
Is it possible for you to create a tiny demo that can reproduce the issue? Can you use current API and just manually unsubscribe as you do in the code?
See: https://codesandbox.io/s/blazing-shape-c1eidt?file=/src/index.js
Konva mapped onto cube, but while you not click disable DD
then drag and drop will be bugged
In this demo I can't move the star. Because I can't click on it. Looks like you position calculation is not correct. I adopted demo to create dots on clicks: https://codesandbox.io/s/hopeful-zeh-xcg5rg?file=/src/index.js
So you can detach all DD events from the global by yourself, right?
Looks like you position calculation is not correct.
I forgot disable auto pixel ratio and size of Konva Stage had more than should be (i use screen with 1 DPR, thanks that notice this)
So you can detach all DD events from the global by yourself, right?
Yep, of course, but if DD Api will change - all hacks will down.
I see. So, personally, I am not very interested in this API. The workaround is working just fine. Even when attach/detach methods are added, you are still using private API here: stage[
_${type}]?.(syntEvent);
.
I have no plans to change DD API or stage._[event]
methods names.
Just update carefully.
If you REALLY want that as part of the core Konva API, then your initial proposal sounds good to me. Make a Pull Request.
Actually, it will be good to have an official demo of that use case. Because I remember several users with the same use case (drawing Konva canvas into 3d scene).
@eXponenta can you fix the demo, to make coordinates system work correctly?