Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    primarykey
    data
    text
    <p>Here's a demo I made that uses SVG to set four inputs, and vice-versa:<br> <a href="http://phrogz.net/svg/complex-plane-picker.xhtml" rel="nofollow">http://phrogz.net/svg/complex-plane-picker.xhtml</a></p> <p>The top two inputs are the X and Y (real and complex components); the bottom two are polar coordinates (magnitude and angle).</p> <p>There's likely more code in there than you need (auto-scaling as you get near the edge/middle, drawing updated axis ticks), which may make it more confusing as a demo than helpful. </p> <p>In general, though, you need to:</p> <ul> <li>Have an event listener on the <code>keyup</code> or <code>change</code> or <code>input</code> events of the inputs, and update the SVG to match accordingly.</li> <li>Add dragging to your SVG element, and during dragging calculate the appropriate value(s) and set the <code>.value</code> for the input(s) accordingly.</li> </ul> <p>As a tip, during dragging don't attempt to detect <code>mousemove</code> on your draggable element itself. If the mouse moves outside this, you'll stop getting drag events and it won't keep up. Instead, I generally use this logic:</p> <ul> <li>On <code>mousedown</code>: <ol> <li>Record the current cursor position</li> <li>Register a <code>mousemove</code> handler <strong>on the root of the document</strong></li> <li>Register a <code>mouseup</code> handler <strong>on the root of the document</strong></li> </ol></li> <li>On <code>mousemove</code>: <ol> <li>Detect the screenspace delta between the current cursor position and the position at drag start, and use this to <a href="http://phrogz.net/svg/drag_under_transformation.xhtml" rel="nofollow">calculate the delta in the local space of the element being dragged</a> to update its transformation.</li> </ol></li> <li>On <code>mouseup</code>: <ol> <li>Unregister the <code>mousemove</code> handler.</li> </ol></li> </ul> <p>Here's the full source code, in the unlikely event that my site is down:</p> <pre><code>&lt;!DOCTYPE HTML&gt; &lt;html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en"&gt;&lt;head&gt; &lt;meta http-equiv="content-type" content="application/xhtml+xml; charset=utf-8" /&gt; &lt;title&gt;SVG Complex Plane Input&lt;/title&gt; &lt;style type="text/css" media="screen"&gt; html, body { background:#eee; margin:0 } p { margin:0.5em; text-align:center } .complex-plane-picker svg { width:200px; height:200px; display:block; margin:1em auto; stroke-linejoin:round; stroke-linecap:round; pointer-events:all; } .complex-plane-picker rect.bg { fill:#fff; } .complex-plane-picker .axes * { stroke:#ccc; fill:none } .complex-plane-picker .dot { fill:#090; fill-opacity:0.4; stroke:#000; stroke-opacity:0.6; cursor:move; } .complex-plane-picker input { width:6em; } .complex-plane-picker .labels { stroke:none; font-size:6px; font-family:'Verdana'; text-anchor:middle; alignment-baseline:middle; } .complex-plane-picker .scalers { pointer-events:all; } &lt;/style&gt; &lt;/head&gt;&lt;body&gt; &lt;p&gt;Drag the dot to set the complex value.&lt;br/&gt; TODO: Hold down shift to affect only the magnitude. Hold down alt to affect only the angle. Hold down both to snap to the nearest real- or imaginary-only value.&lt;/p&gt; &lt;div class="complex-plane-picker"&gt; &lt;p class="rectangular"&gt;&lt;input type="number" value="0" class='real' /&gt;+&lt;input type="number" value="0" class='imaginary' /&gt;i&lt;/p&gt; &lt;svg viewBox="-50 -50 100 100" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" baseProfile="full"&gt; &lt;rect class="bg" x="-50" y="-50" width="100" height="100" /&gt; &lt;g class="scalers"&gt; &lt;rect x="-5" y="-5" width="10" height="10" fill="#ffc"/&gt; &lt;rect x="-45" y="-45" width="90" height="90" fill="none" stroke="#ffc" stroke-width="10" /&gt; &lt;/g&gt; &lt;g class="axes"&gt; &lt;g&gt; &lt;line x1="-49" y1="0" x2="49" y2="0" /&gt; &lt;polyline points="-44,5 -49,0 -44,-5" /&gt; &lt;polyline points="44,5 49,0 44,-5" /&gt; &lt;/g&gt; &lt;g transform="rotate(90)"&gt; &lt;line x1="-49" y1="0" x2="49" y2="0" /&gt; &lt;polyline points="-44,5 -49,0 -44,-5" /&gt; &lt;polyline points="44,5 49,0 44,-5" /&gt; &lt;/g&gt; &lt;/g&gt; &lt;g class="labels" id="labels"&gt; &lt;text x="0" y="0"&gt;0&lt;/text&gt; &lt;text x="0" y="0"&gt;0&lt;/text&gt; &lt;text x="0" y="0"&gt;0&lt;/text&gt; &lt;text x="0" y="0"&gt;0&lt;/text&gt; &lt;/g&gt; &lt;use class="labels" xlink:href="#labels" transform="rotate(90)" /&gt; &lt;circle class="dot" r="4" /&gt; &lt;/svg&gt; &lt;p class="polar"&gt;&lt;input type="number" value="0" class='magnitude' /&gt;@&lt;input type="number" value="0" class='angle'/&gt;°&lt;/p&gt; &lt;/div&gt; &lt;script type="text/javascript"&gt;&lt;![CDATA[ var svg = document.getElementsByTagName('svg')[0]; var svgNS = svg.getAttribute('xmlns'); var pt = svg.createSVGPoint(); function mxy(evt){ pt.x = evt.clientX; pt.y = evt.clientY; return pt.matrixTransform(svg.getScreenCTM().inverse()); } var dot = document.querySelector('.complex-plane-picker .dot'); var real = document.querySelector('.real'); var imag = document.querySelector('.imaginary'); var size = document.querySelector('.magnitude'); var angl = document.querySelector('.angle'); var labels = svg.querySelectorAll('.complex-plane-picker #labels text'); var scale, maxValue; var updateScale = function(newScale){ scale = newScale; maxValue = 50*scale; var tickSize=1e-6; var e=-5; var f=-1; var fs = [1,2,5] while (tickSize/scale &lt; 10){ if (++f%fs.length==0) ++e; tickSize = fs[f%fs.length]*Math.pow(10,e); } for (var i=labels.length;i;--i){ labels[i-1].firstChild.nodeValue = (tickSize*i).toString().replace(/(\.0*[^0]+)0{3,}.*/,'$1'); labels[i-1].setAttribute('y', -tickSize*i/scale); } // updateRectangularFromDot(); // updatePolarFromDot(); }; var rescaleAsNeeded = function(x,y,jump){ var scaleFactor = 1.03; if (jump &amp;&amp; (x&gt;maxValue || y&gt;maxValue || (x&lt;maxValue*0.1 &amp;&amp; y&lt;maxValue*0.1))){ updateScale(Math.max(x,y)*2/50); } else if (x&gt;maxValue*0.8 || y&gt;maxValue*0.8) updateScale(scale*scaleFactor); else if (x&lt;maxValue*0.1 &amp;&amp; y&lt;maxValue*0.1) updateScale(scale/scaleFactor); }; var updateFromRectangular = function(){ var x = real.value*1; var y = imag.value*1; rescaleAsNeeded(x,y,true); dot.cx.baseVal.value = x/scale; dot.cy.baseVal.value = -y/scale; updatePolarFromDot(); }; real.addEventListener('input',updateFromRectangular,false); imag.addEventListener('input',updateFromRectangular,false); var updateFromPolar = function(){ var hyp = size.value*1; var rad = angl.value*Math.PI/180; var x = Math.cos(rad)*hyp; var y = Math.sin(rad)*hyp; rescaleAsNeeded(x,y,true); dot.cx.baseVal.value = x/scale; dot.cy.baseVal.value = -y/scale; updateRectangularFromDot(); }; size.addEventListener('input',updateFromPolar,false); angl.addEventListener('input',updateFromPolar,false); var updateRectangularFromDot = function(){ real.value = ( dot.cx.baseVal.value*scale).toFixed(2); imag.value = (-dot.cy.baseVal.value*scale).toFixed(2); }; var updatePolarFromDot = function(){ var x = dot.cx.baseVal.value*scale; var y = -dot.cy.baseVal.value*scale; size.value = Math.sqrt(x*x+y*y).toFixed(2); angl.value = (Math.atan2(y,x)*180/Math.PI).toFixed(1); } var dragging = false; dot.addEventListener('mousedown',function(evt){ var offset = mxy(evt); dragging = true; offset.x = dot.cx.baseVal.value - offset.x; offset.y = dot.cy.baseVal.value - offset.y; var scaleTimer; var move = function(evt){ clearTimeout(scaleTimer); var now = mxy(evt); var x = offset.x + now.x; var y = -(offset.y + now.y); dot.cx.baseVal.value = x; dot.cy.baseVal.value = -y; x = Math.abs(x)*scale, y=Math.abs(y)*scale; var oldScale = scale; rescaleAsNeeded(x,y); updatePolarFromDot(); updateRectangularFromDot(); if (oldScale != scale) scaleTimer = setTimeout(function(){move(evt)},1000/30); }; document.documentElement.style.userSelect = document.documentElement.style.MozUserSelect = document.documentElement.style.webkitUserSelect = 'none'; svg.addEventListener('mousemove',move,false); document.documentElement.addEventListener('mouseup',function(){ dragging = false; clearTimeout(scaleTimer); svg.removeEventListener('mousemove',move,false); document.documentElement.style.userSelect = document.documentElement.style.MozUserSelect = document.documentElement.style.webkitUserSelect = ''; },false); },false); dot.addEventListener('dblclick',function(){ dot.cx.baseVal.value = dot.cy.baseVal.value = 0; updateScale(1.0); updatePolarFromDot(); updateRectangularFromDot(); },false); updateScale(1.0); ]]&gt;&lt;/script&gt; &lt;/body&gt;&lt;/html&gt; </code></pre>
    singulars
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload