AS3 Bitmap folding with DrawTriangles Example 2.
Tags: As3 Bitmap folding, Drawtriangles, Flash Page Flipping, AS3 uvtData, beginBitmapFill, drawPath.

Click the printer button and the image is tweened by Flash. Click button again the image is folded and thrown away on a Bezier path.

In Frame 1: Multi-image loader was replace with a single loader function. Drag functions were removed and big difference is the page object is now the container so no offsets are required for ABCD in frame 3 (the page edges start at 1 pixel since 0 would throw an infinity error).

The bitmapData Array was replaced as a single variable and import Tween was added for the 3D rotation effects. A cubic Bezier curve is used as a motion path after the 3D rotation.

      Four empty 'node' clips are the anchor and handle points of the Cubic Bezier.
CubicBezier(this,{x:p0.x,y:p0.y},{x:p1.x,y:p1.y},{x:p2.x,y:p2.y},{x:p3.x,y:p3.y});
The bitmap fold currently only folds from the top-left corner of the image. P0 is placed at corner.x and corner.y

We have to place p3 extremely to the far right (1000 pixels) for our image to disappear off the stage.

p1 and p2: the Bezier's handles to shape the curve.

page: is our empty image container and btn1 our invisible hit button.
Ibar: our preloader bar (1x16 green clip not required).

