简体   繁体   中英

JQuery SVG making objects droppable

I am trying to build a seating reservation web app using SVG. Imagine, I've created rectangles in the svg, which represents an empty seat. I want to allow user to drop an html "image" element on the "rect" to reserve the seat.

However, I couldn't get the droppable to work on the SVG elemnets. Any one has any idea why this is so? Here is the code:

$('#target').svg();
var svg = $("#target").svg('get');
svg.rect(20, 10, 100, 50, 10, 10, { fill: '#666', class: "droppable" });
$('rect')
        .droppable({
           accept: '.product',
           tolerance: 'touch',
           drop: function (event, ui) {
              alert('df');
           }
        }

I looked in to the jQuery-ui source and figured out why it wasn't working with SVGs. jQuery thinks they have a width and height of 0px, so the intersection test fails.

I wrapped $.ui.intersect and set the correct size information before passing the arguments through to the original function. There may be more proportion objects that need fixing but the two I did here are enough to fix my :

$.ui.intersect_o = $.ui.intersect;
$.ui.intersect = function(draggable, droppable, toleranceMode) {
    //Fix helper
    if (draggable.helperProportions && draggable.helperProportions.width === 0 && draggable.helperProportions.height === 0) {
        draggable.helperProportionsBBox = draggable.helperProportionsBBox || $(draggable.element).get(0).getBBox();
        draggable.helperProportions = draggable.helperProportionsBBox;
    }

    //Fix droppable
    if (droppable.proportions && droppable.proportions.width === 0 && droppable.proportions.height === 0) {
        droppable.proportionsBBox = droppable.proportionsBBox || $(droppable.element).get(0).getBBox();
        droppable.proportions = droppable.proportionsBBox;
    }

    return $.ui.intersect_o(draggable, droppable, toleranceMode);
};

With jQuery UI 1.11.4 I had to change Eddie's solution to the following, as draggable.proportions is now a function:

$.ui.intersect_o = $.ui.intersect;
$.ui.intersect = function (draggable, droppable, toleranceMode, event) {
//Fix helper
if (draggable.helperProportions && draggable.helperProportions.width === 0 && draggable.helperProportions.height === 0) {
   draggable.helperProportionsBBox = draggable.helperProportionsBBox || $(draggable.element).get(0).getBBox();
   draggable.helperProportions = draggable.helperProportionsBBox;
}

if (droppable.proportions && !droppable.proportions().width && !droppable.proportions().height)
   if (typeof $(droppable.element).get(0).getBBox === "function") {
       droppable.proportionsBBox = droppable.proportionsBBox || $(droppable.element).get(0).getBBox();
       droppable.proportions = function () {
           return droppable.proportionsBBox;
       };
   }

    return $.ui.intersect_o(draggable, droppable, toleranceMode, event);
};

If you want to restrict the drop on svg elements to hit on visible content only (for example in polygons ) you might want to use this addition to Peter Baumann's suggestion:

$.ui.intersect_o = $.ui.intersect;
$.ui.intersect = function (draggable, droppable, toleranceMode, event) {
    //Fix helper
    if (draggable.helperProportions && draggable.helperProportions.width === 0 && draggable.helperProportions.height === 0) {
        draggable.helperProportionsBBox = draggable.helperProportionsBBox || $(draggable.element).get(0).getBBox();
        draggable.helperProportions = draggable.helperProportionsBBox;
    }

    if (droppable.proportions && !droppable.proportions().width && !droppable.proportions().height)
        if (typeof $(droppable.element).get(0).getBBox === "function") {
            droppable.proportionsBBox = droppable.proportionsBBox || $(droppable.element).get(0).getBBox();
            droppable.proportions = function () {
                return droppable.proportionsBBox;
            };
        }

    var intersect = $.ui.intersect_o(draggable, droppable, toleranceMode, event);
    if (intersect) {
        var dropTarget = $(droppable.element).get(0);
        if (dropTarget.ownerSVGElement && (typeof (dropTarget.isPointInFill) === 'function') && (typeof (dropTarget.isPointInStroke) === 'function')) {
            var x = ( draggable.positionAbs || draggable.position.absolute ).left + draggable.margins.left + draggable.helperProportions.width / 2,
                y = ( draggable.positionAbs || draggable.position.absolute ).top + draggable.margins.top  + draggable.helperProportions.height / 2,
                p = dropTarget.ownerSVGElement.createSVGPoint();
            p.x = x;
            p.y = y;
            p = p.matrixTransform(dropTarget.getScreenCTM().inverse());
            if(!(dropTarget.isPointInFill(p) || dropTarget.isPointInStroke(p)))
                intersect = false;
        }
    }
    return intersect;
};

In case if anyone has the same question in mind, droppable doesn't work in jquery SVG. So in the end, I did the following to create my own droppable event:

  1. In draggable, set the target currently dragged been dragged to draggedObj ,

  2. In the mouse up event, check if the draggedObj is null, if it's not null, then drop the item according to the current position. ( let me know if you need help on detecting the current position)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM