Lancer des objets en créant une classe PanAndThrow

Dans ce didacticiel, nous allons nous moquer et terminer un cours de panoramique et de projection qui nous permettra d’ajouter cet effet à tous les éléments souhaités. Pour ce faire, nous allons créer une visionneuse d'images, mais pas votre visionneuse moyenne. Ici, nous allons zoomer, lancer, faire un panoramique… Ça ressemble presque à une application de ninja, hein?


Étape 1: Introduction

Les classes Pan et Throw vous permettront d’ajouter les fonctionnalités de panoramique et de projection à n’importe quel objet ActionScript de votre choix. Bien que ce tutoriel soit spécifiquement destiné à Flex, la classe elle-même peut être utilisée n'importe où dans ActionScript. J'avais déjà constaté cet effet sur plusieurs sites Web, puis dans Photoshop CS4, et décidé que je le voulais aussi dans mes projets..

Il existe de nombreuses applications pour cet effet. Celui que nous allons utiliser pour ce didacticiel est un visualiseur d’images qui vous permet d’agrandir et de réduire l’image et de modifier le frottement utilisé par l’effet de projection. Cependant, ce tutoriel ne concerne pas vraiment la visionneuse d'images, il s'agit de créer une classe pan et throw. Alors commençons avec ça. Ouvrez votre éditeur Flex préféré et démarrez un projet. Pour plus d'informations sur cette opération dans Flex Builder, voir Adobe LiveDocs. Une fois votre projet créé, ouvrez le fichier MXML. Nous devons ajouter du code à cela avant de créer notre classe.


Étape 2: Notre MXML

Comme ce n’est pas la majeure partie du tutoriel, je ne vais pas passer beaucoup de temps ici. Si vous avez des questions sur cette section qui ne sont pas couvertes, vous pouvez les poser dans les commentaires ci-dessous. Tout d'abord, voici les objets MXML à mettre dans l'application:

            

Vous remarquerez les quatre fonctions appelées dans les balises: init (), changeDecay (), smoothImage () et zoom (). Nous devons écrire ces fonctions. Ceci est le code entre le Mots clés:

 import mx.states.SetStyle; importer mx.effects.Move; import mx.containers.HBox; importer mx.containers.Box; private var imageWidth: Number = 0; private var imageHeight: Number = 0; private var mover: Move = new Move (); // ceci sera appelé lorsque l'application chargera une fonction privée init (): void // Cet événement ajoutera la possibilité de masquer et d'afficher nos contrôles en un clic. control.addEventListener (MouseEvent.CLICK, controlClick); mover.target = control;  // cette fonction effectuera un zoom avant et arrière sur notre image en fonction de la valeur de notre curseur de zoom. fonction privée zoom (): void inside.width = (imageWidth * hSlider.value) / 100; inside.height = (imageHeight * hSlider.value) / 100;  // ceci est appelé lorsque notre image change de taille. fonction privée smoothImage (ev: Event): void // définit le lissage de l'image pour que l'image ait un meilleur aspect lorsqu'elle est transformée. var bmp: Bitmap = ev.target.content as Bitmap; bmp.smoothing = true; imageWidth = inside.width; imageHeight = inside.height;  // nous n'utiliserons pas encore celle-ci. function privée changeDecay (): void // cela modifiera la valeur de décroissance (friction) de notre classe, lorsque nous y arriverons.  fonction privée controlClick (e: MouseEvent): void mover.play (); // cette fonction cache / affiche les contrôles au clic si (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 (); 

Une fois que vous avez votre MXML, vous devez créer un dossier appelé "classes" dans le même dossier que votre fichier MXML. (Si vous utilisez Flash, le dossier doit se trouver dans le même répertoire que votre fichier FLA.) Ceci est notre package de classes et est l'endroit où le fichier PanAndThrow.as ira. Dans Flex Builder, créez une nouvelle classe, placez-la dans le package de classes et appelez-la PanAndThrow. cela créera votre classe - style par défaut.


Étape 3: La fabrication d'une classe

