1 Reply Latest reply on Jul 22, 2010 12:46 AM by Kimmo Jokinen

    Problem: Scalable canvas with affine transformation

    Kimmo Jokinen

      I'm trying to create a canvas that can be scrolled and is scalable through affine transformations. This, I'm trying to achieve by adding a control component to an existing Canvas that listens for MouseEvents and masks the area outside the area defined by the canvas when the control component is created. The base for this component was http://gasi.ch/blog/zooming-in-flash-flex/.

       

      Next I'm explain how the component is supposed to work and then on to the problem.

       

      This is how the canvas is created. In the constructor MouseListeners and a mask canvas is added to dragCanvas.

                var canvasControl:MatrixCanvasMouseControl = new MatrixCanvasMouseControl(dragCanvas);
      


      The listeners work pretty much the same way as in the link I mentioned earlier. So on to the scaling which is the problem.

       

      The scaling is done by first scaling the dragCanvas with method scaleAt(...):

       

                
      /** START OF MOUSE WHEEL LISTENER */
      
                // CUT
      
                // get the mouseevent position in stage
                var eventStagePoint:Point = new Point();
                          eventStagePoint.x = event.stageX;
                          eventStagePoint.y = event.stageY;          
      
                // scale dragCanvas at that point with scaleAt
                scaleAt(canvas, zoomIn, zoomIn, canvas.globalToLocal(eventStagePoint).x, canvas.globalToLocal(eventStagePoint).y);        
                
                // CUT
           
      /** END OF MOUSE WHEEL LISTENER  */
      
      
        /***
               * Scales the matrix through affine transformation.
               */
              public static function scaleAt(sprite:Sprite, scaleX:Number, scaleY:Number, originX:Number, originY:Number):void
              {
                  var translateMatrix:Matrix = sprite.transform.matrix;
                  // move to origo to preserve form
                  translateMatrix.translate(-originX, -originY);
                  
                  // scale the matrix
                  translateMatrix.scale(scaleX, scaleY);
                  
                  // move back to originX, originY
                  translateMatrix.translate(originX, originY);
                  
                  sprite.transform.matrix = translateMatrix;
              }
      

       

      This scales the maskCanvas also. Next I'm trying to scale the maskCanvas back to it's original position by:

       

                MatrixHelper.scaleAt(maskCanvas, (1 / zoomIn), (1 / zoomIn), maskCanvas.globalToLocal(eventStagePoint).x * (1 / zoomIn), maskCanvas.globalToLocal(eventStagePoint).y * (1 / zoomIn));
      

       

       

      The problem is that when the Canvas is scaled, the mask  changes it's position when I try to scale it back to it's original size and position (as it's size and position changed when dragCanvas was scaled because maskCanvas is a child of dragCanvas).

       

      This might not be the best way to achieve scrolling. I also thought about extending Canvas and creating a parent Canvas that would contain dragCanvas and maskCanvas as it's children. That way scaling one would not affect the other. The problem with this was that I didn't find a way to do that without overriding Canvas functionality. If anyone knows and could explain how this could be done effectively for example by "decorating" it would be great.

       

      I would really appreciate if anyone could help me with this. I've struggled with it for quite a few hours already.

       

      I know I haven't explained everything clearly, so, please, ask me to clarify things more, if this was not understandable.

       

      Message was edited by: Kimmo Jokinen

       

      Message was edited by: Kimmo Jokinen

        • 1. Re: Problem: Scalable canvas with affine transformation
          Kimmo Jokinen Level 1

          OK, I found a hacky solution to my problem by going through every rawChildren of the canvas and then scaling it if it's id didn't match with the one I created in the mouseControl component.

           

          This does not seem like a very elegant way but at least it works.

           

          I'm pretty sure that this should be done whole differently architecturally but I'm not experienced enough to find that way.

           

          So this is how I did it.

           

          for (i = 0; i < canvas.rawChildren.numChildren; i++) {
               child = canvas.rawChildren.getChildAt(i) as Object;
               if (!child is DisplayObject) {
                    continue;
               }
               transformAllowed = false;
                                        
               displayChild = DisplayObject(child);
               if (displayChild.hasOwnProperty("id") && (displayChild["id"] == "maskCanvas" || displayChild["id"] == "mouseControlArea")) {     
                    transformAllowed = false;                                   
               } else {
                    transformAllowed = true;
               }
               if (transformAllowed) {
                    MatrixHelper.scaleAt(displayChild, zoomOut, zoomOut, canvas.globalToLocal(eventStagePoint).x, canvas.globalToLocal(eventStagePoint).y);
               } 
          }