Move a SVG element

I have

<rect x="25" y="25" width="94" height="28" class="draggable" id="new_row"/>

and am trying to create a function so I can move it (right) so it becomes

<rect x="25" y="25" width="94" height="28" class="draggable" id="new_row" transform="translate(5,0)"/>
function nudgeRight() {
        //get the element
        var svg_rect = document.querySelector("rect#new_row");
        //apply a tranform to it so it moves to the right 5
        //if a transform has been applied, subtract 5 from it
}

Use:
svg_rect.setAttribute('transform','translate(5,0)');

or:
svg_rect.setAttribute('x','30');

ok, moved it using

function nudgeRight() {
        var svg_rect = document.getElementById('new_row');
        var beforeNudge = svg_rect.getAttribute('transform');
		svg_rect.setAttribute('transform','translate(5,0)');
        var afterNudge = svg_rect.getAttribute('transform');

       console.log(beforeNudge+' => '+afterNudge);

    }

the result…


the result is good (console)
How can I add 5 to the original x position
so I can move it by 5 across the screen?so the console would read

translate(0,0) => translate(5,0)
translate(5,0) => translate(10,0)
translate(10,0) => translate(15,0)

We can use getCTM() to get the current value.

That gives an SVGMatrix object, where the e property can be interpreted as being the amount of nudge.

    function nudgeRight() {
        var svg_rect = document.getElementById('new_row');
        var currentX = svg_rect.getCTM().e;
        var nudgeX = currentX + 5;
        svg_rect.setAttribute('transform','translate(' + nudgeX + ',0)');
    }

But I might update that to instead be:

    function nudgeRight() {
        var svg_rect = document.getElementById('new_row');
        var currentX = svg_rect.getCTM().e;
        svg_rect.setAttribute('transform',`translate(${currentX + 5},0)`);
    }

Note: Instead of single quotes, backticks are used so that variable insertion can occur.

It would be better though to use a matrix transformation and apply that to the SVG instead, but that is a topic that I would rather defer to others here more skilled with applying matrix manipulations, before I wade into the subject.

1 Like

You could add 5 to the ‘x’ attribute:

function nudgeRight() {
   const svg_rect = document.getElementById('new_row');
   let beforeNudge = parseInt(svg_rect.getAttribute('x'));
   svg_rect.setAttribute('x',beforeNudge+5);
   let afterNudge = svg_rect.getAttribute('x');

   console.log(beforeNudge+' => '+afterNudge);
}
1 Like

I agree, updating the x attribute seems to be a better solution than applying a translation.

works great…
So, in order to get the x position of a transform(translate()) I need to use the getCTM() method?

But it would be better to simply change the x attribute instead.

Thanks…

Oh, fouund an issue with the translate()
when I load up the page& inpect the element


when I press the button once,

nice, but another press reveals

where did the extra 2 come from?

1 Like

so, it looks like the function is good, the 2 seems to be added before (console)

Debugging this issue could be done, but it seems to be a waste of time.
Why? Because an overall change needs to occur with your code.

Instead of using transform translate, you need to update the HTML attribute values instead.
When that’s done, your problem will also magically disappear because transform translate will no longer be used.

1 Like

I guess it comes from the stroke width.

There’s a typo but it does not matter:

translte

