2016-01-15 15 views
12

Ich bin ein ziehbar Elemente machen mit interactjs.ioWie Elemente auf andere ziehbar Elemente schnappen mit interact.js

Ich brauche zu implementieren genau das gleiche Verhalten, dass jQuery UI-Snap. Sie können ein Beispiel hier im official documentation sehen:

Das Verhalten ist: schnappt zu allen anderen ziehbar Elemente

In interactjs.io, in der Dokumentation, Sie haben "Snapping" (l ink documentation) , aber ich finde den Weg nicht, es zu kodieren.

ich eine Geige hier geschaffen haben: Fiddle Link

Das ist mein JS-Code:

interact('.draggable') 
    .draggable({ 
    onmove: dragMoveListener, 
    snap: {}, 
    }); 


    function dragMoveListener (event) { 
    var target = event.target, 
     // keep the dragged position in the data-x/data-y attributes 
     x = (parseFloat(target.getAttribute('data-x')) || 0) + event.dx, 
     y = (parseFloat(target.getAttribute('data-y')) || 0) + event.dy; 

    // translate the element 
    target.style.webkitTransform = 
    target.style.transform = 
     'translate(' + x + 'px, ' + y + 'px)'; 

    // update the position attributes 
    target.setAttribute('data-x', x); 
    target.setAttribute('data-y', y); 
    } 

ich ändern müssen den Abschnitt Code Snap, die ziehbar Elemente zu machen, mit anderen schnappen.

Snap: {}

Dank !!

+0

Ist mit jQuery UI eine Option? –

+0

Ich hatte gehofft, es zu vermeiden, weil ich interactive.js Stil mochte, aber es scheint, als wäre es die beste Lösung – cwj

+0

@KalimahApps nop in diesem Moment, aber danke – chemitaxis

Antwort

2

Nach was ich gefunden habe, glaube ich nicht, dass dies out of the box machbar ist. Es gibt mehrere Probleme auf Github mit Kommentaren von Schöpfer entlang der Linien von:

Ich denke, dass mehrere draggables schnappen sollte funktionieren gut, aber die Problem ist, dass das Knacken auf der Zeigerposition basiert und es nicht Bezug die Position oder Abmessungen des Elements.

https://github.com/taye/interact.js/issues/59#issuecomment-50981271

Die beste Lösung, die ich mit ist die Schaffung eines Gitters kann kommen, die das Verhältnis Ihrer ziehbar Elemente übereinstimmt.

css:

* { box-sizing: border-box; } 

.draggable { 
    position: absolute; 
    width: 90px; 
    height: 90px; 
    padding: 5px; 
    float: left; 
    margin: 0 10px 10px 0; 
    font-size: .9em; 
    overflow:hidden; 
} 

js:

targets: [ 
    interact.createSnapGrid({ x: 30, y: 30 }) 
], 

https://jsfiddle.net/taea58g2/

3

Der folgende Code kann Ihnen einige Ideen um das Ergebnis zu erhalten, die Sie möchten. Es funktioniert mit ziehbaren Elementen unterschiedlicher Größe. Function targets werden verwendet, um die Zielpunkte und Linien festzulegen.

Sie können es in this jsfiddle testen.

var AXIS_RANGE = 12; 
var CORNER_RANGE = 14; 
var CORNER_EXCLUDE_AXIS = 8; 
var AXIS_EXTRA_RANGE = -6; 

var myItems = []; 
var currentElement = null; 
var offX1, offY1, offX2, offY2; 

function getPosition(element) { 
    return { 
    x: parseFloat(element.getAttribute('data-x')) || 0, 
    y: parseFloat(element.getAttribute('data-y')) || 0 
    }; 
} 

function isBetween(value, min, length) { 
    return min - AXIS_EXTRA_RANGE < value && value < (min + length) + AXIS_EXTRA_RANGE; 
} 

function getDistance(value1, value2) { 
    return Math.abs(value1 - value2); 
} 

