block by micahstubbs f4dcb0f587e54ea80a67b3efa773b995

Multiple Shape Drag Canvas with Force Simulation

Full Screen

a Canvas example that shows how to drag multiple shapes inside of another shape. notice that the red network-node circles can be dragged independently. notice that the black rectangle can also be dragged, keeping it’s red-circle children in the same relative positions.

you could also think about this problem as “how to create a hierarchy of draggable shapes?”. the short answer is: do it in the dragSubject() function d3-drag docs on drag subjects

a fork of Multiple Shape Drag Canvas

inspiration from blocks graph neighbors on mouseover and Force-Directed Graph

an iteration on this very helpful stackoverflow answer

this collection of d3-drag experiments also exist in github repo form at micahstubbs/d3-drag-experiments

index.html

<!DOCTYPE html>
<html>
  <head>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.10.2/d3.min.js"></script>
    <meta charset="utf-8">
    <title>d3-drag-experiments</title>
  </head>
  <body>
    <script src="vis.js"></script>
  </body>
</html>

preview.xcf

gimp xcf file��B�B�gimp-image-grid(style solid)
(fgcolor (color-rgba 0.000000 0.000000 0.000000 1.000000))
(bgcolor (color-rgba 1.000000 1.000000 1.000000 1.000000))
(xspacing 10.000000)
(yspacing 10.000000)
(spacing-unit inches)
(xoffset 0.000000)
(yoffset 0.000000)
(offset-unit inches)
���)Screen Shot 2017-09-10 at 9.36.34 PM.png�	

\���B�B�B�B������	v	�	�	�	�	�	�	�	�


