I have recently been working on a Logo creating application. We will be releasing it this month, until then I cannot post a link. Part of the task was to move some items around on the stage and then take a ‘photo’ of them, photo being a bitmap. In the application itself this process was very in depth and went well beyond what I will discuss in this tutorial. This tutorial is merely a sample of how to move multiple MovieClips around on inside a container, and then shoot a bitmap of them.
The biggest issue I ran into when doing this was the fact that the Bitmap.draw(aMovie) method always draws the bitmap from the ‘aMovie’ movie clip’s X and Y axis points. So, when I moved the clips around, the ‘aMovie’ ’s X and Y axis points never changed. They always remained 0,0. So, when i drew the bitmap, it was drawing blank space. The width and height were correct, but the point (0,0) where it was drawing from was not. So I devised an algorithm to reposition the 2 containers so that the inner container, the one holding the objects, is always positioned so the objects minimum X and minimum Y are at the outer container’s, the one we want to draw, 0,0 point. This way when we shoot the bitmap, we get the entire image we need.
Here is the DEMO: Drag the objects and click snapshot to view the bitmap:
Here is the SOURCE.
The tutorial requires you to have an image loaded from the path ‘logos/business/mark_0.gif’. It also creates a text field on the stage. After you take a snap shot of that clip, the bitmap that is created is placed on the stage. All the movieClips are draggable.
Here is a walk through of the code:
First we need to import all of our Assets. The most only one we are using is the BitmapData class. The BitmapData class well allow Flash the functionality it needs to draw bitmap images from movie clips. The other 3 imports are used when dealing with some of the advanced features in the BitmapData class. They are outside the scope of this tutorial. If you need more information on them, please use the extensive Flash Help Files.
import flash.geom.Rectangle;
import flash.geom.Matrix;
import flash.geom.ColorTransform;
Next we need to create our empty containers. They will be used to attach objects and/or load objects. In this tutorial we are creating a textField object and loading an image dynamically into the Flash Movie. The ‘holder’ clip will only store one movie clip ‘imgHold’. ‘imgHold’ will hold all of our assests. You will see why this is later on when we take a snapshot of the movie clip to be used as our bitmap.
var holder:MovieClip = this.createEmptyMovieClip(‘holder’, this.getNextHighestDepth());
//content holder
holder.createEmptyMovieClip(‘imgHold’, 1);
Inside our ‘imgHold’ clip that is nested in our ‘holder’ clip, both of which we created in the last step, we create another empty movie clip ‘img1′. We use the ‘myMC1′ variable assignment to reference this clip. We could very well use ‘holder.imgHold.img1′ , but that is longer, messier, and not at versatile. As you work more with Flash and ActionScript, ActionScript 2 especially, you will learn that this type of variable assignment is very useful.
I like to use the autoSize() feature of the text field because we never really know how long the text is, in pixels.
var myMC1:MovieClip = holder.imgHold.createEmptyMovieClip(‘img1′, 1);
myMC1.createTextField(‘t1′, 1, 0, 0, 200, 30);
myMC1.t1.text = "yoyoTest";
myMC1.t1.selectable = false;
myMC1.t1.autoSize = true;
myMC1._x = 10;
myMC1._y = 20;
In the second clip: again we create a new movie clip inside our holder named ‘img2′. In this clip we load our external image. In this tutorial I do not attach a preloader to monitor the image load in the Flash movie. It isn’t needed for the purposes of this tutorial.
We use a simple utility function ‘positionHolder()’ to position our outer most container.
var myMC2:MovieClip = holder.imgHold.createEmptyMovieClip(‘img2′, 2);
myMC2.createEmptyMovieClip(‘hold’, 2).loadMovie(‘logos/business/mark_0.gif’);
myMC2._x = 40;
myMC2._y = 60;
//set the holder’s position
positionHolder();
In the next few lines we create an array of our draggable objects. We will use this array to reference these objects later. This is a more applied example of the variable assignment that I spoke of earlier. Using the array, we can not only call our object with a more simplified variable but we can also access that object in a loop. Applying effects can be simplified greatly when using an array of objects instead of applying the effects to each object individually. We will see more of this later in the tutorial.
var a:Array = new Array(myMC1, myMC2);
We create another movie clip ‘mc1′. We will draw our bitmap image inside this movie clip.
var mc1:MovieClip = this.createEmptyMovieClip(‘mc1′, this.getNextHighestDepth());
Here we apply our button effects. I could just as easily used a For loop to loop through each object and apply the effect but this multiple assignment is just as interesting and useful when writing ActionScript 2. Something = Something = Somthing else. This can be used in any case in ActionScript. A good example is _xscale and _yscale or _x and _y. In the onPress function we swap depths, so the item we click moves to the highest depth. We also start the dragging function of this object. In the onRelease function we stop the drag and call our positioning algorithm. Something I didn’t include here which I probably should have is the updateAfterEvent(); function. In the tutorial it wasn’t needed but when doing anything with dragging and dropping it is always better to call it.
myMC1.onPress = myMC2.onPress = function()
{
this.swapDepths(this._parent.getNextHighestDepth());
this.startDrag();
}
myMC1.onRelease = myMC2.onRelease = function() {
this.stopDrag();
repositionIt();
}
When we press the save button we save the movie. In the Save function we create the new bitmap with the same width and height of our outer most container ‘holder’. I am applying a solid #cccccc background to it just for ease-of-display purposes. Might even be useful to draw a stroke around it once it is attached. I chose the most simple of two. We then attach the bitmap to our ‘mc1′ movie clip we created earlier. Then we draw() the contents of our outer most container into that bitmap. So, the bitmap already exists in the mc1 clip but it is a blank canvas so to speak. Then, when we call Draw and pass it the holder movie clip, Flash reads the pixel data of that movie clip and creates an image of those pixels in that bitmap object. After all that, we enable our bitmap to be dragged via our dragSnap() function.
btn.onPress = function()
{
//be sure to reposition the clips before drawing the bitmap
repositionIt();
save();
}
function save():Void
{
//create the bitmap
var snapshot:BitmapData = new BitmapData(holder._width, holder._height, false, 0×00cccccc);
//attach the bitmap
mc1.attachBitmap(snapshot, 1);
//draw a bitmap image of the holder clip into mc1
snapshot.draw(holder);
//allow drag and drop for mc1
dragSnap();
}
//Function to drag and drop the bitMap image we draw
function dragSnap()
{
mc1.onPress = function()
{
this.startDrag();
}
mc1.onRelease = function()
{
this.stopDrag();
}
}
Okay, Here we are at the repositioning algorithm. This function loops through all the objects on the stage ( using the array we built above ) and finds the Minimum X value and the Minumum Y value. This coordinate is where we need the ‘holder’ movie clip’s (0,0) point to be. If the objects minimum x and minimum y values are not positioned at the ‘holder’ movie clip’s (0,0) point then the bitmap we draw will not look how we need it to look. So, the algorithm finds the minimum values using the drawIt() function. It then translates those local values into global values. Local values are those inside the imgHold movie clip and we translate them to the actual position on the _root stage. For this we use the localToGlobal() function in Flash. Which, to be honest was a first for me. But, when in need it is a very useful function. Once we translate the values , we need to find the offset between those values and the current ‘holder’ movie clip’s position. After that we need to move the ‘holder’ movie clip to those new translated coordinates and move the ‘imgHold’ movie clip to the offset values. So, if the ‘holder’ movie clip’s movement is negative, then we need to find the absolute value of the negative offset values and move the ‘imgHold’ in the positive direction. If its movement is positive we need to move the ‘imgHold’ in the negative direction. We use a simple utility function called moveElements(xv, yv) to move the containers.
function repositionIt()
{
//get the minX and minY values
var vals = drawIt();
//set our vars
var xv = 0;
var yv = 0;
//translate our minX and minY to global values
var myPoint:Object = {x:vals[0], y:vals[1]};
holder.imgHold.localToGlobal(myPoint);
//set our offset values
var xoff = myPoint.x - holder._x;
var yoff = myPoint.y - holder._y;
//check for negative or positive movement
//negate the values we retrieve
if( xoff < 0 )
{
xv = Math.abs(xoff);
}else{
xv = -(xoff);
}
if( yoff < 0 )
{
yv = Math.abs(yoff);
}else{
yv = -(yoff);
}
//set your outer holder to the translated values
holder._x = myPoint.x;
holder._y = myPoint.y;
//move the holder.imgHold clip to the desired offset position
moveElements(xv, yv);
}
//moveElement( xv, yv )
// xv = new x value
// yv = new y value
function moveElements(xv, yv)
{
holder.imgHold._x += xv;
holder.imgHold._y += yv;
}
Here is our function to get the minimum x and y values. Notice in the For loop I use Array Access to access my objects instead of using the actual name of the object. This becomes very useful when dealing with data dynamically. Especially for things that load many images or movies or code that deals heavily with different Depths. Array Access is perfect for storing all sorts of objects and then later accessing them in a loop. Also pay attention that this function returns an Array value. The strict data-typing of the function is very important here. If it isn’t data-typed as an Array , it will throw an error.
function drawIt():Array
{
var xval = 0;
var yval = 0;
//loop through our array of elements and get the min and max x,y values
for(var i = 0; i < a.length; i++ )
{
//calculate max x and y values
if(a[i-1])
{
if( a[i]._x < xval)
{
xval = a[i]._x;
}
if(a[i]._y < yval)
{
yval = a[i]._y
}
}
else
{
//calculate min x and y values
if( a[i]._x != xval)
{
xval = a[i]._x;
}
if( a[i]._y != yval )
{
yval = a[i]._y;
}
}
}
// b array ( x coor, y coor ) of the holder.imgHold’s contents
var b = new Array(xval, yval);
return b;
}
//utility function to set the holder in the center of the stage
function positionHolder():Void
{
holder._x = Stage.width/2;
holder._y = Stage.height/2;
}
Post any questions or comments on our Google Group: nothingGrinder Tutorials. Thanks.