Drag and drop grid
For a current project I am working on I have needed to be able to load a group of thumbnails and then allow simple drag and drop actions to re-order the thumbnails and save the new order as well as being able to delete a thumbnail.
I’ve spent some time working on this prototype and I am now pretty happy with the results. You can drag and drop a box from any position and it will re-order all the boxes in their new, correct positions. There where some bugs and problems to overcome in the beginning, such as finding the correct position of the dragged box. Eventually I figured out a method that could simply calculate this new position based on the number of columns I had set for the grid and the items x and y position. The basic equation is:
position = ( insertRow * columns) + insertColumn;
I then replace insertRow and insertColumn with functions to calculate based on the box’s x and y. This gave me a number between 0 and the total number of boxes, I could then splice out the box from an array of boxes and then re-insert it in the correct position. After I had a newly ordered array of boxes I can loop through the array and re-set the position of each box. I have included the final code underneath the example:
var boxNum:int = 18;
var cols:int = 7;
var padding:int = 24;
var boxes:Array = [];
var saved:Array;
createChildren();
function createChildren():void
{
for (var i:int = 0; i < boxNum; i++)
{
var box:Box = new Box();
box.redTxt.text = i.toString();
addChild(box);
// stop the textfields from dispatching events
box.redTxt.mouseEnabled = false;
box.greenTxt.mouseEnabled = false;
box.invisibleBtn.useHandCursor = false;
// store a reference to the Box
boxes[i] = box;
// set initial positions
arrange();
// set dragger
box.invisibleBtn.addEventListener(MouseEvent.MOUSE_DOWN, _mouseDown);
box.deleteBtn.addEventListener(MouseEvent.CLICK, _mouseDelete, false, 0, true);
}
}
function arrange():void
{
saved = [];
for (var i:int = 0; i < boxes.length; i++)
{
boxes[i].x = 0 + (boxes[i].width + padding) * (i % cols);
boxes[i].y = 0 + (boxes[i].height + padding) * int(i / cols);
boxes[i].arrayIndex = i;
boxes[i].greenTxt.text = i.toString();
// update the saved boxes too
saved[i] = boxes[i];
}
}
function remove($mc:Box):void
{
// remove from array
boxes.splice($mc.arrayIndex, 1);
// remove event listeners
$mc.invisibleBtn.addEventListener(MouseEvent.MOUSE_DOWN, _mouseDown);
$mc.deleteBtn.addEventListener(MouseEvent.CLICK, _mouseDelete, false, 0, true);
// remove child
removeChild($mc);
// remove for garbage collection
$mc = null;
// check the current grid
checkGrid();
// re-position boxes
arrange();
}
function checkGrid():void
{
var hasChanged:Boolean = false;
// if a box has been deleted
if(saved.length != boxes.length)
{
hasChanged = true;
}
// if a box has been repositioned
for(var i:String in boxes)
{
if(boxes[i].arrayIndex != i)
{
hasChanged = true;
}
}
if(hasChanged)
{
// tell the world
trace(”something has changed! update”)
}
}
function _mouseDelete(evt:MouseEvent):void
{
remove(evt.target.parent);
}
function _mouseDown(evt:MouseEvent):void
{
stage.addEventListener(MouseEvent.MOUSE_UP, _mouseUp);
// swap the Box’s depth to the highest possible
var tmp:Sprite = evt.target.parent as Sprite;
setChildIndex(tmp, numChildren - 1);
tmp.startDrag();
}
function _mouseUp(evt:MouseEvent):void
{
stage.removeEventListener(MouseEvent.MOUSE_UP, _mouseUp);
stopDrag();
// calculate new array positions
var insertRow:int = getInsertRow(evt.target.parent as Sprite);
var insertCol:int = getInsertColumn(evt.target.parent as Sprite);
var insertPoint:int = (insertRow * cols) + insertCol;
// remove box from array
boxes.splice(evt.target.parent.arrayIndex, 1);
// insert box to array (using insertPoint as the index)
boxes.splice(insertPoint, 0, evt.target.parent);
// check the current layout
checkGrid();
// reposition
arrange();
}
function getInsertRow(mc:Sprite):int
{
var rows:int = Math.ceil(boxNum / cols) - 1;
var tmp:int = Math.floor((mc.y + (mc.height * 0.5)) / (mc.height + padding));
// normalize value
if (tmp < 0)
{
tmp = 0;
}
if (tmp > rows)
{
tmp = rows;
}
return tmp;
}
function getInsertColumn(mc:Sprite):int
{
var tmp:int = Math.floor((mc.x + (mc.width * 0.5)) / (mc.width + padding));
// normalize value
if (tmp < 0)
{
tmp = 0;
}
if (tmp >= cols)
{
tmp = cols - 1;
}
return tmp;
}