Lancia oggetti creando una classe PanAndThrow

In questo tutorial ci metteremo a punto e finiremo una panoramica e una classe di lancio che ci consentirà di aggiungere questo effetto a qualsiasi elemento desideriamo. Per fare ciò, creeremo un visualizzatore di immagini, ma non il tuo spettatore medio. Qui avremo lo zoom, il lancio, il panning ... Sembra quasi un'app ninja, eh?


Step 1: Introduzione

La classe Pan and Throw ti permetterà di aggiungere il pan e la funzionalità a qualsiasi oggetto ActionScript che desideri. Sebbene questo tutorial sia specifico per Flex, la classe stessa può essere utilizzata ovunque sia ActionScript. Avevo visto questo effetto su diversi siti web, poi su Photoshop CS4 e ho deciso che volevo questo anche sui miei progetti.

Esistono molte applicazioni per questo effetto; quello che useremo per questo tutorial è un visualizzatore di immagini che ti permette di ingrandire e rimpicciolire l'immagine e modificare l'attrito che l'effetto di proiezione usa. Tuttavia, questo tutorial non riguarda realmente il visualizzatore di immagini, ma riguarda la creazione di una panoramica e il lancio di una classe. Quindi iniziamo con questo. Apri il tuo editor Flex preferito e avvia un progetto; per informazioni su come farlo in Flex Builder, consultare Adobe LiveDocs. Una volta creato il progetto, apri il file MXML. Dobbiamo aggiungere del codice a questo prima di creare la nostra classe.


Passaggio 2: il nostro MXML

Dal momento che questa non è la parte principale del tutorial, non ho intenzione di passare molto tempo qui. Se avete domande su questa sezione che non sono coperte, potete chiedere nei commenti qui sotto. Innanzitutto, ecco gli oggetti MXML da inserire nell'applicazione:

            

Noterai le quattro funzioni chiamate nei tag: init (), changeDecay (), smoothImage () e zoom (). Abbiamo bisogno di scrivere quelle funzioni. Questo è il codice tra il tags:

 import mx.states.SetStyle; import mx.effects.Move; import mx.containers.HBox; import mx.containers.Box; private var imageWidth: Number = 0; private var imageHeight: Number = 0; private var mover: Move = new Move (); // questo verrà chiamato quando l'applicazione carica la funzione privata init (): void // Questo evento aggiungerà la possibilità di nascondere e mostrare i nostri controlli con un clic. control.addEventListener (MouseEvent.CLICK, controlClick); mover.target = control;  // questa funzione ingrandirà e ridurrà l'immagine in base al valore del nostro dispositivo di scorrimento dello zoom. funzione privata zoom (): void inside.width = (imageWidth * hSlider.value) / 100; inside.height = (imageHeight * hSlider.value) / 100;  // questo viene chiamato quando la nostra immagine cambia dimensione. private function smoothImage (ev: Event): void // imposta l'uniformità dell'immagine in modo che l'immagine abbia un aspetto migliore quando viene trasformata. var bmp: Bitmap = ev.target.content come Bitmap; bmp.smoothing = true; ImageWidth = inside.width; imageHeight = inside.height;  // non useremo questa funzione ancora privata changeDecay (): void // questo cambierà il valore di decadimento (attrito) della nostra classe, quando ci arriveremo.  private function controlClick (e: MouseEvent): void mover.play (); // questa funzione nasconde / mostra i controlli al clic if (control.y! = -5) mover.stop (); mover.yTo = -5; mover.play ();  else if (e.target == control) mover.stop (); mover.yTo = (control.height - 10) * -1; mover.play (); 

Una volta che hai MXML, devi creare una cartella chiamata "classes" nella stessa cartella del tuo file MXML. (Se si utilizza Flash, la cartella deve essere nella stessa dir del file FLA.) Questo è il nostro pacchetto di classi ed è dove andrà il file PanAndThrow.as. In Flex Builder, crea una nuova classe, inseriscila nel pacchetto classi e chiamala PanAndThrow; questo creerà la tua classe - stile predefinito.


Step 3: I Makings di una classe

Ecco la nostra lezione di base di PanAndThrow. Salvalo come PanAndThrow.as nella tua nuova cartella "classi".

 // namespace declaration package classes // class dichiarazione public class PanAndThrow / * Questo è chiamato il costruttore, questo metodo / funzione verrà chiamato quando crei * un'istanza del tuo oggetto, o istanzia il tuo oggetto. * per questa classe non facciamo nulla perché stiamo andando a fare tutto * nella funzione Init * / public function PanAndThrow () 

Quali variabili e funzioni abbiamo bisogno nella nostra classe PanAndThrow? Per ottenere ciò, puoi chiedertelo "cosa deve fare la mia classe, cosa ha bisogno di sapere e cosa deve essere in grado di farlo?" Quindi creiamo qualche pseudo-codice.

Nota rapida

Quando ho sviluppato questa classe per la prima volta, ho messo tutto nel costruttore, ma questo ha causato un problema quando ho creato i metodi start e stop a causa dell'ambito. Non ho potuto creare un'istanza di questa classe su un ambito globale con tutte le informazioni richieste. Ho quindi creato una funzione init (), quindi l'istanza potrebbe essere avviata e interrotta dall'esterno della classe.


Step 4: Il nostro pseudo-codice

"Pseudo-codice" significa solo codice falso, che possiamo usare per aiutarci a pensare a quale codice reale avremo bisogno.

 classi di pacchetti public class PanAndThrow / * Queste saranno le variabili che creiamo. Quindi cosa dobbiamo sapere? * anObjectToThrow; * anObjectToThrowItIn; * ObjectLocation; * PreviousObjectLocation; * Decadimento; // per la fisica * questi sono ovvi, ma questa lista diventerà molto più grande * poiché vediamo esattamente ciò di cui abbiamo bisogno nelle nostre funzioni * / funzione pubblica PanAndThrow ()  / * Quindi, cosa farà la nostra classe ? * dentro(); // ha bisogno di avviare * stop (); // vogliamo essere in grado di fermarlo in qualche modo. * inizio(); // se smettiamo dobbiamo essere in grado di riavviarlo. * pan (); * gettare(); * /

Ora che abbiamo qualche pseudo-codice possiamo iniziare a costruire la classe. Iniziamo con la funzione init (). Questo ci porterà anche a uno dei principi della programmazione orientata agli oggetti chiamata incapsulamento, che si occupa dell'accesso di pezzi del codice.

Questo codice dovrebbe andare nella classe PanAndThrow che abbiamo appena iniziato. (Non sei sicuro di dove? Consulta il suggerimento rapido sulla classe dei documenti.)

 // grazie a OOP, è possibile utilizzare una classe di livello inferiore e una classe di livello superiore (una che estende // la classe di livello inferiore). Come qui, quasi tutti gli oggetti che utilizzerai estendono la classe // Sprite. Quindi devo solo chiedere un oggetto Sprite e puoi dare un Box o un Button. private var targetObject: Sprite = new Sprite (); private var eventObject: Sprite = new Sprite (); private var originalDecay: Number = .9; private var buttonDown: Boolean = false; private var moveY: Boolean = true; private var moveX: Boolean = true; private var TargetClick: Boolean = true; // Lo useremo per verificare per quanto tempo il tuo mouse è rimasto su un oggetto senza muoversi. private var t: Timer; private var timerInterval: int = 100; funzione pubblica init (ObjectToMove: Sprite, ObjectToEventise: Sprite, DecayAmout: Number = .9, isMoveY: Boolean = true, isMoveX: Boolean = true, OnlyMoveOnTargetClick: Boolean = true): void targetObject = ObjectToMove; eventObject = ObjectToEventise; originalDecay = DecayAmount; moveX = isMoveX; moveY = isMoveY; TargetClick = OnlyMoveOnTargetClick; t = new Timer (timerInterval); inizio(); 

Solo un paio di cose che voglio sottolineare. Nella funzione per init ho impostato alcuni argomenti per essere uguale a un valore. Ciò significa che sto dando loro un valore predefinito, rendendoli così opzionali. Quando si impostano i valori predefiniti per gli argomenti di una funzione, devono essere gli ultimi parametri: non è possibile avere una variabile richiesta dopo un'opzione facoltativa. Il motivo per cui ho aggiunto le variabili di default è di rendere la chiamata più breve se usiamo le impostazioni predefinite. Posso chiamare PanAndThrow (mover, eventer); e sia fatto, invece di PanAndThrow (mover, enventer, decayer, yVal, ...) e così via.

Ti sei mai chiesto cosa significhi "privato" o "pubblico" davanti a funzioni e variabili? Questa è l'esposizione dell'oggetto. Un oggetto "pubblico" è accessibile da qualsiasi altra classe; un oggetto "privato" può essere visto solo dagli altri membri di questa classe; un oggetto "protetto" è nascosto da tutto tranne le classi che si trovano nello stesso pacchetto.

Vogliamo essere in grado di cambiare il decadimento dal nostro MXML, quindi abbiamo bisogno di un hook pubblico per arrivare alla nostra variabile privata; è qui che entrano le funzioni getter e setter:

 private var originalDecay: Number = .9; decadimento della funzione pubblica (): Number return originalDecay; 

Questa è una funzione "getter". Significa che, per le classi esterne, sembra che la classe PanAndThrow abbia una variabile pubblica chiamata "decadimento". Quando tentano di accedervi, restituiremo loro il valore della nostra variabile originalDecay (privata).

Le funzioni di setter sono quasi le stesse, ma consente alle classi esterne di modificare il valore della nostra variabile pubblica "falsa":

 decadimento della funzione pubblica decay (valore: Number): void originalDecay = value; 

Questi sono utili perché puoi mettere la logica in un setter per limitare ciò che entra nella tua var privata. Ad esempio, se metti un numero nel tag MXML per una casella otterrai un'altezza impostata; se metti una% (rendendo il numero una stringa) otterrai una percentuale di altezza. Questo è incorporato nel codice per il setter dell'altezza della scatola. Ora che abbiamo il nostro getter e setter puoi accedere alla variabile di decadimento come questa al di fuori della classe:

 var pt: PanAndThrow = new PanAndThrow (); pt.init (target, parent); pt.decay = 0,7;

Step 5: Start, Listen, Stop

Abbiamo la nostra classe, alcune variabili locali e una funzione init (). Facciamo qualcosa ora. Alla fine della funzione init () abbiamo chiamato "start ();" quindi facciamo la funzione di avvio. Per lo più sono solo un gruppo di ascoltatori:

 funzione pubblica start (): void // Con il mouse in giù, stiamo cercando di avviare la nostra azione pan, ma dobbiamo essere in grado // per controllare il nostro OnlyMoveOnTargetClick che abbiamo assegnato al nostro campo globale TargetClick targetObject.addEventListener (MouseEvent. MOUSE_DOWN, handleOverTarget); eventObject.addEventListener (MouseEvent.MOUSE_DOWN, handleOverTarget); // Quando chiamiamo il nostro pan, utilizza un listener di movimento del mouse, il che significa che viene chiamato ogni volta che il mouse // si sposta, quindi dobbiamo vedere come limitare quando si muove l'oggetto target. eventObject.addEventListener (MouseEvent.MOUSE_MOVE, moveIt); // questo è per lanciare l'oggetto dopo un pan, questo è un po 'complicato perché la funzione throwIt () chiama un altro listener. targetObject.addEventListener (MouseEvent.MOUSE_UP, throwIt); eventObject.addEventListener (MouseEvent.MOUSE_UP, throwIt); // il metodo throwItOut fa sì che il nostro oggetto agisca come se lasciassi andare il pulsante del mouse, ma viene generato quando // il mouse lascia l'oggetto padre targetObject.addEventListener (MouseEvent.MOUSE_OUT, throwItOut); eventObject.addEventListener (MouseEvent.MOUSE_OUT, throwItOut); // questo è il listener del timer, questo controllerà per vedere se hai tenuto premuto il mouse per un po ', // spiegherò la necessità di questo quando arriveremo alla funzione timerOut () t.addEventListener (TimerEvent. TIMER, timerout); t.start (); 

La funzione stop () è quasi la stessa, ma stiamo rimuovendo gli ascoltatori.

 public function stop (): void targetObject.removeEventListener (MouseEvent.MOUSE_DOWN, handleOverTarget); eventObject.removeEventListener (MouseEvent.MOUSE_DOWN, handleOverTarget); eventObject.removeEventListener (MouseEvent.MOUSE_MOVE, moveIt); targetObject.removeEventListener (MouseEvent.MOUSE_UP, throwIt); eventObject.removeEventListener (MouseEvent.MOUSE_UP, throwIt); targetObject.removeEventListener (MouseEvent.MOUSE_OUT, throwItOut); eventObject.removeEventListener (MouseEvent.MOUSE_OUT, throwItOut); t.removeEventListener (TimerEvent.TIMER, timerOut); t.stop (); 

Ora possiamo ascoltare cosa sta succedendo, passiamo attraverso ognuna di queste funzioni di ascolto.


Passaggio 6: MouseEvent.MOUSE_DOWN

Daremo un'occhiata al gestore di eventi handleOverTarget.

 private function handleOverTarget (e: MouseEvent): void buttonDown = true; arMousePrevX = MousePrevX = MouseCurrX = eventObject.mouseX; arMousePrevY = MousePrevY = MouseCurrY = eventObject.mouseY; if (e.currentTarget == targetObject ||! TargetClick) overTarget = true;  else if (e.target.toString (). search (targetObject.toString ()) < 0)  overTarget = false;  

Questa funzione verrà chiamata quando è presente un evento MOUSE_DOWN sull'oggetto evento o sull'oggetto di destinazione. È molto importante notare che se metto un listener su un oggetto genitore, il gestore sarà anche chiamato quando l'evento si verifica su un bambino. In questo caso il mio oggetto target è un figlio dell'oggetto evento. Quando clicco sull'oggetto di destinazione, questo metodo verrà chiamato due volte: prima il mouse verso il basso sul bambino, poi il secondo verso il basso sul genitore. Questo è molto importante per questo, perché decideremo se il mouse in basso sarà in grado di spostare il nostro oggetto di destinazione, quindi è davvero necessario essere in grado di sapere se quel mouse in basso era sul bambino o no.

La prima affermazione è piuttosto semplice: imposta la nostra variabile di classe buttonDown su true.

Anche i prossimi due sono abbastanza facili, tranne che ho introdotto un paio di nuove variabili che dovranno essere inserite nella nostra lista delle variabili di classe: MousePrevX, MousePrevY, arMousePrevX, arMousePrevY, MouseCurrX e MouseCurrY. Questi verranno utilizzati molto nelle funzioni di trascinamento e scorrimento, quindi aspetterò di parlarne fino ad allora.

L'istruzione if controlla se l'oggetto su cui si è fatto clic è l'oggetto di destinazione. Ricorda, TargetClick è stato impostato sull'argomento passato a init (), OnlyMoveOnTargetClick; se questo è falso, vogliamo trattare ogni oggetto figlio come oggetto di destinazione quando cliccato. Ecco perché abbiamo il controllo "||! TargetClick".

Questa è la parte facile. La parte successiva è un po 'più complicata.

E.currentTarget restituisce l'oggetto che ha attivato l'evento. E.target restituirà l'oggetto che era il vero bersaglio. Quindi potrei dire questo, giusto?

 if (e.target == targetObject ||! TargetClick) overTarget = true;  else overTarget = false; 

È abbastanza semplice, ma è sbagliato. Cosa succede se il mio oggetto target ha figli? Quindi il mio oggetto e.currentTarget potrebbe essere targetObject ma e.target è figlio di targetObject e non corrisponderà. Vogliamo che questo si sposti anche se stiamo facendo il giro del mouse su un oggetto figlio.

