D3js Déplacement d'éléments SVG dans un groupe de transformation

Je travaille sur un projet de création de requêtes. J'essaie de créer un générateur de requêtes en utilisant d3.js. Je suis coincé dans une partie où je souhaite déplacer certains éléments dans un groupe transformant. C'est le repo et je suis coincé dans cette fonction . Je souhaite déplacer les opérateurs après la connexion et mettre à jour les lignes connectées. Quelqu'un peut-il m'aider?

var circleDrag = d3.behavior.drag() .on('dragstart', function () { d3.event.sourceEvent.stopPropagation(); }) .on('drag', function () { var parentQboxGroupId = d3.select(this).select(function () { return this.parentNode; }); var grandParent = parentQboxGroupId.select(function(){ return this.parentNode; }); var drawingGroup = d3.select('#'+grandParent.attr('id')); var currentC = d3.select(this); dragging = true; drawingGroup .select('.lineInsideQbox') .attr('x1', currentC.attr('cx')) .attr('y1', currentC.attr('cy')) .style('stroke','green') .style('stroke-width','2px'); dummyLine.src = currentC.attr('id'); console.log('CIRCLE IS BEING DRAGGED' + JSON.stringify(dummyLine)); }) .on('dragend', function () { console.log('drag circle end'); //if(!selectedCircle.id){ // dummyLine.target = selectedQbox.id; //} dummyLine.target = selectedCircle.id; dragging = false; console.log('DRAG END : SELCTED NODE : '+ JSON.stringify(selectedCircle)); console.log('DRAG END : DUMMY LINE : '+ JSON.stringify(dummyLine)); var targetNode = d3.select('#'+dummyLine.target); var srcNode = d3.select('#'+dummyLine.src); console.log('split : ' + dummyLine.src.split('--')); var group = '#' + (dummyLine.src).split('--')[1]; console.log('G: ' + group); d3.select(group).append('line') .attr('id', function () { var a = (dummyLine.src).split('--'); var b = (dummyLine.target).split('--'); if( a[0]== 'nodeRight'){ return dummyLine.src + '__' + dummyLine.target; }else{ return dummyLine.target + '__' + dummyLine.src; } }) .attr('class', function () { var a = (dummyLine.src).split('--'); var b = (dummyLine.target).split('--'); return 'line '+ a[1]+' '+b[1]; }) .attr('x1', srcNode.attr('cx')) .attr('y1',srcNode.attr('cy')) .attr('x2',targetNode.attr('cx')) .attr('y2',targetNode.attr('cy')) .style('stroke', 'black') .style('stroke-width', '3px') ; dummyLine.src = null; dummyLine.target = null; }); 

EDIT: lorsque j'essaie de supprimer une boîte de requête. Je peux déposer d'autres opérateurs à l'intérieur. Ensuite, je devrais pouvoir les connecter à l'intérieur. Voici l'image montrant ce que j'essaie.

Entrez la description de l'image ici

Après les connexions effectuées, j'essaie de déplacer la grande boîte et les petits opérateurs individuellement. C'est là que le code se brise.

Le problème principal est que, pour déplacer l'opérateur, vous utilisez une traduction pour déplacer tout le groupe (étiquette) qui comprend l'image, les deux cercles et la ligne. Vous définissez ensuite l'autre extrémité de la ligne à l'aide des valeurs CX, CY de l'autre opérateur auquel elle est connectée. Cela ne fonctionnera pas parce que les valeurs CX et CY des cercles ne sont pas mises à jour lorsque vous effectuez une traduction, de sorte qu'un deuxième mouvement met les valeurs x, y au point original des cercles, et non au point déplacé. Pour résoudre, au lieu de traduire tout le groupe, traduisez uniquement l'image, mettez à jour les valeurs cx et cy des cercles, puis mettez à jour les valeurs de ligne x, y avec le nouveau cx, cy des cercles:

Tous les amendements nécessaires se trouvent dans le fichier de votre opérateurDrag.js. Tout d'abord, lorsque vous ajoutez les cercles, ajoutez un attribut qui détient les valeurs cx et cy initiales. Nous aurons besoin de ces derniers lors du calcul du nouveau cx, lorsque vous traînerez l'opérateur:

Changer de ceci:

  var op = currGroup .append('image') .attr('class', 'operator') .attr('width', elem.attr('width') * 0.75) .attr('height', elem.attr('height') * 0.75) .attr('x', d3.mouse(this)[0]) .attr('y', d3.mouse(this)[1]) .attr('xlink:href', elem.attr('href')); currGroup .append('circle') .attr('class', 'node nodeLeft') .attr('id', function () { return 'nodeLeft--' + currGroup.attr('id'); }) .attr('cx', op.attr('x')) .attr('cy', op.attr('height') / 2 + Number(op.attr('y'))) .attr('r', 5) .style('fill', 'red') .on('mouseover', function () { selectedCircle = { id: d3.select(this).attr('id'), cls: 'nodeLeft' } }) .call(circleDrag) ; currGroup .append('circle') .attr('class', 'node nodeRight') .attr('id', function () { return 'nodeRight--' + currGroup.attr('id'); }) .attr('cx', Number(op.attr('x')) + Number(op.attr('width'))) .attr('cy', op.attr('height') / 2 + Number(op.attr('y'))) .attr('r', 5) .style('fill', 'red') .on('mouseover', function () { selectedCircle = { id: d3.select(this).attr('id'), cls: 'nodeRight' } }) .call(circleDrag) ; 

Pour cela (le code mis à jour est contenu dans les commentaires commençant par #SB):

  var op = currGroup .append('image') .attr('class', 'operator') .attr('width', elem.attr('width') * 0.75) .attr('height', elem.attr('height') * 0.75) .attr('x', d3.mouse(this)[0]) .attr('y', d3.mouse(this)[1]) .attr('xlink:href', elem.attr('href')); currGroup .append('circle') .attr('class', 'node nodeLeft') .attr('id', function () { return 'nodeLeft--' + currGroup.attr('id'); }) .attr('cx', op.attr('x')) .attr('cy', op.attr('height') / 2 + Number(op.attr('y'))) // #SB: add a reference to the original cx and cy position. // we will need it to set new cx cy when moving operator .attr('data-cx', op.attr('x')) .attr('data-cy', op.attr('height') / 2 + Number(op.attr('y'))) //---------------------------------------------------------------------- .attr('r', 5) .style('fill', 'red') .on('mouseover', function () { selectedCircle = { id: d3.select(this).attr('id'), cls: 'nodeLeft' } }) .call(circleDrag) ; currGroup .append('circle') .attr('class', 'node nodeRight') .attr('id', function () { return 'nodeRight--' + currGroup.attr('id'); }) .attr('cx', Number(op.attr('x')) + Number(op.attr('width'))) .attr('cy', op.attr('height') / 2 + Number(op.attr('y'))) // #SB: add a reference to the original cx and cy position. // we will need it to set new cx cy when moving operator .attr('data-cx', Number(op.attr('x')) + Number(op.attr('width'))) .attr('data-cy', op.attr('height') / 2 + Number(op.attr('y'))) //---------------------------------------------------------------------- .attr('r', 5) .style('fill', 'red') .on('mouseover', function () { selectedCircle = { id: d3.select(this).attr('id'), cls: 'nodeRight' } }) .call(circleDrag) ; 

Une fois que vous avez fait cela, accédez à votre méthode de trainer pour les opérateurs. C'est le code que nous allons changer:

  .on('drag', function () { var g = d3.select(this); var currentOp = g.select('.operator'); var parent = g.select(function () { return this.parentNode; }).select('.qbox'); var dx = d3.event.x; var dy = d3.event.y; var mouse = {dx: d3.event.x, dy: d3.event.y}; var currentObj = { x: currentOp.attr('x'), y: currentOp.attr('y'), width: currentOp.attr('width'), height: currentOp.attr('height') }; var parentObj = { x: parent.attr('x'), y: parent.attr('y'), width: parent.attr('width'), height: parent.attr('height') }; //console.log('parent width : ' + parent.attr('width')); //console.log('parent width : ' + currentOp.attr('width')); //g.attr('transform', 'translate(' + x + ',' + y + ')'); var loc = getXY(mouse, currentObj, parentObj); g.attr('transform', 'translate(' + loc.x + ',' + loc.y + ')'); d3.select('#' + g.attr('id')).selectAll('.line')[0].forEach(function (e1) { var line = d3.select(e1); console.log('-------------------'); console.log('line : ' + line.attr('id')); console.log('-------------------'); var split = line.attr('id').split('__'); if(g.attr('id') == split[0]){ //change x2, y2 var otherNode = d3.select('#'+split[1]); line.attr('x2', otherNode.attr('cx')); line.attr('y2', otherNode.attr('cy')); }else{ var otherNode = d3.select('#'+split[0]); line.attr('x1', otherNode.attr('cx')); line.attr('y1', otherNode.attr('cy')); } }) })) 

La première chose est, ne traduisez pas l'objet entier, seulement l'image:

  var g = d3.select(this); var currentOp = g.select('.operator'); var parent = g.select(function () { return this.parentNode; }).select('.qbox'); //#SB: added a reference to the parent id var parent_id = g.select(function () { return this.parentNode; }).attr('id'); //--------------------------------------- var dx = d3.event.x; var dy = d3.event.y; var mouse = {dx: d3.event.x, dy: d3.event.y}; var currentObj = { x: currentOp.attr('x'), y: currentOp.attr('y'), width: currentOp.attr('width'), height: currentOp.attr('height') }; var parentObj = { x: parent.attr('x'), y: parent.attr('y'), width: parent.attr('width'), height: parent.attr('height') }; var loc = getXY(mouse, currentObj, parentObj); //#SB: Do not translate everything, the cx, cy values of the circle are not updated // when translating which will make future moves calculate incorrectly g.selectAll('image').attr('transform', 'translate(' + loc.x + ',' + loc.y + ')'); 

Ensuite, au lieu de traduire les cercles, modifiez leurs valeurs cx et cy en utilisant les valeurs cx, cy et translate d'origine:

  g.selectAll('circle') .attr('cx', function () { return parseFloat(d3.select(this).attr('data-cx')) + parseFloat(loc.x); }) .attr('cy', function () { return parseFloat(d3.select(this).attr('data-cy')) + parseFloat(loc.y); }); 

La dernière chose est la mise à jour des lignes. Dans votre code d'origine, vous choisissez toutes les lignes dans le groupe d'opérateurs, mais vous ne serez que quelques lignes en sélectionnant uniquement ce groupe. Certaines lignes peuvent faire partie d'un autre groupe d'opérateurs mais être connectées à l'opérateur qui se déplace. Dans ce cas, nous devons sélectionner toutes les lignes dans le groupe parent et vérifier si la ligne est connectée à l'opérateur que nous déménageons. Si elle est connectée, nous mettons à jour les valeurs x et y:

 //#SB: Select all the lines in the parent group instead of only group of the // operator we are moving. There can be lines that exists on other groups that // do not exist within the group that is being moved. d3.select('#' + parent_id).selectAll('.line')[0].forEach(function (el) { var parent_id = g.attr('id') var line = d3.select(el) var nodeType = line.attr('id').split("__"); // id tells us if the line is connected to the left or right node var operators = line.attr('class').split(" "); // class holds info on what operators the line is connected to var sourceCircleId = nodeType[0].split("--")[0] + '--' + operators[1]; var targetCircleId = nodeType[1].split("--")[0] + '--' + operators[2]; if (parent_id == operators[1] || parent_id == operators[2]) { // the line is connected to the operator we are moving line.attr('x1', d3.select('#' + sourceCircleId).attr('cx')) line.attr('y1', d3.select('#' + sourceCircleId).attr('cy')) line.attr('x2', d3.select('#' + targetCircleId).attr('cx')) line.attr('y2', d3.select('#' + targetCircleId).attr('cy')) } }); 

Code OnDrag complet:

  .on('drag', function () { var g = d3.select(this); var currentOp = g.select('.operator'); var parent = g.select(function () { return this.parentNode; }).select('.qbox'); //#SB: added a reference to the parent id var parent_id = g.select(function () { return this.parentNode; }).attr('id'); //--------------------------------------- var dx = d3.event.x; var dy = d3.event.y; var mouse = {dx: d3.event.x, dy: d3.event.y}; var currentObj = { x: currentOp.attr('x'), y: currentOp.attr('y'), width: currentOp.attr('width'), height: currentOp.attr('height') }; var parentObj = { x: parent.attr('x'), y: parent.attr('y'), width: parent.attr('width'), height: parent.attr('height') }; var loc = getXY(mouse, currentObj, parentObj); //#SB: Do not translate everything, the cx, cy values of the circle are not updated // when translating which will make future moves calculate incorrectly g.selectAll('image').attr('transform', 'translate(' + loc.x + ',' + loc.y + ')'); g.selectAll('circle') .attr('cx', function () { return parseFloat(d3.select(this).attr('data-cx')) + parseFloat(loc.x); }) .attr('cy', function () { return parseFloat(d3.select(this).attr('data-cy')) + parseFloat(loc.y); }); //#SB: Select all the lines in the parent group instead of only group of the // operator we are moving. There can be lines that exists on other groups that // do not exist within the group that is being moved. d3.select('#' + parent_id).selectAll('.line')[0].forEach(function (el) { var parent_id = g.attr('id') var line = d3.select(el) var nodeType = line.attr('id').split("__"); // id tells us if the line is connected to the left or right node var operators = line.attr('class').split(" "); // class holds info on what operators the line is connected to var sourceCircleId = nodeType[0].split("--")[0] + '--' + operators[1]; var targetCircleId = nodeType[1].split("--")[0] + '--' + operators[2]; if (parent_id == operators[1] || parent_id == operators[2]) { // the line is connected to the operator we are moving line.attr('x1', d3.select('#' + sourceCircleId).attr('cx')) line.attr('y1', d3.select('#' + sourceCircleId).attr('cy')) line.attr('x2', d3.select('#' + targetCircleId).attr('cx')) line.attr('y2', d3.select('#' + targetCircleId).attr('cy')) } }); }))