How to Animate a Line Drawing With Flash Using Actionscript 3: Part 2

If you haven’t read Part 1 on Animating a Line Drawing, I suggest you start there. It goes into the basics of recording your animation using points with the click of a mouse. Here, in Part 2, I have decided to go into more depth of enhancements of the line drawing tool for greater control while experimenting with options and certain effects.

Go ahead and play around with the v2 of the line drawing below. I will then explain a few of the parts of the tool.

Get Adobe Flash player

Added Features

As you can see, the interface is a bit more robust than my original demo. Here is a list of additional options that I will explain how they are implemented:

Apply Strokes as a Mask

Applying the strokes as a mask to an underlying image requires only a few lines of actionscript to achieve. Both the lines_mc Sprite (the mask) and the underlying texture_mc sprite (the image) will need to be set to cacheAsBitmap, then the mask can be simply applied as such:

lines_mc.cacheAsBitmap=true;
texture_mc.cacheAsBitmap=true;
texture_mc.mask=lines_mc;
texture_mc.visible=true;

To remove the applied mask as seen in the demo simply do the converse and null out the mask:

lines_mc.cacheAsBitmap=false;
texture_mc.cacheAsBitmap=true;
texture_mc.mask=null;
texture_mc.visible=false

Soften the Line Stroke

Softening the line is simply done with Greensock’s blurfilter. I used a simple slider ("Softness") for the user to get the blur value that the filter will implement. The blur can either be applied to the whole lines_mc sprite:

if (controller_mc.globalSoftness_ck.selected){
	TweenLite.to(lines_mc, .2, {blurFilter:{blurX:blurAmount, blurY:blurAmount}})
}else{
	TweenLite.to(lines_mc, .2, {blurFilter:{remove:true}})
}

or its children (the current stroke)

TweenLite.to(line_mc, time, {blurFilter:{blurX:blurAmount, blurY:blurAmount}})

Remember, this demo was set up to have the ability to record the animation for future playback. Therefore, the blur amount will need to be pushed to the pointsArray like all other atributes each line requires as explained in part one:

var obj:Object = {pointX:evt.currentTarget.mouseX, pointY:evt.currentTarget.mouseY, color:curColor,lineThickness:lineThickness, blur:blurAmount}
pointsArray.push(obj)

Add Streaks to the Line

I thought it would be fun to experiment with adding random streaks of lines at each vector point. in my demo, I have the angle of streaks going down at a random rate to appear as simple ink drips. I have also experimented with different angles that appear more like fireworks (going upward). I have also set other random values to play with different effects.

Since the object has all the parameters we need for the streak effect, this object gets passed to the drawStreak() method. A new streakTimeline is created for each streak (however many random number is set) scaling the x and the blur amount and other attributes if desired. The streakTimeline is then appended to the end of the lineTimeline to make the streaks sequential after each point change.

function drawStreak(obj:Object){
	var rn:int= rand.random(8,20,true); // random number of streak
	var streak_array:Array=[];
	for (var s:int=0; s

Experiment with the random values to get the desired effect.

Make the Drawing Continuous

Originally, the Part 1 demo was set up for simple mouse clicks to create each point of a line to more easily illustrate the workings of recording an animation by using fewer points. Here, I have take it a step farther to allow the pen to continuously draw like a regular drawing application should perform and still be able to click to add points to the drawing.

In order to have this work properly, only a few event listeners were needed to be added (and removed). The mouse_down event still calls the draw point once but within a separate mouseDown method. Within this method an additional mouse_move event is added that also calls the drawPoint method. On the mouse_up event, the necessary listeners are removed.

stage.addEventListener(MouseEvent.MOUSE_DOWN, mouseDown);
function mouseDown(evt:MouseEvent){
	checkLocation(evt)
	stage.removeEventListener(MouseEvent.MOUSE_DOWN, mouseDown);
	stage.addEventListener(MouseEvent.MOUSE_MOVE, checkLocation);
	stage.addEventListener(MouseEvent.MOUSE_UP, removeMovement)
}
function removeMovement(evt:MouseEvent){
	stage.addEventListener(MouseEvent.MOUSE_DOWN, mouseDown);
	stage.removeEventListener(MouseEvent.MOUSE_MOVE, checkLocation);
	stage.removeEventListener(MouseEvent.MOUSE_UP, removeMovement)
}

I have set a minimum length of the line within checkLocation so that too many points are not created when the user is drawing. This not only helps with saving memory, but also helps smooth out the points causing a more fluid-looking drawing.

function checkLocation(evt:MouseEvent){
	var MIN_LEN:int = 6;  // use smaller number for tighter curves
	var leng:Number=MIN_LEN;
	if (lastPoint!=null){
		var dx:Number = evt.target.mouseX-lastPoint.x;
		var dy:Number = evt.target.mouseY-lastPoint.y;
		leng = Math.sqrt(dx * dx + dy * dy);
	}
	if (evt.target.mouseX>=texture_mc.x && leng>=MIN_LEN){ // check a minimum length to smooth out points
		drawPoint(evt)
	}
}

I also included a way to lift the pen up and have a new starting point by pressing the "d" on the keyboard. This way the recorded animation doesn't have unwanted strokes. This is simply done by adding a keyboard event and removing the drawing listeners as well as setting the lastPoint to null, setting started to false and pushing the string "null" to the pointsArray.

stage.addEventListener(KeyboardEvent.KEY_DOWN, reportKeyDown);
function reportKeyDown(event:KeyboardEvent):void {
	if (String.fromCharCode(event.charCode)=="d"){
		stopDrawing();
		lastPoint = null;
		if (started){
			pointsArray.push("null")
		}
		started=false;
		stage.addEventListener(KeyboardEvent.KEY_UP, startDrawing);
	}
}
function stopDrawing(evt:*=null){
	stage.removeEventListener(MouseEvent.MOUSE_DOWN, mouseDown)
}
function startDrawing(evt:*=null){
	stage.addEventListener(MouseEvent.MOUSE_DOWN, mouseDown)
}

If the value of the object in pointsArray is "null", when the recorded values are passed back to create the animation, it sees it as a necessary new first point. Important note: Parsing the string from the preset box to an array of objects may causes slowness when handling a large array of recorded points. It would run much faster if handling the array as objects already captured, but for this demo to maintain the ability to copy a string into the preset window, I have left it as it is and may timeout with too many points.

function drawPreset(evt:MouseEvent){
	var lp:Point;
	var _idx:int=0;
	for each (var obj:Object in presetArray){
		//trace(obj.pointY)
		if (_idx===0 || lp==null){
			lp = new Point(obj.pointX, obj.pointY);
			blur = obj.blur
		}else if (obj!="null"){
	...
// create line drawing

Download the Resources

Here is the flash resource file if you desire to play with around with the full application yourself: LineDraw_v2 (699). Don't forget to leave your comments if you find this tutorial useful or if you have any questions or suggestions. Have fun!