function getSnapCoords(element, axis) { 
    var result = { 
    isOK: false 
    }; 
    if (currentElement && currentElement !== element) { 
    var pos = getPosition(element); 
    var cur = getPosition(currentElement); 
    var distX1a = getDistance(pos.x, cur.x); 
    var distX1b = getDistance(pos.x, cur.x + currentElement.offsetWidth); 
    var distX2a = getDistance(pos.x + element.offsetWidth, cur.x); 
    var distX2b = getDistance(pos.x + element.offsetWidth, cur.x + currentElement.offsetWidth); 
    var distY1a = getDistance(pos.y, cur.y); 
    var distY1b = getDistance(pos.y, cur.y + currentElement.offsetHeight); 
    var distY2a = getDistance(pos.y + element.offsetHeight, cur.y); 
    var distY2b = getDistance(pos.y + element.offsetHeight, cur.y + currentElement.offsetHeight); 
    var distXa = Math.min(distX1a, distX2a); 
    var distXb = Math.min(distX1b, distX2b); 
    var distYa = Math.min(distY1a, distY2a); 
    var distYb = Math.min(distY1b, distY2b); 
    if (distXa < distXb) { 
     result.offX = offX1; 
    } else { 
     result.offX = offX2 
    } 
    if (distYa < distYb) { 
     result.offY = offY1; 
    } else { 
     result.offY = offY2 
    } 
    var distX1 = Math.min(distX1a, distX1b); 
    var distX2 = Math.min(distX2a, distX2b); 
    var distY1 = Math.min(distY1a, distY1b); 
    var distY2 = Math.min(distY2a, distY2b); 
    var distX = Math.min(distX1, distX2); 
    var distY = Math.min(distY1, distY2); 
    var dist = Math.max(distX, distY); 
    var acceptAxis = dist > CORNER_EXCLUDE_AXIS; 

    result.x = distX1 < distX2 ? pos.x : pos.x + element.offsetWidth; 
    result.y = distY1 < distY2 ? pos.y : pos.y + element.offsetHeight; 

    var inRangeX1 = isBetween(pos.x, cur.x, currentElement.offsetWidth); 
    var inRangeX2 = isBetween(cur.x, pos.x, element.offsetWidth); 
    var inRangeY1 = isBetween(pos.y, cur.y, currentElement.offsetHeight); 
    var inRangeY2 = isBetween(cur.y, pos.y, element.offsetHeight); 

    switch (axis) { 
     case "x": 
     result.isOK = acceptAxis && (inRangeY1 || inRangeY2); 
     break; 
     case "y": 
     result.isOK = acceptAxis && (inRangeX1 || inRangeX2); 
     break; 
     default: 
     result.isOK = true; 
     break; 
    } 
    } 
    return result; 
} 

$('.draggable').each(function() { 
    var pos = getPosition(this); 
    this.style.transform = 'translate(' + pos.x + 'px, ' + pos.y + 'px)'; 
    myItems.push(getPosition(this)); 
}); 

interact('.draggable').draggable({ 
    onstart: function(event) { 
    currentElement = event.target; 
    var pos = getPosition(currentElement); 
    offX1 = event.clientX - pos.x; 
    offY1 = event.clientY - pos.y; 
    offX2 = event.clientX - currentElement.offsetWidth - pos.x; 
    offY2 = event.clientY - currentElement.offsetHeight - pos.y; 
    }, 
    onmove: dragMoveListener, 
    snap: { 
    targets: 
     (function() { 
     var snapPoints = []; 
     $('.draggable').each(function() { 
      (function(element) { 
      // Slide along the X axis 
      snapPoints.push(
       function(x, y) { 
       var data = getSnapCoords(element, "x"); 
       if (data.isOK) { 
        return { 
        x: data.x + data.offX, 
        range: AXIS_RANGE 
        }; 
       } 
       }); 
      // Slide along the Y axis 
      snapPoints.push(
       function(x, y) { 
       var data = getSnapCoords(element, "y"); 
       if (data.isOK) { 
        return { 
        y: data.y + data.offY, 
        range: AXIS_RANGE 
        }; 
       } 
       }); 
      // Snap to corner 
      snapPoints.push(
       function(x, y) { 
       var data = getSnapCoords(element); 
       if (data.isOK) { 
        return { 
        x: data.x + data.offX, 
        y: data.y + data.offY, 
        range: CORNER_RANGE 
        }; 
       } 
       }); 
      })(this); 
     }); 
     return snapPoints; 
     })() 
    }, 
    onend: function(event) { 
    $('.draggable').each(function() { 
     currentElement = null; 
     myItems.push(getPosition(this)); 
    }); 
    } 
}); 

