views:

1419

answers:

3

I'm attempting to drag an object using dojo.dnd but want the avatar to be in the same position as the object was (relative to the mouse)

i.e. if a person clicks in the middle of the object then the mouse cursor will be in the middle of the avatar.

I've had all sorts of strange results. if i connect a function to body.onmousemove the drop part of the dnd fails.

How can i get this working?

<html>
<head>
<title>DnD Events</title>
<style type="text/css">
.target
{
border: 1px dotted gray;
width: 300px;
height: 300px;
padding: 5px;
-moz-border-radius: 8pt 8pt;
radius: 8pt;
}
.source
{
border: 1px dotted skyblue;
height: 200px;
width: 300px;
-moz-border-radius: 8pt 8pt;
radius: 8pt;
}
.dojoDndItemOver
{
background: #feb;
border: 1px dotted gray;
}


.target .dojoDndItemAnchor
{
background: #ededed;
border: 1px solid gray;
}
.dojoDndAvatarHeader {
display: none;
}
</style>
<script type="text/javascript" src="dojo/dojo.js" djconfig="parseOnLoad: true, isDebug:false"></script>
<script type="text/javascript">
dojo.require("dojo.dnd.Source");
dojo.require("dojo.dnd.Container");
dojo.require("dojo.dnd.Moveable");
dojo.require("dojo.dnd.Manager");
dojo.require("dojo.dnd.Avatar");

var mouse = { x: 0, y: 0 , handle:undefined};
function mouseCoords(ev) {
var px, py;
ev = ev || window.event;
if (ev.pageX || ev.pageY) {
px = ev.pageX; py = ev.pageY;
} else {
px = ev.clientX + dojo.body().scrollLeft - dojo.body().clientLeft;
py = ev.clientY + dojo.body().scrollTop - dojo.body().clientTop;
}


mouse = { x: px, y: py };
// dojo.byId("msg").innerHTML = dojo.toJson(mouse);


}
//dnd WORKS when following lines are commented out. (positioning fails)
var mchandle = dojo.connect(document, "onmousemove", 'mouseCoords');
//dojo.query(".dojoDndItem").connect("onclick", 'mouseCoords');
//dojo.dnd.Source.onMouseDown('mouseCoords')
</script>
<script type="text/javascript">








var item_price;
var total = 0;
function AddItems(target, nodes) {
for (var i = 0; i < nodes.length; i++)
{ total += parseFloat((target.getItem(nodes[i].id)).data); }
dojo.byId("cost").innerHTML = total;
}


function SubstractItems(target, nodes) {
for (var i = 0; i < nodes.length; i++) {
total -= parseInt((target.getItem(nodes[i].id)).data);
}
dojo.byId("cost").innerHTML = total;
}


function ShowPrice(target, nodes) {
var sum = 0;
for (var i = 0; i < nodes.length; i++) {
dojo.dnd.manager().OFFSET_X = 0 - (mouse.x - dojo._abs(nodes[i]).x);
dojo.dnd.manager().OFFSET_Y = 0 - (mouse.y - dojo._abs(nodes[i]).y);
dojo.dnd.manager().updateAvatar();
sum += parseInt((target.getItem(nodes[i].id)).data);
}

dojo.byId("msg").innerHTML = "Selected Item Price is $" + sum;
}


function ClearMsg()
{ dojo.byId("msg").innerHTML = ""; }


function init() {






dojo.subscribe("/dnd/drop", function(source, nodes, iscopy) {
var t = dojo.dnd.manager().target;
ClearMsg();
if (t == source) { return; }
if (t == cart) { AddItems(t, nodes); }
if (t == shelf) { SubstractItems(t, nodes); }



});


dojo.subscribe("/dnd/start", function(source, nodes, iscopy) {
ShowPrice(source, nodes);
});


dojo.subscribe("/dnd/cancel", function() {
ClearMsg();
});


}




dojo.addOnLoad(init);
















</script>





</head>
<body style="font-size: 12px;">


<table>
<tbody>
<tr valign="top">
<td>
SOURCE
<div dojotype="dojo.dnd.Source" jsid="shelf" class="source" id="source1" accept="red,blue"
singular="false">
<img src="BLUE.png" class="dojoDndItem" dndtype="blue" dnddata="10" title="$10" />
<img src="RED.png" class="dojoDndItem" dndtype="red" dnddata="60" title="$60" />
<img src="BLUE.png" class="dojoDndItem" dndtype="blue" dnddata="13" title="$13" />
<img src="RED.png" class="dojoDndItem" dndtype="red" dnddata="15" title="$15" />
<img src="BLUE.png" class="dojoDndItem" dndtype="blue" dnddata="3" title="$3" />
<img src="RED.png" class="dojoDndItem" dndtype="red" dnddata="148" title="$148" />
<img src="BLUE.png" class="dojoDndItem" dndtype="blue" dnddata="1" title="$1" />
<img src="RED.png" class="dojoDndItem" dndtype="red" dnddata="10" title="$10" />
<img src="BLUE.png" class="dojoDndItem" dndtype="blue" dnddata="3" title="$3" />
</div>
</td>
<td>
TARGET
<div dojotype="dojo.dnd.Source" jsid="cart" class="target" accept="red,blue" id="target1">
</div>
</td>
<td>
Total Price (USD): <span id="cost">0.00</span><br />
<b>Message: <span id="msg" style="color: blue"></span></b>
<td>
</tr>
<tbody />
</table>
</body>
</html>






