Javascript 鼠标在自动缩放的 SVG 中的位置

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/10298658/
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

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-24 00:44:07  来源:igfitidea点击:

Mouse position inside autoscaled SVG

javascriptsvg

提问by Riwall

I am experiencing troubles concerning the position of mouse cursor inside my SVG document. I'd like to design a potentiometer that will follow the cursor when dragged, using JavaScriptin an HTML page.

我在 SVG 文档中遇到鼠标光标位置的问题。我想设计一个电位器,拖动时会跟随光标,在 HTML 页面中使用JavaScript

I tried evt.clientX/Y and evt.screenX/Y but as my SVG is in autoscale, coordinates inside my SVG are different. I have been searching for an answer for days now but I couldn't find any solution (either knowing my SVG rescaling factor in real time or have a function for mouse location in SVG coordinates system).

我试图evt.clientX / Y和evt.screenX / Y但是我的SVG是自动缩放,我的SVG内坐标是不同的。我几天来一直在寻找答案,但我找不到任何解决方案(要么实时知道我的 SVG 缩放因子,要么在 SVG 坐标系中具有鼠标定位功能)。

The rotation will follow a simple rule:

轮换将遵循一个简单的规则:

if ( evt.screenX < xc)

ang = Math.atan( (evt.screenY - yc)/(evt.screenX - xc) )*360/(2*Math.PI) - 90;
if( evt.screenX > xc )
ang = Math.atan( (evt.screenY - yc)/(evt.screenX - xc) )*360/(2*Math.PI) + 90;

如果(evt.screenX < xc)

ang = Math.atan( (evt.screenY - yc)/(evt.screenX - xc) )*360/(2*Math.PI) - 90;
if( evt.screenX > xc )
ang = Math.atan( (evt.screenY - yc)/(evt.screenX - xc) )*360/(2*Math.PI) + 90;

With (xc;yc) as center of rotation and replacing all evt.screenX/Y by the coordinates of the mouse inside my SVG.

以 (xc;yc) 作为旋转中心并用我的 SVG 内的鼠标坐标替换所有 evt.screenX/Y。

回答by Phrogz

See this code, which not only shows how to transform from screen space to global SVG space, but also how to transform a point from SVG space into the transformed space of an element:
http://phrogz.net/svg/drag_under_transformation.xhtml

看这段代码,它不仅展示了如何从屏幕空间转换到全局 SVG 空间,还展示了如何将一个点从 SVG 空间转换到元素的转换空间:http:
//phrogz.net/svg/drag_under_transformation.xhtml

In short:

简而言之:

// Find your root SVG element
var svg = document.querySelector('svg');

// Create an SVGPoint for future math
var pt = svg.createSVGPoint();

// Get point in global SVG space
function cursorPoint(evt){
  pt.x = evt.clientX; pt.y = evt.clientY;
  return pt.matrixTransform(svg.getScreenCTM().inverse());
}

svg.addEventListener('mousemove',function(evt){
  var loc = cursorPoint(evt);
  // Use loc.x and loc.y here
},false);

Edit: I've created a sample tailored to your needs (albeit only in global SVG space):
http://phrogz.net/svg/rotate-to-point-at-cursor.svg

编辑:我已经根据您的需要创建了一个示例(尽管仅在全局 SVG 空间中):http:
//phrogz.net/svg/rotate-to-point-at-cursor.svg

It adds the following method to the above:

它在上面添加了以下方法:

function rotateElement(el,originX,originY,towardsX,towardsY){
  var angle = Math.atan2(towardsY-originY,towardsX-originX);
  var degrees = angle*180/Math.PI + 90;
  el.setAttribute(
    'transform',
    'translate('+originX+','+originY+') ' +
      'rotate('+degrees+') ' +
      'translate('+(-originX)+','+(-originY)+')'
  );
}

回答by Mehul Popat

@Phrogz: Thanks for your wonderful example and I learned from that. I have changed some of it like below to make it a bit easy right. As I thinking that like we handle mouse events in core java we can also handle same way here so I tried my way in your example.

