Umfeld Cocos-Ersteller 2.4 ZusammenfassungModul Funktion Ein Mechanismus zur Ereignisüberwachung sollte ein wesentlicher Bestandteil aller Spiele sein. Egal, ob Sie auf eine Schaltfläche klicken oder ein Objekt ziehen, die Ereignisüberwachung und -verteilung ist von entscheidender Bedeutung. Zugehörige DokumenteSowohl CCGame als auch CCInputManager sind an der Registrierung von Ereignissen beteiligt, sie sind jedoch für unterschiedliche Teile verantwortlich. QuellcodeanalyseWie gelangen Ereignisse zur Engine (vom Browser)? Um diese Frage zu beantworten, müssen wir verstehen, woher die Interaktion zwischen der Engine und dem Browser kommt. CCGame.js// Ereignis system_initEvents initialisieren: function () { var win = Fenster, hiddenPropName; //_ Systemereignisse registrieren //Systemereignisse registrieren, hier rufen wir die Methode CCInputManager auf, wenn (this.config.registerSystemEvent) _cc.inputManager.registerSystemEvent(diese.Leinwand); // document.hidden bedeutet, dass die Seite ausgeblendet ist. Das folgende if wird verwendet, um die Browserkompatibilität zu gewährleisten, if (typeof document.hidden !== 'undefined') { hiddenPropName = "versteckt"; } sonst wenn (Typ von document.mozHidden !== 'undefiniert') { hiddenPropName = "mozHidden"; } sonst wenn (Typ von Dokument.msHidden !== 'undefined') { hiddenPropName = "msHidden"; } sonst wenn (Typ von Dokument.webkitHidden !== 'undefiniert') { hiddenPropName = "webkitHidden"; } // Ist die aktuelle Seite ausgeblendet? var hidden = false; //Rückruf, wenn die Seite ausgeblendet ist und das Ereignis game.EVENT_HIDE ausgibt Funktion onHidden () { wenn (!versteckt) { versteckt = wahr; Spiel.emit(Spiel.EVENT_HIDE); } } //_ Um die Onshow-API an die meisten Plattformen anzupassen. // Zur Anpassung an die Onshow-API der meisten Plattformen. Es sollte sich auf den Teil zur Parameterübergabe beziehen … // Rückruf, wenn die Seite sichtbar ist, und Ausgabe der Ereignisfunktion game.EVENT_SHOW onShown (arg0, arg1, arg2, arg3, arg4) { if (versteckt) { versteckt = falsch; spiel.emit(spiel.EVENT_SHOW, arg0, arg1, arg2, arg3, arg4); } } // Wenn der Browser versteckte Eigenschaften unterstützt, registrieren Sie das Ereignis zur Änderung des visuellen Seitenstatus, wenn (hiddenPropName) { var Änderungsliste = [ "Sichtbarkeitsänderung", "mozsichtbarkeitsänderung", "msvisibilitychange", "WebKitSichtbarkeitsänderung", "qbrowserVisibilityChange" ]; // Aus Kompatibilitätsgründen durchlaufe die Ereignisse in der Liste oben // Nachdem sich der verborgene Status geändert hat, rufe die Callback-Funktion onHidden/onShown basierend auf dem sichtbaren Status auf für (var i = 0; i < changeList.length; i++) { document.addEventListener(changeList[i], Funktion (Ereignis) { var sichtbar = Dokument[hiddenPropName]; //_QQ App sichtbar = sichtbar || Ereignis["versteckt"]; wenn (sichtbar) beiVersteckt(); anders aufAnzeigen(); }); } } // Einige Kompatibilitätscodes zu Änderungen des visuellen Status der Seite werden hier ausgelassen. // Registrieren Sie Ausblend- und Anzeigeereignisse und pausieren oder starten Sie die Hauptlogik des Spiels neu. dies.auf(spiel.EVENT_HIDE, funktion () { spiel.pause(); }); dies.auf(spiel.EVENT_SHOW, funktion () { spiel.fortsetzen(); }); } Tatsächlich gibt es nur ein bisschen Kerncode ... Um die Kompatibilität mit verschiedenen Plattformen aufrechtzuerhalten,
Werfen wir einen Blick auf CCInputManager. CCInputManager.js//Registrieren Sie das Systemereigniselement auf Canvas registerSystemEvent (Element) { wenn(dieses._istRegisterEvent) zurückgeben; // Bereits registriert, direkt zurück dies._glView = cc.view; lass selfPointer = dies; Lassen Sie canvasBoundingRect = this._canvasBoundingRect; // Auf Größenänderungsereignisse achten und dies ändern._canvasBoundingRect window.addEventListener('Größe ändern', this._updateCanvasBoundingRect.bind(this)); lassen Sie Verbot = sys.isMobile; let supportMouse = ('Maus' in sys.capabilities); // Ob Touchlet unterstützt werden soll supportTouches = ('touches' in sys.capabilities); // Registrierungscode für Mausereignisse weggelassen //_Touch-Ereignis registrieren // Touch-Ereignisse registrieren if (supportTouches) { // Veranstaltungskarte let _touchEventsMap = { "touchstart": Funktion (touchesToHandle) { selfPointer.handleTouchesBegin(touchesToHandle); element.fokus(); }, "touchmove": Funktion (berührtZuHandle) { selfPointer.handleTouchesMove(touchesToHandle); }, "touchend": Funktion (touchesToHandle) { selfPointer.handleTouchesEnd(touchesToHandle); }, "touchcancel": Funktion (touchesToHandle) { selfPointer.handleTouchesCancel(touchesToHandle); } }; // Durchsuche die Karte, um Ereignisse zu registrieren let registerTouchEvent = function (eventName) { let handler = _touchEventsMap[Ereignisname]; // Ereignisse im Canvas registrieren element.addEventListener(eventName, (function(event) { wenn (!event.changedTouches) return; lass body = Dokument.body; // Den Versatz berechnen canvasBoundingRect.adjustedLeft = canvasBoundingRect.left - (body.scrollLeft || window.scrollX || 0); canvasBoundingRect.adjustedTop = canvasBoundingRect.top – (body.scrollTop || window.scrollY || 0); // Holen Sie den Berührungspunkt vom Ereignis und rufen Sie die Rückruffunktion handler(selfPointer.getTouchesByEvent(event, canvasBoundingRect)) auf. // Stoppen Sie das Aufsteigen des Ereignisses event.stopPropagation(); event.preventDefault(); }), FALSCH); }; für (let eventName in _touchEventsMap) { registerTouchEvent(Ereignisname); } } // Ändern Sie die Eigenschaft, um anzuzeigen, dass die Ereignisregistrierung abgeschlossen wurde. this._isRegisterEvent = true; } Im Code wird vor allem eine Reihe nativer Events wie Touchstart registriert. Im Event-Callback werden die Funktionen in selfPointer(=this) zur Verarbeitung aufgerufen. Hier verwenden wir das Touchstart-Ereignis als Beispiel, nämlich die Funktion handleTouchesBegin. // Behandeln Sie das Touchstart-Ereignis handleTouchesBegin (touches) { lass selTouch, index, curTouch, touchID, handleTouches = [], locTouchIntDict = this._touchesIntegerDict, jetzt = sys.now(); // Berührungspunkte durchlaufen für (let i = 0, len = touches.length; i < len; i++) { // Aktueller Berührungspunkt selTouch = touches[i]; // Berührungspunkt-ID touchID = selTouch.getID(); // Die Position des Berührungspunkts in der Berührungspunktliste (this._touches) index = locTouchIntDict[touchID]; // Wenn der Index nicht abgerufen wird, bedeutet dies, dass es sich um einen neuen Berührungspunkt handelt (gerade gedrückt) wenn (index == null) { // Einen ungenutzten Index abrufen let ungenutzterIndex = this._getUnUsedIndex(); //Kann nicht abgerufen werden, es tritt ein Fehler auf. Möglicherweise wurde die maximale Anzahl unterstützter Berührungspunkte überschritten. wenn (unbenutzterIndex === -1) { cc.logID(2300, unbenutzter Index); weitermachen; } //_curTouch = this._touches[unbenutzterIndex] = selTouch; //Berührungspunkte speichern curTouch = this._touches[unusedIndex] = new cc.Touch(selTouch._point.x, selTouch._point.y, selTouch.getID()); curTouch._lastModified = jetzt; curTouch._setPrevPoint(selTouch._prevPoint); locTouchIntDict[touchID] = unbenutzter Index; // Zur Liste der Berührungspunkte hinzufügen, die verarbeitet werden müssen handleTouches.push(curTouch); } } //Bei einem neuen Kontakt ein Touch-Event generieren und an den EventManager verteilen wenn (handleTouches.length > 0) { // Diese Methode verarbeitet die Position des Berührungspunkts entsprechend der Skala this._glView._convertTouchesWithScale(handleTouches); let touchEvent = neues cc.Event.EventTouch(handleTouches); touchEvent._eventCode = cc.Event.EventTouch.BEGAN; eventManager.dispatchEvent(touchEvent); } }, In der Funktion wird ein Teil des Codes zum Filtern verwendet, ob es neue Berührungspunkte gibt, und der andere Teil wird zum Verarbeiten und Verteilen von Ereignissen (falls erforderlich) verwendet. Wie gelangen Ereignisse von der Engine zu den Knoten?Die Weitergabe von Ereignissen an Knoten erfolgt hauptsächlich in der Klasse CCEventManager. Beinhaltet Speicherereignis-Listener, Verteilungsereignisse usw. Beginnen wir mit _dispatchTouchEvent als Einstiegspunkt. CCEventManager.js// Versenden von events_dispatchTouchEvent: function (event) { // Touch-Listener sortieren // TOUCH_ONE_BY_ONE: Typ des Touch-Event-Listeners, Touch-Punkte werden einzeln versendet // TOUCH_ALL_AT_ONCE: Touch-Punkte werden alle auf einmal versendet this._sortEventListeners(ListenerID.TOUCH_ONE_BY_ONE); dies._sortEventListeners(ListenerID.TOUCH_ALL_AT_ONCE); // Listener-Liste abrufen var oneByOneListeners = this._getListeners(ListenerID.TOUCH_ONE_BY_ONE); var allAtOnceListeners = this._getListeners(ListenerID.TOUCH_ALL_AT_ONCE); //_ Wenn keine Touch-Listener vorhanden sind, direkt zurückkehren. // Wenn kein Listener vorhanden ist, einfach zurückkehren. wenn (null === oneByOneListeners und null === allAtOnceListeners) zurückkehren; // Variablen speichern var originalTouches = event.getTouches(), mutableTouches = cc.js.array.copy(originalTouches); var oneByOneArgsObj = {event: event, needsMutableSet: (oneByOneListeners && allAtOnceListeners), touches: mutableTouches, selTouch: null}; // //_ Verarbeite zuerst die Zielhandler // Wird nicht umkippen. Der Sinn besteht darin, zuerst einzelne Berührungsereignisse zu behandeln. wenn (oneByOneListeners) { // Kontakte durchlaufen und der Reihe nach verteilen for (var i = 0; i < originalTouches.length; i++) { event.currentTouch = originalTouches[i]; event._propagationStopped = event._propagationImmediateStopped = false; this._dispatchEventToListeners(oneByOneListeners, this._onTouchEventCallback, oneByOneArgsObj); } } // //_ Standardhandler verarbeiten 2. // Wird nicht umkippen. Es fühlt sich an, als ob die zweite Sache darin besteht, Multi-Touch-Ereignisse zu verarbeiten (alle auf einmal zu versenden). wenn (allAtOnceListeners und mutableTouches.length > 0) { this._dispatchEventToListeners(allAtOnceListeners, this._onTouchesEventCallback, {Ereignis: Ereignis, Berührungen: mutableTouches}); wenn (event.isStopped()) zurückkehren; } // Aktualisieren Sie die Touch-Listener-Liste, hauptsächlich um Listener zu entfernen und hinzuzufügen. this._updateTouchListeners(event); }, Die wichtigsten Aufgaben der Funktion sind Sortieren, Verteilen an die registrierte Listener-Liste und Aktualisieren der Listener-Liste. Nichts Besonderes. Sie fragen sich vielleicht, warum dieser Befehl so abrupt erfolgt? Ach, das ist das Wichtigste! Informationen zur Rolle der Sortierung finden Sie im offiziellen Dokument zur Übertragung von Berührungsereignissen. Durch diese Sortierung wird das Problem der Kontaktpunktzuordnung zwischen Knoten unterschiedlicher Ebenen/unterschiedlicher zIndex-Werte realisiert. Auf die Sortierung werde ich später noch eingehen, sie ist erstaunlich. /** * Ereignisse an Listener-Listen verteilen * @param {*} Listener Listener-Liste * @param {*} onEvent Ereignis-Callback * @param {*} eventOrArgs Ereignis/Parameter */ _dispatchEventToListeners: Funktion (Listener, bei Ereignis, Ereignis oder Argumente) { //Müssen Sie die Verteilung stoppen? var shouldStopPropagation = false; // Holen Sie sich einen Listener mit fester Priorität (Systemereignis) var fixedPriorityListeners = listeners.getFixedPriorityListeners(); // Holen Sie sich den Listener mit Szenengraph-Priorität (normalerweise sind die Listener, die wir hinzufügen, hier) var sceneGraphPriorityListeners = listeners.getSceneGraphPriorityListeners(); /** * Listener-Auslösereihenfolge: * Feste Prioritätsstufe < 0 * Szenengraph-Priorität * Feste Priorität > 0 */ var i = 0, j, selListener; if (fixedPriorityListeners) { //_ Priorität < 0 wenn (fixedPriorityListeners.length !== 0) { // Durchlaufe die Listener um Ereignisse zu verteilen für (; i < listeners.gt0Index; ++i) { selListener = fixedPriorityListeners[i]; // Wenn der Listener aktiviert und nicht angehalten ist und beim Event-Manager registriert wurde // Das letzte onEvent dient dazu, die Funktion _onTouchEventCallback zu verwenden, um Ereignisse an den Listener zu verteilen // onEvent gibt einen Booleschen Wert zurück, der angibt, ob es notwendig ist, mit der Verteilung von Ereignissen an nachfolgende Listener fortzufahren. Wenn dies zutrifft, beenden Sie die Verteilung, wenn (selListener.isEnabled() && !selListener._isPaused() && selListener._isRegistered() && onEvent(selListener, eventOrArgs)) { sollteStopPropagation = wahr; brechen; } } } } //Die Triggercodes der anderen beiden Prioritäten weglassen}, In der Funktion werden die Ereignisse durch Durchlaufen der Listener-Liste nacheinander verteilt. Ob die Verteilung fortgesetzt werden soll, wird anhand des Rückgabewerts von onEvent bestimmt. Nachdem ein Berührungsereignis von einem Knoten empfangen wurde, wird die Weiterleitung im Allgemeinen beendet. Anschließend wird die Logik der Blasenverteilung von diesem Knoten aus ausgeführt. Dies ist auch ein wichtiger Punkt, nämlich, dass nur ein Knoten auf das Berührungsereignis reagiert. Was die Priorität des Knotens betrifft, so ist dies der oben erwähnte Sortieralgorithmus. // Rückruf des Touch-Ereignisses. Verteilen Sie Ereignisse an listeners_onTouchEventCallback: function (listener, argsObj) { //_ Überspringen, wenn der Listener entfernt wurde. // Überspringen, wenn der Listener entfernt wurde. wenn (!listener._isRegistered()) gibt false zurück; var event = argsObj.event, selTouch = event.currentTouch; Ereignis.aktuellesTarget = listener._node; // isClaimed: Listener, ob das Ereignis beansprucht werden soll, var isClaimed = false, removedIdx; var getCode = event.getEventCode(), EventTouch = cc.Event.EventTouch; // Wenn das Ereignis ein Touch-Start-Ereignis ist, if (getCode === EventTouch.BEGAN) { // Wenn Multi-Touch nicht unterstützt wird und bereits ein Berührungspunkt vorhanden ist, if (!cc.macro.ENABLE_MULTI_TOUCH && eventManager._currentTouch) { // Wenn der Berührungspunkt von einem Knoten beansprucht wurde und der Knoten im Knotenbaum aktiv ist, wird das Ereignis nicht verarbeitet. let node = eventManager._currentTouchListener._node; if (Knoten && node.activeInHierarchy) { gibt false zurück; } } // Wenn der Listener ein entsprechendes Ereignis hat if (listener.onTouchBegan) { // Versuchen Sie, an den Listener zu verteilen, und geben Sie einen Booleschen Wert zurück, um anzugeben, ob der Listener das Ereignis beansprucht. isClaimed = listener.onTouchBegan(selTouch, event); // Wenn das Ereignis beansprucht und der Listener registriert ist, einige Daten speichern, if (isClaimed && listener._registered) { listener._claimedTouches.push(selTouch); eventManager._currentTouchListener = Listener; eventManager._currentTouch = selTouch; } } } // Wenn der Listener bereits eine Berührung beansprucht hat und die aktuelle Berührung vom aktuellen Listener beansprucht wird, sonst wenn (listener._claimedTouches.length > 0 && ((removedIdx = listener._claimedTouches.indexOf(selTouch)) !== -1)) { // Direkt mit nach Hause nehmen isClaimed = true; // Wenn Multi-Touch nicht unterstützt wird und ein Berührungspunkt vorhanden ist und es sich nicht um den aktuellen Berührungspunkt handelt, verarbeiten Sie das Ereignis nicht, wenn (!cc.macro.ENABLE_MULTI_TOUCH && eventManager._currentTouch && eventManager._currentTouch !== selTouch) { gibt false zurück; } // Ereignisse an Listener verteilen // Bei BEENDET oder ABGEBROCHEN müssen Sie die Kontakte im Listener und Event-Manager bereinigen, wenn (getCode === EventTouch.MOVED && listener.onTouchMoved) { listener.onTouchMoved(selTouch, Ereignis); } sonst wenn (getCode === EventTouch.ENDED) { wenn (listener.onTouchEnded) listener.onTouchEnded(selTouch, Ereignis); wenn (listener._registered) listener._claimedTouches.splice(entfernteIdx, 1); eventManager._clearCurTouch(); } sonst wenn (getCode === EventTouch.CANCELED) { wenn (listener.onTouchCancelled) listener.onTouchCancelled(selTouch, Ereignis); wenn (listener._registered) listener._claimedTouches.splice(entfernteIdx, 1); eventManager._clearCurTouch(); } } //_ Wenn das Ereignis gestoppt wurde, kehren Sie direkt zurück. // Wenn das Ereignis gestoppt wurde, direkt zurückkehren (stopPropagationImmediate() für das Ereignis aufrufen usw.) wenn (event.isStopped()) { eventManager._updateTouchListeners(Ereignis); gibt true zurück; } // Wenn das Ereignis beansprucht wird und der Listener das Ereignis frisst (x) (es muss nicht weitergegeben werden, der Standardwert ist „false“, aber es ist „true“ in den Touch-Serienereignissen von Node) wenn (isClaimed && listener.swallowTouches) { wenn (argsObj.needsMutableSet) argsObj.touches.splice(selTouch, 1); gibt true zurück; } gibt false zurück; }, Die Hauptfunktion besteht darin, Ereignisse zu verteilen und Kompatibilitätsverarbeitung für mehrere Berührungspunkte durchzuführen. Wichtig ist der Rückgabewert. Wenn das Event vom Listener beansprucht wird, wird true zurückgegeben, um die Weitergabe des Events zu verhindern. Wo wird die Veranstaltung angemeldet?Für die auf dem Node aufgerufene On-Funktion liegt der entsprechende Code natürlich im CCNode. Schauen wir uns an, was die On-Funktion macht. /** * Registrieren Sie eine Rückruffunktion des angegebenen Typs auf dem Knoten * @param {*} Typ Ereignistyp * @param {*} Rückruf Rückruffunktion * @param {*} Ziel Ziel (wird zum Binden verwendet) * @param {*} useCapture in der Erfassungsphase registriert */ on (Typ, Rückruf, Ziel, useCapture) { // Handelt es sich um ein Systemevent (Maus, Berührung)? let forDispatch = this._checknSetupSysEvent(Typ); if (fürVersand) { //Ereignis registrieren return this._onDispatch(type, callback, target, useCapture); } // Nicht-Systemereignisse weglassen, einschließlich Positionsänderungen, Größenänderungen usw. }, Die offiziellen Kommentare sind ziemlich lang, deshalb werde ich eine vereinfachte Version schreiben. Kurz gesagt wird es verwendet, um eine Rückruffunktion für ein Ereignis zu registrieren. // Verteilung registrieren event_onDispatch (Typ, Rückruf, Ziel, useCapture) { //_ Akzeptiere auch Parameter wie: (Typ, Rückruf, UseCapture) // Sie können auch Parameter wie diese erhalten: (Typ, Rückruf, UseCapture) // Parameterkompatibilitätsverarbeitung if (typeof target === 'boolean') { useCapture = Ziel; Ziel = undefiniert; } sonst useCapture = !!useCapture; // Wenn keine Rückruffunktion vorhanden ist, melde einen Fehler und kehre zurück. wenn (!Rückruf) { Fehlercode 6800. zurückkehren; } // Holen Sie sich unterschiedliche Listener basierend auf useCapture. var listeners = null; wenn (useCapture) { Listener = this._capturingListeners = this._capturingListeners || neues EventTarget(); } anders { Listener = this._bubblingListeners = this._bubblingListeners || neues EventTarget(); } // Wenn das gleiche Callback-Ereignis registriert wurde, wird keine Verarbeitung durchgeführt, wenn ( !listeners.hasEventListener(type, callback, target) ) { // Ereignisse bei Listenern registrieren listeners.on(type, callback, target); // Speichern Sie dies im __eventTargets-Array des Ziels, das verwendet wird, um die Funktion targetOff vom Ziel aus aufzurufen, um den Listener zu löschen. wenn (Ziel && Ziel.__eventTargets) { target.__eventTargets.push(dies); } } Rückruf zurückgeben; }, Der Knoten enthält zwei Listener, einen _capturingListeners und einen _bubblingListeners. Was ist der Unterschied? Ersteres wird in der Erfassungsphase registriert, letzteres in der Blasenphase. Die spezifischeren Unterschiede werden später erläutert. event-target.js(Ereignisziel)//_Registrieren Sie einen bestimmten Ereignistyp-Rückruf für das Ereignisziel. Ereignisse dieser Art sollten mit „emit“ ausgegeben werden. proto.on = Funktion (Typ, Rückruf, Ziel, einmal) { //Wenn keine Callback-Funktion übergeben wird, melde einen Fehler und kehre zurück wenn (!Rückruf) { Fehlercode 6800. zurückkehren; } // Wenn der Rückruf bereits existiert, nicht verarbeiten, if ( !this.hasEventListener(type, callback, target) ) { //Ereignis registrieren this.__on(type, callback, target, once); wenn (Ziel && Ziel.__eventTargets) { target.__eventTargets.push(dies); } } Rückruf zurückgeben; }; Am Ende gibt es noch ein weiteres ... Aus callbacks-invoker.js(RückrufInvoker)//_ Ereignis hinzufügen Verwaltung proto.on = function (key, callback, target, once) { // Holen Sie sich die dem Ereignis entsprechende Rückrufliste let list = this._callbackTable[key]; // Wenn es nicht existiert, hol dir eines aus dem Pool if (!list) { Liste = this._callbackTable[Schlüssel] = callbackListPool.get(); } // Callback-bezogene Informationen speichern let info = callbackInfoPool.get(); info.set(Rückruf, Ziel, einmal); Liste.callbackInfos.push(info); }; Endlich ist es vorbei! Unter diesen sind sowohl callbackListPool als auch callbackInfoPool js.Pool-Objekte, also ein Objektpool. Die Callback-Funktionen werden letztendlich in _callbackTable gespeichert. Wie wird das Event ausgelöst?Bevor wir uns mit der Auslösung befassen, werfen wir einen Blick auf die Auslösereihenfolge. Schauen wir uns zunächst einen offiziellen Kommentar an.
Was bedeutet das? Der vierte Parameter der On-Funktion, useCapture, bedeutet, dass, wenn er auf „true“ gesetzt ist, das Ereignis in der Erfassungsphase registriert wird, das heißt, es kann frühestens aufgerufen werden. // Prüfen ob es ein Systemereignis ist_checknSetupSysEvent (Typ) { // Müssen Sie einen neuen Listener hinzufügen? let newAdded = false; // Ob eine Verteilung erforderlich ist (wird für System-Ereignisse benötigt) let fürDispatch = false; // Wenn das Ereignis ein Touch-Ereignis ist, if (_touchEvents.indexOf(type) !== -1) { // Wenn kein Touch-Event-Listener vorhanden ist, erstellen Sie einen neuen if (!this._touchListener) { this._touchListener = cc.EventListener.create({ Ereignis: cc.EventListener.TOUCH_ONE_BY_ONE, SchwalbenBerührungen: wahr, Besitzer: dieser, Maske: _searchComponentsInParent(diese, cc.Maske), beiBeginn der Berührung: _touchStartHandler, beiBerührenBewegung: _touchMoveHandler, beiBerührenEnde: _touchEndHandler, bei abgebrochener Berührung: _touchCancelHandler }); //Füge den Listener zum EventManager hinzu eventManager.addListener(dies._touchListener, dies); neuHinzugefügt = wahr; } fürDispatch = wahr; } // Das ausgelassene Ereignis ist der Code für das Mausereignis, das dem Touch-Ereignis ähnelt. // Wenn ein Listener hinzugefügt wird und der aktuelle Knoten nicht aktiv ist, if (newAdded && !this._activeInHierarchy) { // Wenn der Knoten nach einer Weile immer noch nicht aktiv ist, unterbrechen Sie die Ereignisübermittlung des Knotens. cc.director.getScheduler().schedule(Funktion () { wenn (!this._activeInHierarchy) { eventManager.pauseTarget(dieses); } }, dies, 0, 0, 0, falsch); } Rücksendung zum Versand; }, Was ist der Sinn? In der Zeile // Touch-Start-Ereignishandler var _touchStartHandler = function (touch, event) { var pos = touch.getLocation(); var Knoten = dieser.Besitzer; // Wenn sich der Berührungspunkt innerhalb des Knotenbereichs befindet, wird das Ereignis ausgelöst und „true“ zurückgegeben, was bedeutet, dass ich das Ereignis ausgeführt habe! wenn (node._hitTest(pos, this)) { Ereignistyp = Ereignistyp.TOUCH_START; event.touch = berühren; Ereignisblasen = wahr; //An diesen Knoten verteilenode.dispatchEvent(event); gibt true zurück; } gibt false zurück; }; Es ist ganz einfach. Holen Sie sich den Kontaktpunkt und bestimmen Sie, ob der Kontaktpunkt innerhalb des Knotens liegt. Wenn ja, verteilen Sie ihn! //_ Ereignisse in den Ereignisstrom verteilen. dispatchEvent (Ereignis) { _doDispatchEvent(dieses, Ereignis); _cachedArray.length = 0; }, //Ereignisse verteilen Funktion _doDispatchEvent (Besitzer, Ereignis) { var Ziel, i; Ereignis.Ziel = Eigentümer; //_ Ereignis.CAPTURING_PHASE // Erfassen phase_cachedArray.length = 0; // Holen Sie sich die Knoten in der Erfassungsphase und speichern Sie sie in _cachedArray Besitzer._getCapturingTargets(Ereignis.Typ, _cachedArray); //_ erfassen Ereignis.EreignisPhase = 1; // Vom Ende zum Anfang durchlaufen (dh vom Stammknoten zum übergeordneten Knoten des Zielknotens) für (i = _cachedArray.length - 1; i >= 0; --i) { Ziel = _cachedArray[i]; // Wenn der Zielknoten einen Listener für die Erfassungsphase registriert, if (target._capturingListeners) { event.currentTarget = Ziel; //_ Feuerereignis // Verarbeiten Sie das Ereignis auf dem Zielknoten target._capturingListeners.emit(event.type, event, _cachedArray); //_ prüfen, ob die Ausbreitung gestoppt wurde // Wenn die Übermittlung des Ereignisses gestoppt wurde, kehren Sie zurück wenn (event._propagationStopped) { _cachedArray.length = 0; zurückkehren; } } } // Zwischengespeichertes Array löschen _cachedArray.length = 0; //_ Ereignis.AT_TARGET //_ prüft, ob beim Erfassen von Rückrufen zerstört wurde // Die eigene Phase des Zielknotens event.eventPhase = 2; event.currentTarget = Eigentümer; // Wenn der Besitzer einen Listener für die Erfassungsphase registriert hat, verarbeite das Ereignis if (owner._capturingListeners) { Besitzer._capturingListeners.emit(Ereignis.Typ, Ereignis); } // Wenn das Ereignis nicht gestoppt wurde und der Propagation-Listener registriert ist, verarbeite das Ereignis, wenn (!event._propagationImmediateStopped && owner._bubblingListeners) { Besitzer._bubblingListeners.emit(Ereignis.Typ, Ereignis); } // Wenn das Ereignis nicht gestoppt wurde und das Ereignis weitergeleitet werden muss (Standard: „true“) wenn (!event._propagationStopped && event.bubbles) { //_ Ereignis.BUBBLING_PHASE // Bubbling-Phase // Holen Sie sich den Knoten in der Bubbling-Phase owner._getBubblingTargets(event.type, _cachedArray); //_ propagieren Ereignis.EreignisPhase = 3; // Von Anfang bis Ende durchlaufen (vom übergeordneten Knoten zum Stammknoten), die Auslöselogik entspricht der Erfassungsphase für (i = 0; i < _cachedArray.length; ++i) { Ziel = _cachedArray[i]; wenn (target._bubblingListeners) { event.currentTarget = Ziel; //_ Feuerereignis target._bubblingListeners.emit(Ereignis.Typ, Ereignis); //_ prüfen, ob die Ausbreitung gestoppt wurde wenn (event._propagationStopped) { _cachedArray.length = 0; zurückkehren; } } } } // Zwischengespeichertes Array löschen _cachedArray.length = 0; } Ich frage mich, ob Sie nach dem Lesen dieses Artikels ein besseres Verständnis der Ereignisauslösesequenz haben? _getCapturingTargets (Typ, Array) { // Beginnen Sie beim übergeordneten Knoten. var parent = this.parent; // Wenn der übergeordnete Knoten nicht leer ist (der übergeordnete Knoten des Stammknotens ist leer) während (Elternteil) { // Wenn der Knoten einen Listener in der Erfassungsphase und ein Abhörereignis des entsprechenden Typs hat, fügen Sie den Knoten dem Array hinzu, wenn (parent._capturingListeners && parent._capturingListeners.hasEventListener(Typ)) { array.push(übergeordnet); } //Setze den Knoten auf seinen übergeordneten Knoten parent = parent.parent; } }, Bei einer Bottom-Up-Traversierung werden die Knoten, die die Bedingungen erfüllen, auf dem Weg zum Array hinzugefügt, und dann erhalten Sie alle Knoten, die verarbeitet werden müssen! Rückrufe-invoker.jsproto.emit = Funktion (Schlüssel, arg1, arg2, arg3, arg4, arg5) { // Ereignisliste abrufen const list = this._callbackTable[key]; // Wenn die Ereignisliste existiert if (list) { // Wird das Ereignis „list.isInvoking“ ausgelöst? const rootInvoker = !list.isInvoking; list.isInvoking = true; // Rückrufliste abrufen und durchlaufen const infos = list.callbackInfos; für (sei i = 0, len = infos.length; i < len; ++i) { const info = infos[i]; wenn (info) { sei Ziel = info.ziel; lassen Sie Rückruf = info.rückruf; // Wenn die Callback-Funktion mit once registriert ist, breche diese Funktion zuerst ab if (info.once) { this.off(Schlüssel, Rückruf, Ziel); } // Wenn das Ziel übergeben wurde, verwenden Sie den Aufruf, um sicherzustellen, dass dieser auf das richtige if (Ziel) { zeigt. Rückruf.Anruf(Ziel, arg1, arg2, arg3, arg4, arg5); } anders { Rückruf(arg1, arg2, arg3, arg4, arg5); } } } // Wenn das aktuelle Ereignis nicht ausgelöst wird if (rootInvoker) { list.isInvoking = falsch; // Wenn abgebrochene Rückrufe vorhanden sind, rufen Sie die Funktion purgeCanceled auf, um die entfernten Rückrufe zu filtern und das Array zu komprimieren, wenn (list.containCanceled) { list.purgeCanceled(); } } } }; Der Kern besteht darin, basierend auf dem Ereignis eine Liste von Rückruffunktionen zu erhalten, die Anrufe zu durchlaufen und schließlich nach Bedarf ein Recycling durchzuführen. Das ist alles! AbschlussFügen Sie einen interessanten Listener-Sortieralgorithmus hinzu Im vorherigen Inhalt wurde die Funktion _sortEventListeners erwähnt, mit der die Listener entsprechend der Triggerpriorität sortiert werden. Ich finde diesen Algorithmus ziemlich interessant und möchte ihn mit Ihnen teilen.
Wenn Sie Prioritäten setzen möchten, wie sollten Sie dabei vorgehen? Lassen Sie p1 und p2 jeweils gleich AB sein. Nach oben: A = A.parent
Es sind viele Wörter geschrieben, und hier ist der Code, prägnant und mächtig! // Algorithmus für Szenegrafik Prioritätsanhörer sortieren // Returning -1 (negative Zahl) bedeutet. // den Knoten holen, in dem sich der Hörer befindet, let node1 = l1._getscenegraphpriority (), node2 = l2._getScenegraphPriority (); // Wenn der Hörer 2 leer ist oder der Knoten 2 leer ist oder der Knoten 2 nicht aktiv ist oder der Knoten 2 der Stammknoten ist, hat L1 Vorrang, wenn (! L2 ||! Node2 ||! Rückgabe -1; // gleichen wie oben else if (! L1 ||! Node1 ||! Node1._activeInhierarchy || node1._parent === null) Rückgabe 1; // Verwenden Sie P1 P2, um den Knoten 1 und den Knoten 2 vorübergehend zu speichern // Ex: Ich denke, es bedeutet, ob ein Austausch auftritt (Austausch) Sei p1 = node1, p2 = node2, ex = false; // Wenn die übergeordneten Knoten von P1 und P2 nicht gleich sind, verfolgen Sie die Quelle, wobei (p1._parent._id! == p2._parent._id) { // Wenn der Großelternknoten von P1 leer ist (der übergeordnete Knoten von P1 ist der Stammknoten), wird Ex auf True und P1 auf Knoten 2 gesetzt. Ansonsten zeigt P1 auf den übergeordneten Knoten p1 = p1._parent._parent === null? p2 = p2._parent._parent === null? } // Wenn P1 und P2 auf denselben Knoten verweisen, dh die Knoten 1 und 2 eine Eltern-Kind-Beziehung, dh Fall 3 if (p1._id === p2._id) { // Wenn P1 auf Knoten 2 zeigt, hat L1 Vorrang. Andernfalls hat L2 Vorrang if (p1._id === node2._id) Rückgabe -1; if (p1._id === node1._id) Rückgabe 1; } // Hinweis: Zu diesem Zeitpunkt sind die übergeordneten Knoten von P1 und P2 gleich // Wenn Ex wahr ist, haben die Knoten 1 und 2 keine Eltern-Kind-Beziehung, dh Fall 2 // Wenn Ex falsch ist, haben die Knoten 1 und 2 denselben übergeordneten Knoten, nämlich Fall 1 EX? }, Zusammenfassen Das Spiel beginnt mit CCGame, das CcInputManager und CCEventManager für die Registrierung von Ereignissen bezeichnet. In den nachfolgenden Interaktionen ruft der Rückruf der Engine die Zuhörer in CCEventManager auf und verarbeitet dann die Ereignisse. Wenn es trifft, wird es an die in EventTarget gespeicherte Veranstaltungsliste übergeben, und die Reise ist abgeschlossen. Das obige Erläuterung ist eine detaillierte Erklärung, wie Cocoscreator -Systemereignisse generiert und ausgelöst werden. Das könnte Sie auch interessieren:
|
<<: Beispielcode zum Konvertieren von Videos mit der ffmpeg-Befehlszeile
>>: Gründe und Lösungen für die fehlende Remote-Verbindung zur MySQL-Datenbank unter CentOS7
Inhaltsverzeichnis Hintergrund erkunden Zusammenf...
1: SVN installieren yum install -y Subversion 2. ...
Inhaltsverzeichnis 01 sql_slave_skip_counter-Para...
Einführung: Als ich mir in letzter Zeit die Frage...
Inhaltsverzeichnis Hintergrund Virtuelle Dateien ...
MySQL-Abfrage mit mehreren Bedingungen und dem Sc...
Ursprünglich sollte dieses siebte Kapitel eine aus...
Inhaltsverzeichnis 1. Docker installieren 2. Inst...
Derzeit gibt es viele Betriebsaktivitäten für öff...
Inhaltsverzeichnis 1. Standortobjekt 1. URL 2. Ei...
0. Hintergrund Hardware: Xiaomi Notebook Air 13/I...
Es gibt eine einfache CSS-Methode, um das Popup-F...
Inhaltsverzeichnis 1. Trigger-Einführung 1. Was i...
<br />Wenn Sie sich diesen Titel ansehen, ko...
1. Es muss die InnoDB-Speicher-Engine verwendet w...