SVG与变换矩阵坐标

时间:2011-01-31 12:26:32

标签: html5 svg

我想在矩形元素

上实现像svg-edit这样的功能
  1. 旋转矩形
  2. 调整大小
  3. 拖动
  4. 旋转SVG矩形它工作正常,但是当我想调整矩形大小时它有问题。坐标不正常;我使用变换矩阵进行旋转 targetelement.setAttribute(transform,rotate(45,cx,cy)) 但是当元素旋转时,坐标移动。我也使用inverse函数反转变换矩阵它解决了问题,但它不能用于拖动函数。

2 个答案:

答案 0 :(得分:100)

我已经在我的网站上创建了一个我相信你所描述的工作示例:
http://phrogz.net/svg/drag_under_transformation.xhtml

通常,您可以通过以下方式将鼠标光标转换为对象的本地空间:

  1. 创建mousemove事件处理程序:

    var svg = document.getElementsByTagName('svg')[0];
    document.documentElement.addEventListener('mousemove',function(evt){
      ...
    },false);
    
  2. 在该事件处理程序中,将鼠标坐标(以像素为单位)转换为SVG文档的全局空间:

    var pt = svg.createSVGPoint();
    pt.x = evt.clientX;
    pt.y = evt.clientY;
    var globalPoint = pt.matrixTransform(svg.getScreenCTM().inverse());
    
  3. 将全局点转换为要拖动的对象的空间:

    var globalToLocal = dragObject.getTransformToElement(svg).inverse();
    var inObjectSpace = globalPoint.matrixTransform( globalToLocal );
    
  4. 对于Stack Overflow后代,这里是我的SVG + XHTML演示的完整来源(如果我的网站关闭):

    <!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:!@phrogz.net">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>
    

答案 1 :(得分:1)

对于使用Chrome的用户,请在

之后添加以下行
var pt    = svg.createSVGPoint();

SVGElement.prototype.getTransformToElement = SVGElement.prototype.getTransformToElement || function(elem) {
  return elem.getScreenCTM().inverse().multiply(this.getScreenCTM());
};

此处有更多信息:https://github.com/cpettitt/dagre-d3/issues/202