Quindi ecco String.search in soccorso. Se il nostro attuale target non è il nostro targetObject, useremo un "else if" per vedere se riusciamo a trovare il nostro obiettivo nella destinazione. e.target.toString () produrrà qualcosa di simile a questo: "application2.eventobject3.targetobject2.targetchild4" per il nostro oggetto target dove targetObject.toString () produrrà qualcosa come questo "application2.eventobject3.targetobject2" tutto quello che devo fare per scoprire se il nostro obiettivo è un figlio del nostro targetObject è da questo:

 e.target.toString (). Ricerca (targetObject.toString ())

Se c'è una corrispondenza restituirà il primo indice della partita, o se non c'è una corrispondenza restituirà un -1, quindi possiamo vedere se è maggiore di -1 e viola, abbiamo trovato se l'oggetto essere cliccato è un figlio del nostro targetObject.

(Potremmo controllare i figli oi genitori dell'oggetto tramite la funzione getChildAt () e la proprietà parent, ma questa è un'alternativa pulita).


Passaggio 7: TimerOut e Pan

Anche la funzione timer è abbastanza semplice, soprattutto perché lo abbiamo già fatto. Bene, quasi fatto prima. Quando abbiamo trascinato un po 'il nostro piccolo targetObject e decidiamo che non vogliamo lasciarlo andare, lo amiamo troppo e, bruscamente, fermiamo il mouse, cosa accadrebbe se lasciassi andare il pulsante del mouse in quel punto? bene, cosa pensi che succederebbe? Non ho intenzione di rispondere per te, ti aiuterò semplicemente con il codice per evitare che accada. Nel codice finale, commenta queste tre righe. Questo dovrebbe sembrare davvero familiare, l'abbiamo usato solo nel gestore button down, fatta eccezione per una variabile, MouseDragged. Lo useremo quando chiamiamo la nostra altra funzione:

 funzione privata timerOut (e: TimerEvent): void MouseDragged = false; arMousePrevX = MousePrevX = MouseCurrX = eventObject.mouseX; arMousePrevY = MousePrevY = MouseCurrY = eventObject.mouseY; 

Quindi, se stai chiedendo perché abbiamo bisogno di questo evento del timer, probabilmente non hai provato a tirarlo fuori per vedere cosa stava succedendo. Quindi fallo.

Questa prossima funzione è una delle nostre funzioni principali; è la funzione pan. C'è molto coinvolgimento quindi tuffiamoci nel nostro pseudo-codice:

 funzione privata moveIt (e: MouseEvent): void / * ok, quindi cosa abbiamo bisogno di questa funzione? * ha bisogno di filtrare il nostro obiettivo. * così vediamo se siamo sopra il nostro oggetto di destinazione * / // se (siamo sopra il nostro oggetto di destinazione) // // quali strumenti avremo bisogno di eseguire una panoramica? // beh, forse dovremmo controllare se il pulsante è giù // if (button down) // // potremmo aver bisogno di impostare la variabile button down. buttonDown = true; // e se siamo in questa funzione a questo punto il nostro pulsante è inattivo e // il mouse si è spostato - è un trascinamento: quindi MouseDragged = true; // se spostiamo l'oggetto secondo il movimento del mouse, allora dovremmo // probabilmente sapere dove si trova il nostro mouse: MouseCurrX, Y = mouseX corrente, Y; // questa è un'introduzione al nostro mouse artificiale prev, che verrà spiegato // nella prossima funzione. L'ar sta per 'artificial' o 'after release', // quale preferite. Questo deve essere impostato sulla posizione attuale del mouse precedente. // arMousePrevX = MousePrevX; // arMousePrevY = MousePrevY; // quindi dobbiamo spostare effettivamente il targetObject, // ma ricordiamo le nostre variabili, moveX e moveY, quindi: // se moveX move x; // se moveY muove y; // abbiamo bisogno di resettare il nostro Decay (attrito) allo stato originale: // Decay = originalDecay; // che dovrebbe finire if // // che altro? // // impostiamo il nostro buttonDown su true prima, quindi lasciamolo impostato su false qui. // buttonDown = false; // se questo non è un click di destinazione, dovremmo impostare il nostro overTarget su false così: // if (! TargetClick) // overTarget = false; // questo è tutto. // // ci sono alcune cose che vogliamo accadere indipendentemente dalle condizioni. // prima, dobbiamo impostare la nostra variabile mousePrevX, Y - PRIMA che il mouse sia // spostato di nuovo! // MousePrevX = eventObject.mouseX; // MousePrevY = eventObject.mouseY; // Ecco altre due variabili per tenere traccia di: xOpposideEdge e yOppositeEdge // stiamo testando per vedere quale sia la dimensione del nostro oggetto target in relazione // al nostro oggetto evento; se uno è più grande, dobbiamo cambiare il comportamento del rimbalzo. // if (targetObject.width> eventObject.width) xOppositeEdge = true; // else xOppositeEdge = false; // if (targetObject.height> eventObject.height) yOppositeEdge = true; // else yOppositeEdge = false; // e infine dobbiamo interrompere e riavviare il nostro timer. //t.stop (); //t.start (); //