Voici notre classe de base PanAndThrow. Enregistrez-le sous PanAndThrow.as dans votre nouveau dossier "classes".

 // classes de package de déclaration d'espace de noms // classe de déclaration de classe publique classe PanAndThrow / * on l'appelle le constructeur, cette méthode / fonction sera appelée lorsque vous créez * une instance de votre objet ou instanciez votre objet. * pour cette classe, nous ne faisons rien car nous allons tout faire * dans la fonction Init * / fonction publique PanAndThrow () 

De quelles variables et fonctions avons-nous besoin dans notre classe PanAndThrow? Pour obtenir cela, vous pouvez vous demander: "Qu'est-ce que ma classe doit faire, que doit-elle savoir et que doit-elle être capable de le faire?" Créons donc du pseudo-code.

Note rapide

Lorsque j'ai développé cette classe pour la première fois, j'ai tout mis dans le constructeur, mais cela a posé un problème lorsque j'ai créé les méthodes start et stop en raison de leur portée. Je ne pouvais pas instancier cette classe sur une portée globale avec toutes les informations requises. J'ai donc créé une fonction init (), afin que l'instance puisse être démarrée et arrêtée de l'extérieur de la classe.


Étape 4: Notre pseudo-code

"Pseudo-code" signifie simplement un faux code, que nous pouvons utiliser pour nous aider à réfléchir au code réel dont nous aurons besoin.

 classes de paquetages public class PanAndThrow / * Ce seront les variables que nous allons créer. Alors, que devons-nous savoir? * anObjectToThrow; * anObjectToThrowItIn; * ObjectLocation; * PreviousObjectLocation; * Pourriture; // pour la physique * ce sont les plus évidentes, mais cette liste deviendra beaucoup plus grande * à mesure que nous verrons exactement ce dont nous avons besoin dans nos fonctions * / fonction publique PanAndThrow ()  / * Alors, que va faire notre classe? ? * init (); // il faut qu'il commence * stop (); // nous voulons pouvoir l'arrêter d'une manière ou d'une autre. * début(); // si nous nous arrêtons, nous devons pouvoir le redémarrer. * la poêle(); * jeter(); * /

Maintenant que nous avons un pseudo-code, nous pouvons commencer à construire la classe. Commençons par la fonction init (). Cela nous amènera également à l’un des principes de la programmation orientée objet appelée encapsulation, qui traite de l'accès aux morceaux du code.

Ce code devrait aller dans la classe PanAndThrow que nous venons de commencer. (Vous ne savez pas où? Consultez le conseil rapide de Document Class.)

 // Grâce à la programmation orientée objet, une classe de niveau inférieur et une classe de niveau supérieur (une classe qui étend // la classe de niveau inférieur) peuvent être utilisés. Comme ici, presque tout objet que vous utiliserez étend la classe // Sprite. Donc, je dois juste demander un objet Sprite et vous pouvez donner une boîte ou un bouton. 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; // Nous l'utiliserons pour vérifier la durée pendant laquelle votre souris est restée appuyée sur un objet sans bouger. var privé: minuterie; var privée timerInterval: int = 100; fonction publique 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); début(); 

Je voudrais juste souligner quelques points. Dans la fonction init, j'ai défini quelques arguments comme étant égaux à une valeur. Cela signifie que je leur donne une valeur par défaut, ce qui les rend facultatifs. Lors de la définition des valeurs par défaut pour les arguments d'une fonction, il doit s'agir des derniers paramètres. Vous ne pouvez pas avoir de variable obligatoire après une variable facultative. La raison pour laquelle j'ai ajouté des variables par défaut est de raccourcir l'appel si nous utilisons les paramètres par défaut. Je peux appeler PanAndThrow (mover, eventer); et être fait, au lieu de PanAndThrow (mover, enventer, decayer, yVal,…) et ainsi de suite.

Vous êtes-vous déjà demandé ce que signifie "privé" ou "public" devant des fonctions et des variables? C'est l'exposition de l'objet. Un objet "public" est accessible à toute autre classe. un objet "privé" ne peut être vu que par les autres membres de cette classe; un objet "protégé" est caché de tout sauf des classes qui sont dans le même paquet.

