jstd-web/node_modules/diagram-js/lib/features/touch/TouchInteractionEvents.js

373 lines
8.2 KiB
JavaScript
Raw Normal View History

2025-11-25 15:23:22 +08:00
import {
forEach
} from 'min-dash';
import {
event as domEvent,
closest as domClosest
} from 'min-dom';
import Hammer from 'hammerjs';
import {
toPoint
} from '../../util/Event';
/**
* @typedef {import('didi').Injector} Injector
*
* @typedef {import('../../core/Canvas').default} Canvas
* @typedef {import('../../core/ElementRegistry').default} ElementRegistry
* @typedef {import('../../core/EventBus').default} EventBus
* @typedef {import('../interaction-events/InteractionEvents').default} InteractionEvents
*/
var MIN_ZOOM = 0.2,
MAX_ZOOM = 4;
var mouseEvents = [
'mousedown',
'mouseup',
'mouseover',
'mouseout',
'click',
'dblclick'
];
function log() {
// console.log.apply(console, arguments);
}
function get(service, injector) {
return injector.get(service, false);
}
function stopEvent(event) {
event.preventDefault();
if (typeof event.stopPropagation === 'function') {
event.stopPropagation();
} else if (event.srcEvent && typeof event.srcEvent.stopPropagation === 'function') {
// iPhone & iPad
event.srcEvent.stopPropagation();
}
if (typeof event.stopImmediatePropagation === 'function') {
event.stopImmediatePropagation();
}
}
function createTouchRecognizer(node) {
function stopMouse(event) {
forEach(mouseEvents, function(e) {
domEvent.bind(node, e, stopEvent, true);
});
}
function allowMouse(event) {
setTimeout(function() {
forEach(mouseEvents, function(e) {
domEvent.unbind(node, e, stopEvent, true);
});
}, 500);
}
domEvent.bind(node, 'touchstart', stopMouse, true);
domEvent.bind(node, 'touchend', allowMouse, true);
domEvent.bind(node, 'touchcancel', allowMouse, true);
// A touch event recognizer that handles
// touch events only (we know, we can already handle
// mouse events out of the box)
var recognizer = new Hammer.Manager(node, {
inputClass: Hammer.TouchInput,
recognizers: [],
domEvents: true
});
var tap = new Hammer.Tap();
var pan = new Hammer.Pan({ threshold: 10 });
var press = new Hammer.Press();
var pinch = new Hammer.Pinch();
var doubleTap = new Hammer.Tap({ event: 'doubletap', taps: 2 });
pinch.requireFailure(pan);
pinch.requireFailure(press);
recognizer.add([ pan, press, pinch, doubleTap, tap ]);
recognizer.reset = function(force) {
var recognizers = this.recognizers,
session = this.session;
if (session.stopped) {
return;
}
log('recognizer', 'stop');
recognizer.stop(force);
setTimeout(function() {
var i, r;
log('recognizer', 'reset');
for (i = 0; (r = recognizers[i]); i++) {
r.reset();
r.state = 8; // FAILED STATE
}
session.curRecognizer = null;
}, 0);
};
recognizer.on('hammer.input', function(event) {
if (event.srcEvent.defaultPrevented) {
recognizer.reset(true);
}
});
return recognizer;
}
/**
* A plugin that provides touch events for elements.
*
* @param {Injector} injector
* @param {Canvas} canvas
* @param {EventBus} eventBus
* @param {ElementRegistry} elementRegistry
* @param {InteractionEvents} interactionEvents
*/
export default function TouchInteractionEvents(
injector, canvas, eventBus,
elementRegistry, interactionEvents) {
// optional integrations
var dragging = get('dragging', injector),
move = get('move', injector),
contextPad = get('contextPad', injector),
palette = get('palette', injector);
// the touch recognizer
var recognizer;
function handler(type, buttonType) {
return function(event) {
log('element', type, event);
var gfx = getGfx(event.target),
element = gfx && elementRegistry.get(gfx);
// translate into an actual mouse click event
if (buttonType) {
event.srcEvent.button = buttonType;
}
return interactionEvents.fire(type, event, element);
};
}
function getGfx(target) {
var node = domClosest(target, 'svg, .djs-element', true);
return node;
}
function initEvents(svg) {
// touch recognizer
recognizer = createTouchRecognizer(svg);
function startGrabCanvas(event) {
log('canvas', 'grab start');
var lx = 0, ly = 0;
function update(e) {
var dx = e.deltaX - lx,
dy = e.deltaY - ly;
canvas.scroll({ dx: dx, dy: dy });
lx = e.deltaX;
ly = e.deltaY;
}
function end(e) {
recognizer.off('panmove', update);
recognizer.off('panend', end);
recognizer.off('pancancel', end);
log('canvas', 'grab end');
}
recognizer.on('panmove', update);
recognizer.on('panend', end);
recognizer.on('pancancel', end);
}
function startGrab(event) {
var gfx = getGfx(event.target),
element = gfx && elementRegistry.get(gfx);
// recognizer
if (move && canvas.getRootElement() !== element) {
log('element', 'move start', element, event, true);
return move.start(event, element, true);
} else {
startGrabCanvas(event);
}
}
function startZoom(e) {
log('canvas', 'zoom start');
var zoom = canvas.zoom(),
mid = e.center;
function update(e) {
var ratio = 1 - (1 - e.scale) / 1.50,
newZoom = Math.max(MIN_ZOOM, Math.min(MAX_ZOOM, ratio * zoom));
canvas.zoom(newZoom, mid);
stopEvent(e);
}
function end(e) {
recognizer.off('pinchmove', update);
recognizer.off('pinchend', end);
recognizer.off('pinchcancel', end);
recognizer.reset(true);
log('canvas', 'zoom end');
}
recognizer.on('pinchmove', update);
recognizer.on('pinchend', end);
recognizer.on('pinchcancel', end);
}
recognizer.on('tap', handler('element.click'));
recognizer.on('doubletap', handler('element.dblclick', 1));
recognizer.on('panstart', startGrab);
recognizer.on('press', startGrab);
recognizer.on('pinchstart', startZoom);
}
if (dragging) {
// simulate hover during dragging
eventBus.on('drag.move', function(event) {
var originalEvent = event.originalEvent;
if (!originalEvent || originalEvent instanceof MouseEvent) {
return;
}
var position = toPoint(originalEvent);
// this gets really expensive ...
var node = document.elementFromPoint(position.x, position.y),
gfx = getGfx(node),
element = gfx && elementRegistry.get(gfx);
if (element !== event.hover) {
if (event.hover) {
dragging.out(event);
}
if (element) {
dragging.hover({ element: element, gfx: gfx });
event.hover = element;
event.hoverGfx = gfx;
}
}
});
}
if (contextPad) {
eventBus.on('contextPad.create', function(event) {
var node = event.pad.html;
// touch recognizer
var padRecognizer = createTouchRecognizer(node);
padRecognizer.on('panstart', function(event) {
log('context-pad', 'panstart', event);
contextPad.trigger('dragstart', event, true);
});
padRecognizer.on('press', function(event) {
log('context-pad', 'press', event);
contextPad.trigger('dragstart', event, true);
});
padRecognizer.on('tap', function(event) {
log('context-pad', 'tap', event);
contextPad.trigger('click', event);
});
});
}
if (palette) {
eventBus.on('palette.create', function(event) {
var node = event.container;
// touch recognizer
var padRecognizer = createTouchRecognizer(node);
padRecognizer.on('panstart', function(event) {
log('palette', 'panstart', event);
palette.trigger('dragstart', event, true);
});
padRecognizer.on('press', function(event) {
log('palette', 'press', event);
palette.trigger('dragstart', event, true);
});
padRecognizer.on('tap', function(event) {
log('palette', 'tap', event);
palette.trigger('click', event);
});
});
}
eventBus.on('canvas.init', function(event) {
initEvents(event.svg);
});
}
TouchInteractionEvents.$inject = [
'injector',
'canvas',
'eventBus',
'elementRegistry',
'interactionEvents',
'touchFix'
];