飞机追随鼠标效果

实现一个小飞机一直追随鼠标移动的效果,效果如下:

screenshots

上代码

let plane = document.createElement('div')
plane.style.backgroundImage = 'url(plane.png)'
plane.style.backgroundRepeat = 'no-repeat'
plane.style.backgroundSize = 'cover'
plane.style.width = '50px'
plane.style.height = '50px'
plane.style.position = 'absolute'
plane.style.top = window.innerHeight / 2 + 'px'
plane.style.left = window.innerWidth / 2 + 'px'
plane.style.transitionProperty = 'top,left'
plane.style.transitionDuration = '.5s,.5s'
plane.style.transitionTimingFunction = 'linear, linear'

document.body.style.position = 'relative'
document.body.style.height = '100vh'

document.body.appendChild(plane)

let mx = 0, my = 0, deg = 0;
let x, y;
window.addEventListener('mousemove', function (e) {
    x = e.x
    y = e.y
    mx = e.x - plane.offsetLeft - plane.clientWidth / 2
    my = e.y - plane.offsetTop - plane.clientHeight / 2
    deg = Math.atan(my / mx) * (180 / Math.PI) + 45
    if (mx < 0) {
        deg += 180
    }
})

move()

function move() {
    plane.style.left = (mx > 0 ? x - plane.clientWidth : x) + 'px';
    plane.style.top = (my > 0 ? y - plane.clientHeight : y) + 'px';
    plane.style.transform = 'rotate(' + deg + 'deg)'
    requestAnimationFrame(move)
}

plane元素我使用js创建的,样式也是通过js设置的。当然,也可以在Html中定义。

代码解释

其实这个效果的主要逻辑就是,监听鼠标的移动事件,然后改变plane相对定位的left,top到鼠标的当前坐标即可。但是其中还是有一些要点要注意。

飞机的方向根据鼠标位置不同而改变

如果我们只改变坐标位置,那么不会出现上图的效果,我们要根据鼠标移动的位置,改变plane的旋转角度。

mx,my分别是鼠标相对plane的位置移动的距离。mx就是相对于plane的x轴移动的位置,my就是y轴的。

有了mx,my那么根据下面这个图,不难发现我们能到得到偏移角度α\alpha的正切值就是tanα=mymx\tan\alpha=\frac{my}{mx}

image-20230210171219192

然后我们可以通过Mat.atan(my/mx)计算出α\alpha的正切值,但是这个是弧度制的,通过转化为角度制(可以去看我三角函数的文章三角函数笔记),我们就得到了偏移角度。

为什么要加45°呢?是因为,我们的飞机图片原本大概就偏转了45°(如下图),再加45°,那么就是飞机头指向鼠标的角度。如果是垂直向上的飞机图片,那么就要偏转90°

image-20230210154108582

代码中还有一句判断,mx<0时,会将当前的计算的角度再加180°。这是为什么呢?这是因为在mx<0时,相当于飞机头从向左转到向右,我们观察角度如何变化,除去本身飞机图片偏转45°加上我们修正的45°。偏转的绝对角度过程如下:0°→90°→180°,我们发现,偏转绝对角度超过了90°,我们观察tan\tan的函数图像:

image-20230210155739000

当超过90°时,mx开始小于0,my>0这时tanα\tan\alpha为负值,用tan\tan计算的角度变成了如下图所示的角度:

image-20230210164420705

此时Mat.atan(my/mx)其实计算的角度是上图的角α\alpha,而我们要得值其实是:90°+β|\beta|,通过换算:

90°+β=90°+(90°+α)   (α<0,β<0)90°+|\beta|=90°+(90°+\alpha) \ \ \ (\alpha<0,\beta<0),那么偏转角度=180°+α\alpha

mx开始小于0,my<0的情况:

image-20230210165618066

其实公式是一样的:90°+β=90°+(90°+α)=180°+α   (α<0,β<0)90°+|\beta|=90°+(90°+\alpha)=180°+\alpha \ \ \ (\alpha<0,\beta<0)

那么为什么my<0的时候不需要做任何处理呢?其实我们观察,my<0时,也有两种情况:

image-20230210170131125

左边这种,在mx<0的时候已经处理了,右边这种情况因为偏转角度没有超过90°所以不需要特别处理。

requestAnimationFrame()

会把每一帧中的所有DOM操作集中起来,在一次重绘或回流中就完成,并且重绘或回流的时间间隔紧紧跟随浏览器的刷新频率,一般来说,这个频率为每秒60帧。

在隐藏或不可见的元素中,requestAnimationFrame将不会进行重绘或回流,这当然就意味着更少的的cpu,gpu和内存使用量。


飞机追随鼠标效果
https://www.zhaojun.inkhttps://www.zhaojun.ink/archives/plane-follow-mouse
作者
卑微幻想家
发布于
2023-02-10
许可协议