How do I force jquery to center an element when it

2019-01-20 12:01发布

问题:

I want some square-shaped draggable objects (in this case just <td> boxes with numbers in them) to be able to snap to some empty table cells and snap to the center of those cells (empty td boxes), not the (outer) edge those cells, which is what is seems to do by default.

Here's my script.:

<script type="text/javascript">
 $(document).ready(function () {
     $(".inputs div").draggable( {
       snap: ".spaces"
     });    
 });    
</script>

EDIT: Here is the entire file

 <!DOCTYPE html>
  <head>
   <title>Draggable</title>
   <script type="text/javascript" src="jquery-1.7.2.min.js"></script>
   <script type="text/javascript" src="jquery-ui-1.8.21.custom.min.js"></script>
   <script type="text/javascript" src="jquery.ui.touch-punch.min.js"></script>
   <style>
    .block {
        z-index:9999;
        cursor:move;
    }
    li {
        list-style:none;
    }
    tr {
        border: 2px solid black;
    }
    table {
        border: 2px solid black;
    }
    .inputs div {
        float:left;
        background-color:#FFFFFF;
        color:#004E66;
        font-size:x-large;
        margin:2px;
        padding:20px;
        border:1px solid black;
    }
    .spaces td {
        background-color:#666666;
        margin:2px;
        padding:36px;
        border:2px solid black;
    }
 </style>
</head>

<body>
<form id="form1">
  <div class="inputs">
    <div>1</div>
    <div>2</div>
    <div>3</div>    
    <div>4</div>
    <div>5</div>
    <div>6</div>
  </div>
  <br/>
  <table class="spaces">
    <tr>
        <td></td>
        <td></td>
        <td></td>
    </tr>
    <tr>
        <td></td>
        <td></td>
        <td></td>
    </tr>
  </table>
</form>
<!-- here we are telling jquery to make each element inside of class 'inputs' draggable -->
<script type="text/javascript">
   $(document).ready(function () {
      $(".inputs div").draggable( {
         snap: ".spaces"
      }).center();  
  });   
</script>

Here's a rough text-based illustration of what's going on

-----+---------+
XXXXXXXXXXX   X|
     |         |
     |    Y   x|
     |        X|
-----+-------+X|
     |        X|
  • if the above box is a td cell in the right corner of the topmost tr row
  • then X is where the elements are currently sticking (obviously its not that large, I'm just showing the places where it sticks...actually I removed some of the X's to show how it snaps to the corner once it reaches a certain closeness to it...)
  • basically this model demonstrates that only the external edges of the table have "gravity" with regards to the draggable element. What I really want it to do is snap into the td cells with repulsion to ALL the edges, not with attraction to the external ones.
  • Y is the desired snap-to location for the dragged element.
  • Lastly for presentation reasons I would want the element snap into place with some sort of transition rather than an abrupt jump...

回答1:

I believe this is what you want.

You can obviously change the positioning if you want and it suits your needs better.

Demo is here: DEMO

$(document).ready(function () {


$(".inputs div").draggable( {
        opacity: .4,
        create: function(){$(this).data('position',$(this).position())},
        cursorAt:{left:15},
        cursor:'move',
        start:function(){$(this).stop(true,true)}
   });

     $('.spaces').find('td').droppable({
         drop:function(event, ui){
             snapToMiddle(ui.draggable,$(this));
         }
     });

 }); 


function snapToMiddle(dragger, target){
    var topMove = target.position().top - dragger.data('position').top + (target.outerHeight(true) - dragger.outerHeight(true)) / 2;
    var leftMove= target.position().left - dragger.data('position').left + (target.outerWidth(true) - dragger.outerWidth(true)) / 2;
    dragger.animate({top:topMove,left:leftMove},{duration:600,easing:'easeOutBack'});
}


回答2:

One note on the snapToMiddle function:

In order to get it to work properly for me I had to change it to >

function snapToMiddle(dragger, target){
    var offset = target.offset();
    var topMove = (target.outerHeight(true) - dragger.outerHeight(true)) / 2;
    var leftMove= (target.outerWidth(true) - dragger.outerWidth(true)) / 2;
    dragger.offset({ top: topMove + offset.top, left: leftMove + offset.left })
}

Also, I created an array that associated the dragger to the dropper and on window reSize I make sure all my droppers don't wander off:

$( window ).resize(function() {
    for( i = 0 ; i < assc.length - 1 ; i++ )
    {
        var drp = assc[i].drop;
        var drg = assc[i].drag;
        snapToMiddle(drg,drp);
    }
});


回答3:

http://jsfiddle.net/vtdhV/

I created a fiddle that solves your problem .. sort of (if I understand correctly).

The red divs are centered within the tds, and you snap to those instead. Just remove the border and it will appear to snap to the center of the td. The nature of .draggable will cause the draggables to snap to any edge rather than all edges simultaneously.

If this is not enough, you may have to implement a call handler for .stop that adjusts the position of the element based on your expectations.



回答4:

You need to specify a snap target that is the same size as your dragged element. This isn't a big deal if your draggables are always all the same size. Just add an empty, absolutely positioned div in your cell, and set up your CSS so that it is sized and centered correctly. Then, set your snap selector to be that div. To only snap to the inside of your snap target, set your snapMode to "inner".

$(".inputs div").draggable({
    snap: ".spaces td div",
    snapMode: "inner"
});

But if the size of your draggables may vary, you'll need to resize and recenter your drop targets every time you start dragging an element. Do this on mousedown of the draggable (Use mousedown because dragstart is too late and doesn't work):

$(".inputs div").draggable({
    snap: ".spaces td div",
    snapMode: "inner",
}).mousedown(function(){
    var targets = $(".spaces td div");
    targets.height(this.offsetHeight);
    targets.width(this.offsetWidth);
    targets.each(function(){
        $(this).position({ of: this.parentNode });
    });
});

http://jsfiddle.net/gilly3/mPr3A/



回答5:

Add a div in these tds and center it by giving fixed width and height for tds and newly added divs. Then span the draggables to these divs.

$(document).ready(function () {
    $('<div></div>').appendTo('.spaces td');
    $(".inputs div").draggable({
        snap: ".spaces td div",
        snapMode:'inner'
    });  
}); 

Also css for tds and divs will be as follows.

.block {
    z-index:9999;
    cursor:move;
}

.productCode {

}

li {
    list-style:none;
}
tr {
    border: 2px solid black;
}
table {
    border: 2px solid black;
}

.inputs div {
    float:left;
    background-color:#FFFFFF;
    color:#004E66;
    font-size:x-large;
    margin:2px;
    padding:20px;
    border:1px solid black;
}

.spaces td {
    background-color:#666666;
    width:80px;
    height:80px;            
    border:2px solid black;
}

.spaces td div {
    margin:0 auto;
    width:50px;
    height:70px;
}

Fiddle Demo - http://jsfiddle.net/nsjithin/u25Bs/1/



回答6:

If I got the problem right, replace your current script with this:

$(document).ready(function () {

    // create a bunch of invisible divs
    var divs = $('<div></div><div></div><div></div><div></div>');

    // make them 2/2 and add them to the existing tds
    divs.css({ width:'50%', float:'left' }).appendTo('.spaces td');

    // snap to everything
    $(".inputs div").draggable({snap: ".spaces td, .spaces td div" }).center(); 

}); 

If I didn't just say and I'll fix it.