Gooi objecten door een PanAndThrow-klasse te maken

In deze zelfstudie bespotten we een pan-en-worp-les die ons in staat stelt om dit effect toe te voegen aan elk element dat we willen. Om dit te bereiken, zullen we een image viewer maken - maar niet je gemiddelde kijker. Hier zullen we zoomen, gooien, pannen ... Het klinkt bijna als een ninja-app, he?


Stap 1: Introductie

Met de klasse Pan en Throw kunt u de pan- en throw-functionaliteit toevoegen aan elk gewenst ActionScript-object. Hoewel deze zelfstudie specifiek voor Flex is, kan de klasse zelf overal worden gebruikt waar ActionScript is. Ik had dit effect op verschillende websites gezien en vervolgens in Photoshop CS4 en besloot dat ik dit ook op mijn projecten wilde hebben.

Er zijn veel toepassingen voor dit effect; degene die we gaan gebruiken voor deze tutorial is een afbeeldingsviewer waarmee je kunt in- en uitzoomen op de afbeelding en de wrijving kunt wijzigen die het throw-effect gebruikt. Deze zelfstudie gaat echter niet echt over de image viewer, maar over het maken van een pan- en worpklasse. Dus laten we ermee aan de slag gaan. Open je favoriete Flex-editor en krijg een project op gang; voor informatie over hoe dit te doen in Flex Builder, zie Adobe LiveDocs. Zodra uw project is gemaakt, opent u het MXML-bestand. We moeten hier wat code aan toevoegen voordat we onze klas maken.


Stap 2: Onze MXML

Omdat dit niet het grootste deel van de tutorial is, zal ik hier niet veel tijd doorbrengen. Als u vragen hebt over dit gedeelte die niet worden behandeld, kunt u dit in de onderstaande opmerkingen vragen. Allereerst zijn hier de MXML-objecten die in de toepassing moeten worden geplaatst:

            

Je zult de vier functies zien die in de tags worden genoemd: init (), changeDecay (), smoothImage () en zoom (). We moeten die functies schrijven. Dit is de code tussen de tags:

 import mx.states.SetStyle; importeer mx.effects.Move; importeer mx.containers.HBox; import mx.containers.Box; privé var imageWidth: Number = 0; privé var imageHeight: Number = 0; private var mover: Move = new Move (); // dit wordt aangeroepen wanneer de toepassing private functie start start (): void // Deze gebeurtenis voegt de mogelijkheid toe om te verbergen en onze bedieningselementen weer te geven met een klik. control.addEventListener (MouseEvent.CLICK, controlClick); mover.target = controle;  // deze functie zoomt in en uit in onze afbeelding op basis van de waarde van onze zoomschuifregelaar. privéfunctie zoom (): void inside.width = (imageWidth * hSlider.value) / 100; inside.height = (imageHeight * hSlider.value) / 100;  // dit wordt opgeroepen wanneer ons beeld van grootte verandert. private function smoothImage (ev: Event): void // stel afvlakken in, zodat het beeld er beter uitziet als het wordt getransformeerd. var bmp: Bitmap = ev.target.content als Bitmap; bmp.smoothing = true; imagewidth = inside.width; imageHeight = inside.height;  // we zullen deze nog niet gebruiken private-functie changeDecay (): void // dit zal de verval (wrijving) waarde van onze klasse veranderen, wanneer we daar aankomen.  besturingselement voor persoonlijke functiesClick (e: MouseEvent): void mover.play (); // deze functie verbergt / toont de besturingselementen bij klikken als (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 (); 

Zodra u uw MXML hebt, moet u een map met de naam 'classes' maken in dezelfde map als uw MXML-bestand. (Als u Flash gebruikt, moet de map zich in dezelfde map bevinden als uw FLA-bestand.) Dit is ons klassenpakket en is waar het bestand PanAndThrow.as naartoe gaat. Maak in Flex Builder een nieuwe klasse, plaats deze in het klassenpakket en noem het PanAndThrow; hierdoor wordt je standaardstijl in de klas gemaakt.


Stap 3: The Makings of a Class

