手把手带你实现Mac Dock 程序坞滑动效果

196 阅读3分钟

前言

最近使用Mac 的时候,突发奇想想研究一下Mac 程序坞的滑动效果。结果发现简单的HTML + CSS + JS 就可以实现,于是便有了这篇文章。

实现效果

话不多说,我们先来看看实现效果

实现过程

设计思路

  1. HTML 部分
    基础的HTML 结构
  2. CSS 部分
    给每个应用图标(这里用带背景颜色的盒子来表示)设置一个CSS 变量--i,并通过--i 来设置图标的宽高,这样在--i 变化的时候,就可以实现应用图标的放大和缩小,配合JS 代码从而实现缩放效果
  3. JS 部分
    鼠标在程序坞内移动时,使用函数曲线进行计算,动态得到每个应用图标的--i

HTML 和CSS部分

HTML 和CSS 部分相对来说较为简单,需要注意的是图标之间的间隔不能通过margin 来设置,而是使用了div 盒子来充当间隔。
并且,通过css 变量--i 来设置盒子的大小,通过js 代码来控制--i 的值,这样便可控制盒子缩放,HTML 和CSS 代码如下:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Mac Dock</title>
  <style>
    html, body{
      height: 99%;
    }
    .content{
      width: 100%;
      height: 100%;
      display: flex;
      justify-content: center;
      align-items: end;
      background-color: rgba(0, 0, 0, .8);
      .menu{
        margin-bottom: 20px;
        padding: 12px 15px;
        height: 40px;
        display: flex;
        justify-content: center;
        align-items: end;
        border-radius: 12px;
        background-color: rgba(255, 255, 255, .4);
        backdrop-filter: blur(20px);
        --webkit-backdrop-filter: blur(20px);
        .menu-item{
          --i: 1;
          border-radius: 10px;
          width: calc(40px * var(--i));
          height: calc(40px * var(--i));
          margin-bottom: calc(var(--i) * 15px - 15px);
          background-color: rgb(34, 199, 158);
        }
        .gap{
          --i: 1;
          width: calc(15px * var(--i));
        }
      }
    }
  </style>
</head>
<body>
  <div class="content">  
    <div id="dock" class="menu">
      <div class="menu-item"></div>
      <div class="gap"></div>
      <div class="menu-item"></div>
      <div class="gap"></div>
      <div class="menu-item"></div>
      <div class="gap"></div>
      <div class="menu-item"></div>
      <div class="gap"></div>
      <div class="menu-item"></div>
      <div class="gap"></div>
      <div class="menu-item"></div>
      <div class="gap"></div>
      <div class="menu-item"></div>
      <div class="gap"></div>
      <div class="menu-item"></div>
      <div class="gap"></div>
      <div class="menu-item"></div>
      <div class="gap"></div>
      <div class="menu-item"></div>
    </div>
  </div>
</body>
</html>

JS 部分

当我们鼠标在程序坞上移动的时候,可以看到一个起伏的效果。起伏是一个范围,并且鼠标所在为中心点,图标放大倍数最大,向两边依次递减。

image.png

基于这个起伏的形状,可以联想到正弦函数sin 函数在[0 - π]区间内的函数曲线,我们可以通过这段函数曲线,来计算放大倍数。鼠标所在位置的值为π/2,也是sin 函数的最大值,向两边依次递减且对称。

image.png

首先我们需要给定一个缩放范围range = 300 ,监听程序坞的mousemove事件,使用createCurve 函数来计算以当前鼠标位置为中心,range 范围内每个元素的放大倍数,并通过layout 函数 来对每个元素css 中的变量--i进行修改,从而实现缩放效果。js 代码如下所示:

const items = document.querySelector('.menu').children
const dock = document.querySelector('.menu')
const range = 300
const maxScale = 1.8
document.querySelector('.menu').addEventListener('mousemove', (e) => {
  const curve = createCurve(range, e.clientX, 1, maxScale)
  layout(curve)
  const rect = dock.getBoundingClientRect()
  const width = rect.right - rect.left
})
document.querySelector('.menu').addEventListener('mouseleave', (e) => {
  layout(() => 1)
  dock.style.setProperty('width', 'fit-content')
})
document.querySelector('.menu').addEventListener('mouseenter', (e) => {
  const rect = dock.getBoundingClientRect()
  const width = rect.right - rect.left + 80
  dock.style.setProperty('width', width + 'px')
})
function createCurve(totalDis, topX, minY, maxY){
  return function curve(x) {
    const beginX = topX - totalDis / 2
    const endX = topX + totalDis / 2
    if (x < beginX || x > endX) return minY
    const yDis = maxY - minY
    return (baseCurve((x - beginX) / totalDis) * yDis + minY)
  }
}
function baseCurve(x) {
  if(x < 0 || x > 1) return 1
  return Math.sin(x * Math.PI)
}
function layout(curve) {
  Array.from(items).forEach(item => {
    const rect = item.getBoundingClientRect()
    const x = rect.left + rect.width / 2
    const scale = curve(x)
    item.style.setProperty('--i', scale)
  })
}

结尾

以上就是Mac 程序坞缩放效果的实现过程了,希望对你有些参考作用。