Ammetto che questo è un po 'più psuedo-y rispetto all'ultimo; questo è per due motivi: uno, non si sa cosa sta per succedere, e due, sono davvero entusiasta di ottenere il codice:

 funzione privata moveIt (e: MouseEvent): void // nel nostro pseudo-codice c'erano due condizioni ma possiamo combinarle in una, // testiamo per vedere se il nostro evento era un pulsante in basso, e se siamo sopra il nostro target, // se siamo allora spostiamo l'oggetto target. if (e.buttonDown && overTarget) buttonDown = true; MouseDragged = true; MouseCurrX = eventObject.mouseX; MouseCurrY = eventObject.mouseY; // ecco l'artificiale / dopo il rilascio. di nuovo, benissimo. arMousePrevX = MousePrevX; arMousePrevY = MousePrevY; / * questo è il più importante, nella nostra pseudo era "spostare l'oggetto di destinazione", * quindi dobbiamo tradurlo. Per aiutarci creeremo una variabile locale * Topper per la parte superiore e Sider per la parte. * quindi diamo un'occhiata a Topper (lo stesso vale per Sider). * eventObject.mouseY controlla dove si trova il mouse all'interno dell'eventoObject. * Prendiamo il nostro MousePrev lontano da quello, e questo ci darà quanto l'oggetto * dovrebbe viaggiare, quindi la Y potrebbe viaggiare 2 pixel, o -2 pixel a seconda della * direzione, quindi prendiamo quella modifica e la aggiungiamo alla destinazione posizione corrente *, ma ciò non sta ancora accadendo, questo è solo un var. * / var Topper: int = (eventObject.mouseY - MousePrevY) + targetObject.y; var Sider: int = (eventObject.mouseX - MousePrevX) + targetObject.x; // qui è dove succede, se moveY (ricorda dallo pseudo-codice) allora possiamo // impostare la posizione del target. if (moveY) targetObject.y = Topper; if (moveX) targetObject.x = Sider; // quindi in realtà stiamo usando solo Topper e Sider per memorizzare temporaneamente dove // ​​oggetto target deve spostarsi su Decay = originalDecay ;  else buttonDown = false; se (! TargetClick) overTarget = false;  MousePrevX = eventObject.mouseX; MousePrevY = eventObject.mouseY; if (targetObject.width> eventObject.width) xOppositeEdge = true; else xOppositeEdge = false; if (targetObject.height> eventObject.height) yOppositeEdge = true; else yOppositeEdge = false; t.stop ( ); t.start (); 

E ora stiamo panning.


Step 8: Throw, It, Out, Repeater!

Questa è la seconda grande funzione e con questo avremo la nostra classe costruita! Pronto a fare una panoramica e lanciare qualsiasi oggetto che ritenga opportuno! Ci sono due funzioni che dobbiamo risolvere prima: throwIt (), che impostiamo come gestore dell'evento MOUSE_UP e throwItOut (), che impostiamo come gestore dell'evento MOUSE_OUT.

 funzione privata throwIt (e: MouseEvent): void buttonDown = false; if (MouseDragged) eventObject.addEventListener (Event.ENTER_FRAME, theRepeater);  funzione privata throwItOut (e: MouseEvent): void buttonDown = false; if (e.relatedObject == null || e.relatedObject == eventObject.parent) eventObject.addEventListener (Event.ENTER_FRAME, theRepeater); 