Dit is onze standaard PanAndThrow-les. Sla het op als PanAndThrow.as in je nieuwe map "classes".

 // naamruimtedeclaratiepakketklassen // klassedeclaratie public class PanAndThrow / * dit wordt de constructor genoemd, deze methode / functie wordt opgeroepen wanneer u * een instantie van uw object maakt of een instantie van uw object maakt. * voor deze klasse doen we niets omdat we alles zullen doen * in de Init-functie * / public function PanAndThrow () 

Welke variabelen en functies hebben we nodig in onze PanAndThrow-klasse? Om dat te bereiken, kun je jezelf afvragen "wat moet mijn klas doen, wat moet het weten en wat moet het doen om het te kunnen doen?" Laten we dus een pseudo-code maken.

Snelle notitie

Toen ik deze klasse voor het eerst ontwikkelde, heb ik alles in de constructor geplaatst, maar dat leidde tot een probleem toen ik de start- en stopmethoden maakte vanwege de reikwijdte. Ik kon deze klasse niet instantiëren op een wereldwijd bereik met alle vereiste informatie. Ik heb daarom een ​​init () -functie gemaakt, dus de instantie kan buiten de klas worden gestart en gestopt.


Stap 4: Onze pseudo-code

"Pseudo-code" betekent gewoon valse code, die we kunnen gebruiken om onszelf te helpen nadenken over welke echte code we nodig hebben.

 pakket klassen public class PanAndThrow / * Dit zullen de variabelen zijn die we maken. Dus wat moeten we weten? * anObjectToThrow; * anObjectToThrowItIn; * ObjectLocation; * PreviousObjectLocation; * Verval; // voor de natuurkunde * dit zijn de voor de hand liggende, maar deze lijst zal een stuk groter worden * omdat we precies zien wat we nodig hebben in onze functies * / public function PanAndThrow ()  / * Dus wat gaat onze klas doen ? * in het(); // het moet starten * stop (); // we willen het op de een of andere manier kunnen stoppen. * start (); // als we stoppen, moeten we het opnieuw kunnen starten. * pan(); * gooien(); * /

Nu we wat pseudo-code hebben, kunnen we beginnen met het bouwen van de klas. Laten we beginnen met de functie init (). Dit zal ons ook brengen in een van de principes van Object Oriented Programming genaamd inkapseling, die zich bezighoudt met de toegang van stukken van de code.

Deze code zou moeten gaan in de PanAndThrow-klasse die we net zijn begonnen. (Weet niet precies waar? Bekijk de Quick Tip van de documentklasse.)

 // dankzij OOP kunnen een lagere niveauklasse en een hogere niveauklasse (een die de // // van het lagere niveau uitbreidt) worden gebruikt. Zoals hier geldt, breidt bijna elk object dat u gebruikt de // Sprite-klasse uit. Dus ik moet gewoon om een ​​Sprite-object vragen en je kunt een doos of een knop geven. privé var doelObject: Sprite = nieuwe Sprite (); private var eventObject: Sprite = new Sprite (); private var originalDecay: Number = .9; private var buttonDown: Boolean = false; private var movementY: Boolean = true; private var moveX: Boolean = true; privé var TargetClick: Boolean = true; // We gebruiken dit om te controleren hoe lang uw muis op een object is blijven zitten zonder te bewegen. privé var t: Timer; private var timerInterval: int = 100; openbare functie 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 = nieuwe timer (timerInterval); begin(); 

Slechts een paar dingen die ik wil benadrukken. In de functie voor init heb ik enkele van de argumenten ingesteld om gelijk te zijn aan een waarde. Dat betekent dat ik ze een standaardwaarde geef, waardoor ze optioneel zijn. Bij het instellen van standaardwaarden voor argumenten van een functie, moeten dit de laatste parameters zijn; u kunt geen vereiste variabele hebben na een optionele variabele. De reden dat ik standaardvariabelen heb toegevoegd, is om de oproep korter te maken als we de standaardinstellingen gebruiken. Ik kan PanAndThrow bellen (mover, eventer); en klaar zijn, in plaats van PanAndThrow (verhuizer, enventer, decayer, yVal, ...) enzovoort.

