2012-04-24 25 views
33

Występują problemy dotyczące położenia kursora myszy w dokumencie SVG. Chciałbym zaprojektować potencjometr, który będzie się podążał za kursorem po przeciągnięciu, używając JavaScript na stronie HTML.Pozycja myszy wewnątrz autoskalowania SVG

Próbowałem evt.clientX/Y i evt.screenX/Y, ale ponieważ mój SVG jest w autoskala, współrzędne wewnątrz mojego SVG są różne. Szukałem odpowiedzi od kilku dni, ale nie mogłem znaleźć żadnego rozwiązania (znałem mój współczynnik przeskalowywania SVG w czasie rzeczywistym lub posiadałem funkcję lokalizacji myszy w systemie współrzędnych SVG).

Obrót nastąpi proste reguły:

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;

Z (xc; yc) jako środkiem obrotu i zastępując wszystkie evt.screenX/Y współrzędnymi myszy w moim SVG.

+0

musisz pracować z macierzą transformacji, aby uzyskać prawidłowe współrzędne. Pomocne byłoby jsfiddle. – mihai

Odpowiedz

85

Zobacz ten kod, który nie tylko pokazuje, jak przekształcić z przestrzeni ekranu do globalnej przestrzeni SVG, ale także w jaki sposób przekształcić punkt z przestrzeni SVG do przekształcanej przestrzeni elementu:
http://phrogz.net/svg/drag_under_transformation.xhtml

W skrócie :

// 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: Utworzyłem próbki dopasowanego do swoich potrzeb (choć tylko w globalnej przestrzeni SVG):
http://phrogz.net/svg/rotate-to-point-at-cursor.svg

Dodaje się następującą metodę do powyższego:

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

Cześć, przepraszam, że nie odpowiedziałem wcześniej, faktem jest, że nie mogłem, używając firefox (nie wiem dlaczego). Więc widzę, że twój kod działa oddzielnie, ale wciąż mam do czynienia z dziwnymi problemami, wrócę do ciebie, gdy tylko znajdę sposób, żeby działał w moim projekcie! Dzięki – Riwall

+0

Wygląda na to, że mój getBBox() nie działa tak, jak powinien, więc nie mogę uzyskać właściwego punktu początkowego, którego używasz (muszę to zrobić automatycznie, aby element mógł być łatwo przeniesiony z inkscape, a nawet skopiowany/wklejono do innego svg). Próbowałem wielu rzeczy, ale nic nie działało. – Riwall

+0

@Riwall Proponuję, abyś zadał nowe pytanie dotyczące twojego problemu BBox; pamiętaj o dołączeniu prostego, uproszczonego testu (w kodzie w pytaniu lub JSFiddle), który powiela twój problem, i opisz OS/przeglądarkę/wersję, która przysporzy ci kłopotów. – Phrogz

2

@Phrogz: Dzięki za wspaniały przykład i dowiedziałem się od tego. Zmieniłem trochę tego, jak poniżej, aby było to trochę proste. Ponieważ myślę, że tak jak obsługujemy zdarzenia myszy w rdzeniu java, możemy również obsługiwać tę samą metodę, więc spróbowałem w swoim przykładzie.

Usunąłem funkcję "rotateElement", ponieważ uważam, że jest to trudne i znajduję substytut, jeśli tak.

Zobacz poniższy kod:

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()); 
} 

Więc co mam zrobić to właśnie dodane słuchacza tylko do wąskiego kręgu nie cały SVG i za każdym razem, gdy mysz przeniósł przez ciebie dostanę x, y z getCursor() funkcji, jak stwierdzono powyżej i dam to x, y jako x2, y2 z mojej linii, która nie tłumaczy i nie obraca się. Musisz poruszać myszą, aby zakreślić kółko, a następnie powoli przesuwać się, a jeśli mysz opuści kółko, linia nie będzie się poruszać, ponieważ właśnie dodaliśmy słuchacza tylko w małym okręgu.

0

Uzyskanie prawidłowej współrzędnej svg myszy jest trudne.Po pierwsze, powszechnym sposobem jest użycie clientX i clientY właściwości zdarzenia, a następnie odjęcie go za pomocą getBoundingClientRect() i clientLeft lub 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; 
} 

Ale, czy SVG ma informacje o stylu wyściółka większy niż zero, koordynować przesuwa. Tak więc informacja ta musi być również odejmowanie:

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; 

A nie tak miło jest myśleć, że w niektórych przeglądarkach właściwość border przesunąć również koordynować, a inne nie. Dowiedziałem się, że zmiana ma miejsce, jeśli X i Y właściwości zdarzenia jest , a nie dostępna.

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

Po tej transformacji współrzędne X i Y mogą być usunięte, co powinno zostać naprawione. Ale to nie myśl.

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

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

To rozwiązanie może służyć do klikania, przesuwania myszy, mousedown, ... i tak dalej. Możesz obejrzeć demonstrację na żywo tutaj: https://codepen.io/martinwantke/pen/xpGpZB

Powiązane problemy