Queste due funzioni sono quasi le stesse (dopotutto stanno facendo la stessa cosa solo in momenti diversi). In essi impostiamo buttonDown su false, perché questo è un evento mouse up e controlliamo se il mouse è stato trascinato, usando MouseDragged (che abbiamo impostato nell'ultima funzione) o selezionando "e.relatedObject"; l'oggetto dal quale il mouse è appena uscito.

Se è stato trascinato, aggiungeremo un altro listener. L'evento ENTER_FRAME è davvero interessante. Questa è la base della nostra animazione; ogni volta che entriamo in un nuovo frame verrà eseguita la funzione throw (). Questo è ciò che ci permette di simulare il trascinamento del mouse dopo il rilascio (ricordate arMousePrevX, variabile Y? Questo è ciò che è per). E questo è tutto quello che fa davvero il tiro, simulando un trascinamento del mouse, ovviamente senza mouse. Quindi abbiamo praticamente già la funzione di cui abbiamo bisogno, tranne che dobbiamo sostituire le chiamate alla posizione corrente del mouse con la nostra posizione artificiale del mouse.

Sono quasi un po 'più avanti di me. Quindi con queste due funzioni di evento, throwIt e throwItOut, stanno facendo la stessa cosa, ma quella Se nella seconda funzione è degno di nota. Ho faticato un po 'cercando di ottenere questa funzionalità, fino a quando ho guardato l'evento un po' più vicino. Il problema stava cercando di far sì che l'oggetto di destinazione agisse come se lasciassi andare il pulsante quando il cursore lasciava l'oggetto evento. Vai avanti, prova a farlo senza e.relatedObject. Ho quasi avuto un paio di volte, ma non riuscivo a farlo bene. Quello che fa e.relatedObject è trovare l'oggetto su cui ti trovi, dopo che l'evento è stato chiamato. Ecco perché è veramente figo. Quando il nostro cursore lascia del tutto il film, restituisce un valore null, altrimenti restituirà l'oggetto su cui ci si trova, quindi possiamo verificare se e.relatedObject è null o è un genitore di eventObject. Ciò produce l'azione corretta che stiamo cercando.

Nelle funzioni di cui sopra configuriamo le chiamate a theRepeater (). Questa sarà la funzione di lancio, e ricorda che verrà chiamata ogni volta che entreremo in una nuova trama. Passiamo attraverso questa linea per linea:

 funzione privata theRepeater (e: Event): void // il timer deve essere fermato, provare a rimuoverlo e vedere cosa succede. t.stop (); // ecco una variabile locale che manterrà la posizione corrente del cursore (falso). // beh, è ​​solo "falso" dopo la prima volta. var oldxer: Number = MouseCurrX; var oldyer: Number = MouseCurrY; // ora, proprio come abbiamo fatto in precedenza, dobbiamo trovare la differenza tra la posizione attuale // e quella precedente. quindi come è diverso da prima? perché? var xDiff: Number = MouseCurrX - arMousePrevX; var yDiff: Number = MouseCurrY - arMousePrevY; // se il pulsante è inattivo, non ci sposteremo più, il pulsante interromperà l'azione in questo caso. if (! buttonDown) // prendi la differenza e la segna per decadimento. questo ci darà la nuova // differenza, che sarà leggermente più piccola della precedente, che è come // otteniamo l'effetto di attrito con questo. // per esempio. se Decay è 0,5, la distanza spostata dimezza ogni fotogramma. xDiff = xDiff * Decay; yDiff = yDiff * Decay; // next è una delle parti che mi confondono, questo non sposta l'oggetto in // all, semplicemente verifica se il targetObject ha raggiunto il limite. se lo ha, // dobbiamo rimbalzare indietro. (questo potrebbe essere cambiato in qualche altra azione se vuoi //, puoi anche rimuoverlo, cosa succede se lo fai? prova! // nella funzione pan impostiamo questa variabile, OppositeEdge, questo è dove dobbiamo // usa 'se il targetObject è più grande dell'oggetto Event' che impostiamo nella // funzione init (). Sto solo andando nella x qui perché y è // quasi uguale (che cosa è diverso? perché ? pensaci!) if (xOppositeEdge) / * così prima, "la larghezza di eventObject, - la larghezza di targetObject - 50", * qui, la larghezza di targetObject è maggiore di quella di eventObject * this consentirà al bordo opposto dell'oggetto target di essere di 50 px in dal lato opposto.Se vai al filmato di esempio e riduci l'immagine a * 10% e la butti intorno, quindi aumenta la dimensione al 200% e prova notare * quale vantaggio sta facendo cosa, quindi vedrete la differenza tra i rimbalzi. * Questo è il modo migliore per capire questa parte. * / if (targetObject.x < (eventObject.width - targetObject.width - 50))  xDiff = -1 * xDiff; targetObject.x = eventObject.width - targetObject.width - 50;  // this does the same thing for the other edge. if(targetObject.x > 50) xDiff = -1 * xDiff; targetObject.x = 50;  // questo è se l'oggetto di destinazione è più piccolo di eventObject. else / * così ancora stiamo testando i bordi del targetObject contro l'oggetto * evento. Questa volta abbiamo a che fare con lo stesso bordo (beh, * 5px fuori dal bordo). Quindi rimbalzerà come se stesse colpendo un muro. * / if (targetObject.x < -5)  xDiff = -1 * xDiff; targetObject.x = -5;  if(targetObject.x > (eventObject.width - (targetObject.width - 5))) xDiff = -1 * xDiff; targetObject.x = eventObject.width - (targetObject.width - 5);  if (yOppositeEdge) if (targetObject.y < (eventObject.height - targetObject.height - 50))  yDiff = -1 * yDiff; targetObject.y = eventObject.height - targetObject.height - 50;  if(targetObject.y > 50) yDiff = -1 * yDiff; targetObject.y = 50;  else if (targetObject.y < -5)  yDiff = -1 * yDiff; targetObject.y = -5;  if(targetObject.y > (eventObject.height - (targetObject.height - 5))) yDiff = -1 * yDiff; targetObject.y = eventObject.height - (targetObject.height - 5);  // beh, se hai domande su quella parte, pubblica un commento e risponderò a loro. // qui ci sono il sider e il Topper vars (proprio come quelli della funzione pan). var sider: int = xDiff + targetObject.x; var Topper: int = yDiff + targetObject.y; // abbiamo bisogno di impostare questo pronto per il prossimo andare in giro. MouseCurrX = MouseCurrX + xDiff; MouseCurrY = MouseCurrY + yDiff; // e poi if se moveX, Y (di nuovo come la funzione pan) if (moveY) targetObject.y = Topper; if (moveX) targetObject.x = sider;  // e ora imposta il nostro mouse artificiale prev arMousePrevX = oldxer; arMousePrevY = oldyer; // e se non siamo in modalità senza attrito (OriginalDecay = 1) // stiamo sottraendo una piccola quantità dal nostro decadimento, a // dà un po 'più naturale alleggerimento. if (originalDecay < 1)  Decay = Decay - .004;  // so the moving is done.  // if the button is down we need to remove the listener. else  eventObject.removeEventListener(Event.ENTER_FRAME, theRepeater);  // now we need to check if the effect is over, which is if our x and y diffs are less than 1px. if((Math.abs(xDiff) < 1 && Math.abs(yDiff) < 1))  eventObject.removeEventListener(Event.ENTER_FRAME, theRepeater);  

E con questo, la nostra classe è finita.


Passaggio 9: Codice classe completato

Puoi prendere il codice completato dallo zip Source, collegato nella parte superiore del tutorial. È nella classe PanAndThrow.as.


Step 10: Fai qualcosa con esso

Per fare qualcosa con questo abbiamo bisogno di tornare al MXML e aggiungere alcune righe di codice. Aggiungi la nostra dichiarazione nella sezione delle variabili globali, compila il metodo di decadimento e chiama la nostra funzione pan e throw (). Con tutto ciò che è stato aggiunto, ecco il file MXML completo: