flash native_classes_adding_functionalities

Adding functionalities

We will now look at the movement of the pencil by getting the coordinates of the mouse in order to position it. We will define the two properties positionX and positionY:

// current mouse position
private var positionX:Number;
private var positionY:Number;

These allow us to store the mouse position:

private function moveMouse ( pEvt:MouseEvent ):void
{
// recuperation of the x and y mouse coordinates
positionX = pEvt.stageX;
positionY = pEvt.stageY;
// allocation of the position
x = positionX;
y = positionY;
}

The pencil follows the mouse as in the following illustration:

9.5.1.jpg

When testing the application we see that the movement is not totally smooth, we will therefore use the updateAfterEvent method that we looked at in the chapter entitled Interactivity.

Remember that the method of the MouseEvent class allows us to force the refresh of the player:

private function moveMouse ( pEvt:MouseEvent ):void
{
// recuperation of the x and y mouse coordinates
positionX = pEvt.stageX;
positionY = pEvt.stageY;
// allocation of the position
x = positionX;
y = positionY;
// forces the refresh
pEvt.updateAfterEvent();
}

The movement is now smooth but the cursor of the mouse is still displayed. We will mask it using the hide static method of the mouse class on activation of the graphic object.

Take care not to place this uninterrupted call within the moveMouse method as it would be redundant.

We import the Mouse class:

import flash.ui.Mouse;

Then we modify the activation method:

private function activation ( pEvt:Event ):void
{
// hides the cursor
Mouse.hide();
// listens to the MouseEvent.MOUSE_MOVE event
stage.addEventListener ( MouseEvent.MOUSE_MOVE, moveMouse );
}

At this stage we have extended the capabilites of the MovieClip class and the symbols linked to the Pencil class follow the mouse. We can link our Pencil class to any other symbol at any moment, this benefits from all the functionalities defined by it.

We have however forgotten an essential element. We need to manage the deactivation of the graphic object in order to free up resources. If we delete the pencil from the display list the MouseEvent.MOUSE_MOVE event is still broadcast, the cursor remains masked:

var myPencil:Pencil = new Pencil();
// added to the display list
// displays : [Event type="addedToStage" bubbles=false cancelable=false
eventPhase=2]
addChild ( myPencil );
// deletion of pencil, this does not integrate any deactivation logic
removeChild ( myPencil );

To correctly manage this we listen to the Event.REMOVED_FROM_STAGE event:

public function Pencil ()
{
// the Pencil listens to the Event.ADDED_TO_STAGE event
// broadcasts when this is added to the display list
addEventListener ( Event.ADDED_TO_STAGE, activation );
// the Pencil listens to the Event.REMOVED_FROM_STAGE event
// broadcasts when this is deleted from the display list
addEventListener ( Event.REMOVED_FROM_STAGE, deactivation );
}

Then we add a deactivation listener method in order to integrate the necessary deactivation logic:

private function deactivation ( pEvt:Event ):void
{
// displays the cursor
Mouse.show();
// stops the listener of the MouseEvent.MOUSE_MOVE event
stage.removeEventListener ( MouseEvent.MOUSE_MOVE, moveMouse );
}

It would be interesting to add a rotation effect of the pencil when it gets to the top of the stage in order to simulate a rotation of the wrist:

9.5.2.jpg

By integrating a simple condition we can give a more realistic effect to the pencil. We will start by defining the position of the Y axis from which the pencil will start to lean from, to do this we define a constant property as it will not change on execution:

// limit for Y axis
private static const LIMIT_Y:int = 140;

Then we integrate the condition in the moveMouse method:

private function moveMouse ( pEvt:MouseEvent ):void
{
// recuperation of the x and y mouse coordinates
positionX = pEvt.stageX;
positionY = pEvt.stageY;
// allocation of the position
x = positionX;
y = positionY;
// if the pencil passes the superior zone we incline the pencil
if ( positionY < Pencil.LIMIT_Y )
{
rotation = ( Pencil.LIMIT_Y - positionY );
}
// forces the refresh
pEvt.updateAfterEvent();
}

Here is the complete code of our pencil class at this stage:

package
{
import flash.display.MovieClip;
import flash.events.MouseEvent;
import flash.events.Event;
import flash.ui.Mouse;
public class Pencil extends MovieClip
{
// limit for the Y axis
private static const LIMIT_Y:int = 140;
// current mouse position
private var positionX:Number;
private var positionY:Number;
public function Pencil ()
{
// the Pencil listens to the Event.ADDED_TO_STAGE event
// broadcasts when this is added to the display list
addEventListener ( Event.ADDED_TO_STAGE, activation );
// the Pencil listens to the Event.REMOVED_FROM_STAGE event
// broadcasts when this is deleted from the display list
addEventListener ( Event.REMOVED_FROM_STAGE, deactivation );
}
private function activation ( pEvt:Event ):void
{
// hides the cursor
Mouse.hide();
// listens to the MouseEvent.MOUSE_MOVE event
stage.addEventListener ( MouseEvent.MOUSE_MOVE, moveMouse );
}
private function deactivation ( pEvt:Event ):void
{
// displays the cursor
Mouse.show();
// stops the listener of the MouseEvent.MOUSE_MOVE event
stage.removeEventListener ( MouseEvent.MOUSE_MOVE, moveMouse );
}
private function moveMouse ( pEvt:MouseEvent ):void
{
// recuperation of the x and y mouse coordinates
positionX = pEvt.stageX;
positionY = pEvt.stageY;
// allocation of the position
x = positionX;
y = positionY;
// if the pencil passes the superior zone we incline
the pencil
if ( positionY < Pencil.LIMIT_Y )
{
rotation = ( Pencil.LIMIT_Y - positionY );
}
// forces the refresh
pEvt.updateAfterEvent();
}
}
}

If we test the application, when we arrive in the superior zone the pencil inclines as shown here:

9.5.3.jpg

In order to make the movement of the pencil more natural we add an inertia formula:

private function moveMouse ( pEvt:MouseEvent ):void
{
// recuperation of the x and y mouse coordinates
positionX = pEvt.stageX;
positionY = pEvt.stageY;
// allocation of the position
x = positionX;
y = positionY;
// if the pencil passes the superior zone we incline the pencil
if ( positionY < Pencil.LIMIT_Y )
{
rotation -= ( rotation - ( Pencil.LIMIT_Y - positionY ) ) *.2;
// otherwise the pencil finds its original inclination
} else rotation -= ( rotation - 0 ) *.2;
// forces the refresh
pEvt.updateAfterEvent();
}

It is time to add the concept of drawing which was touched on in the chapter entitled Interactivity of which we will reuse the code.

If we think in terms of separation of tasks it would seem logical that a pencil takes care of its movement and drawing and not the creation of the canvas or drawing. We will therefore define a allocateCanvas method which will indicate to the pencil what container to draw in.

We define a property allowing us to reference the container in the class:

// stores a reference to the drawing container
private var container:DisplayObjectContainer;

Then we import the flash.display.DisplayObjectContainer class:

import flash.display.DisplayObjectContainer;

And add the allocateCanvas method:

// method allowing you to specify the drawing container
public function allocateCanvas ( pCanvas:DisplayObjectContainer ):void
{
container = pCanvas;
}

All types of container can be passed in, on condition that they are of the DisplayObjectContainer type:

// creation of a vectorial trace container
var Canvas:Sprite = new Sprite();
// addition a container to the display list
addChild ( Canvas );
// creation of symbol
var myPencil:Pencil = new Pencil();
// allocation of trace container
myPencil.allocateCanvas ( Canvas );
// addition of symbol to the display list
addChild ( myPencil );
// position of x and y
myPencil.x = 250;
myPencil.y = 200;

Therefore, different types of graphic objects can act as a canvas. Remember that thanks to inheritance a sub-type can be passed everywhere that a super-type is expected.

As well as reusing drawing functionalities that we developed in the chapter entitled Interactivity we are also going to reuse the concept of history combined with the CTRL+Z and CTRL+Y keyboard shortcuts.

We will define three new properties allowing us to store the shapes created and deleted and the stroke in progress:

// array referring to the traced and deleted shapes
private var tracedArray:Array = new Array();
private var arrayOldTraces:Array = new Array();
// references the trace in progress
private var myNewTrace:Shape;

Beware, the flash.display.Shape class should be imported:

import flash.display.Shape;

In the activation method we should first listen to the mouse click in order to know when the user wants to start drawing:

private function activation ( pEvt:Event ):void
{
// hides the cursor
Mouse.hide();
// listens to different events
stage.addEventListener ( MouseEvent.MOUSE_MOVE, moveMouse );
stage.addEventListener ( MouseEvent.MOUSE_DOWN, clickMouse );
}

We add the clickMouse listener method which looks after creating objects and initializing the style of the stroke:

private function clickMouse ( pEvt:MouseEvent ):void
{
if ( container == null ) throw new Error ( "Please call the method allocateCanvas as a preliminary()" );
// recuperation of the x and y mouse coordinates
positionX = pEvt.stageX;
positionY = pEvt.stageY;
// a new shape object is created for each trace
myNewTrace = new Shape();
// we add the trace container to the main container
container.addChild ( myNewTrace );
// then we reference the trace in the array
// referencing the displayed traces
tracedArray.push ( myNewTrace );
// we define a trace style
myNewTrace.graphics.lineStyle ( 1, 0x990000, 1 );
// the pencil tip is moved to this position
// to start drawing from this position
myNewTrace.graphics.moveTo ( positionX, positionY );
// is a new trace intervenes even though we have gone backwards
// we restart from this state
if ( arrayOldTraces.length ) arrayOldTraces = new Array;
// listens to the mouse movement
stage.addEventListener ( MouseEvent.MOUSE_MOVE, draws );
}

Then we define the draws method in order to generate the stroke:

private function draws ( pEvt:MouseEvent ):void
{
if ( myNewTrace != null )
{
// the pencil tip is moved to this position
// to start drawing from this position
myNewTrace.graphics.lineTo ( positionX, positionY );
}
}

In order to stop drawing we listen to the release of the mouse thanks to the MouseEvent.MOUSE_UP event:

private function activation ( pEvt:Event ):void
{
// hides the cursor
Mouse.hide();
// listens to different events
stage.addEventListener ( MouseEvent.MOUSE_MOVE, moveMouse );
stage.addEventListener ( MouseEvent.MOUSE_DOWN, clickMouse );
stage.addEventListener ( MouseEvent.MOUSE_UP, releaseMouse );
}

The releaseMouse listener method is triggered when the mouse is released and interrupts the listening to the MouseEvent.MOUSE_MOVE event:

private function releaseMouse ( pEvt:MouseEvent ):void
{
stage.removeEventListener ( MouseEvent.MOUSE_MOVE, draws );
}

If we test our application everything functions correctly, we can draw, when the mouse is released the stroke is stopped.

9.5.4.jpg

Our application is nearly finished, we will add the concept of history and to do this we import the KeyboardEvent class:

import flash.events.KeyboardEvent;

Then we add the keyboard listener to the activation method:

private function activation ( pEvt:Event ):void
{
// hides the cursor
Mouse.hide();
// listens to the MouseEvent.MOUSE_MOVE event
stage.addEventListener ( MouseEvent.MOUSE_MOVE, moveMouse );
stage.addEventListener ( MouseEvent.MOUSE_DOWN, clickMouse );
stage.addEventListener ( MouseEvent.MOUSE_UP, releaseMouse );
stage.addEventListener ( KeyboardEvent.KEY_DOWN, listenKeyboard );
}

The listenKeyboard listener method looks after the management of the history. We import the Keyboard class:

import flash.ui.Keyboard;

And define two constant properties storing the code of each key:

// code of the Y and Z keys
private static const codeKeyY:int = 89;
private static const codeKeyZ:int = 90;

Then we add the already developed code from the previous chapter for the previous drawing application:

private function listenKeyboard ( pEvt:KeyboardEvent ):void
{
// if the space bar is pressed
if ( pEvt.keyCode == Keyboard.SPACE )
{
// number of shape objects containing traces
var lng:int = tracedArray.length;
// deletion of traces of the display list
while ( lng-- ) container.removeChild ( tracedArray[lng] );
// the history arrays are reinitialised
// the references are deleted
tracedArray = new Array();
arrayOldTraces = new Array();
myNewTrace = null;
}
if ( pEvt.ctrlKey )
{
// to return backwards (CTRL+Z)
if( pEvt.keyCode == Pencil.codeKeyZ && tracedArray.length )
{
// we delete the last trace
var toDelete:Shape = tracedArray.pop();
// we store each deleted trace
// in the specified array
arrayOldTraces.push ( toDelete );
// we delete the trace of the display list
container.removeChild( toDelete );
// if going forwards (CTRL+Y)
} else if ( pEvt.keyCode == Pencil.codeKeyY &&
arrayOldTraces.length )
{
// we recuperate the last added trace
var toDisplay:Shape = arrayOldTraces.pop();
// we replace it in the array of
// traces on display
tracedArray.push ( toDisplay );
// then we display
container.addChild ( toDisplay );
}
}
}

