Javascript 通过可拖动的SVG形状设置HTML输入值?(或画布)
我正在尝试组装一个GUI来设置一些定价。Javascript 通过可拖动的SVG形状设置HTML输入值?(或画布),javascript,html,svg,Javascript,Html,Svg,我正在尝试组装一个GUI来设置一些定价。 有三个类别以图形方式表示,两个滑块用于设置建议的买入/卖出价格 作为练习,我拼凑了一些原型并将它们放在一起。 请快速看一下,它将比我的话更有效地解释它:) (请原谅我的JS…还没有重构) 因为我们的目标是让滑块在HTML输入中设置浮点,所以我倾向于SVG 我的第一个请求只是对我选择的SVG解决方案进行同行评审。。。只是想确保我不会遇到一些固有的和痛苦的障碍。我也对其他解决方案持开放态度 第二个问题。有没有人有好的链接/示例可以帮助我理解如何根据形状的位置
有三个类别以图形方式表示,两个滑块用于设置建议的买入/卖出价格 作为练习,我拼凑了一些原型并将它们放在一起。
请快速看一下,它将比我的话更有效地解释它:)
(请原谅我的JS…还没有重构) 因为我们的目标是让滑块在HTML输入中设置浮点,所以我倾向于SVG
非常感谢所有的帮助。这里是我制作的一个演示,它使用SVG设置四个输入,反之亦然:
前两个输入是X和Y(实部和复部);底部两个是极坐标(幅值和角度) 那里可能有比您需要的更多的代码(当您接近边缘/中间时自动缩放,绘制更新的轴标记),这可能会使演示更混乱,而不是更有用 但是,一般来说,您需要:
- 在输入的
或键控
或更改
事件上有一个事件侦听器,并相应地更新SVG以匹配输入
- 将拖动添加到SVG元素中,并在拖动过程中计算适当的值,并相应地为输入设置
.value
mousemove
。如果鼠标移到这个范围之外,您将停止获取拖动事件,它将无法跟上。相反,我通常使用以下逻辑:
- 在
上:mousedown
- 记录当前光标位置
- 在文档的根目录上注册
handlermousemove
- 在文档的根目录上注册
handlermouseup
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>
SVG复平面输入
html,正文{背景:#eee;边距:0}
p{边距:0.5em;文本对齐:中心}
.complex plane picker svg{width:200px;height:200px;display:block;margin:1em auto;stroke linejoin:round;stroke linecap:round;指针事件:all;}
.complex plane picker rect.bg{fill:#fff;}
.complex plane picker.axes*{stroke:#ccc;fill:none}
.complex plane picker.dot{fill:#090;填充不透明度:0.4;笔划:#000;笔划不透明度:0.6;光标:移动;}
.complex平面选择器输入{width:6em;}
.complex plane picker.标签{笔划:无;字体大小:6px;字体系列:'Verdana';文本定位:中间;对齐基线:中间;}
.complex plane picker.scaler{指针事件:all;}
拖动点以设置复数值。
TODO:按住shift键仅影响幅值。按住alt键仅影响角度。按住这两个键可捕捉到最接近的纯实值或纯虚值
+i
0
0
0
0
@°
maxValue | | y>maxValue | |(xmaxValue*0.8)updateScale(scale*scaleFactor);
否则,如果(xFyi,你网站上的SHIFT和ALT修饰符对我不起作用(Chrome&FF)。@mOrloff这就是为什么它前面写着“TODO”;很明显,我从那个项目中分心了,从来没有完成过这些功能;)如果你把它变成一个教程,甚至只是在你的JavaScript中加入大量非常描述性的注释,那就太酷了。。。JS对我来说仍然有点不自然,我正试图把它拆开,理解它,并保持它的完整性,但我相当挣扎:)@mOrloff我会看看我是否能找到时间来做这件事。嗯@Phrogz,我的修补已经让我走得很远了。我已经把它精简到。。我的下一步是让它玩两个滑块好。。如果我可以就问题与您联系,请在粘贴底部的电子邮件中ping我。。无论如何,非常感谢:)我非常同意您选择SVG而不是画布;当你需要每个对象事件处理和简单更新时,使用像canvas这样的即时模式绘图系统只需要额外的工作。哦,当然,你知道,对吧?@Phrogz,谢谢。。。我原本希望使用范围输入,但FF的不支持扼杀了这个梦想:)