盒阴影随鼠标位置响应

5

我正在尝试在网页上创建动画,其中一个元素的盒子阴影会响应鼠标的位置,即(红色X = 鼠标):

enter image description here

enter image description here

enter image description here

我已经找到了一个跟踪鼠标移动的函数,但我不知道如何将其应用于对象。这是我的代码:

$(document).ready(function() {
  function shadowAnimation() {
    var objectToAnimate = $("#shadow-test");
    document.onmousemove = handleMouseMove;

    function handleMouseMove(event) {
      var eventDoc, doc, body;

      event = event || window.event;

      if (event.pageX == null && event.clientX != null) {
        eventDoc = (event.target && event.target.ownerDocument) || document;
        doc = eventDoc.documentElement;
        body = eventDoc.body;

        event.pageX = event.clientX +
          (doc && doc.scrollLeft || body && body.scrollLeft || 0) -
          (doc && doc.clientLeft || body && body.clientLeft || 0);
        event.pageY = event.clientY +
          (doc && doc.scrollTop || body && body.scrollTop || 0) -
          (doc && doc.clientTop || body && body.clientTop || 0);
      }

      console.log(event.pageX + " " + event.pageY);

    }
  }
});
#shadow-test {
  box-shadow: -10px -10px red;
  border: 1px solid white;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<p id="shadow-test">This is a shadow test</p>


3
也许可以参考tilt.js和https://tobiasahlin.com/blog/how-to-animate-box-shadow/来获得一些灵感。 - Jason Kleban
3个回答

4
请看以下示例(不使用jQuery)。

const p = document.querySelector('#shadow-test');

const clamp = (a, m, n) => {
  const max = Math.max(m, n);
  const min = Math.min(m, n);

  return Math.max(min, Math.min(max, a));
};

const MAX_SHADOW_OFFSET = 30;

const paint = (x, y) => {
  const r = p.getBoundingClientRect();
  const o = Math.min(r.width, r.height, MAX_SHADOW_OFFSET); // compute max shadow offset
  
  const mx = clamp(x, r.left - o, r.right + o); // clamp mouse coordinates within the shadow projection bounding box.
  const my = clamp(y, r.top - o, r.bottom + o);
  const px = r.right - r.width / 2; // compute element bb midpoints.
  const py = r.bottom - r.height / 2;
  const nx = (mx - px) / (r.right - r.left + 2 * o); // project mouse position relative to the bounding box to [-.5, .5];
  const ny = (my - py) / (r.bottom - r.top + 2 * o); 
  
  requestAnimationFrame(() => {
    p.style.boxShadow = `${-1 * nx * o}px ${-1 * ny * o}px var(--shadow-color)`;
  });
};

document.addEventListener('mousemove', (e) => paint(e.clientX, e.clientY), {
  passive: true
});
:root {
  --primary-color: black;
  --secondary-color: white;
  --shadow-color: red;
  --button-color: blue;
  --color: white;
}

body {
  background-color: var(--primary-color);
  padding: 1rem;
}

.center {
  text-align: center;
}

button {
 background-color: var(--button-color);
 color: var(--color);
 border: 0;
 border-radius: .25rem;
 padding: .375rem 1rem;
}

#shadow-test {
  border: 1px solid var(--secondary-color);
  color: var(--secondary-color);
}
<div class="center">
  <button>
    More
  </button>
</div>

<p id="shadow-test">This is a shadow test</p>

相对于其他解决方案,这个算法名称确实有点长,但它并不是很复杂。该算法可以分为以下几个步骤:

  1. 我们确定元素的边界框。我们需要这个来确定我们的盒子阴影的最大偏移量,以及后面投影到归一化坐标空间所需的投影。

  2. 我们将鼠标坐标夹紧在新的边界框内。这样,如果您将鼠标移到离元素很远的地方,它就不会继续移动。我们还需要鼠标坐标来投影到归一化坐标空间中。

  3. 我们找到元素的中心点。中心点左侧的任何内容都将是负数,右侧的任何内容都将是正数,以此类推。我们可以使用符号值来确定阴影应该在哪一侧。在中心点左侧的鼠标将给我们一个负值,在右侧将是正的。

  4. 最后,我们将鼠标坐标投影到归一化坐标空间[-0.5, .5]中。归一化坐标使得通过简单的乘法计算偏移量变得非常容易。将(nx,ny)视为比例值。给定最大阴影偏移量值,我们应该将多少应用到样式上?以及是什么方向?

其他注意事项

在请求动画帧中更新样式通常对性能更好。您还应该注意使用css变量。这样我就不必在代码中硬编码box-shadow的颜色值了。主题完全存在于CSS中。


1
这里需要使用 requestAnimationFrame 吗?即使没有它也能正常工作。所以我想知道为什么需要它!! - Gangula
1
我更喜欢这个,因为它使用了原生JS,过渡更加流畅。 - Gangula

3
你捕获鼠标位置的逻辑比必要的复杂得多。你只需要跟踪鼠标移动事件的 pageX 和 pageY。
要移动阴影,你只需要计算从鼠标到目标元素每个维度中点的距离。然后,你可以将该距离应用于 box-shadow 的大小, 但是受元素可用高度的限制,代码示例如下:

jQuery($ => {
  let $shadow = $('#shadow-test');
  let shadowMax = $shadow.height();
  let shadowMin = shadowMax * -1;
  let shadowMidPoints = [
    $shadow.offset().left + $shadow.width() / 2,
    $shadow.offset().top + $shadow.height() / 2
  ];

  $(document).on('mousemove', e => {
    let shadowX = Math.min(shadowMax, Math.max(shadowMin, shadowMidPoints[0] - e.pageX));
    let shadowY = Math.min(shadowMax, Math.max(shadowMin, shadowMidPoints[1] - e.pageY));
    $shadow.css('box-shadow', `${shadowX}px ${shadowY}px red`);
  });
});
body {
  height: 2000px;
}

#shadow-test {
  box-shadow: -10px -10px red;
  border: 1px solid white;
  margin: 100px;
  background-color: #CCC;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>

<p id="shadow-test">This is a shadow test</p>


1
这个完美地运作了,而且效率更高,感谢您的帮助。 - Miguel Andrade

0

据我所知,您已经实现了鼠标跟踪。 在您的屏幕截图和描述中,当 box-shadow 属性根据鼠标位置变化时并不清楚。 一旦您捕获到鼠标位置并可以计算出鼠标指针的位置,那么只需使用这个 js 代码来更新文本输入框的 box-shadow 属性。 三种情况

$("#shadow-test").css('box-shadow', '-10px -10px red');

$("#shadow-test").css('box-shadow', '10px 10px red');

$("#shadow-test").css('box-shadow', '0px 10px red');

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接