We obtain an intelligent pencil symbol equipped with different functionalities that we have added. One of the main advantages of graphic sub-classes resides in their ease of reuse. We can link another symbol to our graphic sub-class at any moment, this automatically inherits the functionalities of the pencil class and becomes operational.

Before this we are going to add another functionality linked to the mouse wheel. It would be elegant to be able to choose the colour of the stroke using the mouse wheel. When the user uses it the colour of the stroke and the pencil will be modified.

Don’t forget that in the sub-class we are on the timeline of the symbol. We modify the pencil symbol by adding numerous key images:

9.5.5.jpg

When the user uses the wheel we move the timeline’s player head of the symbol. We add two new properties that allow us to position the player head when the mouse is moved:

// position of the player head
private var image:int;
private var index:int;

We initialize the index property in the constructor which will be incremented later on, we therefore initialise at 0:

public function Pencil ()
{
// the Pencil listens to the Event.ADDED_TO_STAGE event
// broadcasts when this is added to the display list
addEventListener ( Event.ADDED_TO_STAGE, activation );
// the Pencil listens to the Event.REMOVED_FROM_STAGE event
// broadcasts when this is deleted from the display list
addEventListener ( Event.REMOVED_FROM_STAGE, deactivation );
// initialisation of properties used for
// the change of colours
image = 0;
index = 1;
}

In the activation method we add a MouseEvent.MOUSE_WHEEL listener event to the Stage object:

private function activation ( pEvt:Event ):void
{
// hides the cursor
Mouse.hide();
// listens to different events
stage.addEventListener ( MouseEvent.MOUSE_MOVE, moveMouse );
stage.addEventListener ( MouseEvent.MOUSE_DOWN, clickMouse );
stage.addEventListener ( MouseEvent.MOUSE_UP, releaseMouse );
stage.addEventListener ( KeyboardEvent.KEY_DOWN, listenKeyboard );
stage.addEventListener ( MouseEvent.MOUSE_WHEEL, mouseWheel );
}

The mouseWheel listener method looks after moving the player head as a loop, using the % modulo operator, the index property stores the index of the colour selected:

// moves the player head when using the wheel
private function mouseWheel ( pEvt:MouseEvent ):void
{
gotoAndStop ( index = (++image%totalFrames)+1 );
}

If we test the application, when the mouse wheel is used, the pencil changes colour. Thanks to the modulo, the colours progress in a loop.

We just need to change the stroke colour corresponding to the pencil colour chosen by the user. To do this we will store the available colours in the array in a static property.

Why use a static property here?

As the colours are global to all the pencil occurrences that could be created, so maintaining a global scope we create a static array of colours:

// array containing the available colours
private static var colours:Array = [ 0x5BBA48, 0xEA312F, 0x00B7F1,
0xFFF035, 0xD86EA3, 0xFBAE34 ];

We just have to modify the trace colour in the clickMouse method, to do this we use the index property which represents the index of the colour in the array:

private function clickMouse ( pEvt:MouseEvent ):void
{
if ( container == null ) throw new Error ( "Please first call
the allocateCanvas() method);
// recuperation of the x and y mouse coordinates
positionX = pEvt.stageX;
positionY = pEvt.stageY;
// a new shape object is created for each trace
myNewTrace = new Shape();
// we add the trace container to the main container
container.addChild ( myNewTrace );
// then we reference the trace in the array
// referencing the displayed traces
tracedArray.push ( myNewTrace );
// we define a trace style
myNewTrace.graphics.lineStyle ( 2, Pencil.colours[index-1], 1 );
// the pencil tip is moved to this position
// to start drawing from this position
myNewTrace.graphics.moveTo ( positionX, positionY );
// is a new trace intervenes even though we have gone backwards
// we restart from this state
if ( arrayOldTraces.length ) arrayOldTraces = new Array;
// listens to the mouse movement
stage.addEventListener ( MouseEvent.MOUSE_MOVE, draws );
}

