views:

1857

answers:

6

I'm trying to create a map application similar to this. Click the SWF Preview tab on the left of the image. Specifically, noticed how you can pan around, and the clickable buttons on the map move with it. Basically, how do they do that?

My application has a map that you can click and pan around using a startDrag() function. I have a separate layer with other, clickable movie clips that I'd like to follow the pans of the map layer. Unfortunately, Flash limits you to dragging only one movie clip at a time. Somebody proposed a solution using a prototype, but I can't get that working correctly, and I'm not sure if it's because I'm using ActionScript 3.0 or not.

Can anybody outline a better way for me to accomplish what I'm trying to do, or a better way to do what I'm currently doing? Appreciate it.

+1  A: 

I can't drag anything int SWF you linked, just zoom via mouseWheel.

You can make more than one movieClip dragging at once using events mousedown, mouseMove and mouseUp.

Add event handler for events via Mouse.addListener(object).

  • In mouseDown set some flag and remeber current mouse position.
  • In mouseMove if the flag is set calculate move offset and just change .x and .y of all movieclips you want to drag.
  • In mouseUp set the flag to false
Jakub Kotrla
A: 

can you add the movieclips to a parent clip and have the event handler drag the parent?

DanWoolston
+2  A: 

Simple. Put your map and the clickable buttons into a new MovieClip, you could call it interactiveMapContainer or something similar, then call your startDrag method on interactiveMapContainer and you'll still be able to click the buttons once you've dragged it about.

Jakub Kotrla's method will also work very well, although it is slightly more complicated.

defmeta
This was the solution I ended up using. I had some scoping problems at first, but once I added _parent to _parent.interactiveMapContainer it was all good. Thanks for the simple solution!
Matt S
+2  A: 

I think defmeta's method is the best one if it's possible to make everything be children of the "main" draggable object -- it's the fastest (in terms of UI response time) and most simple, and requires the least amount of code.

If that is not an option, there are a couple of other methods at your disposal:

  • If you're using Flex, you can have other objects move along with the map by listening to MoveEvent.MOVE events on it and moving themselves accordingly
  • You can implement the drag functionality yourself so that you'll have more control over it (see below).

Here's an example that implements drag functionality for an object that will also move a bunch of "sibling" objects along with it. In this example,

  • Any variable beginning with an underscore is a private member of the class that contains this code
  • _siblings is a Dictionary in which the keys are the other objects that need to be moved.

.

private var _dragStartCoordinates:Point = null;
private var _siblingsDragStartCoordinates:Dictionary = null;

private function mouseDownHandler(event:MouseEvent):void
{
    this.startDrag();
    _dragStartCoordinates = new Point(this.x, this.y);

    _siblingsDragStartCoordinates = new Dictionary(true);
    for (var sibling:DisplayObject in _siblings)
    {
        _siblingsDragStartCoordinates[sibling] = new Point(sibling.x, sibling.y);
    }

    stage.addEventListener(MouseEvent.MOUSE_UP, dragMouseUpHandler, false, 0, true);
    stage.addEventListener(MouseEvent.MOUSE_MOVE, dragMouseMoveHandler, false, 0, true);
}

private function dragMouseUpHandler(event:MouseEvent):void
{
    stage.removeEventListener(MouseEvent.MOUSE_UP, dragMouseUpHandler, false);
    stage.removeEventListener(MouseEvent.MOUSE_MOVE, dragMouseMoveHandler, false);
    this.stopDrag();
    moveSiblings();
    _dragStartCoordinates = null;
    _siblingsDragStartCoordinates = null;
}

private function dragMouseMoveHandler(event:MouseEvent):void
{
    moveSiblings();
}

// expects _dragStartCoordinates and _siblingsDragStartCoordinates
// to be set
private function moveSiblings():void
{
    var xDiff:Number = this.x - _dragStartCoordinates.x;
    var yDiff:Number = this.y - _dragStartCoordinates.y;

    for (var sibling:DisplayObject in _siblings)
    {
        sibling.x = _siblingsDragStartCoordinates[sibling].x + xDiff;
        sibling.y = _siblingsDragStartCoordinates[sibling].y + yDiff;
    }
}
hasseg
This seems like a comprehensive solution that would work. One question though: As you're dragging it, would it be visibly moving, or would it only move after you've un-clicked the mouse button?
Matt S
The siblings would be visibly moving since they are being moved in the MOUSE_MOVE event handler. And actually they should be moved in the MOUSE_UP event handler as well since otherwise the final coordinates might not be correct after very fast mouse movements. I'll add that to the example.
hasseg
"sibling.move" ? does DisplayObject have a move function? I'm not sure what you're doing here.
Iain
Whoops -- good catch. It seems I was thinking of mx.core.UIComponent there. I'll fix that.
hasseg
+1  A: 

