使用SVG JS(ES6 +)将与绘图线(路径)链接的拖放元素与另一个元素的宽度相同

时间:2018-12-07 16:46:00

标签: javascript svg ecmascript-6

大家好。请告诉我从代码组织的角度来看,如何实现拖放元素以及在拖动元素之间绘制链接。

我部分设法实现了这一目标,但是遇到了性能或焦点损失的问题(我想)。

在阅读article并理解了拖放的基本知识之后,我想在组件之间创建链接,但是有一个困难:在拖动元素并同时绘制链接线时,如果拖动元素从焦点飞出并掉到了画布上。

以下是常规逻辑:

let svg = document.querySelector('svg');

svg.addEventListener('mousedown', startDrag);
svg.addEventListener('mousemove', drag);
svg.addEventListener('mouseup', endDrag);
svg.addEventListener('mouseleave', endDrag);
    
let selectedElement = null;    
    
function startDrag(evt) {
  // select element evt.target
  // search all need lines communication with this element
}
function drag(evt) {
  // set coords selectedElement (x, y) by mouse position with offeset
  // set coords need LINES, BUT have problem (;,,,;)(\/ 
}
function endDrag(evt) {
   selectedElement = false;
   // clear temp map object line with 
}

我非常感谢您的帮助,并考虑如何紧凑地介绍问题所在

我录制了一个视频,但是直到那时我才意识到,没有翻译者https://youtu.be/y5cdIWu7_qQ,我什么也说不出来。

我非常希望您能理解我...

