Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

SVG coordinates with transform matrix

Tags:

html

svg

I want to implement the functionality like svg-edit on rectangle element

  1. Rotate rectangle
  2. Resizing
  3. Drag

Rotating the SVG rectangle it works fine, but when I want to resize the rectangle it has a problem. The coordinates are not working right; I use the transform matrix to rotate targetelement.setAttribute(transform,rotate(45,cx,cy)) but when the element has been rotated the coordinates are moved. I'm also using inverse function to inverse the transform matrix it resolves the problem but its not working with drag function.

like image 581
Abdul Rauf Avatar asked Jan 31 '11 12:01

Abdul Rauf


People also ask

What is transform matrix in SVG?

The transform attribute defines a list of transform definitions that are applied to an element and the element's children. Note: As of SVG2, transform is a presentation attribute, meaning it can be used as a CSS property.

What is CTM SVG?

For each given element, the accumulation of all transformations that have been defined on the given element and all of its ancestors up to and including the element that established the current viewport (usually, the 'svg' element which is the most immediate ancestor to the given element) is called the current ...

What are SVG coordinates?

For all elements, SVG uses a coordinate system or grid system similar to the one used by canvas (and by a whole lot of other computer drawing routines). That is, the top left corner of the document is considered to be the point (0,0), or point of origin.

How do I rotate a path in SVG?

Just use the element type selector and add the transform: rotate(180deg) property to it like in the below snippet. Show activity on this post. Inline you would do it like this: x-axis flip : transform="scale(-1 1)"


1 Answers

I have created a working example of what I believe you are describing on my site here:
http://phrogz.net/svg/drag_under_transformation.xhtml

In general, you convert the mouse cursor into the local space of an object by:

  1. Creating a mousemove event handler:

    var svg = document.getElementsByTagName('svg')[0]; document.documentElement.addEventListener('mousemove',function(evt){   ... },false); 
  2. In that event handler, convert the mouse coordinates (in pixels) into the global space of your SVG document:

    var pt = svg.createSVGPoint(); pt.x = evt.clientX; pt.y = evt.clientY; var globalPoint = pt.matrixTransform(svg.getScreenCTM().inverse()); 
  3. Convert the global point into the space of the object you are dragging:

    var globalToLocal = dragObject.getTransformToElement(svg).inverse(); var inObjectSpace = globalPoint.matrixTransform( globalToLocal ); 

For Stack Overflow posterity, here's the full source of my SVG+XHTML demo (in case my site is down):

<!DOCTYPE HTML> <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en"><head>  <meta http-equiv="content-type" content="application/xhtml+xml;charset=utf-8"/> <title>Dragging Transformed SVG Elements</title> <style type="text/css" media="screen">   html, body {     background:#eee; margin:0;     user-select:none; -moz-user-select:none; -webkit-user-select:none;   }   p { margin:0.5em; text-align:center }   svg {     position:absolute; top:5%; left:5%; width:90%; height:90%;     background:#fff; border:1px solid #ccc   }   svg rect { stroke:#333 }   svg .drag { cursor:move }   svg .sizer { opacity:0.3; fill:#ff0; stroke:#630;}   #footer {     position:absolute; bottom:0.5em; margin-bottom:0;     width:40em; margin-left:-20em; left:50%; color:#666;     font-style:italic; font-size:85%   }   #dragcatch { position:absolute; left:0; right:0; top:0; bottom:0; z-index:-1} </style> </head><body> <p>Showing how to drag points inside a transformation hierarchy.</p> <svg viewBox="0 0 200 100" xmlns="http://www.w3.org/2000/svg"  xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" baseProfile="full">   <g transform="scale(1.2,0.8)">     <rect transform="translate(50,20) rotate(30)"      class="drag resize" x="50" y="30" width="50" height="30" fill="#69c" />     <rect class="drag resize" x="5" y="5" width="90" height="50" fill="#c66" />   </g> </svg> <p id="footer">   Copyright © 2011 <a href="mailto:[email protected]">Gavin Kistner</a>.    Comments/criticisms welcome. </p> <script type="text/javascript"><![CDATA[   var svg   = document.getElementsByTagName('svg')[0];   var svgNS = svg.getAttribute('xmlns');   var pt    = svg.createSVGPoint();    function createOn(root,name,prop){     var el = document.createElementNS(svgNS,name);     for (var a in prop) if (prop.hasOwnProperty(a)) el.setAttribute(a,prop[a]);     return root.appendChild(el);   }    function rectCorner(rect){     pt.x = rect.x.animVal.value + rect.width.animVal.value;     pt.y = rect.y.animVal.value + rect.height.animVal.value;     return pt.matrixTransform(rect.getTransformToElement(svg));   }    function pointIn(el,x,y){     pt.x = x; pt.y = y;     return pt.matrixTransform(el.getTransformToElement(svg).inverse());   }    function cursorPoint(evt){     pt.x = evt.clientX; pt.y = evt.clientY;     return pt.matrixTransform(svg.getScreenCTM().inverse());   }    // Make all rects resizable before drag, so the drag handles become drag   for (var a=svg.querySelectorAll('rect.resize'),i=0,len=a.length;i<len;++i){     (function(rect){       var dot = createOn(svg,'circle',{'class':'drag sizer',cx:0,cy:0,r:5});       var moveDotToRect = function(){         var corner = rectCorner(rect);         dot.setAttribute('cx',corner.x);         dot.setAttribute('cy',corner.y);       }       moveDotToRect();       rect.addEventListener('dragged',moveDotToRect,false);       dot.addEventListener('dragged',function(){         var rectXY = pointIn(rect,dot.cx.animVal.value,dot.cy.animVal.value);         var w = Math.max( rectXY.x-rect.x.animVal.value, 1 );         var h = Math.max( rectXY.y-rect.y.animVal.value, 1 );         rect.setAttribute('width', w);         rect.setAttribute('height',h);       },false);     })(a[i]);   }    for (var a=svg.querySelectorAll('.drag'),i=0,len=a.length;i<len;++i){     (function(el){       var onmove; // make inner closure available for unregistration       el.addEventListener('mousedown',function(e){         el.parentNode.appendChild(el); // move to top         var x = el.tagName=='circle' ? 'cx' : 'x';         var y = el.tagName=='circle' ? 'cy' : 'y';         var mouseStart   = cursorPoint(e);         var elementStart = { x:el[x].animVal.value, y:el[y].animVal.value };         onmove = function(e){           var current = cursorPoint(e);           pt.x = current.x - mouseStart.x;           pt.y = current.y - mouseStart.y;           var m = el.getTransformToElement(svg).inverse();           m.e = m.f = 0;           pt = pt.matrixTransform(m);           el.setAttribute(x,elementStart.x+pt.x);           el.setAttribute(y,elementStart.y+pt.y);           var dragEvent = document.createEvent("Event");           dragEvent.initEvent("dragged", true, true);           el.dispatchEvent(dragEvent);         };         document.body.addEventListener('mousemove',onmove,false);       },false);       document.body.addEventListener('mouseup',function(){         document.body.removeEventListener('mousemove',onmove,false);       },false);     })(a[i]);   } ]]></script> <div id="dragcatch"></div> </body></html> 
like image 180
Phrogz Avatar answered Sep 19 '22 23:09

Phrogz