web-dev-qa-db-ja.com

変換マトリックスを使用したSVG座標

長方形要素にsvg-editなどの機能を実装したい

  1. 長方形を回転
  2. サイズ変更
  3. 引っ張る

SVGの長方形を回転させても問題なく動作しますが、長方形のサイズを変更したい場合は問題があります。座標が正しく機能していません。変換行列を使用してtargetelement.setAttribute(transform,rotate(45,cx,cy))を回転させますが、要素が回転すると座標が移動します。また、inverse関数を使用して変換行列を反転し、問題を解決しますが、ドラッグ関数では機能しません。

29
Abdul Rauf

私がここに私のサイトで説明していると私が信じていることの実際的な例を作成しました:
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 );
    

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:[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>
100
Phrogz

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

1
Rumen Tabakov