&
6
:
J
Z^n~��������"2BFVfv������.���� !f%�&'�'�'�'�'�(	(()(9(I(Y(i(y-$2U6S6c6s6�6�6�6�6�6�6�6�777#;F<1@@ @0@@@P@`@p@�@�@�@�@�@�@�@�AAA A0A@APA`ApA�A�A�A�A�A�A�A�BBB B0B@BPB`Bp��2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2��2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2��2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2���@��@��@���@��@��@��� � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � ��� � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � ��� � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � ����������������������������������������������@�@�@�@��2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2��� � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � ����������������������������������������������@�@�@�@��2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2��� � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � ��������������
d�����<�����<����=�����<�����<�����<�����<�����=�����<�����<�����<�����<�����=�����<�����<�����<����<������<�����<�����<�����<����=����������xCDz��4���)�0��1���
���/������-������+���'�1��*������*������*��J�R�*���*�+��=�E�*��r�z�*������*������+������-������/���
���1���1�7��4��ӓQB~��;�����<����=�����<�����<�����<�����<�����=�����<�����<�����<�����<�����=�����<�����<�����<����<������<�����<�����<�����<����=����������xCDz��4���)�0��1���
���/������-������+���&�1��*������*������*��J�R�*�+�+��=�E�*��q�z�*������*������+������-������/���
���1���1�7��4��ӓQB~��;�����<����=�����<�����<�����<�����<�����=�����<�����<�����<�����<�����=�����<�����<�����<����<������<�����<�����<�����<����=�����������������������������������@�@�@�@��2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2���2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2���2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2�2���@��@��@���@��@��@��� � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � ��� � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � ��� � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � ��������������=����=������	���ʗ���7���'�!��2���?2���
1���
1��h0���0���0��{0��80�0��/0��e0���0���1���2��E
2���	3���r5������6��޺�������=������	���ʗ���7���&�!��2���?2���
1���
1��h0���0���0��{0��80�0��.0��e0���0���1���2��E
2���	3���q5������6��޺�������=������������=����=�����<����=�����;�
@��5��<����;����:��W�:����9����9��l�9��)�9�:���9��T�9����9����9����:��2�;����;��d��|����=����=�����<����=�����;�
@��5��<����;����:��W�:����9����9��l�9��)�9�:���9��S�9����9����9����:��2�;����;��d��|����=����=�����<����=�����;��(�����<�����<�����<����=�����<�����<�����<�����<�����=�����<�����<�����<�����<�����=�����<�����<�����<����<�����=�����<�����<�����<����<������<�����<�����<�����<����=�����<�����<�����<�����<�����=�����<�����<��=�����<�����=�����<�����<�����<����<�����=�����<�����<�����<����<������<�����<�����<�����<����=�����<�����<�����<�����<����=�����<�����<�����<�����<�����=�����<������(�����<�����<�����<����=�����<�����<�����<�����<�����=�����<�����<�����<�����<�����=�����<�����<�����<����<�����=�����<�����<�����<����<������<�����<�����<�����<����=�����<�����<�����<�����<�����=�����<�����<��=�����<�����=�����<�����<�����<����<�����=�����<�����<�����<����<������<�����<�����<�����<����=�����<�����<�����<�����<����=�����<�����<�����<�����<�����=�����<������(�����<�����<�����<����=�����<�����<�����<�����<�����=�����<�����<�����<�����<�����=�����<�����<�����<����<�����=�����<�����<�����<����<������<�����<�����<�����<����=�����<�����<�����<�����<�����=�����<�����<��=�����<�����=�����<�����<�����<����<�����=�����<�����<�����<����<������<�����<�����<�����<����=�����<�����<�����<�����<����=�����<�����<�����<�����<�����=�����<���������������ˊU0g���3���V����0���
�'��.������,����d�,��\���*����E�*���+������)��d���)��i���)������)�����*����P�+��m���+���
���-����2��.���	�^��0���w���4���n(>���d����ˉU0g���3���V����0���
�&��.���
���,����d�,��\���*����E�*���+������)��d���)��i���)������)�����*����P�+��m���+���
���-����2��.���	�^��0���w���4���n(>���d����������������������@�@�@�@������������������������������=�����<�����=�����<�����<������<�����<������<�����<������<�����<�����<�����<����=�����<����=�����<����=�����<�����=�����<�����=�����<�����<������<�����<������<�����<������<�����<�����<�����<����=�����<����=�����<����=�����<�����=�����<�����=�����<�����<������<�����<������<�����<������<�����<�����<�����<����=�����<����=�����<����=�����<����=�����<�����=�����<�����=�����<�����������=�����<�����=�����<�����<������<�����<������<�����<������<�����<�����<�����<����=�����<����=�����<����=�����<�����=�����<�����=�����<�����<������<�����<������<�����<������<�����<�����<�����<����=�����<����=�����<����=�����<�����=�����<�����=�����<�����<������<�����<������<�����<������<�����<�����<�����<����=�����<����=�����<����=�����<����=�����<�����=�����<�����=�����<�����������=�����<�����=�����<�����<������<�����<������<�����<������<�����<�����<�����<����=�����<����=�����<����=�����<�����=�����<�����=�����<�����<������<�����<������<�����<������<�����<�����<�����<����=�����<����=�����<����=�����<�����=�����<�����=�����<�����<������<�����<������<�����<������<�����<�����<�����<����=�����<����=�����<����=�����<����=�����<�����=�����<�����=�����<�������5�����<�����<����������<�����	����/��Ĭ��	�����-��ˬ��
����,��Ѫ������+��۪��
����*�����:������:������;�����;�����;�����;�����;������:��ɫ��:��Ϫ��:��ת��:��ު��:�����:�����:������;�����;�����;�����;������:��ƭ��:��ͬ��:��Ԫ��:��ܪ��:�����:�����:������;�����;�����;�����;�����;��­��:��Ȭ��:��Ϊ��:��ת��:��ߪ��:�����:�����:������;�����;�����3�5�����<��������8���C�X��3����M��0��^0���/���
.���'
.���.���.��.��\.��x.���.���/�/���
0��>0���0����e	���.��Ĭ����<���-��ˬ����͚hDx�����,��Ѫ������+��۪��
����*�����:������:������;�����;�����;�����;�����;������:��ɫ��:��Ϫ��:��ת��:��ު��:�����:�����:������;�����;�����;�����;������:��ƭ��:��ͬ��:��Ԫ��:��ܪ��:�����:�����:������;�����;�����;�����;�����;��­��:��Ȭ��:��Ϊ��:��ת��:��ߪ��:�����:�����:������;�����;�����3�5�����<��������8���C�X��3����M��0��^0���/���
.���&
.���.���.��.��\.��x.���.���/�/���
0��>0���0����e	���.��Ĭ����<���-��ˬ����͚hDx�����,��Ѫ������+��۪��
����*�����:������:������;�����;�����;�����;�����;������:��ɫ��:��Ϫ��:��ת��:��ު��:�����:�����:������;�����;�����;�����;������:��ƭ��:��ͬ��:��Ԫ��:��ܪ��:�����:�����:������;�����;�����;�����;�����;��­��:��Ȭ��:��Ϊ��:��ת��:��ߪ��:�����:�����:������;�����;�����3��@����=����=�����<��߾�=�����<�����<�����=�����<����=�����<������<�����=�����<�����<�����<������<�����=�����<������<�����<������<����=�����<�����<�����=�����<����=�����<������<�����=�����<�����<�����<������<��޿�=�����<�����<�����=�����<����=�����<�����<�����$�����=����<��1��;����;���;����:����:����:����:����:��"�;��\�;����;����<��j������=����=�����<��߾�=�����<�����<�����=�����<����=�����<������<�����=�����<�����<�����<������<�����=�����<������<�����<������<����=�����<�����<�����=�����<����=�����<������<�����=�����<�����<�����<������<��޿�=�����<�����<�����=�����<����=�����<�����<�����$�����=����<��1��;����;���;����:����:����:����:����:��"�;��\�;����;����<��j������=����=�����<��߾�=�����<�����<�����=�����<����=�����<������<�����=�����<�����<�����<������<�����=�����<������<�����<������<����=�����<�����<�����=�����<����=�����<������<�����=�����<�����<�����<������<��޿�=�����<�����<�����=�����<����=�����<�����<�����$��������������������������@�@�@�@�������������������������������<�����<������<�����<�����<�����<����=�����<����=�����<��������#����������#���������%�������Ĭ��%�������ɫ��'�������Ѫ��(�������٪��*����������+����������,������	������.�����	�����/����	�����;�����
I�������<�����<������<�����<�����<�����<����=�����<����=�����<��������#����������#���������%�������Ĭ��%�������ɫ��'�������Ѫ��(�������٪��*����������+����������,�����������������.���xJ�T�������.���2�Z��.������.���
���,���
�J�,��t���*����(�*������)������)��x���)������)������)���,���E�+������,��W
���-���)�R�/���t	���2����1��5��ўlBt���
�������<�����<������<�����<�����<�����<����=�����<����=�����<��������#����������#���������%�������Ĭ��%�������ɫ��'�������Ѫ��(�������٪��*����������+����������,�����������������.���xJ�S�������.���2�Z��.������.���
���,���
�J�,��t���*����(�*������)������)��x���)������)������)���,���E�+������,��W
���-���)�R�/���t	���2����1��5��ўlBt���
�������;������:��Ǭ��:��̪��:��Ԫ��:��ܪ��:�����:�����:������;�����<����
~������;������:��Ǭ��:��̪��:��Ԫ��:��ܪ��:�����:�����:������;�����<����
~������;������:��Ǭ��:��̪��:��Ԫ��:��ܪ��:�����:�����:������;�����<����
~�������<�����<�����<������<�����=�����<������<�����<������<����=�����<�����<�����=�����<����=�����<������<�����=�����<�����<��=������<��޾�=�����<�����<�����=�����<����=�����<�����<����S������<�����<�����<������<�����=�����<������<�����<������<����=�����<�����<�����=�����<����=�����<������<�����=�����<�����<��=������<��޾�=�����<�����<�����=�����<����=�����鴁JY����3���R���2���	�5��/������-���
�)��,��7���+���,������*��D���)����M�)����%�*��[�*��D���*��x���*���� ��+������-��:���.���>
�r�1������3����d(x���8������������<�����<�����<������<�����=�����<������<�����<������<����=�����<�����<�����=�����<����=�����<������<�����=�����<�����<��=������<��޾�=�����<�����<�����=�����<����=�����鴁JY����3���R���2���	�5��/������-���
�)��,��7���+���,������*��D���)����M�)����%�*��[�*��D���*��x���*������+������-��9���.���>
�q�1������3����d(x���8��������������������������������@�@�@�@�������������������������������������������������������������@�@�@�@�
@�
@�
@�
@�
@�
@�
@�
@�
@�
@�
@�
@�
@�
@�
@�
@�
@�
@�
@�
@�
@�
@�
@�
@�
@�
@�
@�
@�
@�
@�
@�
@�
@�
@�
@�
@�
@�
@�
@�
@�
@�
@�
@�
@�
@�
@�
@�
@�
@�
@�
@�
@�
@�
@�
@�
@�
@�
@�
@�
@�	�	�	�	����}x><

vis.js

/* prettier-ignore */
d3.select('body')
  .append('canvas')
  .attr('width', 960)
  .attr('height', 500);

const canvas = d3.select('canvas');
const context = canvas.node().getContext('2d');
const width = canvas.property('width');
const height = canvas.property('height');

const circles = [{ x: 50, y: 60, radius: 10 }, { x: 100, y: 80, radius: 10 }];
const rects = [{ x: 10, y: 10, x2: 210, y2: 210 }];
const radius = 10;

//
// setup force simulation
//

const graph = {
  nodes: [
    { id: 0, size: 10 },
    { id: 1, size: 5 },
    { id: 2, size: 2 },
    { id: 3, size: 3 },
    { id: 4, size: 30 },
    { id: 5, size: 40 }
  ],
  links: [
    { source: 0, target: 1 },
    { source: 0, target: 2 },
    { source: 1, target: 0 },
    { source: 3, target: 0 },
    { source: 4, target: 1 }
  ]
};

const simulation = d3
  .forceSimulation()
  .force('link', d3.forceLink().id(d => d.id))
  .force('charge', d3.forceManyBody())
  .force('center', d3.forceCenter(width / 2, height / 2));

simulation.nodes(graph.nodes).on('tick', ticked);

function ticked() {
  render();
}

//
//
//

render();

canvas.call(
  d3
    .drag()
    .subject(dragSubject)
    .on('start', dragStarted)
    .on('drag', dragged)
    .on('end', dragEnded)
    .on('start.render drag.render end.render', render)
);

function render() {
  context.clearRect(0, 0, width, height);
  // draw the black rectangle
  rects.forEach(rect => {
    context.fillStyle = 'black';
    context.clearRect(0, 0, width, height);
    context.fillRect(rect.x, rect.y, rect.x2, rect.y2);

    // draw a line for each link
    context.strokeStyle = '#aaa';
    context.lineWidth = 1;
    context.beginPath();
    graph.links.forEach(link => {
      context.moveTo(
        graph.nodes[link.source].x + rect.x,
        graph.nodes[link.source].y + rect.y
      );
      context.lineTo(
        graph.nodes[link.target].x + rect.x,
        graph.nodes[link.target].y + rect.y
      );
    });
    context.stroke();

    // draw a circle for each node
    context.beginPath();
    graph.nodes.forEach(node => {
      context.moveTo(node.x + rect.x + radius, node.y + rect.y);
      context.arc(node.x + rect.x, node.y + rect.y, radius, 0, 2 * Math.PI);
    });
    context.fillStyle = 'red';
    context.fill();
  });
}

function dragSubject() {
  let i;
  const n = graph.nodes.length;
  let dx;
  let dy;
  let d2;
  let s2 = radius * radius * 4;
  let node;
  let subject;

  for (i = 0; i < n; i += 1) {
    node = graph.nodes[i];
    console.log('node from dragSubject', node);
    dx = d3.event.x - node.x - rects[0].x;
    dy = d3.event.y - node.y - rects[0].y;
    d2 = dx * dx + dy * dy;

    console.log('dx', dx);
    console.log('dy', dy);
    console.log('d2', d2);
    console.log('s2', s2);

    if (d2 < s2) {
      subject = node;
      s2 = d2;
    } else if (typeof subject === 'undefined') {
      rect = rects[0];
      subject = rect;
    }
  }
  return subject;
}

function dragStarted() {
  // if (!d3.event.active) simulation.alphaTarget(0.3).restart();
  // circles.splice(circles.indexOf(d3.event.subject), 1);
  // circles.push(d3.event.subject);
  d3.event.subject.active = true;
}

function dragged() {
  d3.event.subject.x = d3.event.x;
  d3.event.subject.y = d3.event.y;
}

function dragEnded() {
  d3.event.subject.active = false;
}