the only reason im using the transform way is because it used in a script which allows dragging with the mouse

    <script type="text/javascript"><![CDATA[
      function makeDraggable(evt) {
        var svg = evt.target;

        svg.addEventListener('mousedown', startDrag);
        svg.addEventListener('mousemove', drag);
        svg.addEventListener('mouseup', endDrag);
        svg.addEventListener('mouseleave', endDrag);
        svg.addEventListener('touchstart', startDrag);
        svg.addEventListener('touchmove', drag);
        svg.addEventListener('touchend', endDrag);
        svg.addEventListener('touchleave', endDrag);
        svg.addEventListener('touchcancel', endDrag);

        var selectedElement, offset, transform,
            bbox, minX, maxX, minY, maxY, confined;

        var boundaryX1 = 25;
        var boundaryX2 = 1600;
        var boundaryY1 = 25;
        var boundaryY2 = 900;

        function getMousePosition(evt) {
          var CTM = svg.getScreenCTM();
          if (evt.touches) { evt = evt.touches[0]; }
          return {
            x: (evt.clientX - CTM.e) / CTM.a,
            y: (evt.clientY - CTM.f) / CTM.d
          };
        }

        function startDrag(evt) {
          if (evt.target.classList.contains('draggable')) {
            selectedElement = evt.target;
            offset = getMousePosition(evt);

            // Make sure the first transform on the element is a translate transform
            var transforms = selectedElement.transform.baseVal;

            if (transforms.length === 0 || transforms.getItem(0).type !== SVGTransform.SVG_TRANSFORM_TRANSLATE) {
              // Create an transform that translates by (0, 0)
              var translate = svg.createSVGTransform();
              translate.setTranslate(0, 0);
              selectedElement.transform.baseVal.insertItemBefore(translate, 0);
            }

            // Get initial translation
            transform = transforms.getItem(0);
            offset.x -= transform.matrix.e;
            offset.y -= transform.matrix.f;

            confined = evt.target.classList.contains('confine');
            if (confined) {
                bbox = selectedElement.getBBox();
                minX = boundaryX1 - bbox.x;
                maxX = boundaryX2 - bbox.x - bbox.width;
                minY = boundaryY1 - bbox.y;
                maxY = boundaryY2 - bbox.y - bbox.height;
            }
          }
        }

        function drag(evt) {
          if (selectedElement) {
            evt.preventDefault();

            var coord = getMousePosition(evt);
            var dx = coord.x - offset.x;
            var dy = coord.y - offset.y;

            if (confined) {
                if (dx < minX) { dx = minX; }
                else if (dx > maxX) { dx = maxX; }
                if (dy < minY) { dy = minY; }
                else if (dy > maxY) { dy = maxY; }
            }

            transform.setTranslate(dx, (Math.round(dy / 10) * 10));
		  document.getElementById("x_coord").value = dx;
		  document.getElementById("y_coord").value = dy;          
		  
		  }
        }

        function endDrag(evt) {
          selectedElement = false;         

        }
      }
    ]]> </script>

How can I chang eit so it doesn’t use transform and instead just changes the x and y position

Heres the SVG with the dragging function.

<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1650 900" onload="makeDraggable(evt)">
<defs>
    <script type="text/javascript"><![CDATA[
      function makeDraggable(evt) {
        var svg = evt.target;

        svg.addEventListener('mousedown', startDrag);
        svg.addEventListener('mousemove', drag);
        svg.addEventListener('mouseup', endDrag);
        svg.addEventListener('mouseleave', endDrag);
        svg.addEventListener('touchstart', startDrag);
        svg.addEventListener('touchmove', drag);
        svg.addEventListener('touchend', endDrag);
        svg.addEventListener('touchleave', endDrag);
        svg.addEventListener('touchcancel', endDrag);

        var selectedElement, offset, transform,
            bbox, minX, maxX, minY, maxY, confined;

        var boundaryX1 = 25;
        var boundaryX2 = 1600;
        var boundaryY1 = 25;
        var boundaryY2 = 900;

        function getMousePosition(evt) {
          var CTM = svg.getScreenCTM();
          if (evt.touches) { evt = evt.touches[0]; }
          return {
            x: (evt.clientX - CTM.e) / CTM.a,
            y: (evt.clientY - CTM.f) / CTM.d
          };
        }

        function startDrag(evt) {
          if (evt.target.classList.contains('draggable')) {
            selectedElement = evt.target;
            offset = getMousePosition(evt);

            // Make sure the first transform on the element is a translate transform
            var transforms = selectedElement.transform.baseVal;

            if (transforms.length === 0 || transforms.getItem(0).type !== SVGTransform.SVG_TRANSFORM_TRANSLATE) {
              // Create an transform that translates by (0, 0)
              var translate = svg.createSVGTransform();
              translate.setTranslate(0, 0);
              selectedElement.transform.baseVal.insertItemBefore(translate, 0);
            }

            // Get initial translation
            transform = transforms.getItem(0);
            offset.x -= transform.matrix.e;
            offset.y -= transform.matrix.f;

            confined = evt.target.classList.contains('confine');
            if (confined) {
                bbox = selectedElement.getBBox();
                minX = boundaryX1 - bbox.x;
                maxX = boundaryX2 - bbox.x - bbox.width;
                minY = boundaryY1 - bbox.y;
                maxY = boundaryY2 - bbox.y - bbox.height;
            }
          }
        }

        function drag(evt) {
          if (selectedElement) {
            evt.preventDefault();

            var coord = getMousePosition(evt);
            var dx = coord.x - offset.x;
            var dy = coord.y - offset.y;

            if (confined) {
                if (dx < minX) { dx = minX; }
                else if (dx > maxX) { dx = maxX; }
                if (dy < minY) { dy = minY; }
                else if (dy > maxY) { dy = maxY; }
            }

            transform.setTranslate(dx, (Math.round(dy / 10) * 10));
		  document.getElementById("x_coord").value = dx;
		  document.getElementById("y_coord").value = dy;          
		  
		  }
        }

        function endDrag(evt) {
          selectedElement = false;         

        }
      }
    ]]> </script>