When the mouse is pressed we reference the colours array using the index property in order to choose the corresponding colour.

We add a small final detail to render our class more supple, it would be interesting to be able to create the pencil to be able to be influenced by the rotation speed. We add the Number type friction property:

// stores the friction of the pencil
private var friction:Number;

Then we modify the constructor in order to take into account the speed:

public function Pencil ( pFriction:Number=.1 )
{
// the Pencil listens to the Event.ADDED_TO_STAGE event
// broadcasts when this is added to the display list
addEventListener ( Event.ADDED_TO_STAGE, activation );
// the Pencil listens to the Event.REMOVED_FROM_STAGE event
// broadcasts when this is deleted from the display list
addEventListener ( Event.REMOVED_FROM_STAGE, deactivation );
// initialisation of properties used for
// the change of colours
image = 0;
index = 1;
// affects the friction
friction = pFriction;
}

Finally, we modify the moveMouse method in order to use the passed value, stored in the friction property:

private function moveMouse ( pEvt:MouseEvent ):void
{
// recuperation of the x and y mouse coordinates
positionX = pEvt.stageX;
positionY = pEvt.stageY;
// allocation of the position
x = positionX;
y = positionY;
// if the pencil goes in the superior zone
// we incline the pencil
if ( positionY < Pencil.LIMIT_Y )
{
rotation -= ( rotation - ( Pencil.LIMIT_Y - positionY ) ) *
friction;
// otherwise the pencil finds its original inclination
} else rotation -= ( rotation - 0 ) * friction;
// forces the refresh
pEvt.updateAfterEvent();
}

Remember a very important concept in object oriented programming: encapsulation!

In our Pencil class the properties are all private, as there is no reason that they should be modifiable from the outside. Of course there are situations when the use of private properties makes no sense.

In the case of a geometric point class, the x, y and z properties should be public and accessible. You just have to hide the properties that are no longer practical for the user of the class or just those that may evolve at some point.

Remember the value of being able to change the implementation without altering the programming interface.

By using the private properties in the pencil class we guarantee that no code outside of the class can access and therefore alter the functioning of the application.

The friction that has gone to the initialization of a pencil should be exclusively between 0 and 1. If this is not the case the inclination of the pencil stops and the developer stops the application working.

In the following case the developer doesn’t know the possible values and gives 10 as an inclination speed:

// creation of symbol
var myPencil:Pencil = new Pencil( 10 );

When testing the application the developer realizes that the inclination of the pencil isn’t working anymore. This is due to the fact that the friction value should be between 0 and 1.

Don’t forget that we are developing an intelligent and autonomous object which should be able to adapt to different situations and tell the developer what isn’t working.

This was an essential point that we discussed in the article Allocation control.

We will add a test to the constructor in order to verify the value passed is acceptable. If this is not the case we pass a default value which assures the pencil will work. On the contrary if the value passed is incorrect we display an error message.

We add the condition to the constructor:

public function Pencil ( pFriction:Number=.1 )
{
// the Pencil listens to the Event.ADDED_TO_STAGE event
// broadcasts when this is added to the display list
addEventListener ( Event.ADDED_TO_STAGE, activation );
// the Pencil listens to the Event.REMOVED_FROM_STAGE event
// broadcasts when this is deleted from the display list
addEventListener ( Event.REMOVED_FROM_STAGE, deactivation );
// initialisation of properties used for
// the change of colours
image = 0;
index = 1;
// affects the friction
if ( pFriction > 0 && pFriction <= 1 ) friction = pFriction;
else
{
trace("Error: Friction not correct, the value should be
between 0 and 1");
friction = .1;
}
}

Thanks to this test we control the allocation in order to be sure of the correct execution of the application. This time the developer has passed a value superior to 1. The allocation is controlled and an informational message indicates what is wrong:

/* displays :
Error: Friction not correct, the value should be between 0 and 1
*/
var myPencil:Pencil = new Pencil( 50 );

The developer has all of the information to ensure that the pencil will work. We have to however prepare the possibility of changing speed or movement once the object is created. To do this we will define a method called allocateSpeed which will look after allocating the friction property.

In order to encapsulate our class we will control the allocation of properties using a specific method:

public function allocateSpeed ( pFriction:Number ):void
{
// affects the friction
if ( pFriction > 0 && pFriction <= 1 ) friction = pFriction;
else
{
trace("Error: Friction not correct, the value should be
between 0 and 1");
friction = .1;
}
}

We integrate the previously defined code in the constructor in order to control the allocation of the friction property. We can therefore replace the constructor code by a call to the allocateSpeed method:

public function Pencil ( pFriction:Number=.1 )
{
// the Pencil listens to the Event.ADDED_TO_STAGE event
// broadcasts when this is added to the display list
addEventListener ( Event.ADDED_TO_STAGE, activation );
// the Pencil listens to the Event.REMOVED_FROM_STAGE event
// broadcasts when this is deleted from the display list
addEventListener ( Event.REMOVED_FROM_STAGE, deactivation );
// initialisation of properties used for
// the change of colours
image = 0;
index = 1;
allocateSpeed ( pFriction );
}

By adding the allocateSpeed, method the allocation of the friction property is controlled.

Here is the final code of the pencil class:

package
{
import flash.display.MovieClip;
import flash.display.Shape;
import flash.display.DisplayObjectContainer;
import flash.events.MouseEvent;
import flash.events.KeyboardEvent;
import flash.events.Event;
import flash.ui.Mouse;
import flash.ui.Keyboard;
public class Pencil extends MovieClip
{
// limit for the Y axis
private static const LIMIT_Y:int = 140;
// current mouse position
private var positionX:Number;
private var positionY:Number;
// stores a reference to the drawing container
private var container:DisplayObjectContainer;
// array referring to the traced and deleted shapes
private var tracedArray:Array = new Array();
private var arrayOldTraces:Array = new Array();
// references the trace in progress
private var myNewTrace:Shape;
// code of the Y and Z keys
private static const codeKeyY:int = 89;
private static const codeKeyZ:int = 90;
// position of the player head
private var image:int;
private var index:int;
// array containing the available colours
private static var colours:Array = [ 0x5BBA48, 0xEA312F, 0x00B7F1,
0xFFF035, 0xD86EA3, 0xFBAE34 ];
// stores the friction of the pencil
private var friction:Number;
public function Pencil ( pFriction:Number=.1 )
{
// the Pencil listens to the Event.ADDED_TO_STAGE event
// broadcasts when this is added to the display list
addEventListener ( Event.ADDED_TO_STAGE, activation );
// the Pencil listens to the Event.REMOVED_FROM_STAGE event
// broadcasts when this is deleted from the display list
addEventListener ( Event.REMOVED_FROM_STAGE, deactivation );
// initialisation of properties used for
// the change of colours
image = 0;
index = 1;
// allocation controled by the speed
allocateSpeed ( pFriction );
}
private function activation ( pEvt:Event ):void
{
// hides the cursor
Mouse.hide();
// listens to different events
stage.addEventListener ( MouseEvent.MOUSE_MOVE, moveMouse );
stage.addEventListener ( MouseEvent.MOUSE_DOWN, clickMouse );
stage.addEventListener ( MouseEvent.MOUSE_UP, releaseMouse );
stage.addEventListener ( KeyboardEvent.KEY_DOWN, listenKeyboard );
stage.addEventListener ( MouseEvent.MOUSE_WHEEL, mouseWheel );
}
private function deactivation ( pEvt:Event ):void
{
// displays the cursor
Mouse.show();
// stops listeners to different events
stage.removeEventListener ( MouseEvent.MOUSE_MOVE, moveMouse );
stage.removeEventListener ( MouseEvent.MOUSE_DOWN, clickMouse );
stage.removeEventListener ( MouseEvent.MOUSE_UP, releaseMouse );
stage.removeEventListener ( KeyboardEvent.KEY_DOWN, listenKeyboard
);
stage.removeEventListener ( MouseEvent.MOUSE_WHEEL, mouseWheel
);
}
// moves the player head of the mouse wheel
private function mouseWheel ( pEvt:MouseEvent ):void
{
gotoAndStop ( index = (++image%totalFrames)+1 );
}
private function listenKeyboard ( pEvt:KeyboardEvent ):void
{
// if the space bar is pressed
if ( pEvt.keyCode == Keyboard.SPACE )
{
// number of shape objects containing traces
var lng:int = tracedArray.length;
// deletion of traces of the display list
while ( lng-- ) container.removeChild ( tracedArray[lng]
);
// the history arrays are reinitialised
// the references are deleted
tracedArray = new Array();
arrayOldTraces = new Array();
myNewTrace = null;
}
if ( pEvt.ctrlKey )
{
// to return backwards (CTRL+Z)
if( pEvt.keyCode == Pencil.codeKeyZ &&
tracedArray.length )
{
// we delete the last trace
var toDelete:Shape = tracedArray.pop();
// we store each deleted trace
// in the specified array
arrayOldTraces.push ( toDelete );
// we delete the trace of the display list
container.removeChild( toDelete );
// if going forwards (CTRL+Y)
} else if ( pEvt.keyCode == Pencil.codeKeyY &&
arrayOldTraces.length )
{
// we recuperate the last added trace
var toDisplay:Shape = arrayOldTraces.pop();
// we replace it in the array of display traces
tracedArray.push ( toDisplay );
// then we display
container.addChild ( toDisplay );
}
}
}
private function clickMouse ( pEvt:MouseEvent ):void
{
if ( container == null ) throw new Error ( "Please first call
the allocateCanvas() method);
// recuperation of the x and y mouse coordinates
positionX = pEvt.stageX;
positionY = pEvt.stageY;
// a new shape object is created for each trace
myNewTrace = new Shape();
// we add the trace container to the main container
container.addChild ( myNewTrace );
// then we reference the trace in the array
// referencing the displayed traces
tracedArray.push ( myNewTrace );
// we define a trace style
myNewTrace.graphics.lineStyle ( 2, Pencil.colours[index-1],
1 );
// the pencil tip is moved to this position
// to start drawing from this position
myNewTrace.graphics.moveTo ( positionX, positionY );
// is a new trace intervenes even though we have gone backwards
// we restart from this state
if ( arrayOldTraces.length ) arrayOldTraces = new
Array;
// listens to the mouse movement
stage.addEventListener ( MouseEvent.MOUSE_MOVE, draws );
}
private function moveMouse ( pEvt:MouseEvent ):void
{
// recuperation of the x and y mouse coordinates
positionX = pEvt.stageX;
positionY = pEvt.stageY;
// allocation of the position
x = positionX;
y = positionY;
// if the pencil goes in the superior zone
// we incline the pencil
if ( positionY < Pencil.LIMIT_Y )
{
rotation -= ( rotation - ( Pencil.LIMIT_Y - positionY ) ) *
friction;
// otherwise the pencil finds its original inclination
} else rotation -= ( rotation - 0 ) * friction;
// forces the refresh
pEvt.updateAfterEvent();
}
private function releaseMouse ( pEvt:MouseEvent ):void
{
stage.removeEventListener ( MouseEvent.MOUSE_MOVE, draws );
}
private function draws ( pEvt:MouseEvent ):void
{
if ( myNewTrace != null )
{
// the pencil tip is moved to this position
// to start drawing from this position
myNewTrace.graphics.lineTo ( positionX, positionY );
}
}
// method allowing you to specify the drawing container
public function allocateCanvas ( pCanvas:DisplayObjectContainer ):void
{
container = pCanvas;
}
public function allocateSpeed ( pFriction:Number ):void
{
// affects the friction
if ( pFriction > 0 && pFriction <= 1 ) friction = pFriction;
else
{
trace("Error: Friction not correct, the value should be
between 0 and 1");
friction = .1;
}
}
}
}

Worth remembering

  • Extending native Flash graphic classes allows you to create powerful interactive and reusable objects.
  • In the graphic sub-class, the use of the keyword this makes direct reference to the symbol on the timeline.







Adobe Training center


Mediabox Training Centre © 2000 - 2008 All rights reserved.
Adobe Authorized Training Centre. State convention under number 25 14 02167 14.
Mediabox : SARL au capital de 62.000€ - Activity number: 25 14 02167 14 - SIRET : 493 716 468 00027
MEDIABOX, 102 Avenue des Champs Elysées, 75008 PARIS - Tel. +33(0)2.31.91.96.89 - Fax. +33(0)2.72.68.56.42