我遇到了关于SVG文档中鼠标光标位置的问题。我想在HTML页面中使用 JavaScript 设计一个在拖动时跟随光标的电位计。
我尝试过evt.clientX / Y和evt.screenX / Y但是由于我的SVG在自动缩放,我的SVG里面的坐标是不同的。我现在一直在寻找答案,但我找不到任何解决方案(要么实时知道我的SVG重新缩放因子,要么在SVG坐标系统中具有鼠标位置功能)。
轮换将遵循一个简单的规则:
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;
用(xc; yc)作为旋转中心,用SVG里面鼠标的坐标替换所有evt.screenX / Y.
答案 0 :(得分:105)
请参阅此代码,该代码不仅展示了如何从屏幕空间转换为全局SVG空间,还展示了如何将SVG空间中的点转换为元素的转换空间:
http://phrogz.net/svg/drag_under_transformation.xhtml
简而言之:
// 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);
编辑:我根据您的需求创建了一个样本(尽管只在全球SVG空间中):
http://phrogz.net/svg/rotate-to-point-at-cursor.svg
它在上面添加了以下方法:
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)+')'
);
}
答案 1 :(得分:2)
@Phrogz:谢谢你的精彩榜样,我从那里学到了很多东西。我已经改变了下面的一些内容,使它变得有点容易。我认为就像我们在核心java中处理鼠标事件一样,我们也可以在这里处理相同的方式,所以我在你的例子中尝试了我的方式。
我已经删除了“rotateElement”功能,因为我认为它有些困难,如果有的话我会找到替代品。
见下面的代码:
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());
}
所以我所做的就是我刚刚将听众添加到小圆而不是整个SVG中,每次当鼠标移动时,我将从x, y
函数获得getCursor()
,如上所述,我将给出这个x, y
作为我行的x2, y2
,因为它不会翻译而且不会旋转。您必须将鼠标移动到圆圈然后慢慢移动,如果您的鼠标离开圆圈,则线条将不会移动,因为我们刚刚在右侧的小圆圈上添加了侦听器。
答案 2 :(得分:2)
获得正确的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;
}
但是,如果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;
不太好的想法是,在某些浏览器中,border属性也会移动坐标,而在其他浏览器中则不会。我发现,如果事件属性的x和y 不,则会发生转换。
if(event.x === undefined)
{
x -= parseFloat(style['border-left-width'].replace('px', ''));
y -= parseFloat(style['border-top-width'].replace('px', ''));
}
在此转换之后,x和y坐标可以超出范围,应该修复。但那不是思考。
let width = svg.width.baseVal.value;
let height = svg.height.baseVal.value;
if(x < 0 || y < 0 || x >= width || y >= height)
{
return;
}
此解决方案可用于click,mousemove,mousedown,...等等。 您可以在此处进行实时演示:https://codepen.io/martinwantke/pen/xpGpZB