Search code examples
javascriptjquerysvggradientmouse-position

Rotate SVG gradient based on mouse position


I’d like to rotate the gradient of an svg based on the mouse position. The mechanic should be as followed, where [0,0] is the mouse being in the upper left corder of the window, [100%,0] should be the mouse in the upper right corner of the window etc.

enter image description here

What I have so far is that the angle changes on mouse position (only mouseX) but not based on my desired mechanic: https://codepen.io/magglomag/pen/YzKYLaa

The svg gradient is defined like this:

<defs>
    <linearGradient gradientTransform="rotate( X, 0.5, 0.5 )" id="gradient" gradientUnits="objectBoundingBox">
      <stop offset="0.4" style="stop-color:#33FF8F"/>
      <stop offset="0.6" style="stop-color:#5A33FF"/>
    </linearGradient>
</defs>

The manipulation of the angle is realized by changing the X in the gradientTransform attribute with JS:

$( 'body' ).mousemove( function( e ) {

    mouseX = e.pageX - this.offsetLeft;
    mouseY = e.pageY - this.offsetTop;

    xy = mouseX;

    $( 'svg defs' ).html( '<linearGradient gradientTransform="rotate(' + xy + ', 0.5, 0.5 )"  id="gradient" gradientUnits="objectBoundingBox"><stop offset="0.4" stop-color="#33FF8F"/><stop offset="0.6" stop-color="#5A33FF"/></linearGradient>' );

});

Besides I’d like to add a bit of easing so the change is not that hard. Here’s an example I found which uses easing. Not in combination with a gradient angle change but with a movement but perhaps the underlying code might be helpful: https://www.kirupa.com/canvas/mouse_follow_ease.htm

Any help is much appreciated.


Solution

  • All you need is a bit of math. You need center point of the SVG image and then angle between the mouse and that point:

    Relative to <svg>

      // position of mouse
      mouseX = e.pageX - this.offsetLeft;
      mouseY = e.pageY - this.offsetTop;
      // client rect of the gear
      const svgRoot = document.querySelector("#mysvg");
      const rect = svgRoot.getBoundingClientRect();
      // center point is x+width/2 and y+height/2
      const midx = rect.left + (rect.right - rect.left)/2;
      const midy = rect.top + (rect.bottom - rect.top)/2;
      // angle
      const angle = Math.atan2(midy - mouseY, midx - mouseX);
      // The transform uses degrees (0-365), not radians (0 - 2PI)
      const angleDeg = angle* 180 / Math.PI
    

    Demo: https://codepen.io/MXXIV/pen/OJLzEOV

    Relative to window

      // position of mouse
      mouseX = e.pageX - this.offsetLeft;
      mouseY = e.pageY - this.offsetTop;
      // center point is x+width/2 and y+height/2
      const midx = window.innerWidth/2;
      const midy = window.innerHeight/2;
      // angle
      const angle = Math.atan2(midy - mouseY, midx - mouseX);
      const angleDeg = angle* 180 / Math.PI
    

    Demo: https://codepen.io/MXXIV/pen/eYOyjdP