Thanks hasseg for the code, saved a wee bit of time. Here's my take on it for public use...

    package Tools
{
    import flash.display.MovieClip; 
    import flash.events.*; 
    import flash.geom.Rectangle; 
    import flash.utils.setInterval; 
    import flash.utils.clearInterval; 
    import flash.display.MovieClip;
    import flash.display.DisplayObject;
    import flash.display.Sprite;
    import flash.geom.Point;
    import flash.utils.Dictionary;

    public class DragSync { 
     private var _dragStartCoordinates:Point = null;
     private var _siblingsDragStartCoordinates:Dictionary = null;
     private var _primaryItem:Object = null;
     private var _dragWithPrimary:Array = null;

     public function DragSync(primaryItem:Object,dragWithPrimary:Array)
     {
      _primaryItem = primaryItem;
      _dragWithPrimary = dragWithPrimary;

      _primaryItem.addEventListener(MouseEvent.MOUSE_DOWN, mouseDownHandler);
      _primaryItem.addEventListener(MouseEvent.MOUSE_UP, dragMouseUpHandler);
      _primaryItem.addEventListener(MouseEvent.MOUSE_MOVE, dragMouseMoveHandler);
     }

     private function mouseDownHandler(event:MouseEvent):void
     {
      _dragStartCoordinates = new Point(_primaryItem.x, _primaryItem.y);
      _siblingsDragStartCoordinates = new Dictionary(true);

      for each (var sibling:DisplayObject in _dragWithPrimary)
      {
       _siblingsDragStartCoordinates[sibling] = new Point(sibling.x, sibling.y)
      }
     }

     private function dragMouseUpHandler(event:MouseEvent):void
     {
      moveSiblings();
      _dragStartCoordinates = null;
      _siblingsDragStartCoordinates = null;
     }

     private function dragMouseMoveHandler(event:MouseEvent):void
     {
      moveSiblings();
     }

     // expects _dragStartCoordinates and _siblingsDragStartCoordinates
     // to be set
     private function moveSiblings():void
     {
      if (!_dragStartCoordinates || !_siblingsDragStartCoordinates) return;

      var xDiff:Number = _primaryItem.x - _dragStartCoordinates.x;
      var yDiff:Number = _primaryItem.y - _dragStartCoordinates.y;

      for each (var sibling:DisplayObject in _dragWithPrimary)
      {
       sibling.x = _siblingsDragStartCoordinates[sibling].x + xDiff;
       sibling.y = _siblingsDragStartCoordinates[sibling].y + yDiff;
      }
     }


    } 
}
+1  A: 

Thank you very much. I have changed it as below: package { import flash.display.MovieClip; import flash.events.*; import flash.geom.Rectangle; import flash.utils.setInterval; import flash.utils.clearInterval; import flash.display.MovieClip; import flash.display.DisplayObject; import flash.display.Sprite; import flash.geom.Point; import flash.utils.Dictionary;

public class DragSync { 
    private var _dragStartCoordinates:Point = null;
    private var _siblingsDragStartCoordinates:Dictionary = null;
    private var _primaryItem:Object = null;

private var _workSpace:Object = null; private var _dragWithPrimary:Array = null;

    public function DragSync(primaryItem:Object,workSpace:Object, dragWithPrimary:Array)
    {
            _primaryItem = primaryItem;
            _dragWithPrimary = dragWithPrimary;
_workSpace = workSpace;

            _workSpace.addEventListener(MouseEvent.MOUSE_DOWN, mouseDownHandler);
            _workSpace.addEventListener(MouseEvent.MOUSE_UP, dragMouseUpHandler);
            _workSpace.addEventListener(MouseEvent.MOUSE_MOVE, dragMouseMoveHandler);
_workSpace.addEventListener(MouseEvent.MOUSE_OUT, dragOut);
    }

    private function mouseDownHandler(event:MouseEvent):void
    {
            trace("event mouse down");
_primaryItem.startDrag();
_dragStartCoordinates = new Point(_primaryItem.x, _primaryItem.y);
            _siblingsDragStartCoordinates = new Dictionary(true);

            for each (var sibling:DisplayObject in _dragWithPrimary)
            {
                    _siblingsDragStartCoordinates[sibling] = new Point(sibling.x, sibling.y)
            }
    }

    private function dragMouseUpHandler(event:MouseEvent):void
    {
            moveSiblings();
_primaryItem.stopDrag();
            _dragStartCoordinates = null;
            _siblingsDragStartCoordinates = null;
    }

    private function dragMouseMoveHandler(event:MouseEvent):void
    {
            trace("event mouse MOVE MOVE");
moveSiblings();
    }

private function dragOut(event:MouseEvent):void { _primaryItem.stopDrag(); }

    // expects _dragStartCoordinates and _siblingsDragStartCoordinates
    // to be set
    private function moveSiblings():void
    {
            if (!_dragStartCoordinates || !_siblingsDragStartCoordinates) return;

            var xDiff:Number = _primaryItem.x - _dragStartCoordinates.x;
            var yDiff:Number = _primaryItem.y - _dragStartCoordinates.y;

            for each (var sibling:DisplayObject in _dragWithPrimary)
            {
                    sibling.x = _siblingsDragStartCoordinates[sibling].x + xDiff;
                    sibling.y = _siblingsDragStartCoordinates[sibling].y + yDiff;
            }

    }


} 

}

Ratha