<pattern id="pattern"
           width="8" height="10"
           patternUnits="userSpaceOnUse"
           patternTransform="rotate(45 50 50)">
    <line stroke="#ffcccb" stroke-width="7px" y2="10"/>
  </pattern> 
  <pattern id="pattern2"
           width="8" height="10"
           patternUnits="userSpaceOnUse"
           patternTransform="rotate(45 50 50)">
    <line stroke="#ddd" stroke-width="7px" y2="10"/>
  </pattern> 

<style> 
svg  .row_text, svg .pod_text {
	fill:#999;
	font-size:1.7em;
}
svg .row{
      fill:url(#pattern2);
      stroke:#999;
      stroke-width:2;
}
svg  {
	background-image: url("../images/Data_Center.jpg");
	width: 100%;
	background-size: 100%;
    background-repeat: no-repeat;	
}
svg text.guide {
      fill:#f00;
}
svg line.guide {
	stroke:#f00;
	stroke-width:.2;
	stroke-dasharray:5;
	
}
rect.draggable {
      fill:url(#pattern);
      stroke:#DC143C;
      stroke-width:2;
	  cursor: move;
}
</style>  
</defs> 
<rect x="25" y="25" width="94" height="28" class="draggable" id="new_row" transform="translte(0,0)"/>
<rect x="151" y="97" width="28" height="220" class="row" />
<rect x="151" y="97" width="28" height="220" class="row" />
<text x="162" y="89" class="row_text" text-anchor="middle">One</text><rect x="331" y="85" width="40" height="28" class="row" />
<rect x="331" y="85" width="40" height="28" class="row" />
<text x="342" y="77" class="row_text" text-anchor="middle">two</text><rect x="43" y="144" width="28" height="76" class="row" />
<rect x="43" y="144" width="28" height="76" class="row" />
<text x="54" y="136" class="row_text" text-anchor="middle">t</text></svg>


What can be done is to record the initial x and y values when you start dragging:

          if (evt.target.classList.contains('draggable')) {
            selectedElement = evt.target;
            selectedElement.setAttribute("initialx", selectedElement.getAttribute("x"));
            selectedElement.setAttribute("initialy", selectedElement.getAttribute("y"));
            offset = getMousePosition(evt);

Then instead of setting the translate, we can set the x and y attributes.

          // transform.setTranslate(dx, (Math.round(dy / 10) * 10));
          var x = Math.round(Number(selectedElement.getAttribute("initialx")) + dx);
          var y = Math.round(Number(selectedElement.getAttribute("initialy")) + dy);
          selectedElement.setAttribute("x", x);
          selectedElement.setAttribute("y", y);

That gets it working, and you can drag things around where the x and y attribute are updated to change the position.

After that, removal of code relating to transforms can then be done, to dramatically simplify and shorten the code.

2 Likes

This topic was automatically closed 91 days after the last reply. New replies are no longer allowed.