function dragMoveListener(event) { 
    var target = event.target; 
    var oldPos = getPosition(target); 
    var x = oldPos.x + event.dx; 
    var y = oldPos.y + event.dy; 

    // keep the dragged position in the data-x/data-y attributes 
    target.setAttribute('data-x', x); 
    target.setAttribute('data-y', y); 

    // translate the element 
    target.style.webkitTransform = 
    target.style.transform = 
    'translate(' + x + 'px, ' + y + 'px)'; 

    $('#position').text('x: ' + x + ' - y: ' + y); 

    var result = $.grep(myItems, function(e) { 
    if (e.x == parseInt(target.getAttribute('data-x')) || e.y == parseInt(target.getAttribute('data-y'))) 
     return 1; 
    }); 

    if (result.length >= 1) 
    target.style.backgroundColor = '#CCC'; 
    else 
    target.style.backgroundColor = '#FFF'; 
} 
+0

Meine Elemente haben unterschiedliche Größen und könnte auch in der Größe ändern (nicht auf einem Gitter). Wie würde dieser Code dafür verantwortlich sein? – cwj

+0

Ich habe den Code und die jsfiddle geändert, um Elemente mit verschiedenen Größen zu verarbeiten. Da die Zielpunkte und Linien dynamisch berechnet werden, sollte die gleiche Methode bei der Verarbeitung von Größenänderungsereignissen funktionieren. – ConnorsFan

+1

Ich bin gespannt, ob jemand anderes versuchen wird, die Frage wirklich zu beantworten: (1) mit interact.js, nicht jQuery (2) Fangen an den Elementen, nicht an einem Gitter. – ConnorsFan

2

Ich machte eine JSFiddle ohne interact.js zu verwenden. Ich habe nur jQuery benutzt. Ich habe interactjs.io nicht verwendet, da Sie angedeutet haben, dass Sie es nur bevorzugen, aber nicht benötigen.

Der Code funktioniert mit Elementen unterschiedlicher Größe.

jQuery.fn.reverse = [].reverse; 
 

 
/* Handle add button clicks*/ 
 
$(".add-draggable").click(function() { 
 
    var newDraggable = jQuery("<div class='draggable'></div>"); 
 
    newDraggable.css({ 
 
    position: 'absolute', 
 
    left: 150, 
 
    top: 150 
 
    }) 
 

 
    newDraggable.attr({ 
 
    'data-x': 150, 
 
    'data-y': 150 
 
    }).addClass("large"); 
 

 
    jQuery(".draggable-wapper").append(newDraggable) 
 
}); 
 

 
// initiate blocks 
 
// This is done in revers as when the element is absolutly positioned .poisition() will retrun differnt values for the next element (mostly x will be 0 for all elements) 
 
$(".draggable").reverse().each(function(i, e) { 
 
    _this = jQuery(this); 
 
    position = _this.position(); 
 

 
    _this.css({ 
 
    position: 'absolute', 
 
    left: position.left, 
 
    top: position.top 
 
    }).attr("data-y", position.top).attr("data-x", position.left); 
 
}); 
 

 
// Set some variabkles 
 

 
// Used to differentiate clicks on elements from dragging 
 
var isDragging = false; 
 

 
// Store coordiators of all elements 
 
var coord; 
 

 
// The moving element 
 
var element = null; 
 

 
// The offset to which the moving element snaps to the target element 
 
// in percentage 
 
var snappingYOffset = 20; 
 