Heb je je ooit afgevraagd wat de "private" of "publieke" voor functies en variabelen betekent? Dat is de belichting van het object. Een "openbaar" object kan door elke andere klasse worden benaderd; een "privé" object kan alleen worden gezien door de andere leden van deze klasse; een "beschermd" object is verborgen voor alles behalve klassen die zich in hetzelfde pakket bevinden.

We willen het verval van onze MXML kunnen veranderen, dus we hebben een publieke haak nodig om bij onze privévariabele te komen; hier komen de getter- en setterfuncties binnen:

 private var originalDecay: Number = .9; public function get decay (): Number return originalDecay; 

Dat is een "vangstof" -functie. Dit betekent dat het voor externe klassen lijkt alsof de klasse PanAndThrow een openbare variabele heeft die "verval" wordt genoemd. Wanneer ze proberen toegang te krijgen, zullen we de waarde van onze (privé) originalDecay-variabele aan hen teruggeven.

Setter-functies zijn bijna hetzelfde, maar laat de externe klassen toe om de waarde van onze "nep" openbare variabele te wijzigen:

 public function set decay (value: Number): void originalDecay = value; 

Deze zijn handig omdat je logica in een setter kunt plaatsen om te beperken wat er in je privé-var komt. Als u bijvoorbeeld een nummer in de MXML-tag voor een vak plaatst, krijgt u een ingestelde hoogte; als je een% plaatst (van het getal een string maken), krijg je een percentage van de hoogte. Dat is ingebouwd in de code voor de hoogte-instelling van de box. Nu we onze getter en setter hebben, kun je de decayvariabele op deze manier van buiten de klas benaderen:

 var pt: PanAndThrow = nieuwe PanAndThrow (); pt.init (doel, ouder); pt.decay = .7;

Stap 5: Starten, luisteren, stoppen

We hebben onze klasse, enkele lokale variabelen en een init () -functie. Laten we nu iets doen. Aan het einde van de init () -functie noemden we "start ();" dus laten we de startfunctie maken. Meestal is het gewoon een hoop luisteraars:

 public function start (): void // Met de muis omlaag, zijn we op zoek om onze pan-actie te starten, maar we moeten // om onze OnlyMoveOnTargetClick te kunnen controleren die we hebben toegewezen aan ons globale veld TargetClick targetObject.addEventListener (MouseEvent. MOUSE_DOWN, handleOverTarget); eventObject.addEventListener (MouseEvent.MOUSE_DOWN, handleOverTarget); // Wanneer we onze pan aanroepen, wordt er een muisverplaatsingslistener gebruikt, wat betekent dat deze elke keer dat de // -muis beweegt, wordt aangeroepen, dus we moeten zien hoe te beperken wanneer het doelobject beweegt. eventObject.addEventListener (MouseEvent.MOUSE_MOVE, moveIt); // dit is om het object na een pan te gooien, dit is een beetje lastig omdat de throwIt () -functie een andere luisteraar oproept. targetObject.addEventListener (MouseEvent.MOUSE_UP, throwIt); eventObject.addEventListener (MouseEvent.MOUSE_UP, throwIt); // de throwItOut-methode zorgt ervoor dat ons object doet alsof we de muisknop loslaten, maar het wordt geactiveerd wanneer // de muis het bovenliggende object targetObject.addEventListener verlaat (MouseEvent.MOUSE_OUT, throwItOut); eventObject.addEventListener (MouseEvent.MOUSE_OUT, throwItOut); // dit is de timer luisteraar, dit zal controleren om te zien of je de muis een klein beetje ingedrukt hebt gehouden, ik zal // de noodzaak hiervan uitleggen wanneer we de timerOut () functie t.addEventListener (TimerEvent. TIMER, timerOut); t.start (); 

De stop () -functie is bijna hetzelfde, maar we verwijderen de luisteraars.

 openbare functie 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 (); 

Nu kunnen we luisteren naar wat er aan de hand is, laten we door elk van deze luisteraarfuncties gaan.


Stap 6: MouseEvent.MOUSE_DOWN

We gaan naar de gebeurtenishandler handleOverTarget kijken.

 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;  

Deze functie wordt aangeroepen wanneer zich een gebeurtenis MOUSE_DOWN op het gebeurtenisobject of het doelobject voordoet. Het is heel belangrijk op te merken dat als ik een luisteraar op een bovenliggend object plaats, de handler zelfs wordt gebeld wanneer de gebeurtenis zich voordoet bij een kind. In dit geval is mijn doelobject een onderliggende waarde van het gebeurtenisobject. Wanneer ik op het doelobject klik, wordt deze methode twee keer aangeroepen: eerst met de muis naar beneden op het kind en vervolgens naar de tweede muisknop op het bovenliggende object. Dat is echt belangrijk omdat we gaan bepalen of de muis ons doelobject kan verplaatsen, dus we moeten echt weten of die muis op het kind lag of niet.

De eerste verklaring is vrij eenvoudig: zet onze variabele kolom-button op true.

De volgende twee zijn ook vrij eenvoudig, behalve dat ik een paar nieuwe variabelen heb geïntroduceerd die in onze klassevariabele lijst moeten worden gezet: MousePrevX, MousePrevY, arMousePrevX, arMousePrevY, MouseCurrX en MouseCurrY. Deze zullen veel in de sleep- en pan-functies worden gebruikt, dus ik zal tot die tijd wachten om erover te praten.

De if-opdracht controleert of het geklikte object het doelobject is. Vergeet niet dat TargetClick was ingesteld op het argument dat we hebben doorgegeven aan init (), OnlyMoveOnTargetClick; als dit fout is, willen we elk onderliggende object behandelen als het doelobject wanneer erop wordt geklikt. Dat is waarom we de "||! TargetClick" controle hebben.

Dat is het makkelijke gedeelte. Het volgende deel is een beetje lastiger.

Het e.currentTarget retourneert het object dat de gebeurtenis heeft geactiveerd. Het e.target retourneert het object dat het werkelijke doelwit was. Dus ik zou dit kunnen zeggen, goed?

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

Dat is eenvoudig genoeg, maar het is verkeerd. Wat als mijn doelobject kinderen heeft? Dan kan mijn e.currentTarget het doelobject zijn, maar het e.target is het doelwit van TargetObject en komt niet overeen. We willen dat dit beweegt, zelfs als we ons neerleggen bij een kinderobject.

Dus hier komt String.search te hulp. Als ons currentTarget niet ons doelobject is, gebruiken we een "else if" om te kijken of we ons doelobject in het doel kunnen vinden. e.target.toString () produceert zoiets als dit: "application2.eventobject3.targetobject2.targetchild4" voor ons doelobject waarbij targetObject.toString () zoiets als dit "application2.eventobject3.targetobject2" produceert, alles wat ik moet doen om zoek uit of ons doel een kind is van ons targetObject is hierdoor:

 e.target.toString (). search (targetObject.toString ())

Als er een overeenkomst is, wordt de eerste index van de wedstrijd geretourneerd, of als er geen overeenkomst is, wordt een -1 geretourneerd, dus we kunnen alleen zien of deze groter is dan -1 en altviool, we hebben vastgesteld of het object erop geklikt wordt, is een kind van ons targetObject.

(We zouden de kinderen of ouder (s) van het object kunnen controleren via de functie getChildAt () en bovenliggende eigenschap, maar dit is een goed alternatief.)


Stap 7: TimerOut en Pan

De timerfunctie is ook vrij eenvoudig, vooral omdat we dit eerder hebben gedaan. Nou ja, bijna al eerder gedaan. Wanneer we ons kleine doelobject een beetje hebben gesleept en besluiten dat we het niet willen laten gaan, houden we er gewoon te veel van en stoppen we abrupt met de muis, wat zou er gebeuren als je de muisknop op dat moment loslaat? wel, wat denk je dat er zou gebeuren? Ik ga dat niet voor je beantwoorden, ik ga je gewoon helpen met de code om te voorkomen dat het gebeurt. In de definitieve code becommentarieer deze drie regels. Dit zou er heel vertrouwd uit moeten zien, we gebruikten dit alleen in de knop-omlaag-handler, behalve één variabele, MouseDragged. We gaan dat gebruiken wanneer we onze andere functie noemen:

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

