witch@&*weaves

30daysJS-16 - Mouse Shadow

这个就更是纯数学了。

问题

核心坑点:

核心思路:

我鼠标无论移动到哪,实际上改变的都是映射到偏移量正方形里点的坐标系。

问题一:事件冒泡+offset

hero.addEventListener('mousemove', shadow);


let { offsetX: x, offsetY: y } = e;
if (this !== e.target) {
  x = x + e.target.offsetLeft;
  y = y + e.target.offsetTop;
}

this始终指向是事件绑定(也就是发生时)的Node节点对象,而e.target指向的是引发事件的那个嵌套层级最深的元素,二者不一定相等,导致二者的上级元素也不一定相等。

offset相关指的是上级和下级之间的距离关系,这可能就会出现x,y返回的是e.target和某一元素内部的距离,我们想要的是目标点和视口的距离关系,所以需要保证无论e.target在哪,返回的恒定为视口和目标点的距离关系。

x,y在判定之前已被赋值,是鼠标和事件触发元素内部的距离关系,之后判定内赋值,是加上了事件触发元素和上级元素之间的距离关系。

注意:

offsetX, offsetYoffsetLeft, offsetTop是不一样的:

另外:

当一个事件发生在一个元素上,它会首先运行在该元素上的处理程序,然后运行其parent上的处理程序,然后一直向上到其他ancestors上的处理程序。

引发事件的那个嵌套层级最深的元素被称为目标元素,可以通过 event.target 访问。

问题二:偏移量

仔细观察我们要实现的效果图,便可以看出它们全是围绕着一个中心点旋转。

const xWalk = Math.round((x / width) * walk) - (walk / 2));

这两个变量:

想象一个尺子:
原始:  0──────25♠──────50──────75──────100
                鼠标在这里

减去50:-50──────-25♠──────0──────25──────50
                鼠标变成-25
如果walk = 100
x (鼠标位置) | normalized = x/width*100 | xWalk = normalized - 50
------------|-------------------------|-------------------------
0           | 0                       | -50
width/4     | 25                      | -25
width/2     | 50                      | 0
3*width/4   | 75                      | 25
width       | 100                     | 50

最终我们得到一个[- walk / 2 , walk / 2 ]间的x值,同理,y值也是这样计算出,(x, y)坐标便是在一个正方形区域内移动了:

Pasted image 20260205122522.png

问题三:多值语法

text-shadow 属性支持用逗号分隔的多个阴影值,从第一个开始渲染:

text-shadow: h-shadow1 v-shadow1 blur-radius1 color1, 
             h-shadow2 v-shadow2 blur-radius2 color2,
             ...;

其他支持多值的CSS属性:

!注意!

多值语法的最后一项末尾不能加,逗号,会造成语法错误!!!(血泪教训T T

问题四: 解构赋值

const {offsetWidth: width,  offsetHeight: height} = hero
let { offsetX: x, offsetY: y }  = e

等于

const width =  hero.offsetWidth
const width =  hero.offsetHeight

let x = e.offsetX
let y = e.offsetY

答案代码

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Mouse Shadow</title>
  <link rel="icon" href="https://fav.farm/✅" />
</head>
<body>

  <div class="hero">
    <h1 contenteditable>🔥WOAH!</h1>
  </div>

  <style>
  html {
    color: black;
    font-family: sans-serif;
  }

  body {
    margin: 0;
  }

  .hero {
    min-height: 100vh;
    display: flex;
    justify-content: center;
    align-items: center;
    color: black;
  }

  h1 {
    font-size: 100px;
  }
  </style>

<script>
  const hero = document.querySelector('.hero');
  const text = hero.querySelector('h1');
  const walk = 100; 

  function shadow(e) {
    const { offsetWidth: width, offsetHeight: height } = hero;
    let { offsetX: x, offsetY: y } = e;

    console.log(e, e.target);

    if (this !== e.target) {
      x = x + e.target.offsetLeft;
      y = y + e.target.offsetTop;
    }

    const xWalk = Math.round((x / width * walk) - (walk / 2));
    const yWalk = Math.round((y / height * walk) - (walk / 2));

    text.style.textShadow = `
      ${xWalk}px ${yWalk}px 0 rgba(255,0,255,0.7),
      ${xWalk * -1}px ${yWalk}px 0 rgba(0,255,255,0.7),
      ${yWalk}px ${xWalk * -1}px 0 rgba(0,255,0,0.7),
      ${yWalk * -1}px ${xWalk}px 0 rgba(0,0,255,0.7)
    `;
    
  }

  hero.addEventListener('mousemove', shadow);
</script>
</body>
</html>

#code