Frame 1 code:
    stop(); //do not remove stop!
    import fl.transitions.Tween;
    import fl.transitions.easing.*;
    import fl.transitions.TweenEvent;
    
    var pts=[]; var speed:int = 1; var ii:int=0; var init=true;
    var num:int=1; var total:int=10; var loader:Loader; //get our jpg file.
    
    var tearoff=false; var afterDrop=false;
    var bmpWidth; var bmpHeight; var frontpage:Sprite = new Sprite; var corner:Sprite = new Sprite;
    var matrix:Matrix=new Matrix(); var finished=true;
    var bitmapData;
    
    btn1.addEventListener(MouseEvent.CLICK, buttonClicked); 
    
    function buttonClicked(e:MouseEvent):void {
      btn1.removeEventListener(MouseEvent.CLICK, buttonClicked); 
      if (finished==true) {
        finished=false;
        frontpage.graphics.clear();
        if (num>total) {num=1;}  
        getImageFile("paper"+num+".jpg"); num+=1;
      } else {
        tm4.start();
      }
    }
    
    function getImageFile(filename) {
      loader = new Loader();   
      loader.load(new URLRequest(filename));   
      configureListeners(loader.contentLoaderInfo);
      loader.contentLoaderInfo.addEventListener(ProgressEvent.PROGRESS, loadProgress);
    }
    
    function configureListeners(dispatcher:IEventDispatcher):void {   
      dispatcher.addEventListener(Event.COMPLETE, completeHandler);   
    }
    
    function loadProgress(event:ProgressEvent):void {
      var pcent:Number=event.bytesLoaded/event.bytesTotal*100;
      lbar.width=pcent;  if(pcent==100){;}
    }
    
    function completeHandler(e:Event):void {
      bitmapData = e.target.content.bitmapData;
      bmpWidth=loader.width; bmpHeight=loader.height;
    
      if (page.getChildByName("frontpage")){
        this.removeChild(corner);
        page.removeChild(frontpage);
      }
      
      frontpage.name="frontpage";
      frontpage.x=-bmpWidth/2; frontpage.y=-bmpHeight/2;  //offset to center of paper.
      matrix.identity();
      matrix.translate(page.x,page.y); //use matrix to offset the page registration points.
      frontpage.graphics.beginBitmapFill(bitmapData,null,false,true); //draw frontpage.
      frontpage.graphics.drawRect(0,0,bmpWidth,bmpHeight);
      frontpage.graphics.endFill();
      page.addChild(frontpage); //add frontpage to stage.
      page.x=300; page.y=-bmpWidth/2;        //offset to center of paper.
      page.rotation = -90;         //rotate it. 
      this.setChildIndex(printer1, this.numChildren-2); //place printer on top.
      tm1.start();
      if(init){play(); init=false} //finished - load math functions and frame3.
    }
    
    function addDragCorner() {
      corner.graphics.beginFill(0xff0000,100);
      corner.graphics.drawRect(0,0,20,20);
      corner.graphics.endFill();
      corner.x=page.x-(bmpWidth/2); 
            corner.y=page.y-(bmpHeight/2); corner.name="corner"; corner.buttonMode = true;
      corner.alpha=0;
      this.addChild(corner); //insert the drag box at the top-left corner.
      p0.x=corner.x; p0.y=corner.y;
      if (num%2) {
        p1.x=500;p1.y=400;p2.x=100;p2.y=-400; p3.x=1000;p3.y=-120;
      } else {
        p1.x=200;p1.y=400;p2.x=1000;p2.y=300; p3.x=900;p3.y=900;
      }
      CubicBezier(this,{x:p0.x,y:p0.y},{x:p1.x,y:p1.y},{x:p2.x,y:p2.y},{x:p3.x,y:p3.y});
    }
    
    ///////////////// tweening //////////////////////////////
    
    btn1.addEventListener(MouseEvent.CLICK, buttonClicked); 
    
    var tm1:Timer=new Timer(50,1);  //delay milliseconds, repeatCount for tween.
    var tm2:Timer=new Timer(36,18); //3D flip.
    var tm3:Timer=new Timer(36,18); //insert DragCorner.
    var tm4:Timer=new Timer(50,1);  //follow bezier path.
    var myTween:Tween;
    
    for(var i=1;i<=4;i++){
      this["tm"+String(i)].addEventListener(TimerEvent.TIMER,this["onTimer"+String(i)]);
      this["tm"+String(i)].addEventListener(TimerEvent.TIMER_COMPLETE,this["doneTimer"+String(i)]);
    }
    
    function onFinish(e:TweenEvent):void {tm1.reset();tm2.start();}
    
    function onTimer1(e:TimerEvent):void {
      myTween = new Tween(page, "y", Regular.easeOut, -bmpWidth/2, 200, 1, true);
      myTween.addEventListener(TweenEvent.MOTION_FINISH, onFinish);
    }
    
    function onTimer2(e:TimerEvent):void {
    this.setChildIndex(printer1, this.numChildren-4);
    page.rotationY += 10;page.rotationZ += 5;}
    function onTimer3(e:TimerEvent):void {page.rotationY += 10;}
    function onTimer4(e:TimerEvent):void {moveobj();}
    
    function doneTimer1(e:TimerEvent):void {}
    function doneTimer2(e:TimerEvent):void {tm2.reset();tm3.start();}
    function doneTimer3(e:TimerEvent):void {
      tm3.reset(); addDragCorner(); lbar.width=1;
      btn1.addEventListener(MouseEvent.CLICK, buttonClicked);
    }
    function doneTimer4(e:TimerEvent):void {}
    
    function CubicBezier(mc,p0, p1, p2, p3) {
      pts=[];
      //mc.graphics.lineStyle(2,0xffff00); mc.graphics.moveTo(p0.x,p0.y); pts.push({x:p0.x,y:p0.y});
      for (var u=0; u <= 1; u += .02) { //50 steps - draw cubic bezier arc.
        var x1 = Math.pow(u,3)*(p3.x+3*(p1.x-p2.x)-p0.x)+3*Math.pow(u,2)*(p0.x-2*p1.x+p2.x)+3*u*(p1.x-p0.x)+p0.x;
        var y1 = Math.pow(u,3)*(p3.y+3*(p1.y-p2.y)-p0.y)+3*Math.pow(u,2)*(p0.y-2*p1.y+p2.y)+3*u*(p1.y-p0.y)+p0.y;
        mc.graphics.lineTo(x1,y1);  pts.push({x:x1,y:y1});
      } 
      //mc.graphics.lineTo(p3.x,p3.y);
      pts.push({x:p3.x,y:p3.y}); tm4.repeatCount=pts.length;
    }
    
    function moveobj() {
      if (ii < pts.length-1) {
        corner.x=pts[ii].x-p0.x; corner.y=pts[ii].y-p0.y; ii+=speed;
        foldpaper();
      } else {
        tm4.reset(); ii=0; finished=true;  buttonClicked(null);  
      }
      ii+=1; 
    }
    