Nous voulons pouvoir changer la désintégration de notre MXML, nous avons donc besoin d'un hook public pour accéder à notre variable privée. C'est ici qu'interviennent les fonctions getter et setter:

 private var originalDecay: Number = .9; fonction publique get decay (): Number return originalDecay; 

C'est une fonction "getter". Cela signifie que, pour les classes extérieures, il semble que la classe PanAndThrow ait une variable publique appelée "decay". Quand ils essaieront d'y accéder, nous leur redonnerons la valeur de notre variable (privée) originaleDecay.

Les fonctions de Setter sont presque identiques, mais permettent aux classes extérieures de changer la valeur de notre variable publique "fake":

 fonction publique set decay (value: Number): void originalDecay = value; 

Celles-ci sont utiles car vous pouvez mettre la logique dans un séparateur pour contraindre ce qui entre dans votre variable privée. Par exemple, si vous mettez un numéro dans la balise MXML pour une boîte, vous obtiendrez une hauteur définie; si vous mettez un% (faisant du nombre une chaîne), vous obtiendrez un pourcentage de hauteur. Cela est intégré dans le code pour le réglage de la hauteur de la boîte. Maintenant que nous avons notre getter et notre setter, vous pouvez accéder à la variable de désintégration comme celle-ci depuis l'extérieur de la classe:

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

Étape 5: Commencez, écoutez, arrêtez

Nous avons notre classe, des variables locales et une fonction init (). Faisons quelque chose maintenant. À la fin de la fonction init (), nous avons appelé "start ();" alors faisons la fonction de démarrage. Surtout c'est juste un groupe d'auditeurs:

 public function start (): void // Avec la souris enfoncée, nous cherchons à lancer notre action panoramique, mais nous devons pouvoir // vérifier notre OnlyMoveOnTargetClick que nous avons attribué à notre champ global TargetClick targetObject.addEventListener (MouseEvent. MOUSE_DOWN, handleOverTarget); eventObject.addEventListener (MouseEvent.MOUSE_DOWN, handleOverTarget); // Lorsque nous appelons notre panoramique, il utilise un écouteur de déplacement de souris, ce qui signifie qu'il est appelé à chaque fois que // la souris se déplace. Nous avons donc besoin de voir comment limiter le déplacement de l'objet cible. eventObject.addEventListener (MouseEvent.MOUSE_MOVE, moveIt); // ceci consiste à lancer l'objet après un panoramique, ce qui est un peu délicat car la fonction throwIt () appelle un autre écouteur. targetObject.addEventListener (MouseEvent.MOUSE_UP, throwIt); eventObject.addEventListener (MouseEvent.MOUSE_UP, throwIt); // la méthode throwItOut fait en sorte que notre objet agisse comme si nous relâchions le bouton de la souris, mais il est déclenché lorsque // la souris quitte l'objet parent targetObject.addEventListener (MouseEvent.MOUSE_OUT, throwItOut); eventObject.addEventListener (MouseEvent.MOUSE_OUT, throwItOut); // ceci est l'écouteur de minuterie, ceci vérifiera si vous avez tenu la souris un peu, je vais // expliquer la nécessité de cela quand nous aurons la fonction timerOut () t.addEventListener (TimerEvent. TIMER, timerOut); t.start (); 

La fonction stop () est presque identique, mais nous supprimons les écouteurs.

 fonction publique 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 (); 

Maintenant, nous pouvons écouter ce qui se passe, passons en revue chacune de ces fonctions d'écoute..


Étape 6: MouseEvent.MOUSE_DOWN

Nous allons examiner le gestionnaire d'événement handleOverTarget.

 fonction privée 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;  

