Search code examples
javascriptjqueryhtmljquery-svg

Animate svg elements with Javascript and JQuery


I'm trying to animate the following html elements to implement a functionality similar to a volume wheel.

<svg id="circle_svg" width="200" height="175">
<circle cx="100" cy="85" r="75" stroke="black" stroke-width="2" fill="lightgray"/>
<line id="line_alpha" x1="100" y1="85" x2="100" y2="160" style="stroke:rgb(0,0,255);stroke-width:2"/>
<circle id="dot_alpha" cx="100" cy="160" r="10" stroke="black" stroke-width="2" fill="red"/>
</svg>

The basic idea is that clicking on the red dot and moving the mouse around should result in the following behavior:

  1. The red dot moves along the circle (even if mouse doesn't stay exactly on it).
  2. The end point of the line on the circle follows the red dot.
  3. A number shown somewhere else in the page gets incremented or decremented with the amount of angular displacement.

I found a demo online that allows to drag an svg circle all around the page, by binding the elements of interest to mousedown and mouseup events and rewriting the attribute cx and cy of the circle to the current location of the mouse.

However when testing the code on jsfiddle with my example (or even with the original code) something is not working. Could you please take a look and give me advice on what might be going wrong?


Solution

  • I was able to find the solution to my question (thanks to a friend) and will post it as reference for others:

    The main problem with pasting the code from this online demo into jsfiddle is that the order in which JavaScript libraries and functions is not predictable.

    So some binding might be called before the binded function is defined. Also, the code from the demo is more complicated that what I needed.

    • Here is the code solution on jsfiddle
    • Below is a working snippet for SO site

    var dragging = false
    
    var updateGraphics = function (e) {
    	if (dragging) {
      
        var parentOffset = $('#wheel').offset(); 
        var relX = e.pageX - parentOffset.left;
        var relY = e.pageY - parentOffset.top;
      
        var cx = +$('#circle').attr('cx')
        var cy = +$('#circle').attr('cy')
        var r = +$('#circle').attr('r')
        var dx = relX - cx
        var dy = relY - cy
        //var dx = e.clientX - cx
        //var dy = e.clientY - cy
     
        console.debug('cx: ' + cx);
        console.debug('cy: ' + cy);
        console.debug('dx: ' + dx);
        console.debug('dy: ' + dy);
    
        console.debug('clientX: ' + e.clientX);
        console.debug('clientY: ' + e.clientY);
      
        console.debug('relX: ' + relX);
        console.debug('relY: ' + relY);
      
        var a = Math.atan2(dy, dx)
        var dotX = cx + r * Math.cos(a)
        var dotY = cy + r * Math.sin(a)
        $('#dot').attr('cx', dotX);
        $('#dot').attr('cy', dotY);
        $('#line_2').attr('x2', dotX);
        $('#line_2').attr('y2', dotY);
      }
    }
    
    $('svg').on('mousedown', function (e) {
    	dragging = true
    	updateGraphics(e)
    })
    
    $('svg').on('mouseup', function (e) {
    	dragging = false
    })
    
    $('svg').on('mousemove', updateGraphics)
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
    <svg id="wheel" width="200" height="175" style="background-color:lightgreen;">
    <circle id="circle" cx="100" cy="85" r="75" stroke="black" stroke-width="2" fill="lightgray"/>
    <line id="line_1" x1="100" y1="85" x2="100" y2="160" stroke-dasharray="15,15" style="stroke:rgb(255,0,0);stroke-width:2"/>
    <line id="line_2" x1="100" y1="85" x2="100" y2="160" style="stroke:rgb(0,0,255);stroke-width:2"/>
    <circle id="dot" cx="100" cy="160" r="10" stroke="black" stroke-width="2" fill="red"/>
    </svg>