8

If anyone could help me figure out how to make the draggable elements contained in a div that changes scale based on window size, i'd really appreciate any guidance.

If I do:

element.draggable({ 
    cursor: "move",
    containment: '#container'
});

What will happen is it gives me the containment for the regular size of the container. So if I have a transform: scale(1.5), there will be space in the container that the draggable element can not go.

I've also tried containment: 'parent' but that get's very glitchy.

EDIT

I've found out how to get the top and left containment but I can't figure out how to get the right and bottom.

var containmentArea = $("#container");

containment:  [containmentArea.offset().left, containmentArea.offset().top, ???, ???]

I've tried width and height from containmentArea[0].getBoundingClientRect() but that doesn't seem to be the right move either.


Here is a jsfiddle of some example code.

bryan
  • 8,879
  • 18
  • 83
  • 166
  • Not having looked in detail at the dragFix function (possibly, you could restrain the values there instead of using containment), the bounds themselves do appear to work when I tested it, but needed the dragged element dimensions to be subtracted: `var bounds = container.getBoundingClientRect(); var dragrect = $('.draggable')[0].getBoundingClientRect() .... containment: [bounds.x,bounds.y, bounds.right - dragrect.width, bounds.bottom - dragrect.height]` (fiddle: http://jsfiddle.net/z0gqy9w2/4/ ) – Me.Name Jul 12 '15 at 18:57
  • @Me.Name Hmm, the right and bottom seem to work but now the top and left don't. Editing the dragfix could be a possible solution. Good thinking. – bryan Jul 12 '15 at 19:00
  • Oops, used x and y instead of left and right, x and y work in firefox so had no problems there. This works in Chrome as well (haven't tested ie): `containment: [bounds.left,bounds.top, bounds.right - dragrect.width, bounds.bottom - dragrect.height]` (http://jsfiddle.net/z0gqy9w2/5/) (Still, doing the work in dragfix feels more generic, might take a peek at that later on) – Me.Name Jul 13 '15 at 06:00
  • @Me.Name thanks for taking a crack at this! this doesn't work fully if the [draggable container is inside a scrollable container](http://jsfiddle.net/z0gqy9w2/6/). – bryan Jul 13 '15 at 12:44

3 Answers3

6

A version with resetting the coordinates in the drag event (since they were being recalculated already for the scale transformations), without using the containment:

var percent = 1, containmentArea = $("#container");

function dragFix(event, ui) {
    var contWidth = containmentArea.width(), contHeight = containmentArea.height();
    ui.position.left = Math.max(0, Math.min(ui.position.left / percent , contWidth - ui.helper.width()));
    ui.position.top = Math.max(0, Math.min(ui.position.top  / percent,  contHeight- ui.helper.height()));
}

$(".draggable").draggable({
    cursor: "move",
    drag: dragFix,
});

//scaling here (where the percent variable is set too)

Fiddle

In the example width and height of the container are obtained inside the dragevent, you could also store them when scaling for better performance. By having them calculated inside the event, they still work after rescaling, although the percent variable still has to be set. To be truly generic, it could be obtained inside the event as well (and instead of a fixed container, ui.helper.parent() could be used) Since the offset inside the dragevent is (0,0) related to the container (at least it is for the current setup), took the liberty of simplifying originalleft + (position - originalposition)/percent to position / percent Start offset didn't seem to be necessary any more, so left it out in the fiddle, but can be re-added if needed.

Me.Name
  • 12,259
  • 3
  • 31
  • 48
  • This is borderline perfect! Thank you so much!! Will reward the bounty as soon as it lets me – bryan Jul 13 '15 at 16:15
0

Take a look to this :

http://jsfiddle.net/z0gqy9w2/3/

The edited code is the following one :

    // Matrix regex to take the scale value property of $('#container') element    
    var matrixRegex = /matrix\((-?\d*\.?\d+),\s*0,\s*0,\s*(-?\d*\.?\d+),\s*0,\s*0\)/,
    matches = $('#container').css('transform').match(matrixRegex);
    // Matches have this value : ["matrix(1.5, 0, 0, 1.5, 0, 0)", "1.5", "1.5"] , so we need matches[1] value :
    var scaleValue = matches[1];
    $(".draggable").draggable({
        cursor: "move",
        start: startFix,
        drag: dragFix,
        containment: [containmentArea.offset().left, containmentArea.offset().top, 
                      ( ( containmentArea.offset().left + ( containmentArea.width() * scaleValue ) ) - ( $(".draggable").width() * scaleValue ) ) ,
                      ( ( containmentArea.offset().top + ( containmentArea.height() * scaleValue ) ) - ( $(".draggable").height()  * scaleValue ) ) ]

    });

As you see, here is the trick :

( ( containmentArea.offset().left + ( containmentArea.width() * scaleValue ) ) - ( $(".draggable").width() * scaleValue ) )

Your right max position will be : The main container left offset + the true width of the container ( with scale ) - the item true width (to let it inside the container).

(Tip: Be free to change the "percent" var value as you want too see the results)

regex ref

Community
  • 1
  • 1
SerCrAsH
  • 440
  • 5
  • 14
  • Wow @SerCrAsH thanks for this! You seem to be going in the right direction. For some reason if the container is in a scrollable div, the top and bottom containment boundaries seem to get glitched up. – bryan Jul 12 '15 at 19:21
  • write a `console.log(matches)` and tell me if `matches[1]==[2]` – SerCrAsH Jul 12 '15 at 19:24
  • Instead of matches I just did `scaleValue = percent`. Is that a bad idea? – bryan Jul 12 '15 at 19:28
  • If it worth for you its ok, i though that percent will be a dynamic value and you hardcoded it. :) – SerCrAsH Jul 12 '15 at 19:29
  • Thanks for taking notice. It's a dynamic value but `var percent` gets updated :). I think the offsets are messing it up for me since my container is in a scrollable div. If I don't see the bottom of the page when `.draggable` is dragged, it will not have a bottom boundary. If I see the bottom and not the top of the page when it's dragged then the top boundary appears on half way up the page if that makes any sense.. – bryan Jul 12 '15 at 19:30
  • Maybe that will be helpful [link](http://stackoverflow.com/questions/2522579/how-do-i-get-the-real-height-of-a-overflow-hidden-or-overflow-scroll-div) – SerCrAsH Jul 12 '15 at 19:41
  • I think I'm having trouble still understanding the concept behind it so not sure how helpful this will be for me but thanks for the link. I will try and experiment – bryan Jul 13 '15 at 11:47
0

Here is my solution:

var _zoom = 1.2,
    $element = $('.draggable-element'),
    $container = $('#container');

var containmentW,
    containmentH,
    objW,
    objH;

$element.draggable({

    start: function(evt, ui) {
        ui.position.left = 0;
        ui.position.top = 0;

        containmentW = $container.width() * _zoom;
        containmentH = $container.height() * _zoom;
        objW = $(this).outerWidth() * _zoom;
        objH = $(this).outerHeight() * _zoom;

    },

    drag: function(evt, ui) {

        var boundReached = false,

            changeLeft = ui.position.left - ui.originalPosition.left,
            newLeft = ui.originalPosition.left + changeLeft / _zoom,

            changeTop = ui.position.top - ui.originalPosition.top,
            newTop = ui.originalPosition.top + changeTop / _zoom;


        // right bound check
        if(ui.position.left > containmentW - objW) {
            newLeft = (containmentW - objW) / _zoom;
            boundReached = true;
        }

        // left bound check
        if(newLeft < 0) {
            newLeft = 0;
            boundReached = true;
        }

        // bottom bound check
        if(ui.position.top > containmentH - objH) {
            newTop = (containmentH - objH) / _zoom;
            boundReached = true;
        }

        // top bound check
        if(newTop < 0) {
            newTop = 0;
            boundReached = true;
        }

        // fix position
        ui.position.left = newLeft;
        ui.position.top = newTop;

        // inside bounds
        if(!boundReached) {

            // do stuff when element is dragged inside bounds

        }

    }
});

Link to fiddle

Gab
  • 491
  • 3
  • 6