blog icon indicating copy to clipboard operation
blog copied to clipboard

仿Mac程序坞放大动画

Open CatsAndMice opened this issue 2 years ago • 0 comments

前言

笔者近期换了台MacBook笔记本💪🏽,使用中发现当鼠标hover到它的程序坞时,有一个非常丝滑的放大动画。感觉非常有意思🤩,趁周末实现下,最后分享给屏幕前爱学习的您!😉

真实MacBook笔记本放大动画:

在这里插入图片描述

仿写的放大动画: 在这里插入图片描述

勾股定理

实现的关键就是运用了勾股定理计算,所以咱们要复习下初中时期的数学。

勾股定理:平面上的直角三角形的两条直角连长的长度的平方和等于斜边长的平方

image_AR_CsqGt8W.png

简化成一个公式:

a2+b2=c2

简单复习下勾股定理,然后将公式对应到咱们的放大动画中。

image_HedvzIk_49.png 上图中,假设三角形▲是鼠标,圆○为电脑桌面程序坞中某个应用图标。C斜边的长度即为鼠标与应用图标的距离。当鼠标移动时,程序坞中每一个应用程序与鼠标的距离将会动态的变化;当距离越大时,咱们给应用图标的大小设置得越小,反之亦然。

C斜边的长度:

C=A2+B2​

实践

用emoji表情代替下应用图标

<dl class="content">
    <dd>😆</dd>
    <dd>🫠</dd>
    <dd>🤣</dd>
    <dd>😀</dd>
    <dd>😅</dd>
    <dd>😀</dd>
    <dd>😅</dd>
</dl>

初始化状态给定字体大小为40px

html,
body,
dl,
dd {
    margin: 0;
    padding: 0;
}

.content {
    position: fixed;
    bottom: 10px;
    left: 0;
    cursor: pointer;
    width:100%;
    display: flex;
    align-items: center;
    justify-content: center;
}

.content dd {
    padding: 0 5px;
    font-size: 40px;
}

获取dom,全局监听鼠标的移动事件

const content = document.querySelector(".content")
const dds = Array.from(document.querySelectorAll(".content dd"))
document.addEventListener('mousemove', function (e) {
    //...  
})

每次鼠标移动时,循环计算出每个dd 元素与鼠标距离

document.addEventListener('mousemove', function (e) {
    const { clientX, clientY } = e
    dds.forEach(dd => {
        //获取每个元素的x距离
        //注意元素放大时,将会把父级元素撑大,间接的dd.offsetLeft获取的值也会相应的变大,导致交互有距离差
        const x = dd.offsetLeft + dd.offsetWidth / 2
        const y = dd.offsetTop + dd.offsetHeight / 2 + content.offsetTop
        const gapX = clientX - x
        const gapY = clientY - y
        const gap = Math.sqrt(Math.pow(gapX, 2) + Math.pow(gapY, 2)) 
        //...
    }
})

上述代码中 const gapX = clientX - x;const gapY = clientY - y; 计算结果会出现负数,它并不影响, const gap = Math.sqrt(Math.pow(gapX, 2) + Math.pow(gapY, 2))进行平方后不会存在负数的情况。

最后,我们需要的是一个比例,而不是一个具体的数值,所以gap 再除以一个值:

let val = 1 - gap / (dd.offsetWidth * 7)

gap / (dd.offsetWidth * 7) 前文有说明,距离越大,图标应该设置得去越小,所以需要1 - gap / (dd.offsetWidth * 7) 。实际应用中,图标不能小到用户都看到,val 需要有个最小值。

 let val = 1 - gap / (dd.offsetWidth * 7)
if (val < 0.5) {
    val = 0.5
}

初始化时,给定的字体大小为40px。当val=0.5 时,我们就让emoji 表情为正常大小即可

dd.style.fontSize = 80 * val + 'px'

完整的计算代码不多,这里简单的贴粘一下:

const content = document.querySelector(".content")
const dds = Array.from(document.querySelectorAll(".content dd"))
document.addEventListener('mousemove', function (e) {
    const { clientX, clientY } = e
    dds.forEach(dd => {
        //获取每个元素的x距离
        //注意元素放大时,将会把父级元素撑大,间接的dd.offsetLeft获取的值也会相应的变大,导致交互有距离差
        const x = dd.offsetLeft + dd.offsetWidth / 2
        const y = dd.offsetTop + dd.offsetHeight / 2 + content.offsetTop
        const gapX = clientX - x
        const gapY = clientY - y
        const gap = Math.sqrt(Math.pow(gapX, 2) + Math.pow(gapY, 2))
        let val = 1 - gap / (dd.offsetWidth * 7)
        if (val < 0.5) {
            val = 0.5
        }
        dd.style.fontSize = 80 * val + 'px'
    })
})


最后

原创不易!如果我的文章对你有帮助,你的👍就是对我的最大支持^_^。

CatsAndMice avatar Jun 28 '22 09:06 CatsAndMice