尽管伪造了getBoundingClientRect(),你如何将事件坐标转换为SVG坐标?

时间:2014-01-06 19:23:52

标签: javascript svg

我正在尝试根据鼠标的位置动态绘制SVG元素上的东西。不幸的是,我很难将鼠标坐标从mousemove事件转换为SVG元素的坐标空间。

这是我一直在测试的错误功能:

CylinderDemo.prototype.handleMouseMove = function(evt)
{
    debugEvent = evt;

    var bcr = evt.target.getBoundingClientRect();
    var x2 = evt.clientX - bcr.left;
    var y2 = evt.clientY - bcr.top;

    console.log(evt.target);
    //console.log(evt.clientY+" - "+evt.target.getBBox());

    var d = this.pathForCylinder(this.x0, this.y0, x2, y2, 30);
    this.cap.setAttributeNS(null, "d", d);
}

canvas.onmousemove = function(evt) {
self.handleMouseMove(evt);
}

问题是(至少在Firefox中)getBoundingClientRect()没有给我SVG对象的界限。它为我提供了SVG对象内可绘制对象的边界。当你将鼠标悬停在萤火虫的日志线上并突出显示可绘制元素的微小的子矩形时,它变得非常明显。这意味着我对坐标的转换会产生有缺陷的结果。

我应该使用什么技术将事件坐标系转换为SVG容器的坐标系?

我刚拼凑http://jsfiddle.net/7kvkq/来说明问题。

2 个答案:

答案 0 :(得分:12)

请勿使用getBoundingClientRect()。相反,使用getScreenCTM()

将点从屏幕空间转换为全局SVG空间
var pt = demo.createSVGPoint(); // demo is an SVGElement
demo.addEventListener('mousemove',function(evt) {
  pt.x = evt.clientX;
  pt.y = evt.clientY;
  var svgGlobal = pt.matrixTransform(demo.getScreenCTM().inverse());
  // svgGlobal.x and svgGlobal.y are now in SVG coordinates
},false);

修正演示:http://jsfiddle.net/7kvkq/3/

如果您需要从屏幕空间转换为元素的局部变换,请使用getTransformToElement()进一步转换这一点:

var elTransform = demo.getTransformToElement(someElement);
var elLocal     = svgGlobal.matrixTransform(elTransform );

getTransformToElement()的演示:http://jsfiddle.net/7kvkq/4/

为了获得更好的性能,不是将点变换两次并创建中间点,而是将矩阵组合成一个并使用它来转换坐标:

var demo = document.querySelector('svg'),
    pt   = demo.createSVGPoint(),
    g    = demo.querySelector('#myGroup');

// Assumes that the group does not move with respect to the SVG;
// if so, re-calculate this as appropriate.
var groupXForm = demo.getTransformToElement(g);
demo.addEventListener('mousemove',function(evt) {
    pt.x = evt.clientX;
    pt.y = evt.clientY;
    var xform = groupXForm.multiply(demo.getScreenCTM().inverse());
    var localPoint = pt.matrixTransform(xform);
    // localPoint.x/localPoint.y are the equivalent of your mouse position
},false);

您可以在我的网站上看到使用这些技术的演示:
http://phrogz.net/svg/drag_under_transformation.xhtml

答案 1 :(得分:0)

事件目标可能是也可能不是SVG容器。请参阅MDN Documentation。如果要获取容器的边界框,请直接在容器上调用getBoundingClientRect。我把你的小提琴分到了这里:

http://jsfiddle.net/4RF75/1/

另外,如果可以确定SVG元素不会改变大小,那么缓存边界框可能是个好主意,因为(特别是在WebKit浏览器上)getBoundingClientRect会触发布局,这也可能是在事件处理程序中做的很贵。