var snappingXOffset = 20; 
 

 

 
$(".draggable-wapper").on("mousedown", ".draggable", function() { 
 

 
    _this = element = jQuery(this); 
 
    coord = []; 
 
    isDragging = true; 
 

 
    // Update coord 
 
    jQuery(".draggable").each(function(i, e) { 
 
    if (i == element.index()) 
 
     return true; 
 

 
    ele = jQuery(e); 
 
    var position = ele.position(); 
 
    var elementData = getData(ele); 
 

 
    coord[i] = { 
 
     leftX: position.left, 
 
     rightX: position.left + ele.outerWidth(), 
 
     topY: position.top, 
 
     bottomY: position.top + ele.outerHeight() 
 
    } 
 

 
    jQuery.extend(coord[i], elementData); 
 
    }); 
 

 
    _this.removeData("last-position"); 
 
}); 
 

 
jQuery(document).on("mousemove", function(e) { 
 
    if (!isDragging) 
 
     return; 
 

 

 
    var lastPosition = _this.data("last-position"); 
 
    element.addClass("moving"); 
 

 
    if (typeof lastPosition != 'undefined') { 
 
     // get difference to detemine new position 
 
     var xDelta = e.clientX - lastPosition.x; 
 
     var yDelta = e.clientY - lastPosition.y; 
 

 
     var elementX = parseInt(element.attr("data-x")); 
 
     var elementY = parseInt(element.attr("data-y")); 
 

 
     element.attr({ 
 
     "data-x": elementX + xDelta, 
 
     "data-y": elementY + yDelta 
 
     }).css({ 
 
     "left": elementX + xDelta, 
 
     "top": elementY + yDelta 
 
     }); 
 

 
     // find which element is closer to moving elements and within offset limits 
 
     filterArray(coord, _this); 
 
    } 
 

 
    // Save values for next itertation 
 
    var position = { 
 
     x: e.clientX, 
 
     y: e.clientY 
 
    }; 
 

 
    element.data("last-position", position); 
 
    }) 
 
    .on("mouseup", function() { 
 
    if (isDragging) { 
 
     isDragging = false; 
 
     element.removeClass("moving"); 
 
    } 
 
    }); 
 

 
function filterArray(array, element) { 
 
    // Set coord for moving element 
 
    // x1: left, x2: right, y1: top, y2: bottom 
 

 
    var elementX1 = parseInt(element.attr("data-x")); 
 
    var elementX2 = elementX1 + element.outerWidth(); 
 

 
    var elementY1 = parseInt(element.attr("data-y")); 
 
    var elementY2 = elementY1 + element.outerHeight(); 
 

 
    // Show value inside element 
 
    element.html('y:' + elementY1 + '<br> x: ' + elementX1); 
 

 
    var result = {}; 
 

 
    // Loop through other elements and match the closeset 
 
    array.forEach(function(value, index, originalArray) { 
 
    // Get coordinators of each element 
 
    // x1: left, x2: right, y1: top, y2: bottom 
 
    var x1 = value['leftX']; 
 
    var x2 = value['rightX']; 
 

 
    var y1 = value['topY']; 
 
    var y2 = value['bottomY']; 
 

 
    // Get which element is bigger; the moving or the target element 
 
    var biggerDim = bigger(element, { 
 
     height: value['height'], 
 
     width: value['width'] 
 
    }); 
 

 
    // Show values inside element 
 
    jQuery(".draggable").eq(index).html('y:' + y1 + '<br> x: ' + x1); 
 

 
    // Get offset for partiuclar element 
 
    var xOffset = value['xOffset']; 
 
    var yOffset = value['yOffset']; 
 

 
    // yRange checks if moving element is moving within the Y range of target element 
 
    // This requried to snap if true 
 
    var yRange = (biggerDim.height == 'moving') ? y1 >= (elementY1 - yOffset) && y2 <= (elementY2 + yOffset) : elementY1 > (y1 - yOffset) && elementY2 < (y2 + yOffset); 
 

 
    // xRange checks if moving element is moving within the X range of target element 
 
    // This requried to snap if true 
 
    var xRange = (biggerDim.width == 'moving') ? x1 > (elementX1 - xOffset) && x2 < (elementX2 + xOffset) : elementX1 > (x1 - xOffset) && elementX2 < (x2 + xOffset); 
 

 
    // Is source element (moving) within the Y range 
 
    if (yRange) { 
 

 
     // Is source element within right range of target 
 
     if (elementX1 >= (x2 - xOffset) && elementX1 <= (x2 + xOffset)) { 
 
     // Left side of the moving element 
 
     element.css({ 
 
      "left": x2 
 
     }); 
 
     // Is source element within left range of target 
 
     } else if (elementX2 >= (x1 - xOffset) && elementX2 <= (x1 + xOffset)) { 
 
     // right side of the moving element 
 
     element.css({ 
 
      "left": x1 - element.outerWidth() 
 
     }); 
 
     } 
 
    } 
 

 
    // Is source element (moving) within the X range of target 
 
    if (xRange) { 
 
     if (elementY1 >= (y2 - yOffset) && elementY1 <= (y2 + yOffset)) { 
 
     // Top side of the moving element 
 
     element.css({ 
 
      "top": y2 
 
     }); 
 
     } else if (elementY2 >= (y1 - yOffset) && elementY2 <= (y1 + yOffset)) { 
 
     // bottom side of the moving element 
 
     element.css({ 
 
      "top": y1 - element.outerHeight() 
 
     }); 
 
     } 
 
    } 
 
    }); 
 
} 
 

 
/* Find which element is bigger */ 
 
