Skip to content Skip to sidebar Skip to footer

Set An Html Input Value By Draggable Svg Shape ? (or Canvas)

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/Se

Solution 1:

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

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.

In general, though, you need to:

  • 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.

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:

  • On mousedown:
    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
  • On mousemove:
    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.
  • On mouseup:
    1. Unregister the mousemove handler.

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

<!DOCTYPE HTML><htmlxmlns="http://www.w3.org/1999/xhtml"xml:lang="en"><head><metahttp-equiv="content-type"content="application/xhtml+xml; charset=utf-8" /><title>SVG Complex Plane Input</title><styletype="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-pickerinput { 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><divclass="complex-plane-picker"><pclass="rectangular"><inputtype="number"value="0"class='real' />+<inputtype="number"value="0"class='imaginary' />i</p><svgviewBox="-50 -50 100 100"xmlns="http://www.w3.org/2000/svg"xmlns:xlink="http://www.w3.org/1999/xlink"version="1.1"baseProfile="full"><rectclass="bg"x="-50"y="-50"width="100"height="100" /><gclass="scalers"><rectx="-5"y="-5"width="10"height="10"fill="#ffc"/><rectx="-45"y="-45"width="90"height="90"fill="none"stroke="#ffc"stroke-width="10" /></g><gclass="axes"><g><linex1="-49"y1="0"x2="49"y2="0" /><polylinepoints="-44,5 -49,0 -44,-5" /><polylinepoints="44,5 49,0 44,-5" /></g><gtransform="rotate(90)"><linex1="-49"y1="0"x2="49"y2="0" /><polylinepoints="-44,5 -49,0 -44,-5" /><polylinepoints="44,5 49,0 44,-5" /></g></g><gclass="labels"id="labels"><textx="0"y="0">0</text><textx="0"y="0">0</text><textx="0"y="0">0</text><textx="0"y="0">0</text></g><useclass="labels"xlink:href="#labels"transform="rotate(90)" /><circleclass="dot"r="4" /></svg><pclass="polar"><inputtype="number"value="0"class='magnitude' />@<inputtype="number"value="0"class='angle'/>°</p></div><scripttype="text/javascript"><![CDATA[
  var svg   = document.getElementsByTagName('svg')[0];
  var svgNS = svg.getAttribute('xmlns');
  var pt    = svg.createSVGPoint();

  functionmxy(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);
    } elseif (x>maxValue*0.8 || y>maxValue*0.8) updateScale(scale*scaleFactor);
    elseif (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>

Post a Comment for "Set An Html Input Value By Draggable Svg Shape ? (or Canvas)"