Cette fonction sera appelée en cas d'événement MOUSE_DOWN sur l'objet événement ou sur l'objet cible. Il est très important de noter que si je mets un écouteur sur un objet parent, le gestionnaire sera même appelé lorsque l'événement se produit sur un enfant. Dans ce cas, mon objet cible est un enfant de l'objet événement. Lorsque je clique sur l'objet cible, cette méthode sera appelée deux fois: d'abord pour la souris sur l'enfant, ensuite pour la souris vers le parent. C’est vraiment important pour cela car nous allons décider si la souris vers le bas sera capable de déplacer notre objet cible. Nous devons donc être en mesure de savoir si cette souris est sur l’enfant ou non..

La première déclaration est assez simple: définissez notre variable de classe buttonDown sur true.

Les deux suivantes sont assez faciles également, sauf que j'ai introduit quelques nouvelles variables qui devront être placées dans notre liste de variables de classe: MousePrevX, MousePrevY, arMousePrevX, arMousePrevY, MouseCurrX et MouseCurrY. Celles-ci seront beaucoup utilisées dans les fonctions de traînée et de panoramique, je vais donc attendre pour en parler d'ici là..

L'instruction if vérifie si l'objet sur lequel vous avez cliqué est l'objet cible. N'oubliez pas que TargetClick a été défini sur l'argument que nous avons transmis à init (), OnlyMoveOnTargetClick; si cela est faux, nous voulons traiter chaque objet enfant en tant qu'objet cible lorsque l'utilisateur clique dessus. C'est pourquoi nous avons le chèque "||! TargetClick".

C'est la partie facile. La partie suivante est un peu plus délicate.

E.currentTarget renvoie l'objet qui a déclenché l'événement. E.target retournera l'objet qui était la cible réelle. Donc, je pourrais dire ceci, à droite?

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

C'est assez simple, mais c'est faux. Et si mon objet cible a des enfants? Ensuite, mon e.currentTarget peut être targetObject mais e.target est le fils de targetObject et ne correspondra pas. Nous voulons que cela bouge même si nous descendons sur un objet enfant.

Alors, voici String.search à la rescousse. Si notre currentTarget n'est pas notre targetObject, nous utilisons un "else if" pour voir si nous pouvons trouver notre objet cible dans la cible. e.target.toString () produira quelque chose comme ceci: "application2.eventobject3.targetobject2.targetchild4" pour notre objet cible où targetObject.toString () produira quelque chose comme ceci "application2.eventobject3.targetobject2" tout ce que je dois faire pour savoir si notre cible est un enfant de notre targetObject est par ceci:

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

S'il y a une correspondance, le premier index de la correspondance sera renvoyé, ou s'il n'y a pas de correspondance, il renvoie -1, afin que nous puissions voir s'il est supérieur à -1 et alto, nous avons trouvé si l'objet être cliqué sur est un enfant de notre targetObject.

(Nous pourrions vérifier les enfants ou le (s) parent (s) de l'objet via la fonction getChildAt () et la propriété parent, mais c'est une alternative intéressante.)


Étape 7: TimerOut et Pan

La fonction de minuterie est assez facile aussi, surtout depuis que nous avons fait cela auparavant. Eh bien, presque fait cela avant. Lorsque nous avons traîné un peu autour de notre petit objet cible et que nous avons décidé de ne pas le laisser tomber, nous l’aimons trop et arrêtons brusquement la souris. Que se passerait-il si vous relâchiez le bouton de la souris à ce stade? Eh bien, que va-t-il se passer selon vous? Je ne vais pas répondre à cette question pour vous, je vais simplement vous aider avec le code pour l'empêcher. Dans le code final, commentez ces trois lignes. Cela devrait sembler très familier, nous l'avons simplement utilisé dans le gestionnaire de boutons, sauf pour une variable, MouseDragged. Nous allons utiliser cela lorsque nous appelons notre autre fonction:

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

Donc, si vous demandez pourquoi nous avons besoin de cet événement de minuterie, vous n'avez probablement pas essayé de le sortir pour voir ce qui se passait. Alors fais ça.

