【动效实现】超酷炫的 3D 视差 Hover 效果

7,413 阅读2分钟

今天在看周刊的时候发现一个有趣的动画库 Atroposjs,它可以实现非常酷炫的 3D parallax hover effects(以前锤子官网首页 Banner 的同款效果)。先看实现效果:

屏幕录制2022-07-30 上午10.48.32.gif

这样的小动画往往会让人眼前一亮,忍不住多体验几次,产品曝光度就上来了。

Atroposjs 代码很简洁,仅有 2KB, 零依赖还支持很多定制化配置。如果仅需要实现最基础的 3D 视差悬停效果,仅需几十行代码。

这篇文章,我们将来开发一个最基础的实现,完整代码可以看下面的【码上掘金】 或者我的 Codepen

整体上这个动效分为两个部分,第一部分是鼠标 Hover 到卡片上时,卡片需要感知鼠标的位置,并做出相应的倾斜;第二部分是视差(Parallax) 效果,卡片内的元素根据”远近“的不同,移动速率不一样,以产生层次感。

Hover Effect 🖱

当鼠标在卡片上移动时,卡片会发生的形变。以卡片中心为远点将卡片划分为四个象限,结合 rotateXrotateY 的方向可以得到在各个象限的旋转符号。

屏幕录制2022-07-31 上午10.01.43.gif

rotateX, rotateY 的旋转角度(越来越大)

rotateX,rotateY 要看出 3D 效果,和 perspective 属性相关,它是用来设置观察者和 z=0 平面的距离的。处于 z > 0 位置的物体会比正常看起来更大,处于 z < 0 位置的物体回避正常看起来小。

在第一象限,rotateX,rotateY 都是正数;第二象限 rotateX 是正数,rotateY 是负数,第三象限...

image.png

鼠标距离原点越近,卡片倾斜角度越小,距离越远,倾斜角度越大,如果单看 X 轴或 Y 轴,这个关系是线性的。还有一个特点是,鼠标纵方向移动时需要旋转 X 轴,横向移动时,旋转的是 Y 轴。分析完毕之后就可以开始写代码了。

// mousemove 事件

// 最大旋转角度
const maxRotate = 20

// 原点位置
const centerX = width / 2;
const centerY = height / 2;

// 鼠标位置相对卡片左上角的坐标
const offsetX = clientX - left
const offsetY = clientY - top

// 将和原点的距离线性折算成旋转角度
// 📢 X 方向移动旋转 Y 轴,Y 方向移动旋转 X 轴
let rotateX = (maxRotate * (offsetY - centerY)) / (centerY) * -1;
let rotateY = (maxRotate * (offsetX - centerX)) / (centerX);

// 设置 CSS Variables
this.style.setProperty('--rotateX', rotateX);
this.style.setProperty('--rotateY', rotateY);

接着将旋转角度应用到卡片上。

.parallax-rotator {
  position: relative;
  transition: 300ms ease-out;
  transform: rotateX(calc(var(--rotateX) * 1deg)) rotateY(calc(var(--rotateY) * 1deg));
}

得到效果:

屏幕录制2022-07-31 上午10.20.56.gif

简简单单地几行代码就可以实现这么惊艳的效果,🐂 🍺!

Parallax Effect 😲

视差效果其实就是在 2D 的平面上,利用不同元素位置变化的速度不同,来模拟肉眼对物理世界远近感知的差异。观察者位移,距离近的物体位置变化大,而距离远的物体位置的变化小。

回到卡片上,我们将多张图片分层叠加在一起,给它们分别设置远近系数

<img src="./images/bg.svg" style="--parallaxOffset: -4.5" />
<img src="./images/mountains.svg" style="--parallaxOffset: -2.5" />
<img src="./images/forest-back.svg" style="--parallaxOffset: 0" />
<img src="./images/forest-mid.svg" style="--parallaxOffset: 2" />
<img src="./images/forest-front.svg" style="--parallaxOffset: 4" />

在鼠标移动时,通过远近系数控制物体的偏移,这里可以复用上面的 --rotateX--rotateY,计算出偏移量。需要注意旋转 Y 轴的时候需要 X 方向偏移,旋转 X 轴的时候需要 Y 方向偏移~

transform: translate3d(calc(var(--parallaxOffset) * var(--rotateY) / var(--maxRotate) * 1%),
    calc(var(--parallaxOffset) * var(--rotateX) / var(--maxRotate) * -1%), 0);

屏幕录制2022-07-31 上午10.27.57.gif

总结 📚

最后将两个部分合并在一起就可以得到酷炫吊炸天的效果了,在没动手实践过之前觉得这个效果很难,无从下手。梳理完之后核心只有寥寥数行代码。

文章中我只贴出核心代码,完整实现可以看下面的【码上掘金】 或者我的 Codepen。如果你觉得这个实现还不错的话,欢迎点赞 ❤️ + 评论 + 收藏 + 关注~

文章中用到的素材都来自 Atroposjs 官网,这个库还贴心地提供了 Vue/React/Svelte 的示例,感兴趣的盆友可以看看。