I'm trying to implement a rubber-band selection box in Dojo's gfx with Canvas as the renderer. My intention is to have the selection box drawn when the mouse is clicked down and dragged, then disappear once the mouse is released. Unfortunately I've run into a problem.

jsFiddle example: http://jsfiddle.net/7F9fy/

The main problem is somewhere in onmousemove (or related to it):

whiteRect.connect("onmousemove",function(e) {
    if(isMouseDown) {
        if(whiteRect.groupSelector_) {
            pStat.innerHTML = "dragging...";
            console.debug("dragging...");
            e.stopImmediatePropagation();
            e.preventDefault();
            var ex = (e.x ? e.x : e.clientX);
            var ey = (e.y ? e.y : e.clientY);                   

            if(groupSelector) {
                // Also tried getShape, editing that shape, and setShape on groupSelector--same
                // behavior, though.                    
                var rectX = (ex - cnvDiv.offsetLeft < whiteRect.groupSelector_.x ? ex - cnvDiv.offsetLeft : whiteRect.groupSelector_.x);
                var rectY = (ey - cnvDiv.offsetTop < whiteRect.groupSelector_.y ? ey - cnvDiv.offsetTop : whiteRect.groupSelector_.y);

                surface.remove(groupSelector);
                groupSelector = surface.createRect({
                    x: rectX,
                    y: rectY, 
                    width: Math.abs(ex - cnvDiv.offsetLeft - whiteRect.groupSelector_.x), 
                    height: Math.abs(ey - cnvDiv.offsetTop - whiteRect.groupSelector_.y)
                }).setStroke({color: "blue", width: 3});

            } else {
                groupSelector = surface.createRect({
                    x: whiteRect.groupSelector_.x,
                    y: whiteRect.groupSelector_.y,
                    width: Math.abs(ex - cnvDiv.offsetLeft - whiteRect.groupSelector_.x), 
                    height: Math.abs(ey - cnvDiv.offsetTop - whiteRect.groupSelector_.y)
                }).setStroke({color: "blue", width: 3});
            }
            e.stopPropagation();
        }
    }
});

If I hold down the left mouse button in the shape/group (the white square in the above example) to which my mouse events are connected and start dragging, the box begins to draw, following my drag motion. When I release the mouse, sometimes the box disappears, and sometimes, it doesn't. When it doesn't, the box keeps being drawn and follows mouse movements as defined to do when I'm dragging.

In the jsFiddle, if you watch console.debug or the paragraph reporter under the canvas, you'll see that on occasion, onmouseup doesn't fire when you release the mouse (I checked for mouseup as well, but that has the same issue). In cases where onmouseup never fires, onmousemove continues to fire. If you click again, sometimes a full mouse click series fires (down, up, click, and move), which then makes the drawn rectangle disappear. Sometimes this doesn't happen, though, and onmousemove keeps firing. If you click after the drag/onmousemove becomes 'stuck' and nothing happens, there are no debug lines or changes to reporters for those events, so it's as if all mouse events except onmousemove are being squelched. I tried adding in stopPropagation, stopImmediatePropagation, and preventDefault, but that didn't help. I also tried using Dojo event's stop, but that didn't change the behavior.

For re-drawing the box in onmousemove, I've tried both 'getShape -> edit properties -> setShape' as well as deleting the shape and making a whole new one; neither of these methods stopped the problem and there wasn't any appreciable difference between them.

I'm using Dojo 1.8.3, and this happens in both Chrome (v25) and Firefox (v19), with either Canvas or SVG as the renderer.

Thoughts? Am I missing something obvious here?

有帮助吗?

解决方案

Sorted it out. The problem is the onmouseup event when you decide to stop dragging out the shape can fire on the underlying/attached shape, or on the shape you're dragging. It's random, depending on the cursor position, but favors the drawn shape if you don't have an offset or delay in your drag. (Moveable.js and Mover.js in dojox/gfx pointed me in the right direction.)

I changed my box to a path in the course of trying to make it work, and this seems to perform better, but isn't necessary.

The key was to make a general 'onMouseUp' function, then call that from both the originator-shape's onmouseup as well as the dragged shape's onmouseup. My example is sloppy, but I hope it gets the point across.

jsFiddle: http://jsfiddle.net/n3KGY/1/

Key code:

// General method to clear out a selector if
// one was being drawn.
var selectorMouseUp = function(e) {
    reporter.innerHTML = "onmouseup";
    isMouseDown = false;
    whiteRect.groupSelector_ = null;
    if(groupSelector) {
        if(selectorUp) {
            groupSelector.disconnect(selectorUp);
        }               
        surface.remove(groupSelector);
        groupSelector = null;
    }
    e.stopImmediatePropagation();
    e.stopPropagation();
    e.preventDefault();
};

// Mouseup event for the background/workspace
whiteRect.connect("onmouseup",function(e){
    selectorMouseUp(e);
});

// Make a selector as a path on the surface
// and attach a mouseup to it
var makeSelector = function(x,y,w,h) {
    groupSelector = surface.createPath()
        .moveTo(x,y)
        .hLineTo(x+w).vLineTo(y+h).hLineTo(x).vLineTo(y)
        .setStroke({color: "blue", width: 3})
        .closePath();
    // Attach the same mouseup method as the workspace/background
    selectorUp = groupSelector.connect("onmouseup",function(e){
        reporter.innerHTML = "onmouseup (selector)";
        selectorMouseUp(e);
    });         
};

bigRect.connect("onmousemove",function(e){  
    if(isMouseDown) {
        if(bigRect.groupSelector_) {
            var ex = e.clientX;
            var ey = e.clientY;

            reporter.innerHTML = "dragging at " + ex+","+ey;

            var downX = bigRect.groupSelector_.x;
            var downY = bigRect.groupSelector_.y;
            var leadingX = (ex - grn.offsetLeft < downX ? ex - grn.offsetLeft : downX);
            var leadingY = (ey - grn.offsetTop < downY ? ey - grn.offsetTop : downY);
            var selWidth = Math.abs(ex - grn.offsetLeft - downX);
            var selHeight = Math.abs(ey - grn.offsetTop - downY);

            if(groupSelector) {
                // If there's already a selector being drawn, get rid of it.
                groupSelector.disconnect(selectorUp);
                surface.remove(groupSelector);
            }
            // Draw the current selector
            makeSelector(leadingX,leadingY,selWidth,selHeight);
            e.stopImmediatePropagation();
            e.stopPropagation();
            e.preventDefault();
        }
    }
});
许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top