Flecha Com Começo E Fim - Uma história - Começo, meio e fim - Associação
Uma história - Começo, meio e fim - Associação

Como criar flechas com começo e fim em SVG

Achei isso na prática quando precisei de diagramas técnicos para documentação de API. O problema é que a maioria dos tutoriais mostra apenas setas simples sem controle real de origem e destino. Eu queria que a linha se conectasse precisamente ao centro de cada elemento, não a uma borda qualquer.

flecha com começo e fim

O SVG tem marcadores de seta nativos. Você define o caminho e aplica um marker-end. Mas o controle de começo e fim exige cálculo de interseção, senão a seta fica flutuando ou penetrando o objeto. Primeiro, a estrutura básica do SVG com marcador:

<svg xmlns="http://www.w3.org/2000/svg" width="800" height="600"> <defs> <marker id="arrowhead" markerWidth="10" markerHeight="7" refX="9" refY="3.5" orient="auto"> <polygon points="0 0, 10 3.5, 0 7" fill="#333"/> </marker> </defs> <line x1="100" y1="100" x2="700" y2="500" stroke="#333" stroke-width="2" marker-end="url(#arrowhead)"/> </svg> O truque do refX="9" é importante. Se você colocar refX="0", a ponta da seta vai na coordenada final. Com refX="9", ela recua 9 unidades, o que permite alinhar com bordas de retângulos.

👉 Clique no botão abaixo para saber mais sobre o assunto!

Para conectar no centro de elementos específicos, use JavaScript para calcular as posições: function connectElements(fromEl, toEl, svg) { const fromRect = fromEl.getBoundingClientRect(); const toRect = toEl.getBoundingClientRect(); const svgRect = svg.getBoundingClientRect(); const x1 = fromRect.left + fromRect.width / 2 - svgRect.left; const y1 = fromRect.top + fromRect.height / 2 - svgRect.top; const x2 = toRect.left + toRect.width / 2 - svgRect.left; const y2 = toRect.top + toRect.height / 2 - svgRect.top; const line = document.createElementNS("http://www.w3.org/2000/svg", "line"); line.setAttribute("x1", x1); line.setAttribute("y1", y1); line.setAttribute("x2", x2); line.setAttribute("y2", y2); line.setAttribute("stroke", "#333"); line.setAttribute("stroke-width", "2"); line.setAttribute("marker-end", "url(#arrowhead)"); svg.appendChild(line); }

Eu tinha um projeto onde os elementos podiam se mover com drag-and-drop. A solução foi ouvir o evento drag e reconectar a linha a cada update. Funciona, mas tem um problema: se o elemento de destino for muito grande, a seta invade o retângulo. Para corrigir isso, calcule a interseção com as bordas do retângulo alvo:

function getEdgePoint(rect, targetX, targetY) { const centerX = rect.left + rect.width / 2; const centerY = rect.top + rect.height / 2; const dx = targetX - centerX; const dy = targetY - centerY; const halfWidth = rect.width / 2; const halfHeight = rect.height / 2; const slope = Math.abs(dy / dx); const rectSlope = halfHeight / halfWidth; let x, y; if (slope < rectSlope) { x = dx > 0 ? centerX + halfWidth : centerX - halfWidth; y = centerY + (dx > 0 ? dy : dy) * (halfWidth / Math.abs(dx)); } else { y = dy > 0 ? centerY + halfHeight : centerY - halfHeight; x = centerX + (dy > 0 ? dx : dx) * (halfHeight / Math.abs(dy)); } return { x, y }; } Essa função retorna o ponto exato na borda do retângulo onde a linha deve terminar. O resultado é visualmente limpo, sem sobreposições estranhas.

Se você precisa de algo mais sofisticado, como curvas de Bézier que contornam outros elementos, considere a biblioteca ELK (Event-driven Layout Kit). Ela resolve routing automático em grafos, mas exige configuração adicional. Para setas simples entre dois pontos fixos, o cálculo manual acima resolve em menos de 50 linhas. Outro detalhe prático: marqueles com `orient="auto"` giram conforme o ângulo da linha. Se quiser uma seta fixa (sem rotação), use `orient="0"` e ajuste manualmente o transform.