Как можно провести линию между двумя блоками, если зажать мышь на одном и отпустить на другом?

111
04 января 2021, 21:30

К примеру, между .span1 и .span2

<span class="span1">1</span>
<div style="height:100px;"></div>
<span class="span2">2</span>
Answer 1

Вот собрал что-то похожее из svg+js, вообще я обычно такое делаю на d3.js, тут ради исключения сделал на голом js, на d3 листинг был бы не сильно длиннее, но все же лаконичнее...

Чтобы нарисовать прямоугольник, используйте перетаскивание левой кнопкой мыши на свободном месте.

Переместить прямоугольник можно так же перетаскиванием левой кнопкой.

Соединить прямоугольники можно при помощи перетаскивания правой кнопкой начиная на прямоугольнике

Двойное нажатие на прямоугольник позволяет добавить текст.

let x, y, shape, move, svg = document.querySelector('svg'); 
let attrs = (s, o) => Object.keys(o).forEach(p => s.setAttribute(p, o[p])) 
let clampToCenter = e => [+e.getAttribute('x') + e.getAttribute('width')/2, 
                          +e.getAttribute('y') + e.getAttribute('height')/2] 
attrs(svg, {width: window.innerWidth, height: window.innerHeight}); 
 
function startDrawLink(e) { 
    shape = document.createElementNS('http://www.w3.org/2000/svg', 'line'); 
    shape.source = e.target; 
    let p = clampToCenter(e.target); 
    attrs(shape,{x1: p[0], y1: p[1], x2: e.x, y2: e.y, 
                 stroke: '#000', 'pointer-events': 'none'}) 
} 
 
function startTranslateRect(e){ 
    move = e.target; 
    move.px = +e.target.getAttribute('x'); 
    move.py = +e.target.getAttribute('y'); 
    move.ex = e.x; 
    move.ey = e.y; 
} 
 
function translateRect(e) { 
    attrs(move, {x: move.px + e.x - move.ex, y: move.py + e.y - move.ey}) 
    let p = clampToCenter(move); 
    document.querySelectorAll('line').forEach(l => { 
        let isSrc = l.source === move 
        if (isSrc || l.target === move) { 
            l.setAttribute(isSrc ? 'x1' : 'x2', p[0]) 
            l.setAttribute(isSrc ? 'y1' : 'y2', p[1]) 
        } 
    }) 
    e.target.text && attrs(e.target.text, { 
        x: move.px + e.x - move.ex + e.target.getAttribute('width')/2,  
        y: move.py + e.y - move.ey + e.target.getAttribute('height')/2 
    }) 
} 
 
function doDrawRect(e){ 
    attrs(shape, { 
      x: Math.min(e.x, shape.cx), y: Math.min(e.y, shape.cy), 
      width: Math.abs(e.x - shape.cx), height: Math.abs(e.y - shape.cy) 
    }) 
} 
 
function doDrawLine(e){ 
    if (e.target.nodeName === "rect" && e.target != shape.source) { 
      let p = clampToCenter(e.target) 
      attrs(shape, {x2: p[0], y2: p[1]}) 
      shape.removeAttribute('stroke-dasharray') 
    } else { 
      attrs(shape, {x2: e.x, y2: e.y, 'stroke-dasharray': '10 10'})        
    } 
} 
 
addEventListener('mousedown', e => { 
  if (e.target.nodeName === "rect") { 
    if (e.button === 2)  
        startDrawLink(e) 
    if (e.button === 0)  
      startTranslateRect(e); 
     
  } else { 
    shape = document.createElementNS('http://www.w3.org/2000/svg', 'rect'); 
    shape.cx = e.x; 
    shape.cy = e.y; 
    attrs(shape, {fill: 'white', stroke: 'black'}) 
  } 
  shape && svg.appendChild(shape); 
}) 
 
addEventListener('mousemove', e => { 
  if (move)  
      translateRect(e); 
  if (shape)  
    if (shape.nodeName === "rect") 
      doDrawRect(e); 
    else   
      doDrawLine(e); 
}) 
 
addEventListener('mouseup', e => { 
  if (shape && shape.nodeName === "line") { 
    shape.remove(); 
    if (!shape.getAttribute('stroke-dasharray')) { 
      svg.insertBefore(shape, svg.firstChild) 
      shape.target = e.target; 
    } 
  } 
  shape = null; 
  move = null; 
}) 
 
addEventListener('contextmenu', e => e.preventDefault()) 
 
addEventListener('click', e => { 
  if (e.detail == 2 && e.target.nodeName === "rect") { 
     if (!e.target.text) { 
         e.target.text = document.createElementNS('http://www.w3.org/2000/svg', 'text'); 
         let p = clampToCenter(e.target) 
         attrs(e.target.text, {x: p[0], y: p[1], 'text-anchor': 'middle',  
                               'dominant-baseline': 'middle', 'pointer-events': 'none'}) 
         svg.appendChild(e.target.text); 
     } 
     e.target.text.innerHTML = prompt('enter text', e.target.text.innerHTML || '') 
  } 
})
<body style="margin:0;overflow:hidden;user-select:none"><svg style="background-color: wheat"></svg>

——

UPD: похожий ответ с использованием d3.js

READ ALSO
Не корректная анимация появления div

Не корректная анимация появления div

Есть контейнер содержимого в галерее, у него стоят display:flex и justify-content: center, так же у каждого элемента галереи есть эффект появления и исчезновенияВ...

129
Как изменять вид ползунка в слайдере?

Как изменять вид ползунка в слайдере?

Есть слайдер-ползунок, работающий с помощью jquery-uihttps://codepen

119
Написание структуры контейнера set

Написание структуры контейнера set

Написать структуру, которая обрабатывала бы входные данные следующим образомНа входе даётся целое число, указывающее количество операций

117
Есть ли аналог библиотеки chrono в C?

Есть ли аналог библиотеки chrono в C?

Есть ли какая нибудь замена, либо аналогия временной C++ библиотеки chrono (в ходящей в пространство имен std) в C ??

144