Cette fonction suivante est l’une de nos fonctions principales; c'est la fonction pan. Il y a beaucoup de choses en jeu, alors plongeons-nous dans notre pseudo-code:

 fonction privée moveIt (e: MouseEvent): void / * ok, alors qu'est-ce que nous avons besoin de cette fonction? * il doit casser notre objet cible. * donc voyons si nous sommes sur notre objet cible * / // if (nous sommes sur notre objet cible) // // quels outils allons-nous avoir besoin de faire un panoramique? // eh bien, nous devrions peut-être vérifier si le bouton est enfoncé // si (le bouton est enfoncé) // // nous pourrions avoir besoin de définir la variable du bouton enfoncé. buttonDown = true; // et si nous sommes dans cette fonction à ce stade, notre bouton est enfoncé et // la souris s'est déplacée - c'est un glissement: so MouseDragged = true; // si nous déplaçons l'objet en fonction du déplacement de la souris, nous devrions // probablement savoir où se trouve notre souris: MouseCurrX, Y = current MouseX, Y; // ceci est une introduction à notre souris artificielle prev, qui sera expliquée // dans la fonction suivante. Ar signifie "artificiel" ou "après publication", // celui que vous préférez. Cela doit être réglé sur notre position actuelle de la souris précédente. // arMousePrevX = MousePrevX; // arMousePrevY = MousePrevY; // alors nous devons réellement déplacer le targetObject, // mais nous nous souvenons de nos variables, moveX et moveY, donc: // si moveX move x; // si moveY move y; // nous devons réinitialiser notre décomposition (friction) à son état d'origine: // Decay = originalDecay; // ça devrait finir le si // // quoi d'autre? // // nous avons défini notre bouton Down sur true avant, nous allons donc le définir sur false ici. // buttonDown = false; // s'il ne s'agit pas d'un clic sur la cible, définissez notre overTarget sur false, ainsi: // if (! TargetClick) // overTarget = false; // c'est tout. // // il y a quelques choses que nous voulons avoir quelles que soient les conditions. // d'abord, nous devons définir notre variable mousePrevX, Y - AVANT que la souris soit // déplacée à nouveau! // MousePrevX = eventObject.mouseX; // MousePrevY = eventObject.mouseY; // Voici deux autres variables à surveiller: xOpposideEdge et yOppositeEdge // nous testons pour voir quelle est la taille de notre objet cible par rapport // à notre objet événement; si on est plus gros, il faut changer le comportement du rebond. // if (targetObject.width> eventObject.width) xOppositeEdge = true; // else xOppositeEdge = false; // if (targetObject.height> eventObject.height) yOppositeEdge = true; // else yOppositeEdge = false; // et enfin nous devons arrêter et redémarrer notre minuterie. //t.stop (); //t.start (); //

J'avoue que c'est un peu plus psuedo-y que le dernier; C’est pour deux raisons: premièrement, vous ne savez pas ce qui va arriver, et deuxièmement, je suis vraiment excité d’avoir accès au code:

 fonction privée moveIt (e: MouseEvent): void // dans notre pseudo-code, il s'agissait de deux conditions, mais nous pouvons combiner à une, // nous testons pour voir si notre événement était un bouton enfoncé et si nous sommes au-dessus de notre cible, // si nous sommes alors déplaçons l'objet cible. if (e.buttonDown && overTarget) buttonDown = true; MouseDragged = true; MouseCurrX = eventObject.mouseX; MouseCurrY = eventObject.mouseY; // voici la version artificielle / after release. encore une fois, bien y arriver. arMousePrevX = MousePrevX; arMousePrevY = MousePrevY; / * C’est l’important, dans notre pseudo, c’est "déplacer l’objet cible", * nous devons donc traduire cela. Pour nous aider, nous allons créer une variable locale * Topper pour le haut et Sider pour le côté. * alors regardons Topper (la même chose s’appliquera à Sider). * eventObject.mouseY regarde où se trouve notre souris dans eventObject. * Nous prenons notre MousePrev loin de cela, et cela nous donnera combien d'objet * devrait parcourir, de sorte que le Y puisse parcourir 2 pixels, ou -2 pixels selon la direction *, nous prenons donc ce changement et l'ajoutons à celui de la cible. position actuelle *, mais cela ne se produit pas encore, ceci est juste un var. * / var Topper: int = (eventObject.mouseY - MousePrevY) + targetObject.y; var Sider: int = (eventObject.mouseX - MousePrevX) + targetObject.x; // c'est ici que ça se passe, si moveY (souvenez-vous du pseudo-code), alors nous // pouvons définir la position de la cible. if (moveY) targetObject.y = Topper; if (moveX) targetObject.x = Sider; // nous n'utilisons donc que Topper et Sider pour stocker temporairement l'emplacement où // l'objet cible doit être déplacé vers Decay = originalDecay ;  else buttonDown = false; if (! TargetClick) overTarget = false;  MousePrevX = eventObject.mouseX; MousePrevY = eventObject.mouseY; if (targetObject.width> eventObject.width) xOppositeEdge = true; autre xOppositeEdge = false; if (targetObject.height> eventObject.height) yOppositeEdge = true; autre yOppositeEdge = false; ) t.start (); 