// Перетаскивание элементов
(function () {
    let $document = $(document);

    const $svg = $('#main .canvas-svg'); // 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 selectDrawLineChanel = false; // текущая созданная (рисующая) линия при создании канал связи
    let mapMoveLineChanel = false; // список перемещаемых линий (связей) при перетаскивании блока


    function startDrag(e) {

        if (e.target.classList.contains('output-chanel')) { // при наведении на точку канала
            selectDrawLineChanel = document.createElementNS('http://www.w3.org/2000/svg', 'line');
            //initialiseDragging(e, selectDrawLineChanel);
            let outputChanel = e.target;
            let groupParent = e.target.closest('.draggable-group');
            let positionChanelOutput = getAbsolutePositionElement(outputChanel, outputChanel.cx.baseVal.value, outputChanel.cy.baseVal.value);


            console.log(groupParent);

            let attrsLine = {
                x1: positionChanelOutput.x,
                y1: positionChanelOutput.y,
                dataNameDeviceOutput: groupParent.getAttributeNS(null, 'data-name-device'),
                dataNameChanelOutput: outputChanel.getAttributeNS(null, 'data-name-chanel'),
                strokeWidth: 2,
                stroke: '#000',
                strokeLinecap : 'round',
            };

            setAttrElement(selectDrawLineChanel, attrsLine, null);

            let groupChanelLine = activeSVG.querySelector('.chanel-lines');
            groupChanelLine.appendChild(selectDrawLineChanel);

            console.log('startDrawLine');
        } else if (e.target.classList.contains('draggable')) { // при наведении на элемент
            selectedElement = e.target;
            initialiseDragging(e, selectedElement);
            console.log('startDragElement')
        } else if (e.target.parentNode.classList.contains('draggable-group')) { // при нажатии на устройство при перемещении
            selectedElement = e.target.parentNode;
            initialiseDragging(e, selectedElement);

            // поиск всех линий, относящахся к данному блоку при перемешении самого блока
            let nameDevice = selectedElement.getAttributeNS(null,'data-name-device'); // определяем имя девайса
            let groupChanelLines = activeSVG.querySelector('.chanel-lines');// ищем контейнер всех линий
            let selectMoveArrayLineChanel = groupChanelLines.querySelectorAll('[data-name-device-output],[data-name-device-input]'); // найти все линии с содержащие data-name-device-output | data-name-device-input внутри контейнера
            if (selectMoveArrayLineChanel.length) { // если нашел линии, то есть смысл дальше идти
                mapMoveLineChanel = new Map(); // объект мап line (lineChanel) - circle (chanelOutput | chanelInput)
                for (let i = 0; i < selectMoveArrayLineChanel.length; i++){ // перебираем линии
                    let itemLine = selectMoveArrayLineChanel[i]; // одна линия
                    if(itemLine.getAttributeNS(null, 'data-name-device-output') === nameDevice){ // поиск в output
                        let nameChanelOutput = itemLine.getAttributeNS(null, 'data-name-chanel-output'); // достаем из аттрибута линии название канала
                        let circleChanelOutput = selectedElement.querySelector(`.chanel-list-output [data-name-chanel="${nameChanelOutput}"]`); // находим окружность
                        mapMoveLineChanel.set(itemLine, circleChanelOutput);
                        console.log('add MAP output');
                    }else if(itemLine.getAttributeNS(null, 'data-name-device-input') === nameDevice){ // поиск в input
                        let nameChanelInput = itemLine.getAttributeNS(null, 'data-name-chanel-input'); // ищем порядковый номер канала
                        let circleChanelInput = selectedElement.querySelector(`.chanel-list-input [data-name-chanel="${nameChanelInput}"]`); // поиск окружности канала
                        mapMoveLineChanel.set(itemLine, circleChanelInput);
                        console.log('add MAP input');
                    }
                }
            }

            console.log('startDragElement');
        }
    }
    function drag(e) {
        e.preventDefault();
        if(selectDrawLineChanel){ // рисуем линию за курсором мыши
            let coord = getMousePosition(e);
            setAttrElement(selectDrawLineChanel, {
                x2: coord.x,
                y2: coord.y
            });
            //console.log('drawLine: x =',coord.x, 'offsetElementX =', offsetElement.x, 'y =', coord.y, 'offsetElementY =', offsetElement.y);
        } else if (selectedElement) { // при перетаскивании элемента
            let coord = getMousePosition(e);
            transformElement.setTranslate(coord.x - offsetElement.x, coord.y - offsetElement.y); // переносим сам элемент


            /* PROBLEM HERE -----------------> */
            if(mapMoveLineChanel){ // перебираем все линии с их каналами относящиеся к перетаскиваему блоку
                mapMoveLineChanel.forEach((circle, line, map) => {
                    let coordsCircle = getAbsolutePositionElement(circle, circle.getAttributeNS(null, 'cx'), circle.getAttributeNS(null, 'cy'));

                    setAttrElement(line, { // init new coords for line by position circle-chanel
                        x1:coordsCircle.x,
                        y1:coordsCircle.y,
                    });

                    console.log(line, coordsCircle, mapMoveLineChanel);

                });
            }
            /* <----------------- PROBLEM HERE */
            
            console.log('drag');
        }
    }
    function endDrag(e) {
        if (selectDrawLineChanel) {
            console.log(e.target);
            if (e.target.classList.contains('input-chanel')) {
                let outputChanel = e.target;
                let groupParent = e.target.closest('.draggable-group');

                let cx = outputChanel.cx.baseVal.value;
                let cy = outputChanel.cy.baseVal.value;
                let cc = getAbsolutePositionElement(outputChanel, cx, cy);

                setAttrElement(selectDrawLineChanel, {
                    x2: cc.x,
                    y2: cc.y,
                    dataNameDeviceInput: groupParent.getAttributeNS(null, 'data-name-device'), // сохраняем имя девайса
                    dataNameChanelInput: e.target.getAttributeNS(null, 'data-name-chanel') // сохраняем порядковый номер (ид) канала в данном девайсе
                });

                selectDrawLineChanel = false;
                console.log('endDrawLine (ok)', cc.cx, cc.cy, e.target);
            } else {
                selectDrawLineChanel.remove();
                selectDrawLineChanel = false;
                console.log('endDrawLine (cancel)');
            }
        }
        if(selectedElement){
            selectedElement = false;
            console.log('mapMoveLineChanel =', mapMoveLineChanel);
            mapMoveLineChanel = false;
            console.log('endDragElement');
        }
    }


    function getAbsolutePositionElement(elem, x, y) {
        let svg = elem.ownerSVGElement;

        // Get the cx and cy coordinates
        let pt = svg.createSVGPoint();
        pt.x = x;
        pt.y = y;

        while (true) {
            // Get this elements transform
            let transform = elem.transform.baseVal.consolidate();
            // If it has a transform, then apply it to our point
            if (transform) {
                let matrix = elem.transform.baseVal.consolidate().matrix;
                pt = pt.matrixTransform(matrix);
            }
            // If this element's parent is the root SVG element, then stop
            if (elem.parentNode === svg)
                break;
            // Otherwise step up to the parent element and repeat the process
            elem = elem.parentNode;
        }
        return pt;
    }



    function setAttrElement(element, attrs, namespace = null) {
        for (let attrName in attrs) {
            let snakeAttrName = attrName.replace(/([A-Z])/g, "-$1").toLowerCase();
            element.setAttributeNS(namespace, snakeAttrName, attrs[attrName]);
        }
        return element;
    }



    function convertCoords(element, x, y) {
        let offset = activeSVG.getBoundingClientRect();
        let matrix = element.getScreenCTM();

        return {
            x: (matrix.a * x) + (matrix.c * y) + matrix.e - offset.left,
            y: (matrix.b * x) + (matrix.d * y) + matrix.f - offset.top
        };
    }


    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, element) {
        offsetElement = getMousePosition(e);
        // Make sure the first transformElement on the element is a translate transformElement
        let transforms = element.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);
            element.transform.baseVal.insertItemBefore(translate, 0);
        }
        // Get initial translation
        transformElement = transforms.getItem(0);
        offsetElement.x -= transformElement.matrix.e;
        offsetElement.y -= transformElement.matrix.f;
    }


})();

0 个答案:

没有答案