Dus, als je je afvraagt ​​waarom we deze timer-gebeurtenis nodig hebben, heb je waarschijnlijk niet geprobeerd het eruit te halen om te zien wat er gebeurde. Doe dat.

Deze volgende functie is een van onze hoofdfuncties; het is de pan-functie. Er is veel mee gemoeid, dus laten we onze pseudo-code eens bekijken:

 private function moveIt (e: MouseEvent): void / * ok, dus wat hebben we nodig met deze functie? * het moet ons doelobject verschuiven. * dus laten we kijken of we ons doelobject * / // zijn als (we zijn boven ons doelobject) // // welke gereedschappen moeten we gaan gebruiken? // wel, misschien moeten we controleren of de knop omlaag is // if (knop is ingedrukt) // // we moeten misschien de variabele voor de knop omlaag instellen. buttonDown = true; // en als we op dit punt in deze functie zijn, is onze knop ingedrukt en // de muis is verplaatst - dat is een slepen: dus MouseDragged = true; // als we het object bewegen volgens de muisbeweging, dan zouden we // waarschijnlijk moeten weten waar onze muis is: MouseCurrX, Y = huidig ​​MouseX, Y; // dit is een inleiding tot onze kunstmatige muis vorige, die in de volgende functie zal worden uitgelegd //. De ar staat voor 'artificial' of 'after release', // wat je maar wilt. Dat moet worden ingesteld op onze werkelijke vorige muispositie. // arMousePrevX = MousePrevX; // arMousePrevY = MousePrevY; // dan moeten we het doelobject daadwerkelijk verplaatsen, // maar onze variabelen onthouden, moveX en moveY, dus: // als moveX x verplaatst; // als beweging y y verplaatst; // we moeten ons verval (frictie) terugzetten naar de oorspronkelijke staat: // Decay = originalDecay; // dat zou het if // // moeten voltooien, wat nog meer? // // we hebben onze buttondown eerder op true gezet, dus laten we deze hier op false instellen. // buttonDown = false; // als dit geen doelklik is, moeten we onze overTarget op false zetten, dus: // if (! TargetClick) // overTarget = false; // dat is het. // // er zijn een paar dingen die we willen laten gebeuren, ongeacht de omstandigheden. // eerst moeten we onze mousePrevX, Y-variabele instellen - VOORDAT de muis is // opnieuw verplaatst! // MousePrevX = eventObject.mouseX; // MousePrevY = eventObject.mouseY; // Hier zijn nog twee variabelen om bij te houden: xOpposideEdge en yOppositeEdge // we testen om te zien wat de grootte is van ons doelobject in relatie // tot ons gebeurtenisobject; als een groter is, moeten we het gedrag van de bounce veranderen. // if (targetObject.width> eventObject.width) xOppositeEdge = true; // else xOppositeEdge = false; // if (targetObject.height> eventObject.height) yOppositeEdge = true; // else yOppositeEdge = false; // en tot slot moeten we onze timer stoppen en opnieuw starten. //t.stop (); //t.start (); //

Ik geef toe dat dit iets meer psuedo-y is dan de vorige; dat is om twee redenen: een, je weet niet wat er gaat komen, en twee, ik ben gewoon heel blij dat ik de code heb bereikt:

 private functie moveIt (e: MouseEvent): void // in onze pseudo-code was dit twee voorwaarden, maar we kunnen dan combineren met één, // we testen om te zien of onze gebeurtenis een knop was en of we over onze doel, // als we dan het doelobject verplaatsen. if (e.buttonDown && overTarget) buttonDown = true; MouseDragged = true; MouseCurrX = eventObject.mouseX; MouseCurrY = eventObject.mouseY; // hier is de kunstmatige / na de release. nogmaals, kom daar maar eens op. arMousePrevX = MousePrevX; arMousePrevY = MousePrevY; / * dit is de belangrijkste, in onze pseudo was het "verplaats het doelobject", * dus we moeten dat vertalen. Om ons te helpen, maken we een lokale variabele * Topper voor de top en Sider voor de zijkant. * dus laten we naar Topper kijken (hetzelfde geldt voor Sider). * eventObject.mouseY kijkt naar waar onze muis zich in het eventObject bevindt. * We nemen onze MousePrev hier vanaf, en dat geeft ons hoeveel het object * zou moeten reizen, dus de Y kan 2 pixels reizen, of -2 pixels, afhankelijk van de richting *, dus we nemen die wijziging en voegen deze toe aan de doelwitten huidige * positie, maar dat gebeurt nog niet, dit is slechts een var. * / var Topper: int = (eventObject.mouseY - MousePrevY) + targetObject.y; var Sider: int = (eventObject.mouseX - MousePrevX) + targetObject.x; // hier is waar het gebeurt, als moveY (onthoud van de pseudo-code) dan kunnen wij // de positie van het doel instellen. if (moveY) targetObject.y = Topper; if (moveX) targetObject.x = Sider; // dus eigenlijk gebruiken we alleen Topper en Sider om tijdelijk op te slaan waar het //-doelobject naar Decay = originalDecay moet gaan ;  else buttonDown = false; if (! 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 (); 

En nu zijn we aan het pannen.


Stap 8: Throw, It, Out, Repeater!

Dit is de tweede grote functie en hiermee zullen we onze klas laten bouwen! Klaar om te pannen en elk willekeurig voorwerp te gooien! Er zijn twee functies die we eerst moeten adresseren: throwIt (), die we instellen als een handler voor de MOUSE_UP -gebeurtenis, en throwItOut (), die we instellen als een handler voor de MOUSE_OUT -gebeurtenis.

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

Deze twee functies zijn bijna hetzelfde (ze doen immers hetzelfde op verschillende tijdstippen). Daarin stellen we de knop Down in op false, omdat dit een mouse-upgebeurtenis is en controleert u of de muis is gesleept, met MouseDragged (die we in de laatste functie hebben ingesteld) of door "e.relatedObject" aan te vinken; het voorwerp waar de muis net uit is gekomen.

Als het is gesleept, voegen we nog een luisteraar toe. De ENTER_FRAME-gebeurtenis is echt heel gaaf. Dit is de basis van onze animatie; elke keer dat we een nieuw frame invoeren, wordt de throw () -functie uitgevoerd. Dat is wat ons in staat stelt om na het vrijgeven een muissleping te simuleren (denk aan de arMousePrevX, Y-variabele? Daar is het voor). En dat is alles wat de worp echt doet, het simuleren van een muisslepen, natuurlijk zonder een muis. We hebben dus eigenlijk al de functie die we nodig hebben, behalve dat we de oproepen naar de huidige muispositie moeten vervangen door onze kunstmatige muispositie.

Ik was daar bijna een beetje voor op mezelf. Dus met deze twee gebeurtenisfuncties, throwIt en throwItOut doen ze hetzelfde maar dan als in de tweede functie is het vermelden waard. Ik worstelde een tijdje met het proberen om deze functionaliteit te krijgen, totdat ik de gebeurtenis iets dichterbij zag. Het probleem was om het doelobject te laten werken alsof ik de knop losliet toen de cursor het gebeurtenisobject verliet. Ga je gang, probeer het en doe dit zonder het e.relatedObject. Ik had het bijna een paar keer, maar kon het niet goed krijgen. Wat het e.relatedObject doet, is zoeken naar welk object u zich bevindt, nadat de gebeurtenis is aangeroepen. Dat is waarom het zoooo cool is. Wanneer onze cursor helemaal de film verlaat, wordt er een null geretourneerd, anders wordt het object waarop u zich bevindt geretourneerd, zodat we kunnen controleren of e.relatedObject null is of een bovenliggend element is van het eventObject. Dat levert de juiste actie op waarnaar we op zoek zijn.