@Phrogz:感谢您提供的精彩示例,我从中吸取了教训。我已经像下面那样更改了其中的一些内容,使其变得容易一些。因为我认为就像我们在核心 Java 中处理鼠标事件一样,我们也可以在这里以相同的方式处理,所以我在您的示例中尝试了我的方式。

I have removed "rotateElement" function as I think that it is some difficult and i find a substitute if it.

我已经删除了“rotateElement”功能,因为我认为它有些困难,如果它我会找到替代品。

See below code:

见下面的代码:

var svg=document.getElementById("svg1");
var pt=svg.createSVGPoint();
var end_small=document.getElementById("end_small");
var line=document.getElementById("line1");

end_small.addEventListener('mousemove', function(evt) {

    var loc=getCursor(evt);
    end_small.setAttribute("cx",loc.x);
    end_small.setAttribute("cy",loc.y);

    loc = getCursor(evt); // will get each x,y for mouse move

    line.setAttribute('x2',loc.x); // apply it  as end points of line
    line.setAttribute('y2',loc.y); // apply it as end points of line

}, false);

function getCursor(evt) {
    pt.x=evt.clientX;
    pt.y=evt.clientY;
    return pt.matrixTransform(svg.getScreenCTM().inverse());
}

So what I have done is I have just added listener only to small circle not whole SVG and everytime when mouse moved by you I will get x, yfrom getCursor()function as stated above and I will give this x, yas x2, y2of my line thats it does not translate and does not rotate. You must move your mouse to to circle and then slowly move and if your mouse leave circle then line will not move as we have just added listener only on small circle right.

所以我所做的就是我刚才添加监听器只小圈不完整SVG,每次当鼠标被你感动我会x, ygetCursor()功能上面说,我要把这x, y作为x2, y2我行多数民众赞成在不翻译和不旋转。您必须将鼠标移至圆圈,然后缓慢移动,如果鼠标离开圆圈,则线条将不会移动,因为我们刚刚仅在右侧的小圆圈上添加了侦听器。

回答by Martin Wantke

Getting the correct svg mouse coordinate is tricky. First of all, a common way is to use the clientX and clientY of the event property an substract it with getBoundingClientRect() and clientLeft respectively clientTop.

获得正确的 svg 鼠标坐标很棘手。首先,一个常用的方法是使用事件属性的clientX 和clientY,分别用getBoundingClientRect() 和clientLeft 分别减去clientTop。

svg.addEventListener('click', event =>
{
    let bound = svg.getBoundingClientRect();

    let x = event.clientX - bound.left - svg.clientLeft - paddingLeft;
    let y = event.clientY - bound.top - svg.clientTop - paddingTop;
}

But, if the svg has a padding style information greater then zero, the coordinate is shifting. So this information must be also substract:

但是,如果 svg 的填充样式信息大于零,则坐标正在移动。所以这些信息也必须减去:

let paddingLeft = parseFloat(style['padding-left'].replace('px', ''));
let paddingTop = parseFloat(style['padding-top'].replace('px', ''));

let x = event.clientX - bound.left - svg.clientLeft - paddingLeft;
let y = event.clientY - bound.top - svg.clientTop - paddingTop;

And the not so nice think is, that in some browsers the border property also shift the coordinate, and in other not. I found out, that the shift takes place if the x and y of the event property is notavailable.

不太好的想法是,在某些浏览器中,边框属性也会移动坐标,而在其他浏览器中则不会。我发现,如果事件属性的 x 和 y不可用,则会发生转移。

if(event.x === undefined)
{
    x -= parseFloat(style['border-left-width'].replace('px', ''));
    y -= parseFloat(style['border-top-width'].replace('px', ''));
}

After this transformation the x and y coordinate can out of bound, that should be fix. But that not the think.

在此转换后,x 和 y 坐标可能超出范围,这应该是固定的。但那不是思考。

let width = svg.width.baseVal.value;
let height = svg.height.baseVal.value;

if(x < 0 || y < 0 || x >= width || y >= height)
{
    return;
}

This solution can use for click, mousemove, mousedown, ... and so on. You can reach a live demo here: https://codepen.io/martinwantke/pen/xpGpZB

此解决方案可用于单击、鼠标移动、鼠标按下等。您可以在此处访问现场演示:https: //codepen.io/martinwantke/pen/xpGpZB