1
votes

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.

2
Could you create an interactive example? Surely the SVG is not so big as to not fit in the question or on jsFiddle.Tomáš Zato - Reinstate Monica
Hi @TomášZato, there’s already one on Codepen: codepen.io/magglomag/pen/YzKYLaauser1706680

2 Answers

3
votes

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

3
votes

You could use atan2 as follows to get the angle in radians and convert it into degrees by multiplying 180 / Math.PI:

$('body').mousemove(function(e) {
  var {
    left: offsetX,
    top: offsetY
  } = $('svg').offset();
  centerX = $('svg').width() / 2 + offsetX;
  centerY = $('svg').height() / 2 + offsetY;
  mouseX = e.pageX - centerX;
  mouseY = e.pageY - centerY;

  xy = Math.atan2(mouseY, mouseX) * (180 / Math.PI);
  $('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>');

});
svg {
  width: 150px;
  height: 150px;
}

body {
  height: 100vw;
  width: 100vw;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<svg x="0px" y="0px" viewBox="0 0 150 150">
  
  <style>
	  .st0 { fill:url( #gradient ); }
  </style>

  <defs>
    <linearGradient gradientTransform="rotate( 0, 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>
  
  <path class="st0" d="M149.3,89.5l0.7-24.6l-15.7-0.5c-1-5.4-2.8-10.8-5.3-16l12.4-9.7l-15.4-19.3l-12.4,9.7 c-4.4-3.6-9.3-6.6-14.5-8.8L102.2,5L77.9,0l-3.2,15.3c-5.6,0-11.3,0.8-16.8,2.4L50.4,4L28.5,15.6L36,29.4 c-4.4,3.7-8.2,7.9-11.3,12.6L10,36.2L0.8,59.1l14.7,5.8c-1,5.5-1.2,11.2-0.5,16.8L0,86.5l7.8,23.4l15-4.9c2.9,5,6.5,9.4,10.5,13.2 L25,131.5l21.2,12.9l8.3-13.3c5.3,1.9,10.9,3.1,16.6,3.4l2.3,15.4l24.6-3.6L95.7,131c2.6-1,5.2-2.1,7.8-3.5c2.5-1.4,4.9-2.9,7.2-4.5 l11.8,10.4L139,115l-11.8-10.4c2.8-4.9,4.9-10.2,6.3-15.6L149.3,89.5z M90,102.7c-15.4,8.2-34.7,2.5-43.1-12.8 c-8.3-15.3-2.5-34.4,12.9-42.6c15.4-8.2,34.7-2.5,43,12.8C111.2,75.3,105.4,94.4,90,102.7z"/>
</svg>