In de bovenstaande functies zetten we oproepen op naar deRepeater (). Dit is de throw-functie en onthoud dat deze elke keer dat we een nieuw frame invoeren, wordt aangeroepen. Laten we dit regel voor regel doorlopen:

 private function theRepeater (e: Event): void // de timer moet worden gestopt, probeer dit te verwijderen en kijk wat er gebeurt. t.stop (); // hier is een lokale variabele die de huidige (nep) cursorpositie zal houden. // goed, het is pas "nep" na de eerste keer. var oldxer: Number = MouseCurrX; var oldyer: Number = MouseCurrY; // nu, net zoals we eerder deden, moeten we het verschil vinden tussen onze huidige // en vorige positie. dus hoe is dit anders dan voorheen? waarom? var xDiff: Number = MouseCurrX - arMousePrevX; var yDiff: Number = MouseCurrY - arMousePrevY; // als de knop is ingedrukt, gaan we niet meer bewegen, de knop stopt de actie in dit geval. if (! buttonDown) // neem het verschil en keer het door het verval. dit zal ons het nieuwe // verschil geven, dat iets kleiner zal zijn dan het laatste, dat is hoe // we hiermee het frictie-effect krijgen. // b.v. als Verval 0,5 is, dan zal de bewogen afstand elk beeld halveren. xDiff = xDiff * Verval; yDiff = yDiff * Verval; // next is een van de verwarrende onderdelen voor mij, dit verplaatst het object niet naar // all, het test gewoon om te zien of ons doelobject de rand heeft bereikt. als dat zo is, // moeten we het terugsturen. (dit zou kunnen worden veranderd in een andere actie als je // wilt, je zou het zelfs kunnen verwijderen, wat gebeurt er als je het doet? Probeer het! // in de pan-functie plaatsen we deze variabele, OppositeEdge, dit is waar we zullen // gebruik dat 'als het doelobject groter is dan het gebeurtenisobject' dat we in // de functie init () hebben ingesteld. Ik ga hier alleen door x lopen omdat het y // bijna hetzelfde is (wat is anders? waarom ? denk erover na!) if (xOppositeEdge) / * dus eerst, "de breedte van het eventObject, - de breedte van het targetObject - 50", * hier is de breedte van het targetObject groter dan dat van eventObject * dit staat toe dat de tegenovergestelde rand van het doelobject 50 px is vanaf * de tegenoverliggende rand. Als je naar de voorbeeldfilm gaat en de afbeelding verkleint naar * 10% en rond gooit, verhoog dan de grootte naar 200% en probeer en merk op * welke rand doet wat, dan zie je het verschil tussen de bounces. * Dat is de beste manier om dit onderdeel te begrijpen. * / 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;  // dit is als het doelobject kleiner is dan het eventObject. else / * dus opnieuw testen we de randen van het targetObject tegen het * event-object. Deze keer hebben we te maken met dezelfde rand (goed, * 5px buiten de rand). Dus dit zal stuiteren alsof het een muur raakt. * / 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 (yOffositeEdge) 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);  // wel, als je vragen hebt over dat deel, plaats dan gewoon een opmerking hierover en ik zal ze beantwoorden. // hier zijn de sider en Topper vars (net zoals die uit de pan-functie). var sider: int = xDiff + targetObject.x; var Topper: int = yDiff + targetObject.y; // we moeten dit klaar maken voor de volgende ronde. MouseCurrX = MouseCurrX + xDiff; MouseCurrY = MouseCurrY + yDiff; // en dan de if moveX, Y (opnieuw zoals de pan-functie) if (moveY) targetObject.y = Topper; if (moveX) targetObject.x = sider;  // en stel nu onze kunstmatige muis in prev arMousePrevX = oldxer; arMousePrevY = oldyer; // en als we ons niet in een wrijvingsloze modus bevinden (OriginalDecay = 1) // gaan we een klein deel aftrekken van ons verval, naar // geeft het een iets natuurlijkere versoepeling. 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);  

En daarmee is onze klas klaar.


Stap 9: De voltooide klassencode

U kunt de voltooide code ophalen uit de Bron-zip, die bovenaan de zelfstudie is gekoppeld. Het zit in de klasse PanAndThrow.as.


Stap 10: Doe er iets mee

Om hier iets mee te doen, moeten we teruggaan naar de MXML en enkele regels code toevoegen. Voeg onze verklaring toe in de globale variabele sectie, vul de vervalmethode in en roep onze pan en gooi init () functie. Met alles wat is toegevoegd, hier is het volledige MXML-bestand: