Html 带有变换矩阵的 SVG 坐标
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/4850821/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
SVG coordinates with transform matrix
提问by Abdul Rauf
I want to implement the functionality like svg-edit on rectangle element
我想在矩形元素上实现像 svg-edit 这样的功能
- Rotate rectangle
- Resizing
- 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.
旋转 SVG 矩形效果很好,但是当我想调整矩形的大小时出现问题。坐标不正常;我使用变换矩阵来旋转
targetelement.setAttribute(transform,rotate(45,cx,cy))
但是当元素被旋转时坐标被移动。我还使用inverse
函数来反转变换矩阵,它解决了问题,但它不能使用拖动函数。
回答by Phrogz
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
我已经创建了一个我相信你在我的网站上描述的工作示例:http:
//phrogz.net/svg/drag_under_transformation.xhtml
In general, you convert the mouse cursor into the local space of an object by:
通常,您可以通过以下方式将鼠标光标转换为对象的局部空间:
Creating a
mousemove
event handler:var svg = document.getElementsByTagName('svg')[0]; document.documentElement.addEventListener('mousemove',function(evt){ ... },false);
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());
Convert the global point into the space of the object you are dragging:
var globalToLocal = dragObject.getTransformToElement(svg).inverse(); var inObjectSpace = globalPoint.matrixTransform( globalToLocal );
创建
mousemove
事件处理程序:var svg = document.getElementsByTagName('svg')[0]; document.documentElement.addEventListener('mousemove',function(evt){ ... },false);
在该事件处理程序中,将鼠标坐标(以像素为单位)转换为 SVG 文档的全局空间:
var pt = svg.createSVGPoint(); pt.x = evt.clientX; pt.y = evt.clientY; var globalPoint = pt.matrixTransform(svg.getScreenCTM().inverse());
将全局点转换为您正在拖动的对象的空间:
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):
对于 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>
回答by Rumen Tabakov
For those who use Chrome please add the following lines after
对于那些使用 Chrome 的人,请在后面添加以下几行
var pt = svg.createSVGPoint();
SVGElement.prototype.getTransformToElement = SVGElement.prototype.getTransformToElement || function(elem) {
return elem.getScreenCTM().inverse().multiply(this.getScreenCTM());
};
More info here: https://github.com/cpettitt/dagre-d3/issues/202