通过可拖动的SVG形状设置HTML输入值? (或画布) [英] Set an HTML Input value by draggable SVG shape ? (or canvas)

查看:69
本文介绍了通过可拖动的SVG形状设置HTML输入值? (或画布)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试组合一个用于设置定价的GUI.
有三种类别可以用图形表示,而我们希望使用两个滑块来设置建议的买/卖价格.

I'm trying to put together a GUI for setting some pricing.
There are three categories to be represented graphically, and two sliders with which we want for setting suggested Buy/Sell prices.

作为练习,我抓了一些原型并将它们放在 在jsFiddle上 .
请快速浏览一下,它将比我的话 更有效地解释它:)
(请原谅我的JS ...尚未重构)

As an exercise, I scratched together some prototypes and put them here on jsFiddle.
please take a quick look, it will explain it MUCH more effectively than my words ever could :)
(Please forgive my JS ... haven't refactored it yet)

由于我们的目标是让滑块在HTML输入中设置浮点数,因此我倾向于SVG.

Since our goal is to have the sliders set a float in an HTML input, I'm leaning toward the SVG.

  1. 我的第一个请求只是对我选择说服SVG解决方案进行的同行审查……只是想确保我不会遇到某些固有的痛苦问题.我也欢迎其他解决方案的争论.
  2. 第二个问题.有没有人有任何好的链接/示例可以帮助我理解如何根据形状的位置设置输入值?

(我希望该头寸将代表已定义的$$范围的百分比,因此我们可以推断出一个特定的$$值)

(I'm expecting that the position will represent a percentage of a defined $$ range, and therefore we can extrapolate a specific $$ value)

任何帮助都将不胜感激.

Any and all help is greatly appreciated.

推荐答案

这是我制作的一个演示,该演示使用SVG设置四个输入,反之亦然:
http://phrogz.net/svg/complex-plane-picker.xhtml

Here's a demo I made that uses SVG to set four inputs, and vice-versa:
http://phrogz.net/svg/complex-plane-picker.xhtml

前两个输入是X和Y(实数和复数分量);最下面的两个是极坐标(大小和角度).

The top two inputs are the X and Y (real and complex components); the bottom two are polar coordinates (magnitude and angle).

其中的代码可能超出了您的需要(当您靠近边缘/中间时会自动缩放,绘制更新的轴刻度),这可能会使它作为演示更加混乱,而无济于事.

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.

通常,您需要:

  • 在输入的keyupchangeinput事件上具有事件侦听器,并更新SVG以进行相应匹配.
  • 将拖动添加到SVG元素,然后在拖动期间计算适当的值,并相应地为输入设置.value.
  • Have an event listener on the keyup or change or input events of the inputs, and update the SVG to match accordingly.
  • Add dragging to your SVG element, and during dragging calculate the appropriate value(s) and set the .value for the input(s) accordingly.

提示,在拖动过程中,请勿尝试在可拖动元素本身上检测到mousemove.如果鼠标移到此位置之外,您将停止获取拖动事件,并且不会跟上.相反,我通常使用以下逻辑:

As a tip, during dragging don't attempt to detect mousemove 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:

  • mousedown上:
  1. 记录当前光标位置
  2. 在文档的根目录下注册mousemove处理程序
  3. 在文档的根目录下注册mouseup处理程序
  1. Record the current cursor position
  2. Register a mousemove handler on the root of the document
  3. Register a mouseup handler on the root of the document

  • mousemove上:

    1. 检测当前光标位置和拖动开始位置之间的屏幕空间增量,并使用它计算以更新其转换.
    1. Detect the screenspace delta between the current cursor position and the position at drag start, and use this to calculate the delta in the local space of the element being dragged to update its transformation.

  • mouseup上:

    1. 取消注册mousemove处理程序.
    1. Unregister the mousemove handler.

  • 以下是完整的源代码,以防万一我的网站关闭:

    Here's the full source code, in the unlikely event that my site is down:

    <!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输入值? (或画布)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

    查看全文
    登录 关闭
    扫码关注1秒登录
    发送“验证码”获取 | 15天全站免登陆