In Frame 2 the functions are the same.
In Frame 3: I replaced the static nodes with new Point() and replaces the mouse.x and mouse.y with the drag corner.x and corner.y which now tears our page off automatically using moveobj().
    stop();  //do not remove stop!
    var node1:Point = new Point(); var node2:Point = new Point();
    var node3:Point = new Point(); var node4:Point = new Point();
    
    var mouse:Point=new Point();
    var leftEdge=false; var rightEdge=false; var bottomEdge=false; var topEdge=false;
    var A:Point = new Point(1,1);     //topleft page
    var B:Point = new Point(bmpWidth,1);  //topright page
    var C:Point = new Point(bmpWidth,bmpHeight); //bottomright page
    var D:Point = new Point(1,bmpHeight);           //bottomleft page
    var N:Point= new Point();                   //holds right-angled point of normal.
     
    function foldpaper() { //Main Program Code.
      rightEdge=false; bottomEdge=false; leftEdge=false; topEdge=false; tearoff=false;//reset.
      mouse.x=corner.x; mouse.y=corner.y;
      var midpt:Point = Point.interpolate(A,mouse,.5); //center point of drag line to start corner.
      rightAnglePt(A,mouse,midpt,N,1,1) //get right hand normal (pt1,pt2,pt,newpt,lineLength,LR:int=0)
    
      var AB:Point=Intersection(midpt,N,A,B); //Get page's rectangle intersection points.
      var BC:Point=Intersection(midpt,N,B,C);
      var CD:Point=Intersection(midpt,N,C,D);
      var DA:Point=Intersection(midpt,N,D,A);
      if(AB.x==0 || AB.y==0 || DA.x==0 || DA.y==0){return;} //can't intersect lines, infinity error.
    
      if(AB.x <= A.x && DA.y <=A.y || CD.x <= D.x && DA.y >=D.y) {tearOffImage(AB,0)} //left-side
      if(AB.x >= B.x && BC.y <=B.y || CD.x >= C.x && BC.y >=C.y) {tearOffImage(AB,1)} //right-side 
    
    if (tearoff==false) {
      if(mouse.y < B.y && BC.y < C.y && mouse.x > A.x) { //right-side top drag special case, things reverse.
        node1.x=BC.x; node1.y=BC.y;
        node2.x=AB.x; node2.y=AB.y;
        PointOnHypotenuse(mouse,DA,node4,-bmpHeight); //missing referance point so extend slope line.
        PointToLine(node4,CD,node1,node3); topEdge=true;
      
      } else if(mouse.x < D.x && CD.x < C.x) { //left-side drag special case, things reverse.
        node1.x=DA.x; node1.y=DA.y;
        node2.x=CD.x; node2.y=CD.y;
        PointOnHypotenuse(mouse,AB,node3,-bmpWidth);
        PointToLine(node3,BC,node2,node4); leftEdge=true;
      } else {
        if(AB.x>=A.x && AB.x<=B.x) {node2.x=AB.x; node2.y=AB.y;}
        if(BC.y>=B.y && BC.y<=C.y) {node2.x=BC.x; node2.y=BC.y; rightEdge=true;}
        if(CD.x>=D.x && CD.x<=C.x) {node1.x=CD.x; node1.y=CD.y; bottomEdge=true;}
        if(DA.y>=A.y && DA.y<=D.y) {node1.x=DA.x; node1.y=DA.y;}
        PointToLine(mouse,AB,node2,node3); PointToLine(mouse,DA,node1,node4);
      }
    } //end of if tear off.
      DrawTriangles(); //draw the backpage.
    }
    
    function DrawTriangles() {
      var vertices:Vector.=new Vector.;
      var indices:Vector.=new Vector.();
      var uvtData:Vector.=new Vector.(); 
      var drawpath:Vector. = new Vector.(); 
      var coord:Vector. = new Vector.();
      
      var Bxn2=(B.x-node2.x)/bmpWidth;  // width normalized 1-0 for uvtData.
      var n1Ay=(node1.y-A.y)/bmpHeight; // height normalized 0-1 for uvtData.
      var n2Ay=(node2.y-A.y)/bmpHeight; // short rightside normalized 0-1 for uvtData.
      var n1Ax=(node1.x-A.x)/bmpWidth;  // short bottomside normalized 0-1 for uvtData.
      var Bxn1=(B.x-node1.x)/bmpWidth;  // short bottomside normalized 1-0 for uvtData.
      
      if (tearoff) { //two triangle full rectangle. Mouse is the uvt 0,0 position.
        vertices.push(mouse.x,mouse.y,node4.x,node4.y,node3.x,node3.y,node2.x,node2.y); 
        indices.push(0,1,3, 1,2,3); uvtData.push(1,0, 1,1, 0,1, 0,0); 
        
      } else if (topEdge) { //three triangles
        vertices.push(mouse.x,mouse.y,node4.x,node4.y,node3.x,node3.y,node1.x,node1.y,node2.x,node2.y);
        indices.push(0,1,4, 1,3,4, 1,2,3); uvtData.push(1,0, 1,1, 0,n1Ax, 0,n1Ay, Bxn2,0);
      
      } else if (leftEdge) { //three triangles.
        vertices.push(mouse.x,mouse.y,node1.x,node1.y,node2.x,node2.y,node4.x,node4.y,node3.x,node3.y);
        indices.push(0,1,4, 1,3,4, 1,2,3); uvtData.push(1,0, 1,n1Ay, Bxn2,n2Ay, 0,1, 0,0);
    
      } else   if (rightEdge && bottomEdge) { //three triangles.
        vertices.push(mouse.x,mouse.y,node4.x,node4.y,node1.x,node1.y,node2.x,node2.y,node3.x,node3.y); 
        indices.push(0,1,4, 1,3,4, 1,2,3); uvtData.push(1,0, 1,1, Bxn1,1, Bxn2,n2Ay, 0,0);
        
      } else if (rightEdge) { //two triangles uvt clipped.
        vertices.push(mouse.x,mouse.y,node1.x,node1.y,node2.x,node2.y,node3.x,node3.y); 
        indices.push(0,1,3, 1,2,3); uvtData.push(1,0, 1,n1Ay, 0,n2Ay, 0,0);
    
      } else if (bottomEdge) { //two triangles uvt clipped.
        vertices.push(mouse.x,mouse.y,node4.x,node4.y,node1.x,node1.y,node2.x,node2.y);
        indices.push(0,1,3, 1,2,3); uvtData.push(1,0, 1,1, Bxn1,1, Bxn2,0);
        
      } else { //start corner, one triangle page curl.
        vertices.push(mouse.x,mouse.y,node1.x,node1.y,node2.x,node2.y); 
        indices.push(0,1,2); uvtData.push(1,0, 1,n1Ay, Bxn2,0);
      }
      
    frontpage.graphics.clear();
      
    if (leftEdge==false && topEdge==false && tearoff==false) { //draw the frontpage overlay.
      frontpage.graphics.beginBitmapFill(bitmapData,null,false,true);
      drawpath.push(1,2,2,2,2,2);
      coord.push(node2.x,node2.y, mouse.x,mouse.y, node1.x,node1.y, D.x,D.y, C.x,C.y, B.x,B.y);
      frontpage.graphics.drawPath(drawpath, coord);
      frontpage.graphics.endFill();
    }
      frontpage.graphics.beginBitmapFill(bitmapData,matrix,false,true); //draw the backpage fold image.
      frontpage.graphics.drawTriangles(vertices,indices,uvtData);
      frontpage.graphics.endFill();
    }
    
    function tearOffImage(edge,normal) { //exceeded the page's boundaries so tear off the page.
      tearoff=true; var W; var H;
      if (normal==1) {W=bmpWidth; H=bmpHeight} else {W=-bmpWidth; H=-bmpHeight}
      PointOnHypotenuse(mouse,edge,node2,W);
      rightAnglePt(mouse,node2,node2,node3,H,1);
      rightAnglePt(node2,node3,node3,node4,W,normal);
    }
    
Photo fold. FLASH CS4 Profeesssional - ActionScript 3.
Files:
photofold.fla //FLASH CS4
photofold.swf //compliled CS4
paper1.jpg, paper2.jpg, paper3.jpg, paper4.jpg, paper5.jpg, paper6.jpg, paper7.jpg, paper8.jpg, paper9.jpg, paper10.jpg
photofold.htm //generated flash html page.

How to setup:
Add 10 images in the same directory folder of the photofold.swf file or change total:int=10 in frame 1 to reflect number of images.

Sorry no fla at this time. However here are the stage hints.

  1. A stage symbol named 'page' (it’s a container with nothing in it and is where your images will be display).
  2. A stage symbol button called 'btn1' (the printer button).
  3. Four stage symbols named 'p0, p1, p2, p3' (empty clips that act as the Bezier's four points).
  4. Printer1 and Ibar are no required so you can remark them out, Ibar is the the green preloader 1 pixel width by 16 (standard loading effect).
    Printer1 is the top of the printer which is swapped with the image (photo fly over the printer effect). The tray is on the lowest layer.
    this.setChildIndex(printer1, this.numChildren-2); //place printer on top.

1