Et maintenant nous faisons un panoramique.


Étape 8: Lancer, it, out, répéteur!

C'est la deuxième grande fonction et avec cela nous allons construire notre classe! Prêt à faire un panoramique et à lancer n'importe quel objet qui vous convient! Il faut d'abord traiter deux fonctions: throwIt (), que nous avons définie en tant que gestionnaire de l'événement MOUSE_UP, et throwItOut (), que nous avons définie en tant que gestionnaire de l'événement MOUSE_OUT..

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

Ces deux fonctions sont presque identiques (après tout, elles font la même chose à des moments différents). Dans ceux-ci, nous définissons le paramètre buttonDown sur false, car il s'agit d'un événement survivant avec la souris, et nous vérifions si la souris a été déplacée à l'aide de MouseDragged (que nous avons défini dans la dernière fonction) ou en cochant "e.relatedObject"; l'objet que la souris vient de sortir de.

Si cela a été déplacé, nous ajoutons un autre auditeur. L'événement ENTER_FRAME est vraiment cool. C'est la base de notre animation. chaque fois que nous entrons dans une nouvelle image, la fonction throw () sera exécutée. C’est ce qui nous permet de simuler un glissement de la souris après la publication (rappelez-vous de la variable arMousePrevX, Y? C’est ce à quoi elle sert). Et c’est tout ce que fait le lancer, simuler un glissement de souris, sans souris bien sûr. Nous avons donc à peu près déjà la fonction dont nous avons besoin, sauf que nous devons remplacer les appels de la position actuelle de la souris à notre position de souris artificielle.

J'ai presque eu un peu d'avance sur moi là-bas. Donc, avec ces deux fonctions d’événement, throwIt et throwItOut, ils font la même chose mais que si dans la deuxième fonction est à mentionner. J'ai eu du mal à essayer d'obtenir cette fonctionnalité, jusqu'à ce que je regarde l'événement de plus près. Le problème était d’obtenir que l’objet cible se comporte comme si j’avais lâché le bouton lorsque le curseur a quitté l’objet événement. Allez-y, essayez de le faire sans e.relatedObject. Je l'ai presque eu plusieurs fois, mais je ne pouvais pas le faire correctement. E.relatedObject recherche l’objet sur lequel vous vous trouvez après l’appel de l’événement. C'est pourquoi c'est tellement cool. Lorsque notre curseur quitte complètement le film, il renvoie la valeur null, sinon l'objet retourné est renvoyé. Nous pouvons donc vérifier si e.relatedObject est null ou est un parent de l'événement eventObject. Cela produit l'action correcte que nous recherchons.

