2016-01-15 19 views
12

Sto facendo un elementi trascinabili con interactjs.ioCome scattare elementi ad altri elementi trascinabili con interact.js

ho bisogno di implementare esattamente lo stesso comportamento che jQuery UI scatto. È possibile vedere un esempio qui in official documentation:

Il comportamento è: scatta a tutti gli altri elementi trascinabili

In interactjs.io, nella documentazione, avete "Aggancio" (l ink documentation) , ma non trovo il modo di codificarlo.

ho creato un violino qui: Fiddle Link

Questo è il mio codice JS:

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); 
    } 

ho bisogno di modificare il codice sezione di scatto, per rendere gli elementi trascinabili scattare con gli altri.

scatto: {}

Grazie !!

+0

sta usando jQuery UI un'opzione? –

+0

Speravo di evitarlo perché mi piaceva lo stile di interact.js, ma sembra che potrebbe essere la migliore soluzione – cwj

+0

@KalimahApps nop in questo momento, ma grazie – chemitaxis

risposta

2

Secondo quello che ho trovato, non penso che sia fattibile fuori dalla scatola. Non c'è più problemi su github con i commenti creatore lungo le linee di:

Penso che scattare più draggables dovrebbe funzionare bene, ma il problema è che l'aggancio è basato sulla posizione del puntatore e non considera la posizione o le dimensioni dell'elemento.

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

La soluzione migliore che posso venire in mente è la creazione di una griglia che corrisponde al rapporto tra gli elementi trascinabili.

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

Il codice seguente può darvi alcune idee per ottenere il risultato desiderato. Funziona con elementi trascinabili di diverse dimensioni. Function targets vengono utilizzati per impostare i punti e le linee di destinazione.

È possibile testarlo in this jsfiddle.

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

I miei elementi hanno dimensioni diverse e potrebbero anche essere in grado di ridimensionare (non su una griglia). Come sarebbe questo codice per questo? – cwj

+0

Ho modificato il codice e il jsfiddle per elaborare elementi di dimensioni diverse. Poiché i punti e le linee di destinazione sono calcolati in modo dinamico, lo stesso metodo dovrebbe funzionare durante l'elaborazione degli eventi di ridimensionamento. – ConnorsFan

+1

Sono curioso di vedere se qualcun altro proverà a rispondere veramente alla domanda: (1) usando interact.js, non jQuery (2) agganciando gli elementi, non a una griglia. – ConnorsFan

2

Ho creato un JSFiddle senza utilizzare interact.js. Ho usato solo jQuery. Non ho usato interactjs.io come hai detto che preferisci solo ma non lo richiedono.

Il codice funziona con elementi di dimensioni diverse.

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>