function bigger(moving, target) { 
 
    var width1 = moving.outerWidth(); 
 
    var height1 = moving.outerHeight(); 
 

 
    var width2 = target.width; 
 
    var height2 = target.height; 
 

 
    var result = { 
 
    width: 'target', 
 
    height: 'target' 
 
    }; 
 

 
    if (width1 > width2) 
 
    result.width = 'moving'; 
 

 
    if (height1 > height2) 
 
    result.height = 'moving'; 
 

 
    return result; 
 
} 
 

 
/* Get data releted to a certain element */ 
 
function getData(ele) { 
 
    var height = ele.outerHeight(); 
 
    var width = ele.outerWidth(); 
 

 
    var xOffset = (width * snappingXOffset)/100; 
 
    var yOffset = (height * snappingYOffset)/100; 
 

 
    return { 
 
    height: height, 
 
    width: width, 
 
    xOffset: xOffset, 
 
    yOffset: yOffset 
 
    } 
 
}
.draggable { 
 
    background-color: green; 
 
    border: 1px solid white; 
 
    box-sizing: border-box; 
 
    cursor: move; 
 
    float: left; 
 
    padding: 5px; 
 
    position: relative; 
 
    color: white; 
 
    font-family: "calibri", -webkit-touch-callout: none; 
 
    /* iOS Safari */ 
 
    -webkit-user-select: none; 
 
    /* Chrome/Safari/Opera */ 
 
    -khtml-user-select: none; 
 
    /* Konqueror */ 
 
    -moz-user-select: none; 
 
    /* Firefox */ 
 
    -ms-user-select: none; 
 
    /* Internet Explorer/Edge */ 
 
    user-select: none; 
 
    /* Non-prefixed version, currently 
 
            not supported by any browser */ 
 
} 
 
.draggable.large { 
 
    height: 300px; 
 
    width: 100px; 
 
    font-size: 16px; 
 
} 
 
.draggable.small { 
 
    height: 50px; 
 
    width: 50px; 
 
    font-size: 12px; 
 
} 
 
.draggable.medium { 
 
    height: 100px; 
 
    width: 80px; 
 
    font-size: 12px; 
 
} 
 
.draggable-wapper { 
 
    float: left; 
 
    position: relative; 
 
} 
 
.moving { 
 
    z-index: 2; 
 
    background-color: purple; 
 
} 
 
.add-draggable { 
 
    background-color: green; 
 
    border: 1px solid #0a5e1d; 
 
    border-radius: 5px; 
 
    color: white; 
 
    cursor: pointer; 
 
    font-size: 19px; 
 
    padding: 10px 20px; 
 
    position: absolute; 
 
    right: 15px; 
 
    top: 15px; 
 
    transition: all 0.5s ease 0s; 
 
    font-family: "calibri", 
 
} 
 
.add-draggable:hover { 
 
    opacity: 0.9; 
 
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script> 
 
<div class='draggable-wapper'> 
 
    <div class='draggable small'></div> 
 
    <div class='draggable large'></div> 
 
    <div class='draggable large'></div> 
 
    <div class='draggable large'></div> 
 
    <div class='draggable small'></div> 
 
    <div class='draggable medium'></div> 
 
    <div class='draggable medium'></div> 
 
</div> 
 

 
<div class='add-draggable'> 
 
    Add 
 
</div>