SVG如何在内部矩阵上获取鼠标位置

时间:2014-02-18 11:35:25

标签: javascript html5 svg snap.svg

jsFiddle:http://jsfiddle.net/az6Ug/

使用Snap.svg库,我想拖动SVG并获得鼠标位置。我需要内部矩阵上的鼠标位置,而不是浏览器鼠标位置。 我有一个问题,即如果SVG所属的窗口上有任何滚动,计算出的鼠标位置会被滚动条上的滚动量偏移。

例如,没有滚动,那么它工作正常。或者在滚动条上使用10px的垂直滚动,鼠标位置计算为:true position + 10px。 如果有任何水平滚动,则是相同的交易:偏移滚动量。

在jsFiddle中,我使用一个矩形来显示拖动时计算的鼠标位置。 如您所见,如果没有滚动,则矩形将保留鼠标光标(左上角)。但是通过一些滚动,矩形偏离了鼠标光标。

虽然我正在使用Snap.svg库,但我只使用它来获取拖动事件,鼠标计算是纯Javascript。一个可能的解决方案是用滚动量减去X和Y,但我认为使用SVG的转换函数会有更好的方法。

var S;
var pt;
var svg
var box;

window.onload = function(){
    svg = $('#mysvg')[0];
    S = Snap(svg);

    pt = pt = svg.createSVGPoint(); // create the point

    // add the rectangle
    box = S.rect(10, 10, 50, 50);
    box.attr({ fill : 'red', stroke : 'none' });

    S.drag(
        function(dx, dy, posX, posY, e){
            //onmove
            pt.x = posX;
            pt.y = posY;

            // convert the mouse X and Y so that it's relative to the svg element
            var transformed = pt.matrixTransform(svg.getScreenCTM().inverse());
            box.attr({ x : transformed.x, y : transformed.y });
        },
        function(){
            //onstart
        },
        function(){
            //onend
        }
    );

}

2 个答案:

答案 0 :(得分:6)

以下是我在所有浏览器和所有viewPorts中使用的Javascript示例。它使用svg矩阵变换。事件附加到要拖动的每个元素。在示例中,我包括了html和svg的鼠标位置读数。

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
  <title>E - Universal Drag/Drop</title>
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
</head>
<body style='padding:10px;font-family:arial'>
<center>
<h4>Universal Drag/Drop</h4>
<div style='width:90%;background-color:gainsboro;text-align:justify;padding:10px;border-radius:6px;'>
This example uses matrix transforms, with object methods,  not strings. It can seamlessly drag/drop elements that have previously been transformed and reside it different viewPorts. It employs <b>getScreenCTM</b>,  <b>createSVGTransform</b> and binds the element to a <b>transform List</b>
</div>
<table>
<tr><td align=left>
 Scenerio:<br />
