Javascript-форум (https://javascript.ru/forum/)
-   Элементы интерфейса (https://javascript.ru/forum/dom-window/)
-   -   Абсолютное (translate) смешение элемента <g> внутри SVG (https://javascript.ru/forum/dom-window/76103-absolyutnoe-translate-smeshenie-ehlementa-g-vnutri-svg.html)

Vad0k 05.12.2018 13:20

Абсолютное (translate) смешение элемента <g> внутри SVG
 
Доброго времени суток, форумчане.
Подскажите, как правильно получить координаты элемента группы <g> и элементов вложенных в эту группу относительно SVG контейнера.

Пример кода:
<svg width="100%" height="100%" viewBox="0 0 1000 1000" preserveAspectRatio="xMidYMin slice" x="0" y="0" class="canvas-svg" tabindex="1">
<g transform="translate(100 100)">
     <g transform="translate(100 100)"> 
          <circle r="50" cx="25" cy="25" fill="yellow" />
     </g>
</g>
<svg>

Хотелось бы получить абсолютные координаты окружности относительно родительского SVG, а не относительные относительно контейнера используя JS(ES6+ )<g>.

Может это поможет, что я имею ввиду.

MallSerg 05.12.2018 14:03

Так же как и всегда с помощью арифметики.
Посмотри какие X и Y у тебя есть и сложение каких даст нужный размер.

Vad0k 05.12.2018 14:11

Я забыл упомянуть, что необходимо использовать JS (ES6 >).

рони 05.12.2018 14:33

Цитата:

Сообщение от MallSerg
Так же как и всегда с помощью арифметики.
Посмотри какие X и Y у тебя есть и сложение каких даст нужный размер.

можно пример?

Vad0k 05.12.2018 15:20

Я понимаю, что можно использовать арифметику и парсить аттрибут transform="translate(x y)" рекурсивно к родителю, но есть некоторые сложности, что не всегда я перемещаю элементы через translate, а иногда на некоторых узлах используя атрибуты x, y или cx, cy, а может вообще быть path... .
let outputChanel = e.target; // наведенный элемент
let cx = +outputChanel.getAttributeNS(null, 'cx'); // получить [B]локальную[/B] координату относительно группы <g>, но т.к <g> может быть рекурсивное множество вложенных и относительно смещенных между собой.
let cy = +outputChanel.getAttributeNS(null, 'cy');
let cc = getAbsoluteCoords(outputChanel, cx, cy); // вот хотелось реализовать данную функцию, которая будет брать матрицу и смотреть её параметр смещения по осям (matrix.e и matrix.f), НО относительно родительского <SVG>

selectLineChanel.setAttributeNS(null, 'x2', cc.x);
selectLineChanel.setAttributeNS(null, 'y2', cc.y);

selectLineChanel = false;
console.log('endDrawLine (ok)', cc.cx, cc.cy, e.target);


а вот весь участок
// Перетаскивание элементов
(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 selectLineChanel = false;


    function startDrag(e) {

        if (e.target.classList.contains('output-chanel')) { // при наведении на точку канала
            selectLineChanel = document.createElementNS('http://www.w3.org/2000/svg', 'line');
            //initialiseDragging(e, selectLineChanel);
            let outputChanel = e.target;
            let coord = getMousePosition(e);

            selectLineChanel.setAttributeNS(null, 'x1', coord.x);
            selectLineChanel.setAttributeNS(null, 'y1', coord.y);
            selectLineChanel.setAttributeNS(null, 'stroke-width', 5);
            selectLineChanel.setAttributeNS(null,'stroke', '#000');
            selectLineChanel.setAttributeNS(null,'stroke-linecap', 'round');

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

            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);
            console.log('startDragElement');
        }
    }
    function drag(e) {
        e.preventDefault();
        if(selectLineChanel){
            let coord = getMousePosition(e);
            selectLineChanel.setAttributeNS(null, 'x2', coord.x);
            selectLineChanel.setAttributeNS(null, '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);
            console.log('drag');
        }
    }
    function endDrag(e) {
        if (selectLineChanel) {
            console.log(e.target);
            if (e.target.classList.contains('input-chanel')) {
                let outputChanel = e.target;
                let cx = +outputChanel.getAttributeNS(null, 'cx');
                let cy = +outputChanel.getAttributeNS(null, 'cy');
                let cc = getAbsoluteCoords(outputChanel, cx, cy);

                selectLineChanel.setAttributeNS(null, 'x2', cc.x);
                selectLineChanel.setAttributeNS(null, 'y2', cc.y);

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


    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 getAbsoluteCoords(element, x, y) {
        let offset = activeSVG.getBoundingClientRect();
        let matrix = element.getScreenCTM();

        return {
            x: (x + matrix.e),
            y: (y + matrix.f)
        };
    }



    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);
        
        let transforms = element.transform.baseVal;
        if (transforms.length === 0 || transforms.getItem(0).type !== SVGTransform.SVG_TRANSFORM_TRANSLATE) {
           
            let translate = activeSVG.createSVGTransform();
            translate.setTranslate(0, 0);
            element.transform.baseVal.insertItemBefore(translate, 0);
        }
      
        transformElement = transforms.getItem(0);
        offsetElement.x -= transformElement.matrix.e;
        offsetElement.y -= transformElement.matrix.f;
    }

})();

MallSerg 05.12.2018 15:59

Цитата:

Сообщение от рони (Сообщение 499808)
можно пример?

Не думаю что это поможет автору. К тому же в документации есть функция которая сама делает сложение и масштабирование для ленивых.
<body>
<svg width="100%" height="100%" viewBox="0 0 1000 1000" preserveAspectRatio="xMidYMin slice" x="0" y="0" class="canvas-svg" tabindex="1">
<g transform="translate(100 100)">
     <g transform="translate(100 100)">
          <circle r="50" cx="25" cy="25" fill="yellow" />
     </g>
</g>
<svg>
<script>
_=(document.querySelector("g")).getCTM();
alert ( "x="+_.e+"   y="+_.f);
</script>

Vad0k 05.12.2018 16:44

Я немного изучил характеристики данного примера
Физ. размер svg (x: 840px) в обозревателе, viewbox width: 1000px для svg => w:0.84 per 1px.
Значение отображается x: 84,0px относительно вложенной группы не учитывая другие вложенности.

По сути из примера я бы хотел бы получить значение: x: 100 + 100 + 25
независимо от уровня вложенности, без перевода viewbox в viewport.

Возможно, я не сумел объяснить, что мне нужно... мне просто бы хотелось получить физ. абсолютное позиционирование внутри SVG

<svg width="100%" height="100%" viewBox="0 0 1000 1000" preserveAspectRatio="xMidYMin slice" x="0" y="0" class="canvas-svg" tabindex="1">
<g transform="translate(100 100)">
     <g transform="translate(100 100)"> 
          <circle r="50" cx="25" cy="25" fill="yellow" />
     </g>
</g>
<svg>


function getAbsoluteCoords(element, x, y) {
        let matrix = element.getCTM();

        return {
            x: (x + matrix.e),
            y: (y + matrix.f)
        };
    }

но данный пример возвращает относительно вложенной выше группы родителя, тоесть x: 84px.

рони 05.12.2018 18:22

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

Vad0k 05.12.2018 18:33

Я ни в коем случае не обвиняю никого. Я просто посчитал, что любой кто зайдет помочь, может увидеть сообщение "Спасибо", и подумать не вдаваясь в детали, что вопрос уже решен.
Помню, когда сидел на 4pda, не разрешали внутри темы создавать подтем вопросы(модерировали) (для меня это просто не привычно,... поймите меня тоже, правильно.).

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

рони 05.12.2018 18:34

Vad0k,
может чем поможет ...
http://www.codedread.com/dragtest2.svg

Vad0k 05.12.2018 19:01

Эх... я уже попадал на данный пример из Реализация Drag and Drop
На основе него уже действительно хорошо разобрался и реализовал хороший drag & drop.

У меня возникла сложность в другом моменте (при рисовании линий из рис. в теме).
Когда я навожу на окружность, я пытаюсь взять абсолютные координаты окружности (чтобы линию привязать к центру окружности независимо, где человек щелкнет и потянет линию. но сама окружность находиться в группе (прямоугольник визуально), и если доставить координаты окружности, то получаю их относительно группы, что не подходит) (линии рисуются в другой группе, чтобы визуально отделить от блоков). На данный момент линии тянуться и располагаются где надо используя координаты курсора мыши и это выглядит немного некрасиво (тоесть человеку, для красоты, нужно щелкать идеально по центру окружности, а я бы хотел бы немного помочь удобней сделать, для этого нужно как-то получить эти координаты).

Вижу решение одно, но оно мне не нравиться: это рекурсивно перебирать группы и смотреть их позиционирование распарсивая transform="translate" и суммируя в координату, но скорее это ресурсоемкая будет задачей, когда элементов будет больше

<!-- СЕТКА -->
                <g class="grid"><rect width="100%" height="100%" fill="url(#grid)" /></g>
                <!-- СЕТКА -->

                <!-- СПИСОК КАНАЛОВ -->
                <g class="chanel-lines"></g>
                <!-- СПИСОК КАНАЛОВ -->

                <!-- УСТРОЙТСВА -->
                <g class="svg-schema"></g>
                <!-- УСТРОЙТСВА -->

Внутри устройств лежат уже сами блоки обернуты в <g> с transform="translate(x y)"... В общем как-то так

Vad0k 06.12.2018 10:47

Решение найдено. Готовых методов нет в ES6+ для SVG2.0... Можно только рекурсией к родительскому элементу получить.
решение

RX200 28.12.2018 00:24

Цитата:

Сообщение от Vad0k
Можно только рекурсией к родительскому элементу получить.

Я подобные задачи решал не через рекурсию, вот пример может и вам подойдет.
<head>
<script type="text/javascript">
function test(){
	let svgns = "http://www.w3.org/2000/svg";
	let svg = document.getElementsByClassName('canvas-svg')[0];
	let elm_g = document.getElementById("circle");
	let CTM = svg.getScreenCTM();
	let elm_g_pos_x = (elm_g.getBoundingClientRect().left - CTM.e) / CTM.a;
	let path_x = document.createElementNS(svgns, "path");
	path_x.setAttributeNS(null, "d", "M"+elm_g_pos_x+",0 v1000");
	path_x.setAttributeNS(null, "fill", "transparent");
	path_x.setAttributeNS(null, "stroke", "black");
	svg.appendChild(path_x);
	let elm_g_pos_y = (elm_g.getBoundingClientRect().top - CTM.f) / CTM.d;
	let path_y = document.createElementNS(svgns, "path");
	path_y.setAttributeNS(null, "d", "M0,"+elm_g_pos_y+" h1000");
	path_y.setAttributeNS(null, "fill", "transparent");
	path_y.setAttributeNS(null, "stroke", "black");
	svg.appendChild(path_y);
	let elm_g_pos_cx = elm_g_pos_x + elm_g.getAttributeNS(null, "r") * 1;
	let path_cx = document.createElementNS(svgns, "path");
	path_cx.setAttributeNS(null, "d", "M"+elm_g_pos_cx+",0 v1000");
	path_cx.setAttributeNS(null, "fill", "transparent");
	path_cx.setAttributeNS(null, "stroke", "black");
	path_cx.setAttributeNS(null, "stroke-dasharray", "4");
	svg.appendChild(path_cx);
	let elm_g_pos_cy = elm_g_pos_y + elm_g.getAttributeNS(null, "r") * 1;
	let path_cy = document.createElementNS(svgns, "path");
	path_cy.setAttributeNS(null, "d", "M0,"+elm_g_pos_cy+" h1000");
	path_cy.setAttributeNS(null, "fill", "transparent");
	path_cy.setAttributeNS(null, "stroke", "black");
	path_cy.setAttributeNS(null, "stroke-dasharray", "4");
	svg.appendChild(path_cy);
}
window.onload = test;
</script>
</head>
<body>
<svg width="100%" height="100%" viewBox="0 0 1000 1000" preserveAspectRatio="xMidYMin slice" x="0" y="0" class="canvas-svg" tabindex="1">
<g transform="translate(100 100)">
     <g transform="translate(100 100)"> 
          <circle id="circle" r="50" cx="25" cy="25" fill="yellow" />
     </g>
</g>
<svg>
</body>

рони 28.12.2018 00:54

RX200,
на всякий случай
getBoundingClientRect()
и
run
[html run]
... минимальный код страницы с вашей проблемой
[/html]

О том, как вставить в сообщение исполняемый javascript и html-код, а также о дополнительных возможностях форматирования - читайте http://javascript.ru/formatting.

RX200 28.12.2018 01:27

рони,
Исправил. А вот красным он не те строки почему то выделяет. от *!* и */!* пришлось отказаться)

рони 28.12.2018 09:09

Цитата:

Сообщение от RX200
от *!* и */!* пришлось отказаться)

отдельной строкой делали?
*!*
 
и 

*/!*
123
*!*
 
и 

*/!*

RX200 28.12.2018 17:01

Цитата:

Сообщение от рони
отдельной строкой делали?

Да я так и сделал на четыре резные строки, 8, 14, 20, 27 но выделились почему то 8 и еще какие то три которых я не выделял, я перепроверил но в тексте были одни строки выделены а визуально другие только первая строка 8 совпала.


Часовой пояс GMT +3, время: 18:25.