这是我制作的一个演示,它使用 SVG 设置四个输入,反之亦然: http:
//phrogz.net/svg/complex-plane-picker.xhtml
前两个输入是 X 和 Y(实数和复数分量);底部两个是极坐标(大小和角度)。
那里的代码可能比您需要的要多(靠近边缘/中间时自动缩放,绘制更新的轴刻度),这可能会使它作为演示更令人困惑而不是有用。
不过,一般来说,您需要:
keyup
在输入的or change
or事件上有一个事件侦听input
器,并相应地更新 SVG 以匹配。
- 将拖动添加到您的 SVG 元素,并在拖动期间计算适当的值并相应地设置
.value
输入。
作为提示,在拖动过程中不要尝试检测mousemove
您的可拖动元素本身。如果鼠标移到此之外,您将停止获得拖动事件并且它不会跟上。相反,我通常使用这个逻辑:
- 开
mousedown
:
- 记录当前光标位置
- 在文档的根目录上注册一个
mousemove
处理程序
- 在文档的根目录上注册一个
mouseup
处理程序
- 开
mousemove
:
- 检测当前光标位置和拖动开始位置之间的屏幕空间增量,并使用它来计算被拖动元素的本地空间中的增量以更新其变换。
- 开
mouseup
:
- 取消注册
mousemove
处理程序。
这是完整的源代码,万一我的网站关闭了:
<!DOCTYPE HTML>
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en"><head>
<meta http-equiv="content-type" content="application/xhtml+xml; charset=utf-8" />
<title>SVG Complex Plane Input</title>
<style type="text/css" media="screen">
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; }
</style>
</head><body>
<p>Drag the dot to set the complex value.<br/> 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.</p>
<div class="complex-plane-picker">
<p class="rectangular"><input type="number" value="0" class='real' />+<input type="number" value="0" class='imaginary' />i</p>
<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">
<rect class="bg" x="-50" y="-50" width="100" height="100" />
<g class="scalers">
<rect x="-5" y="-5" width="10" height="10" fill="#ffc"/>
<rect x="-45" y="-45" width="90" height="90" fill="none" stroke="#ffc" stroke-width="10" />
</g>
<g class="axes">
<g>
<line x1="-49" y1="0" x2="49" y2="0" />
<polyline points="-44,5 -49,0 -44,-5" />
<polyline points="44,5 49,0 44,-5" />
</g>
<g transform="rotate(90)">
<line x1="-49" y1="0" x2="49" y2="0" />
<polyline points="-44,5 -49,0 -44,-5" />
<polyline points="44,5 49,0 44,-5" />
</g>
</g>
<g class="labels" id="labels">
<text x="0" y="0">0</text>
<text x="0" y="0">0</text>
<text x="0" y="0">0</text>
<text x="0" y="0">0</text>
</g>
<use class="labels" xlink:href="#labels" transform="rotate(90)" />
<circle class="dot" r="4" />
</svg>
<p class="polar"><input type="number" value="0" class='magnitude' />@<input type="number" value="0" class='angle'/>°</p>
</div>
<script type="text/javascript"><![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 < 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 && (x>maxValue || y>maxValue || (x<maxValue*0.1 && y<maxValue*0.1))){
updateScale(Math.max(x,y)*2/50);
} else if (x>maxValue*0.8 || y>maxValue*0.8) updateScale(scale*scaleFactor);
else if (x<maxValue*0.1 && y<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);
]]></script>
</body></html>