Положение мыши внутри автомасштабируемого SVG
Я испытываю проблемы относительно положения курсора мыши внутри моего документа SVG. Я хотел бы создать потенциометр, который будет следовать за курсором при перетаскивании, используя JavaScript на странице HTML.
Я попробовал 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) в качестве центра вращения и заменяя все evt.screenX/Y координатами мыши внутри моего SVG.
Ответы
Ответ 1
Смотрите этот код, который не только показывает, как преобразовать из пространства экрана в глобальное пространство 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)+')'
);
}
Ответ 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
моей строки, которая не преобразуется и не вращается. Вы должны перемещать мышь в круг, а затем медленно перемещаться, и если ваша мышь покидает круг, линия не будет двигаться, поскольку мы только что добавили слушателя только на маленьком круге вправо.
Ответ 3
Получение правильной координаты мыши 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;
И не очень приятно думать, что в некоторых браузерах свойство границы также сдвигает координату, а в другом нет. Я узнал, что сдвиг происходит, если х и у свойства события недоступны.
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;
}
Это решение может использоваться для кликов, mousemove, mousedown,... и так далее.
Вы можете найти демо-версию здесь: https://codepen.io/martinwantke/pen/xpGpZB