Adding connectors on mouse over similar to draw.io

380 views Asked by At

I am trying to simulate an arrow feature similar to draw.io where on mouse hover on an object, i would like to show the blue dots on the center of each side and arrows on all 4 sides. Any idea how to implement the same.

enter image description here

Appreciate your response.. Thank you.

1

There are 1 answers

0
Ruben Helsloot On

I've made an example using a square. Hovering spawns crosses and arrows along each side. I recommend reading this documentation about paths in SVG to see how I drew these shapes.

const size = 200,
  margin = 100,
  offset = 10,
  crossesPerSide = 4;
const arrowPath = 'M-5,0 h10 v30 h10 l-15,15 l-15,-15 h10 Z';
const crossPath = 'M-6,-5 l12,10 m0,-10 l-12,10';

const g = d3.select('svg')
  .append('g')
  .on('mouseover', () => {
    g.selectAll('.arrow')
      .data('nesw'.split(''))
      .enter()
      .append('path')
      .classed('arrow', true)
      .attr('d', arrowPath)
      .attr('transform', (d, i) => `
        translate(${margin} ${margin})
        translate(${size / 2} ${size + offset})
        rotate(${i * 90} 0 ${-offset - size / 2})
      `)

    g.selectAll('.cross')
      .data(d3.range(crossesPerSide * 4))
      .enter()
      .append('path')
      .classed('cross', true)
      .attr('d', crossPath)
      .attr('transform', (d, i) => {
        const sideIdx = Math.floor(i / crossesPerSide);
        const crossIdx = i % crossesPerSide;
        const spacePerCross = (size / crossesPerSide);
        return `
          translate(${margin} ${margin})
          rotate(${sideIdx * 90} ${size / 2} ${size / 2})
          translate(${spacePerCross * (crossIdx + 0.5)}, 0)
        `;
      })
  })
  .on('mouseleave', () => {
    g.selectAll('.arrow').remove();
    g.selectAll('.cross').remove();
  });

g.append('rect')
  .attr('x', margin)
  .attr('y', margin)
  .attr('width', size)
  .attr('height', size)
  .attr('stroke', 'black')
  .attr('fill', 'white')
svg {
  border: solid 1px red;
}

.cross {
  stroke: darkblue;
  stroke-width: 2;
  opacity: 0.6;
}

.arrow {
  fill: darkblue;
  opacity: 0.2;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<svg width="400" height="400"></svg>