Доброго времени форумчане.
Сижу сейчас на работе и тут мне прилетело задание сделать визуальный конфигуратор ... застрял на реализации связей между компонентами:
Реализовываю с использованием SVG + JS (ES6)... Перенос элементов реализовал, но линии прям не поддаются пока что сделать. Подумал перейти на библиотеку, чтобы упростить работу (связывания элементов).
Посоветуйте, по возможности алгоритм или библиотеку для манипулирования SVG (построение связей).
// Перетаскивание элементов
(function () {
let $document = $(document);
const $svg = $('#main .canvas-svg');
// PC
$document.on('mousedown', $svg, startDrag);
$document.on('mousemove', $svg, drag);
$document.on('mouseup', $svg, endDrag);
$document.on('mouseleave', $svg, endDrag);
// MOBILE
$document.on('touchstart', $svg, startDrag);
$document.on('touchmove', $svg, drag);
$document.on('touchend', $svg, endDrag);
$document.on('touchleave', $svg, endDrag);
$document.on('touchcancel', $svg, endDrag);
let selectedElement = false,
offsetElement,
transformElement;
let selectLineChanel = false;
let arrayLineChanel = [];
function startDrag(e) {
if (e.target.classList.contains('input-chanel')) { // при наведении на точку канала
let lineChanel = document.createElementNS('http://www.w3.org/2000/svg', 'line');
lineChanel.setAttributeNS(null, 'x1', e.target.getAttributeNS(null,'cx'));
lineChanel.setAttributeNS(null, 'y1', e.target.getAttributeNS(null,'cy'));
lineChanel.setAttributeNS(null, 'stroke-width', 5);
lineChanel.setAttributeNS(null,'stroke', '#000');
e.target.parentNode.insertBefore(lineChanel, e.target);
arrayLineChanel.push(lineChanel); // добавляем линию в массив
selectLineChanel = true;
console.log('startDrawLine');
} else if (e.target.classList.contains('draggable')) { // при наведении на элемент
selectedElement = e.target;
initialiseDragging(e);
console.log('startDragElement')
} else if (e.target.parentNode.classList.contains('draggable-group')) {
selectedElement = e.target.parentNode;
initialiseDragging(e);
console.log('startDragElement');
}
}
function drag(e) {
if(selectLineChanel){
e.preventDefault();
let coord = getMousePosition(e);
arrayLineChanel[arrayLineChanel.length-1].setAttributeNS(null, 'x2',coord.x - offsetElement.x);
arrayLineChanel[arrayLineChanel.length-1].setAttributeNS(null, 'y2',coord.y - offsetElement.y);
console.log('drawLine:',coord.x, offsetElement.x);
} else if (selectedElement) {
e.preventDefault();
let coord = getMousePosition(e);
transformElement.setTranslate(coord.x - offsetElement.x, coord.y - offsetElement.y);
console.log('drag');
}
}
function endDrag(e) {
if(selectLineChanel && !e.target.classList.contains('output-chanel')){
arrayLineChanel[arrayLineChanel.length-1].remove();
selectLineChanel = false;
console.log('endDrawLine');
}
if(selectedElement){
selectedElement = false;
console.log('endDragElement');
}
}
function getMousePosition(e) {
let CTM = activeSVG.getScreenCTM(); // Возвращает DOMMatrix, представляющую матрицу, которая преобразует систему координат текущего элемента в систему координат окна просмотра SVG для фрагмента документа SVG.
if (e.touches) { e = e.touches[0]; } // iPhone
return {
x: (e.clientX - CTM.e) / CTM.a,
y: (e.clientY - CTM.f) / CTM.d
}
}
function initialiseDragging(e) {
offsetElement = getMousePosition(e);
// Make sure the first transformElement on the element is a translate transformElement
let transforms = selectedElement.transform.baseVal;
if (transforms.length === 0 || transforms.getItem(0).type !== SVGTransform.SVG_TRANSFORM_TRANSLATE) {
// Create an transformElement that translates by (0, 0)
let translate = activeSVG.createSVGTransform();
translate.setTranslate(0, 0);
selectedElement.transform.baseVal.insertItemBefore(translate, 0);
}
// Get initial translation
transformElement = transforms.getItem(0);
offsetElement.x -= transformElement.matrix.e;
offsetElement.y -= transformElement.matrix.f;
}
})();