UNPKG

@visactor/vrender-core

Version:
300 lines (293 loc) 22 kB
"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