【网页特效】丝滑的macOS Dock效果

8,369 阅读5分钟

大家好,我是 Steven。

今期我们又来做一些花里胡哨的特效,就是模仿 macOS 的 Dock 这个图标放大的效果:

01.gif

如果是单纯的逐个图标放大,其实就没什么难度。不过我们的作风是,要做就做得够细致,当游标在同一个图标上左右移动时,都会影响到左边和右边的图标大小的,达到一个很连贯的效果,这个就是今次的难度所在。

好了,那我们就开始吧。

这个教程的视频版本在 www.bilibili.com/video/BV1Sf… ,欢迎三连关注!

HTML 的部分

打开 CodePen 编辑器,在 HTML 的部分加入一些图标,在这里我会用 Emoji 表情符号来代表,加入一个 <ul> 列表的标签,class 设定为 dock,然后用 <li> 装着每一个表情符号,再在外面套上一层 <div>,class 是 glass

02.png

CSS 的部分

然后是 CSS 的部分,加入 html 选择器,把基础的文字大小设定为 15px。加入 body 选择器,将 marginpadding 设定为 0display 设定为 flex,宽度设定为 100%min-height 最小的高度设定为 100vhoverflow 设定为 hidden

然后要把它定位到页面的下方,align-items 设定为 flex-end

03.png

加入 .glass 选择器,宽度设定为 100%,高度设定为 8rem,然后背景颜色设定为浅灰色,display 设定为 flexjustify-content 设定为 center

加入 .dock 选择器,将图标居中以及横向排列,list-style 列表的样式设定为 nonemarginpadding 设定为 0display 设定为 flexjustify-contentalign-items 设定为 center

再设定图标的样式,加入 .dock li 选择器,由于这里用的是表情符号,所以可以通过设定 font-size 来调整它的大小,font-size 设定为 6rem。然后加入一些 padding,左右设定为 0.5remcursor 游标的样式设定为 default 即是预设的箭头。

04.png

好了,样式的部分都差不多了,接下来就是实现特效的部分。

动画的原理

首先我们了解一下这个效果的原理,举例我们将游标移动到第四个图标上的时候,它会放大。除此之外,在它旁边的第三个和第五个图标都会稍为放大一点,而当游标的位置比较靠左的时候,第三个就会比第五个放大一点,反之亦然。

所以我们就要知道游标在某一个图标上的时候,是在左边,中间还是右边的位置。

JavaScript 的部分

来到 JavaScript 的部分,先用 querySelectorAll() 把所有 .dock li 获取回来。然后通过回圈,为每一个 li 加入 mousemove 事件监听器。

定义一个变量 item,赋值为 e.target,因为稍后会多次引用到。然后要知道图标所在的 li 容器的宽度,定义变量 itemRect,赋值为 item.getBoundingClientRect()

然后就是计算游标在图标上的位置,定义变量 offset,赋值为 e.clientX 减去 itemRect.left,由于它可能是负值,所以套上 Math.abs(),再除以 itemRect.width

现在当游标移到图标左边的时候,offset 会趋近于 0,移到右边的时候,offset 会趋近于 1

05.png

有了这个值,我们就可以计算图标的放大比例了。定义两个变量,分别是 prevnext,通过 previousElementSibling 以及 nextElementSibling 获取当前游标所在的图标,前一个以及后一个的 li 元素。

再定义一个变量 scale,假如我想图标放到最大是 1.6 倍,这里就赋值为 0.6

然后就计算放大的比率,并且写入到对应元素的 CSS 变量之中。在设定 prev 之前,要先判断一下是否能够获取到到它,因为当游标在第一个图标上的时候,prev 就会是 null。然后通过 setProperty(),将放大的比率设定到 CSS 的变量里面,我会设定为 1 加上 scale 乘以 Math.abs(offset - 1)offset - 1 是因为现在正在设定左边的元素,所以 offset 的值要相反,即是将 01 改为 10

然后运用相同的逻辑设定右边元素的放大比率,不同的是,这里的 offset 直接使用就可以了,而对于当前的图标呢,就直接设定为 1 + scale 就是最大值了。

06.png

定义一个用于重置 --scale 的函式,名为 resetScale(),把所有 li--scale 重置为 1

07.png

总共有两处位置需要执行这个函式的,分别是在 mousemove 事件里面,以及当游标离开这个 Dock 的时候。新增 mouseleave 事件监听器,并且执行 resetScale(),就可以了。

08.png

09.png

通过开发者工具,看一下 --scale 的变化:

10.gif

在游标移动的过程当中,会将超出范围的 li--scale 重置为 1,以及在游标离开 Dock 的时候,所有 li--scale 都重置为 1

最后一步

好了,来到这里,就差一步就完成了。

回到 CSS 的部份,将文字大小的设定值更改为 6rem 乘以 var(--scale),因为涉及到运算,所以套上一层 calc()

.dock 选择器内初始化 --scale1。测试一下:

11.gif

放大效果就达到了,不过定位有点问题。这个也不难解决,将 position 设定为 relative。然后计算一下 top 相对于图标放大后的位移就可以了。再增加一点点的 transition 动画过渡的设定。

12.png

我们顺便将点击图标然后载入中的效果都做出来吧,如果你有兴趣的话,暂停一下,试试自己实现一下。

这里也很简单,定义一个叫做 loading 的动画设定,通过 translateY() 将它上下移动,然后加入 .dock li.loading 选择器,即是当 liloading 这个 class 的时候,执行这个动画,并且无限次重覆。

13.png

再到 JavaScript 的部分,加入针对 li 的点击事件监听器,然后为目标的元素加上 loading 这个 class 就可以了。

14.png

我们来看看这个案例的完成效果

Final.gif

以上,就是今期要介绍的全部内容。


这个案例的源代码在 codepen.io/stevenlei/p…

你的支持是我的动力,请关注 CodingStartup 起码课,我们一起加油!