Dans les fonctions ci-dessus, nous établissons des appels vers theRepeater (). Ce sera la fonction de projection, et rappelez-vous qu'elle sera appelée chaque fois que nous entrons dans un nouveau cadre. Passons en revue cette ligne par ligne:

 fonction privée theRepeater (e: Event): void // le temporisateur doit être arrêté, essayez de le supprimer et voyez ce qui se passe. t.stop (); // voici une variable locale qui tiendra la position actuelle du curseur (faux). // Et bien ce n'est que "faux" après la première fois. var oldxer: Number = MouseCurrX; var oldyer: Number = MouseCurrY; // maintenant, comme nous l'avons fait auparavant, nous devons trouver la différence entre notre // position actuelle et précédente. Alors, en quoi est-ce différent d'avant? Pourquoi? var xDiff: Number = MouseCurrX - arMousePrevX; var yDiff: Number = MouseCurrY - arMousePrevY; // si le bouton est enfoncé, nous n'allons plus bouger, le bouton arrêtera l'action dans ce cas. if (! buttonDown) // prend la différence et la chronomètre par décroissance. cela nous donnera la nouvelle // différence, qui sera légèrement plus petite que la dernière, et c'est ainsi que nous obtenons l'effet de friction. // par exemple. Si Decay est égal à 0.5, la distance parcourue sera divisée par deux. xDiff = xDiff * Decay; yDiff = yDiff * Decay; // next est l'une des parties qui déroutent pour moi, cela ne déplace pas l'objet // tout, il teste simplement pour voir si notre targetObject a atteint le bord. Si c'est le cas, nous devons le faire rebondir. (ceci pourrait être changé en une autre action si vous voulez, vous pouvez même le supprimer, que se passe-t-il si vous le faites? essayez-le! // dans la fonction pan, nous définissons cette variable, OppositeEdge, c'est là que nous allons // utilisez cela 'si le targetObject est plus grand que l'objet Event' que nous avons défini dans // la fonction init (). Je vais seulement parcourir le x ici car le y est // presque identique (qu'est-ce qui est différent? pourquoi ? Pensez-y!) if (xOppositeEdge) / * so first, "la largeur de eventObject, - la largeur de targetObject - 50", * ici, la largeur de targetObject est supérieure à celle de eventObject * this permet au bord opposé de l’objet cible d’être à 50 px de * le bord opposé Si vous allez à l’exemple de film et que vous réduisez l’image à * 10% et que vous la lancez, augmentez la taille à 200% et essayez de remarquez * quel bord fait quoi, alors vous verrez la différence entre les rebonds. * C’est la meilleure façon de comprendre cette partie. * / 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;  // Ceci est le cas si l'objet cible est plus petit que eventObject. else / *, nous testons à nouveau les bords de targetObject par rapport à l'objet * event. Cette fois, nous traitons avec le même bord (enfin, * 5 pixels hors du bord). Donc, ça va rebondir comme si on frappait un mur. * / 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);  // Si vous avez des questions à propos de cette partie, postez un commentaire à ce sujet et j'y répondrai. // voici les vars sider et Topper (comme ceux de la fonction pan). var sider: int = xDiff + targetObject.x; var Topper: int = yDiff + targetObject.y; // nous devons préparer cela pour le prochain tour. MouseCurrX = MouseCurrX + xDiff; MouseCurrY = MouseCurrY + yDiff; // et ensuite if if moveX, Y (encore une fois, comme la fonction pan) if (moveY) targetObject.y = Topper; if (moveX) targetObject.x = sider;  // et définissons maintenant notre souris artificielle prev arMousePrevX = oldxer; arMousePrevY = oldyer; // et si nous ne sommes pas en mode sans friction (OriginalDecay = 1) // nous allons soustraire une petite quantité de notre désintégration, pour // lui donne un assouplissement un peu plus naturel. 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);  

Et avec ça, notre classe est finie.


Étape 9: Le code de classe terminé

Vous pouvez récupérer le code complet dans le zip source, lié en haut du didacticiel. C'est dans la classe PanAndThrow.as.


Étape 10: Faites quelque chose avec

Pour faire quelque chose avec cela, nous devons revenir au MXML et ajouter quelques lignes de code. Ajoutez notre déclaration dans la section des variables globales, indiquez la méthode decay et appelez notre fonction pan and throw init (). Avec tout ce qui a été ajouté, voici le fichier MXML complet: