【问题标题】:Rotate SVG gradient based on mouse position根据鼠标位置旋转 SVG 渐变
【发布时间】:2019-09-06 10:13:20
【问题描述】:

我想根据鼠标位置旋转 svg 的渐变。 机制应该如下,其中[0,0]是鼠标在窗口的左上角,[100%,0]应该是鼠标在窗口的右上角等等。

到目前为止,我所拥有的是鼠标位置的角度变化(仅 mouseX),但不是基于我想要的机制: https://codepen.io/magglomag/pen/YzKYLaa

svg 渐变是这样定义的:

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

角度的操作是通过JS改变gradientTransform属性中的X来实现的:

$( '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>' );

});

此外,我还想添加一些缓和,这样更改就不会那么难了。这是我发现的一个使用缓动的例子。不结合渐变角度变化,而是结合运动,但底层代码可能会有所帮助:https://www.kirupa.com/canvas/mouse_follow_ease.htm

非常感谢任何帮助。

【问题讨论】:

  • 您能创建一个交互式示例吗?当然,SVG 并没有大到不适合问题或 jsFiddle。
  • 嗨 @TomášZato,Codepen 上已经有一个:codepen.io/magglomag/pen/YzKYLaa

标签: javascript jquery svg gradient mouse-position


【解决方案1】:

您只需要一点数学知识。您需要 SVG 图像的中心点,然后是鼠标与该点之间的角度:

相对于&lt;svg&gt;

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

演示:https://codepen.io/MXXIV/pen/OJLzEOV

相对于窗口

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

演示:https://codepen.io/MXXIV/pen/eYOyjdP

【讨论】:

  • 非常感谢!我刚刚意识到我对鼠标位置有点不精确。在您的解决方案中,渐变直接对鼠标朝向齿轮的角度做出反应(这也是一个不错的机制),但我的意思是鼠标相对于整个视口的位置。所以当鼠标在左上角时渐变的角度是45度,在视口的右上角是135度,在右下角是225度,在左下角是315度。
  • 好吧,你只需要设置midx = window.innerWidth/2
  • 不。根本不需要rect,那是从svg中心的角度。想想看,窗口左边是x=0,右边是x=window.innerWidth,所以中间是window.innerWidth/2。高度相同。
  • @shrys 检查更新的示例以查看它是否有效。请注意,innerWidth 是必需的,而不是外部的。否则它将无法在 iframe 等中工作。
  • @user1706680 我只是将我的答案中的代码提取到某个函数中,然后从您链接的文章中复制的代码中调用它。尝试使用它,如果它不起作用,你可以问另一个问题,有人,也许是我,会帮你解决它。但我认为你可以做到。
【解决方案2】:

您可以按如下方式使用atan2 来获取以弧度为单位的角度,并通过乘以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>

【讨论】:

  • 非常感谢!我刚刚意识到我对鼠标位置有点不精确。在您的解决方案中,渐变直接对鼠标朝向齿轮的角度做出反应(这也是一个不错的机制),但我的意思是鼠标相对于整个视口的位置。所以当鼠标在左上角时渐变的角度是45度,在视口的右上角是135度,在右下角是225度,在左下角是315度。