A 400x400 DIV contains an SVG with viewBox=0 0 330 330.<br />
1.) The blue rect element is contained in a &lt;g&gt;.<br />
2.) The &lt;g&gt; element has been transformed.<br />
3.) The maroon rect resides in a different viewPort.<br />
4.) The orange circle has been transformed.<br />
5.) Drag/Drop the circles and rectangles.<br />
</td>
<td align=left>
<div id="svgDiv" style='background-color:lightgreen;width:400px;height:400px;'>
<svg id="mySVG" width="100%" height="100%" viewBox="0 0 300 300" onmousemove="svgCursor(evt)">
<circle onmousedown=startDrag(evt) onmousemove=drag(evt) onmouseup=endDrag() id="redCircle" cx="120" cy="180" r="40" fill="red" stroke="black" stroke-width="2" />
<circle onmousedown=startDrag(evt) onmousemove=drag(evt) onmouseup=endDrag() id="orangeCircle" cx="200" cy="200" r="40" fill="orange" stroke="black" stroke-width="2" />
<svg viewBox="0 100 800 800">
<rect onmousedown=startDrag(evt) onmousemove=drag(evt) onmouseup=endDrag() id="maroonRect"  x="220" y="250" width="60" height="60" fill="maroon" stroke="black" stroke-width="2"  />
</svg>
<g id="myG" >
<rect onmousedown=startDrag(evt) onmousemove=drag(evt) onmouseup=endDrag()     id="blueRect"  x="220" y="250" width="60" height="60" fill="blue" stroke="black" stroke-width="2"  />
</g>
</svg>
</div>
</td>
<td align=left>
<b>HTML Page Values:</b><br />
<input type=text id=htmlMouseXValue size=1 />: mouse X<br />
<input type=text id=htmlMouseYValue size=1 />: mouse Y<br />
<br />
<b>SVG Image Values:</b><br />
<input type=text id=svgXValue size=1 />: svg X<br />
<input type=text id=svgYValue size=1 />: svg Y<br />
</td>
</tr></table>
<br />SVG Source:<br />
<textarea id=svgSourceValue style='font-size:110%;font-family:lucida console;width:90%;height:200px'></textarea>
<br />Javascript:<br />
<textarea id=jsValue style='border-radius:26px;font-size:110%;font-weight:bold;color:midnightblue;padding:16px;background-color:beige;border-width:0px;font-size:100%;font-family:lucida console;width:90%;height:400px'></textarea>
</center>
<div id='browserDiv' style='padding:3px;position:absolute;top:5px;left:5px;background-color:gainsboro;'></div>
<script id=myScript>
var TransformRequestObj
var TransList
var DragTarget=null;
var Dragging = false;
var OffsetX = 0;
var OffsetY = 0;
//---mouse down over element---
function startDrag(evt)
{
    if(!Dragging) //---prevents dragging conflicts on other draggable elements---
    {
        DragTarget = evt.target;
        //---reference point to its respective viewport--
        var pnt = DragTarget.ownerSVGElement.createSVGPoint();
        pnt.x = evt.clientX;
        pnt.y = evt.clientY;
        //---elements transformed and/or in different(svg) viewports---
        var sCTM = DragTarget.getScreenCTM();
        var Pnt = pnt.matrixTransform(sCTM.inverse());

        TransformRequestObj = DragTarget.ownerSVGElement.createSVGTransform()
        //---attach new or existing transform to element, init its transform list---
        var myTransListAnim=DragTarget.transform
        TransList=myTransListAnim.baseVal

        OffsetX = Pnt.x
        OffsetY = Pnt.y

        Dragging=true;
    }
}
//---mouse move---
function drag(evt)
{
    if(Dragging)
    {
        var pnt = DragTarget.ownerSVGElement.createSVGPoint();
        pnt.x = evt.clientX;
        pnt.y = evt.clientY;
        //---elements in different(svg) viewports, and/or transformed ---
        var sCTM = DragTarget.getScreenCTM();
        var Pnt = pnt.matrixTransform(sCTM.inverse());
        Pnt.x -= OffsetX;
        Pnt.y -= OffsetY;

        TransformRequestObj.setTranslate(Pnt.x,Pnt.y)
        TransList.appendItem(TransformRequestObj)
        TransList.consolidate()
    }
}
//--mouse up---
function endDrag()
{
    Dragging = false;
    svgSourceValue.value=svgDiv.innerHTML
}
//---onload---
function initTransforms()
{
//---place some transforms on the elements---

    //--- transform orange circle---
    var transformRequestObj=mySVG.createSVGTransform()
    var animTransformList=orangeCircle.transform
    var transformList=animTransformList.baseVal
    //---translate---
    transformRequestObj.setTranslate(130,-300)
    transformList.appendItem(transformRequestObj)
    transformList.consolidate()
    //----scale---
    transformRequestObj.setScale(.5,.9)
    transformList.appendItem(transformRequestObj)
    transformList.consolidate()
    //----skewY---
    transformRequestObj.setSkewY(52)
    transformList.appendItem(transformRequestObj)
    transformList.consolidate()

    //--init Transform on myG---
    var transformRequestObj=mySVG.createSVGTransform()
    var animTransformList=myG.transform
    var transformList=animTransformList.baseVal
    //---translate---
    transformRequestObj.setTranslate(-50,-120)
    transformList.appendItem(transformRequestObj)
    transformList.consolidate()
    //----skewX---
    transformRequestObj.setSkewX(15)
    transformList.appendItem(transformRequestObj)
    transformList.consolidate()
    //----skewY---
    transformRequestObj.setSkewY(20)
    transformList.appendItem(transformRequestObj)
    transformList.consolidate()
    //---rotate---
    transformRequestObj.setRotate(30,200,200)
    transformList.appendItem(transformRequestObj)
    transformList.consolidate()

}

document.onmousemove = htmCursor
//---event is the html event object---
function htmCursor(event)
{
    var event = event || window.event;
    myMouseX=event.clientX;
    myMouseY=event.clientY;
    myMouseX = myMouseX + document.documentElement.scrollLeft;
    myMouseY = myMouseY + document.documentElement.scrollTop;

    htmlMouseXValue.value=myMouseX
    htmlMouseYValue.value=myMouseY
}
//---evt is the svg event object--
function svgCursor(evt)
{
    var rect = svgDiv.getBoundingClientRect();
    svgXValue.value=evt.clientX-rect.left
    svgYValue.value=evt.clientY-rect.top
}
</script>
<script>
document.addEventListener("onload",init(),false)
function init()
{
    initTransforms()
    svgSourceValue.value=svgDiv.innerHTML
    jsValue.value=myScript.text
}
</script>

</body>

</html>

答案 1 :(得分:0)

也许你可以使用getCTM而不是getScreenCTM,所以......

var transformed = pt.matrixTransform(svg.getCTM().inverse());

我认为您可能需要调整类似

的内容
pt.x = posX - S.node.offsetLeft;
pt.y = posY - S.node.offsetTop;

jsfiddle here

更新:如上所述在Firefox中不起作用,您可以将clientX / Y与原始getScreenCTM一起使用,所以

pt.x = e.clientX
pt.y = e.clientY;

fiddle here

我猜你需要的不仅仅是这里的例子(在这种情况下你可以使用Snaps自己的拖动功能),所以很难判断这个解决方案是否适用于你需要的其他情况。