@visactor/vrender-core
Version:
## Description
300 lines (293 loc) • 22 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", {
value: !0
}), exports.EventManager = void 0;
const vutils_1 = require("@visactor/vutils"), federated_event_1 = require("./federated-event"), constant_1 = require("./constant"), util_1 = require("./util"), PROPAGATION_LIMIT = 2048;
function isMouseLike(pointerType) {
return "mouse" === pointerType || "pen" === pointerType;
}
const DEFAULT_CLICK_INTERVAL = 200;
class EventManager {
constructor(root, config) {
this.dispatch = new vutils_1.EventEmitter, this.cursorTarget = null, this.pauseNotify = !1,
this.mappingState = {
trackingData: {}
}, this.eventPool = new Map, this.onPointerDown = (from, target) => {
if (!(from instanceof federated_event_1.FederatedPointerEvent)) return void vutils_1.Logger.getInstance().warn("EventManager cannot map a non-pointer event as a pointer event");
const e = this.createPointerEvent(from, from.type, target);
if (this.dispatchEvent(e, "pointerdown"), "touch" === e.pointerType) this.dispatchEvent(e, "touchstart"); else if (isMouseLike(e.pointerType)) {
const isRightButton = 2 === e.button;
this.dispatchEvent(e, isRightButton ? "rightdown" : "mousedown");
}
this.trackingData(from.pointerId).pressTargetsByButton[from.button] = e.composedPath(),
this.freeEvent(e);
}, this.onPointerMove = (from, target) => {
var _a, _b;
if (!(from instanceof federated_event_1.FederatedPointerEvent)) return void vutils_1.Logger.getInstance().warn("EventManager cannot map a non-pointer event as a pointer event");
const e = this.createPointerEvent(from, from.type, target), isMouse = isMouseLike(e.pointerType), trackingData = this.trackingData(from.pointerId), outTarget = this.findMountedTarget(trackingData.overTargets);
if (trackingData.overTargets && outTarget && outTarget !== this.rootTarget && outTarget !== e.target) {
const outType = "mousemove" === from.type ? "mouseout" : "pointerout", outEvent = this.createPointerEvent(from, outType, outTarget || void 0);
if (this.dispatchEvent(outEvent, "pointerout"), isMouse && this.dispatchEvent(outEvent, "mouseout"),
!e.composedPath().includes(outTarget)) {
const leaveEvent = this.createPointerEvent(from, "pointerleave", outTarget || void 0);
for (leaveEvent.eventPhase = leaveEvent.AT_TARGET; leaveEvent.target && !e.composedPath().includes(leaveEvent.target); ) leaveEvent.currentTarget = leaveEvent.target,
this.notifyTarget(leaveEvent), isMouse && this.notifyTarget(leaveEvent, "mouseleave"),
leaveEvent.target = leaveEvent.target.parent;
this.freeEvent(leaveEvent);
}
this.freeEvent(outEvent);
}
if (outTarget !== e.target) {
const overType = "mousemove" === from.type ? "mouseover" : "pointerover", overEvent = this.clonePointerEvent(e, overType);
this.dispatchEvent(overEvent, "pointerover"), isMouse && this.dispatchEvent(overEvent, "mouseover");
let overTargetAncestor = null == outTarget ? void 0 : outTarget.parent;
for (;overTargetAncestor && overTargetAncestor !== this.rootTarget.parent && overTargetAncestor !== e.target; ) overTargetAncestor = overTargetAncestor.parent;
if (!overTargetAncestor || overTargetAncestor === this.rootTarget.parent) {
const enterEvent = this.clonePointerEvent(e, "pointerenter");
enterEvent.eventPhase = enterEvent.AT_TARGET;
let currentTarget = enterEvent.target;
const outTargetAncestors = new Set;
let ancestor = outTarget;
for (;ancestor && ancestor !== this.rootTarget; ) outTargetAncestors.add(ancestor),
ancestor = ancestor.parent;
for (;currentTarget && currentTarget !== outTarget && currentTarget !== this.rootTarget.parent; ) outTargetAncestors.has(currentTarget) || (enterEvent.currentTarget = currentTarget,
this.notifyTarget(enterEvent), isMouse && this.notifyTarget(enterEvent, "mouseenter")),
currentTarget = currentTarget.parent;
this.freeEvent(enterEvent);
}
this.freeEvent(overEvent);
}
this.dispatchEvent(e, "pointermove"), "touch" === e.pointerType && this.dispatchEvent(e, "touchmove"),
isMouse && (this.dispatchEvent(e, "mousemove"), this.cursorTarget = e.target, this.cursor = (null === (_b = null === (_a = e.target) || void 0 === _a ? void 0 : _a.attribute) || void 0 === _b ? void 0 : _b.cursor) || this.rootTarget.getCursor()),
trackingData.overTargets = e.composedPath(), this.freeEvent(e);
}, this.onPointerOver = (from, target) => {
var _a, _b;
if (!(from instanceof federated_event_1.FederatedPointerEvent)) return void vutils_1.Logger.getInstance().warn("EventManager cannot map a non-pointer event as a pointer event");
const trackingData = this.trackingData(from.pointerId), e = this.createPointerEvent(from, from.type, target), isMouse = isMouseLike(e.pointerType);
this.dispatchEvent(e, "pointerover"), isMouse && this.dispatchEvent(e, "mouseover"),
"mouse" === e.pointerType && (this.cursorTarget = e.target, this.cursor = (null === (_b = null === (_a = e.target) || void 0 === _a ? void 0 : _a.attribute) || void 0 === _b ? void 0 : _b.cursor) || this.rootTarget.getCursor());
const enterEvent = this.clonePointerEvent(e, "pointerenter");
for (enterEvent.eventPhase = enterEvent.AT_TARGET; enterEvent.target && enterEvent.target !== this.rootTarget.parent; ) enterEvent.currentTarget = enterEvent.target,
this.notifyTarget(enterEvent), isMouse && this.notifyTarget(enterEvent, "mouseenter"),
enterEvent.target = enterEvent.target.parent;
trackingData.overTargets = e.composedPath(), this.freeEvent(e), this.freeEvent(enterEvent);
}, this.onPointerOut = (from, target) => {
if (!(from instanceof federated_event_1.FederatedPointerEvent)) return void vutils_1.Logger.getInstance().warn("EventManager cannot map a non-pointer event as a pointer event");
const trackingData = this.trackingData(from.pointerId);
if (trackingData.overTargets) {
const isMouse = isMouseLike(from.pointerType), outTarget = this.findMountedTarget(trackingData.overTargets), outEvent = this.createPointerEvent(from, "pointerout", outTarget || void 0);
this.dispatchEvent(outEvent), isMouse && this.dispatchEvent(outEvent, "mouseout");
const leaveEvent = this.createPointerEvent(from, "pointerleave", outTarget || void 0);
for (leaveEvent.eventPhase = leaveEvent.AT_TARGET; leaveEvent.target && leaveEvent.target !== this.rootTarget.parent; ) leaveEvent.currentTarget = leaveEvent.target,
this.notifyTarget(leaveEvent), isMouse && this.notifyTarget(leaveEvent, "mouseleave"),
leaveEvent.target = leaveEvent.target.parent;
trackingData.overTargets = [], this.freeEvent(outEvent), this.freeEvent(leaveEvent);
}
this.cursorTarget = null, this.cursor = "";
}, this.onPointerUp = (from, target) => {
var _a;
if (!(from instanceof federated_event_1.FederatedPointerEvent)) return void vutils_1.Logger.getInstance().warn("EventManager cannot map a non-pointer event as a pointer event");
const now = util_1.clock.now(), e = this.createPointerEvent(from, from.type, target);
if (this.dispatchEvent(e, "pointerup"), "touch" === e.pointerType) this.dispatchEvent(e, "touchend"); else if (isMouseLike(e.pointerType)) {
const isRightButton = 2 === e.button;
this.dispatchEvent(e, isRightButton ? "rightup" : "mouseup");
}
const trackingData = this.trackingData(from.pointerId), pressTarget = this.findMountedTarget(trackingData.pressTargetsByButton[from.button]);
let clickTarget = pressTarget;
if (pressTarget && !e.composedPath().includes(pressTarget)) {
let currentTarget = pressTarget;
for (;currentTarget && !e.composedPath().includes(currentTarget); ) {
if (e.currentTarget = currentTarget, this.notifyTarget(e, "pointerupoutside"), "touch" === e.pointerType) this.notifyTarget(e, "touchendoutside"); else if (isMouseLike(e.pointerType)) {
const isRightButton = 2 === e.button;
this.notifyTarget(e, isRightButton ? "rightupoutside" : "mouseupoutside");
}
currentTarget = currentTarget.parent;
}
delete trackingData.pressTargetsByButton[from.button], clickTarget = currentTarget;
}
if (clickTarget) {
const clickEvent = this.clonePointerEvent(e, "click");
clickEvent.target = clickTarget, clickEvent.path = [], clickEvent.detailPath = [],
trackingData.clicksByButton[from.button] || (trackingData.clicksByButton[from.button] = {
clickCount: 0,
target: clickEvent.target,
timeStamp: now
});
const clickHistory = trackingData.clicksByButton[from.button];
clickHistory.target === clickEvent.target && now - clickHistory.timeStamp < (null !== (_a = this._config.clickInterval) && void 0 !== _a ? _a : 200) ? ++clickHistory.clickCount : clickHistory.clickCount = 1,
clickHistory.target = clickEvent.target, clickHistory.timeStamp = now, clickEvent.detail = clickHistory.clickCount,
isMouseLike(clickEvent.pointerType) ? (this.dispatchEvent(clickEvent, "click"),
2 === clickHistory.clickCount && this.dispatchEvent(clickEvent, "dblclick")) : "touch" === clickEvent.pointerType && this._config.supportsTouchEvents && (this.dispatchEvent(clickEvent, "tap"),
2 === clickHistory.clickCount && this.dispatchEvent(clickEvent, "dbltap")), this.dispatchEvent(clickEvent, "pointertap"),
this.freeEvent(clickEvent);
}
this.freeEvent(e);
}, this.onPointerUpOutside = (from, target) => {
if (!(from instanceof federated_event_1.FederatedPointerEvent)) return void vutils_1.Logger.getInstance().warn("EventManager cannot map a non-pointer event as a pointer event");
const trackingData = this.trackingData(from.pointerId), pressTarget = this.findMountedTarget(trackingData.pressTargetsByButton[from.button]), e = this.createPointerEvent(from, from.type, target);
if (pressTarget) {
let currentTarget = pressTarget;
for (;currentTarget; ) e.currentTarget = currentTarget, this.notifyTarget(e, "pointerupoutside"),
"touch" === e.pointerType ? this.notifyTarget(e, "touchendoutside") : isMouseLike(e.pointerType) && this.notifyTarget(e, 2 === e.button ? "rightupoutside" : "mouseupoutside"),
currentTarget = currentTarget.parent;
delete trackingData.pressTargetsByButton[from.button];
}
this.freeEvent(e);
}, this.onWheel = (from, target) => {
if (!(from instanceof federated_event_1.FederatedWheelEvent)) return void vutils_1.Logger.getInstance().warn("EventManager cannot map a non-wheel event as a wheel event");
const wheelEvent = this.createWheelEvent(from, target);
this.dispatchEvent(wheelEvent), this.freeEvent(wheelEvent);
}, this.rootTarget = root, this.mappingTable = {}, this._config = Object.assign({
clickInterval: 200
}, config), this.addEventMapping("pointerdown", this.onPointerDown), this.addEventMapping("pointermove", this.onPointerMove),
this.addEventMapping("pointerout", this.onPointerOut), this.addEventMapping("pointerleave", this.onPointerOut),
this.addEventMapping("pointerover", this.onPointerOver), this.addEventMapping("pointerup", this.onPointerUp),
this.addEventMapping("pointerupoutside", this.onPointerUpOutside), this.addEventMapping("wheel", this.onWheel);
}
addEventMapping(type, fn) {
this.mappingTable[type] || (this.mappingTable[type] = []), this.mappingTable[type].push({
fn: fn,
priority: 0
}), this.mappingTable[type].sort(((a, b) => a.priority - b.priority));
}
dispatchEvent(e, type) {
e.propagationStopped = !1, e.propagationImmediatelyStopped = !1, this.propagate(e, type),
this.dispatch.emit(type || e.type, e);
}
mapEvent(e) {
var _a, _b, _c, _d, _e, _f, _g;
if (!this.rootTarget) return;
const mappers = this.mappingTable[e.type];
let target;
const cacheKey = `${e.canvasX}-${e.canvasY}`;
if ((null === (_a = this._prePointTargetCache) || void 0 === _a ? void 0 : _a[cacheKey]) && (null === (_c = null === (_b = this._prePointTargetCache) || void 0 === _b ? void 0 : _b[cacheKey]) || void 0 === _c ? void 0 : _c.stage) && (null === (_e = null === (_d = this._prePointTargetCache) || void 0 === _d ? void 0 : _d[cacheKey]) || void 0 === _e ? void 0 : _e.stage.renderCount) === (null === (_f = this._prePointTargetCache) || void 0 === _f ? void 0 : _f.stageRenderCount) ? target = this._prePointTargetCache[cacheKey] : (target = this.pickTarget(e.viewX, e.viewY, e),
e.pickParams || (this._prePointTargetCache = {
[cacheKey]: target,
stageRenderCount: null !== (_g = null == target ? void 0 : target.stage.renderCount) && void 0 !== _g ? _g : -1
})), mappers) for (let i = 0, j = mappers.length; i < j; i++) mappers[i].fn(e, target); else vutils_1.Logger.getInstance().warn(`[EventManager]: Event mapping not defined for ${e.type}`);
}
propagate(e, type) {
if (!e.target) return;
const composedPath = e.composedPath();
e.eventPhase = e.CAPTURING_PHASE;
for (let i = 0, j = composedPath.length - 1; i < j; i++) if (e.currentTarget = composedPath[i],
this.notifyTarget(e, type), e.propagationStopped || e.propagationImmediatelyStopped) return;
if (e.eventPhase = e.AT_TARGET, e.currentTarget = e.target, this.notifyTarget(e, type),
!e.propagationStopped && !e.propagationImmediatelyStopped) {
e.eventPhase = e.BUBBLING_PHASE;
for (let i = composedPath.length - 2; i >= 0; i--) if (e.currentTarget = composedPath[i],
this.notifyTarget(e, type), e.propagationStopped || e.propagationImmediatelyStopped) return;
}
}
propagationPath(target) {
const propagationPath = [ target ];
for (let i = 0; i < 2048 && target !== this.rootTarget && target.parent; i++) {
if (!target.parent) throw new Error("Cannot find propagation path to disconnected target");
propagationPath.push(target.parent), target = target.parent;
}
return propagationPath.reverse(), propagationPath;
}
notifyTarget(e, type) {
if (this.pauseNotify) return;
type = null != type ? type : e.type;
const key = e.eventPhase === e.CAPTURING_PHASE || e.eventPhase === e.AT_TARGET ? `${type}capture` : type;
this.notifyListeners(e, key), e.eventPhase === e.AT_TARGET && this.notifyListeners(e, type);
}
findMountedTarget(propagationPath) {
if (!propagationPath) return null;
let currentTarget = propagationPath[0];
for (let i = 1; i < propagationPath.length && propagationPath[i].parent === currentTarget; i++) currentTarget = propagationPath[i];
return currentTarget;
}
createPointerEvent(from, type, target) {
var _a, _b;
const event = this.allocateEvent(federated_event_1.FederatedPointerEvent);
return this.copyPointerData(from, event), this.copyMouseData(from, event), this.copyData(from, event),
event.nativeEvent = from.nativeEvent, event.originalEvent = from, event.target = target || this.pickTarget(null !== (_a = event.viewX) && void 0 !== _a ? _a : event.global.x, null !== (_b = event.viewY) && void 0 !== _b ? _b : event.global.y, event),
"string" == typeof type && (event.type = type), event;
}
createWheelEvent(from, target) {
var _a, _b;
const event = this.allocateEvent(federated_event_1.FederatedWheelEvent);
return this.copyWheelData(from, event), this.copyMouseData(from, event), this.copyData(from, event),
event.nativeEvent = from.nativeEvent, event.originalEvent = from, event.target = target || this.pickTarget(null !== (_a = event.viewX) && void 0 !== _a ? _a : event.global.x, null !== (_b = event.viewY) && void 0 !== _b ? _b : event.global.y, event),
event;
}
clonePointerEvent(from, type) {
const event = this.allocateEvent(federated_event_1.FederatedPointerEvent);
event.nativeEvent = from.nativeEvent, event.originalEvent = from.originalEvent,
this.copyPointerData(from, event), this.copyMouseData(from, event), this.copyData(from, event),
event.target = from.target, event.path = from.composedPath().slice();
const p = from.composedDetailPath();
return event.detailPath = p && p.slice(), event.type = null != type ? type : event.type,
event;
}
copyWheelData(from, to) {
to.deltaMode = from.deltaMode, to.deltaX = from.deltaX, to.deltaY = from.deltaY,
to.deltaZ = from.deltaZ;
}
copyPointerData(from, to) {
from instanceof federated_event_1.FederatedPointerEvent && to instanceof federated_event_1.FederatedPointerEvent && (to.pointerId = from.pointerId,
to.width = from.width, to.height = from.height, to.isPrimary = from.isPrimary, to.pointerType = from.pointerType,
to.pressure = from.pressure, to.tangentialPressure = from.tangentialPressure, to.tiltX = from.tiltX,
to.tiltY = from.tiltY, to.twist = from.twist);
}
copyMouseData(from, to) {
from instanceof federated_event_1.FederatedMouseEvent && to instanceof federated_event_1.FederatedMouseEvent && (to.altKey = from.altKey,
to.button = from.button, to.buttons = from.buttons, to.ctrlKey = from.ctrlKey, to.shiftKey = from.shiftKey,
to.metaKey = from.metaKey, [ "client", "movement", "canvas", "screen", "global", "offset", "viewport" ].forEach((key => {
to[key].x = from[key].x, to[key].y = from[key].y;
})));
}
copyData(from, to) {
to.isTrusted = from.isTrusted, to.srcElement = from.srcElement, to.timeStamp = util_1.clock.now(),
to.type = from.type, to.detail = from.detail, to.view = from.view, to.which = from.which,
to.layer.x = from.layer.x, to.layer.y = from.layer.y, to.page.x = from.page.x, to.page.y = from.page.y,
to.pickParams = from.pickParams;
}
trackingData(id) {
return this.mappingState.trackingData[id] || (this.mappingState.trackingData[id] = {
pressTargetsByButton: {},
clicksByButton: {},
overTarget: null
}), this.mappingState.trackingData[id];
}
allocateEvent(constructor) {
var _a;
this.eventPool.has(constructor) || this.eventPool.set(constructor, []);
const event = (null === (_a = this.eventPool.get(constructor)) || void 0 === _a ? void 0 : _a.pop()) || new constructor(this);
return event.eventPhase = event.NONE, event.currentTarget = null, event.path = [],
event.detailPath = [], event.target = null, event;
}
freeEvent(event) {
var _a;
if (event.manager !== this) throw new Error("It is illegal to free an event not managed by this EventManager!");
const constructor = event.constructor;
this.eventPool.has(constructor) || this.eventPool.set(constructor, []), null === (_a = this.eventPool.get(constructor)) || void 0 === _a || _a.push(event);
}
notifyListeners(e, type) {
const listeners = e.currentTarget._events[type];
if (listeners) if ("fn" in listeners) listeners.once && e.currentTarget.removeEventListener(type, listeners.fn, {
once: !0
}), listeners.fn.call(listeners.context, e); else for (let i = 0, j = listeners.length; i < j && !e.propagationImmediatelyStopped; i++) listeners[i].once && e.currentTarget.removeEventListener(type, listeners[i].fn, {
once: !0
}), listeners[i].fn.call(listeners[i].context, e);
this.emitDelegation(e, type);
}
emitDelegation(e, type) {
const listeners = e.currentTarget._events[constant_1.WILDCARD];
if (listeners) if ("fn" in listeners) listeners.fn.call(listeners.context, e, type); else for (let i = 0, j = listeners.length; i < j && !e.propagationImmediatelyStopped; i++) listeners[i].fn.call(listeners[i].context, e, type);
}
pickTarget(x, y, e) {
let target;
const pickResult = this.rootTarget.pick(x, y);
return target = pickResult && pickResult.graphic ? pickResult.graphic : pickResult && pickResult.group ? pickResult.group : this.rootTarget.AABBBounds.contains(x, y) ? this.rootTarget : null,
e && (e.pickParams = pickResult.params), target;
}
release() {
this.dispatch.removeAllListeners(), this.eventPool.clear(), this.rootTarget = null,
this.mappingTable = null, this.mappingState = null, this.cursorTarget = null;
}
}
exports.EventManager = EventManager;
//# sourceMappingURL=event-manager.js.map