3

I have been trying to get jQuery UI's draggable/droppable to work properly with round elements, but it seems that all of the methods I've tried make elements visually round, but they are still treated like squares.

The specific issue I am having is when using a droppable with a border-radius or clip-path, the draggable can be dropped on the "corners" that should not be there...

To better illustrate my issue:

Border-radius Example

<div class="droppable"></div>
<div class="draggable"></div>

.droppable {
    position:relative;
    background:green;
    height:200px;
    width:200px;
    border-radius: 50%;
}
.draggable {
    background:black;
    height: 25px;
    width: 25px;
}
.touched {
    background:red;
}

$('.draggable').draggable();
$('.droppable').droppable({
    tolerance: 'touch',
    over: function () {
        $(this).addClass('touched');
    }
});

Clip-path Example

<svg width="0" height="0">
    <clipPath id="clipping">
        <circle cx="100" cy="100" r="100" />
    </clipPath>
</svg>

.droppable {
    position:relative;
    background:green;
    width:200px;
    height:200px;
    clip-path: url(#clipping);
}

I have dug through the API Documentation for both Draggable and Droppable and I've searched for other ways to create circular/round elements, hence clip-path, but haven't been able to come up with a work-around.

Is there any way to get draggable and droppable to treat round elements like round elements?

apaul
  • 16,092
  • 8
  • 47
  • 82

2 Answers2

3

Here is a way it could be solved with some additional JS:

$('.draggable').draggable({
   drag: function (aEvent, aUi) {

      $(".droppable").each(function() {

         var myX = aUi.offset.left;
         var myY = aUi.offset.top;

         var myR = $(this).outerWidth() / 2;
         var myCX = $(this).offset().left + myR;
         var myCY = $(this).offset().top + myR;

         if (checkIntersection(myX, myY, myCX, myCY, myR))
         {
             $(this).addClass('touched');
         }
     });
   }    
});
$('.droppable').droppable({
   tolerance: 'touch'
});

function checkIntersection(aX, aY, aCX, aCY, aR) {
   return (Math.pow(aX - aCX, 2) + Math.pow(aY - aCY, 2) < Math.pow(aR, 2));
}

So here is what I did:

  1. I moved the function to the drag event of the draggable as this gets called over and over and so we can constantly check if we are now in the circle.

  2. There is an .each() loop as you now have to check every droppable in turn.

  3. I am checking whether or not the x and y coordinates of the draggable overlap with the droppable. This is done in the function checkIntersection. For a full explanation of the algorithm used see Equation for testing if a point is inside a circle.

As a note, right now I am only checking the intersection with one of the corners of the little square, you would have to do this for all corners, to be fully precise.

Here is the fiddle: http://jsfiddle.net/V3Hkg/2/

Community
  • 1
  • 1
ced-b
  • 3,957
  • 1
  • 27
  • 39
  • This looks promising can you add some explanation and relevant documentation? – apaul Sep 07 '13 at 15:34
  • Added some more data. Hope that helps. – ced-b Sep 07 '13 at 15:40
  • it would also seem that this would become a lot more complicated if the draggable was also round – apaul Sep 07 '13 at 15:42
  • also a little concerned about the potential expense of using a drag function – apaul Sep 07 '13 at 15:44
  • @apaul34208 On the rounded draggable, see http://stackoverflow.com/questions/481144/equation-for-testing-if-a-point-is-inside-a-circle. As far as efficiency is concerned. You have to test this, but that math has to be done somewhere, whether jQuery does it or you. – ced-b Sep 07 '13 at 15:50
  • Thanks for steering me in the right direction, sorry to give you a hard time about it, the algorithm is definitely what I needed, though I'm going to have to play with it for a while... the actual use case looks a bit more like this: http://jsfiddle.net/apaul34208/EqSNW/3/ – apaul Sep 08 '13 at 16:52
2

This is because jQuery is defining the draggable/droppable areas via the bounding box of the element, not its visual appearance as defined in CSS. It doesn't know this information by default. Take a look at this tutorial entitled Hover and Click Trigger for Circular Elements with jQuery. Perhaps it will give you insight into how to bind the drag/drop events to only the circular area.

BigMacAttack
  • 4,479
  • 3
  • 30
  • 39
  • you mentioned that jQuery "knows" only about the bounding box. Would it then help if some `clip-path`ed element also had a `-webkit-shape-outside` declaration, which I understand, is responsible for setting the bounding box? – maxheld Apr 28 '17 at 08:16