A: 

I've got it now...

dojo.subscribe("/dnd/start", function(source, nodes, iscopy) {
var px = 0;
var py = 0;

for (var i = 0; i < nodes.length; i++) {
var nPos = dojo._abs(nodes[i]);
px = nPos.x > px ? nPos.x : px;
py = nPos.y > py ? nPos.y : py;

}
dojo.dnd.manager().OFFSET_X = 0 - (source._lastX - px);
dojo.dnd.manager().OFFSET_Y = 0 - (source._lastY - py);

ShowPrice(source, nodes);
});

but it will always cause a dnd cancel event to occur since the mouse is on the avatar.
any ideas on how to get the drop working?

+2  A: 

Dojo's DND is a limited in this sense. The avatar is positioned offset so the move events are not trapped by the node representing the drag. Typical source/targets will not work in this case. I have created a hybrid "mover/source" dnd example that may help you along with what you are trying to accomplish:

http://svn.dojotoolkit.org/src/demos/trunk/beer/src/dnd.js

Basically, we connect "mousedown" to some node. When that is fired, we create a clone of that node directly over where the the original node is in the page (see _dragStart function). Then, we register temporary mousemove and mouseup event listeners. mousemove is a tight function optimized for speed. simply set the top/left position of the "avatar" (the clone) relative to e.pageX and e.pageY (normalized event object parts).

when mouseup is fired, we disconnect both mouseup and mousemove events (this._listeners). In the sample the "overTarget" function simply returns true. You can change this logic to be whatever you need to ensure the current pageX/pageY coords are within a bounding box of your choice (a Source/Target, whatever, however you like).

In the example, I have it animating back to the original x/y of the "source", or turning it into a dojo.dnd.Moveable (it being the cloned node), creating a sort of markupfactory out of the source. You will likely want to just use this to add whatever data to your cart, and destroy the avatar.

Hope this helps.

dante
Please forgive my ignorance as i'm rather new to dojo.(i named the file beer.js i.e. /dojo/beer.js)how do i implement the hybrid you created?dojo.require("demos.beer.src.dnd");dojo.require("beer.MadeDnd");<script type="text/javascript" src="dojo/beer.js" djconfig="parseOnLoad: true, isDebug:false"></script>all or some of the above?Your help is appreciated.
A: 

For the time being...(since i didn;t know how to implement the hybrid js)

i've utilised the cancel event and compared the mouse coords to each source object on the page to determine which one it's was intended to be dropped into.

JS in page is now:

var mchandle;
dojo.require("dojo.dnd.Source");
var lastSrc;
function init() {
    dojo.subscribe("/dnd/drop", function(source, nodes, iscopy) {
     dojo.byId("msg").innerHTML += " drop";
     dojo.disconnect(mchandle);
    });
    dojo.subscribe("/dnd/start", function(source, nodes, iscopy) {
     lastSrc = source;
     mchandle = dojo.connect(dojo.doc, "onmousemove", "mouseCoords");
     dojo.byId("msg").innerHTML = "start";
     var px = 0;
     var py = 0;
     for (var i = 0; i < nodes.length; i++) {
      var nPos = dojo._abs(nodes[i]);
      px = nPos.x > px ? nPos.x : px;
      py = nPos.y > py ? nPos.y : py;
     }
     dojo.dnd.manager().OFFSET_X = 0 - (source._lastX - px);
     dojo.dnd.manager().OFFSET_Y = 0 - (source._lastY - py);


    });
    dojo.subscribe("/dnd/cancel", function() {
     dojo.byId("msg").innerHTML += " cancel";
     dojo.query("[dojotype=\"dojo.dnd.Source\"]").forEach(function(node, index, array) {
      var elemXY = dojo.coords(node);
      if ( //in source box
      (elemXY.x <= document.mouse.x && document.mouse.x <= (elemXY.x + elemXY.w)) && (elemXY.y <= document.mouse.y && document.mouse.y <= (elemXY.y + elemXY.h))) {
       var s = new dojo.dnd.Source(node, null);
       s.insertNodes(true, lastSrc.getSelectedNodes(), null, null);
      }
     });
     dojo.disconnect(mchandle);
     lastSrc = null;
    });
}
dojo.addOnLoad(init);
function mouseCoords(ev) {
    var px, py;
    ev = ev || window.event;
    if (ev.pageX || ev.pageY) {
     px = ev.pageX;
     py = ev.pageY;
    } else {
     px = ev.clientX + dojo.body().scrollLeft - dojo.body().clientLeft;
     py = ev.clientY + dojo.body().scrollTop - dojo.body().clientTop;
    }
    document.mouse = {
     "x": px,
     "y": py
    };
}

A Bug is still occurring. I drag to the target (all is well.) i drag from the target and a duplicate avatar is stuck on the screen. firebug reports an error.

_5.getItem(_6[i].id) is undefined
} catch (e) {\r\n                                 dojo.js (line 203)

could you offer any help?

error comes from dojo._loadPath in dojo.js and occurs between onmousemove events FYI