视差悬停特效:鼠标跟随的沉浸式交互体验

大家好,我是石小石,小册《油猴脚本实战指南》作者。


视差悬停效果

最近在 Trae 官网发现了一个有趣的交互效果:当鼠标悬停在图标上时,图标会根据鼠标的移动产生跟随位移,看起来很炫酷。

于是我用AI问了一下,才知道这种效果叫"视差悬停效果",挺有意思。

视差悬停效果"(Parallax Hover Effect)是一种常见的前端视觉交互效果,模拟景深原理,让页面元素在鼠标移动时产生错位移动,营造出立体或空间感,增强用户交互体验。

用Trae实现一个类似效果

先让Trae帮忙实现一个类似效果:

生成的代码如下:

js 复制代码
<template>
  <!-- 外层容器,负责监听鼠标移动和离开事件 -->
  <div class="logo-container" @mousemove="handleMouseMove" @mouseleave="resetEffect">
    <!-- 中心 Logo 容器,包含所有球体 -->
    <div class="logo" ref="logo">
      <!-- 循环生成球体 -->
      <div
        v-for="(sphere, index) in spheresConfig"
        :key="index"
        class="sphere"
        ref="spheres"
        :style="{
          width: sphere.size + 'px',       // 球体直径
          height: sphere.size + 'px',      // 球体直径
          top: sphere.top,                 // 定位:上边距
          left: sphere.left,               // 定位:左边距
          right: sphere.right,             // 定位:右边距
          bottom: sphere.bottom,           // 定位:下边距
          backgroundColor: sphere.color    // 球体颜色
        }"
      ></div>
    </div>
  </div>
</template>

<script setup>
import { ref } from 'vue'

// 中心 Logo 元素引用
const logo = ref(null)
// 所有球体元素的引用
const spheres = ref([])

// 球体配置数组
const spheresConfig = [
  { size: 60, top: '40px', left: '40px', color: '#3498db' },
  { size: 60, top: '40px', right: '40px', color: '#e74c3c' },
  { size: 60, bottom: '40px', left: '40px', color: '#2ecc71' },
  { size: 60, bottom: '40px', right: '40px', color: '#f39c12' },
  { size: 70, top: '120px', left: '120px', color: '#9b59b6' },
  { size: 50, top: '70px', left: '170px', color: '#1abc9c' },
  { size: 45, top: '180px', left: '70px', color: '#d35400' },
  { size: 40, top: '150px', right: '60px', color: '#27ae60' },
  { size: 35, top: '210px', right: '120px', color: '#8e44ad' },
  { size: 25, top: '30px', left: '150px', color: '#c0392b' }
]

// 鼠标移动事件
const handleMouseMove = (e) => {
  const container = e.currentTarget
  const rect = container.getBoundingClientRect()

  // 计算鼠标在容器中的相对位置(-1 ~ 1)
  const x = ((e.clientX - rect.left) / rect.width - 0.5) * 2
  const y = ((e.clientY - rect.top) / rect.height - 0.5) * 2

  // 中心 Logo 随鼠标旋转
  logo.value.style.transform = `rotateX(${-y * 20}deg) rotateY(${x * 20}deg)`

  // 每个球体根据鼠标位置做视差位移
  spheres.value.forEach((sphere, index) => {
    const sphereX = x * 35 * Math.cos(index * 0.8) // X 方向位移
    const sphereY = y * 35 * Math.sin(index * 0.8) // Y 方向位移
    const sphereZ = Math.abs(x * y) * 70 + index * 8 // Z 方向位移(层次感)

    sphere.style.transform = `translate3d(${sphereX}px, ${sphereY}px, ${sphereZ}px)`
  })
}

// 鼠标移出时恢复初始位置
const resetEffect = () => {
  logo.value.style.transform = 'rotateX(0) rotateY(0)'
  spheres.value.forEach(sphere => {
    sphere.style.transform = 'translate3d(0, 0, 0)'
  })
}
</script>

<style scoped>
/* 外层容器,定义大小和透视深度 */
.logo-container {
    position: relative;
    width: 300px;
    height: 300px;
    margin: 100px auto;
    perspective: 1200px;
}

/* 中心 Logo 容器 */
.logo {
    width: 100%;
    height: 100%;
    position: relative;
    transform-style: preserve-3d;
    transition: transform 0.1s ease-out;
}

/* 球体公共样式 */
.sphere {
    position: absolute;
    border-radius: 50%;
    transition: transform 0.3s ease-out;
    box-shadow: inset -8px -8px 16px rgb(0 0 0 / 30%), 
        8px 16px rgb(255 255 255 / 30%);
}
</style>

效果还是很不错的:

这个demo的原理其实很简单,主要分为三步:

  1. 获取鼠标相对位置
  2. 模拟相机视角变化
  3. 实现分层错位运动

获取鼠标相对位置

js 复制代码
const rect = container.getBoundingClientRect();
const x = ((e.clientX - rect.left) / rect.width - 0.5) * 2;
const y = ((e.clientY - rect.top) / rect.height - 0.5) * 2;

getBoundingClientRect() 是浏览器 DOM API 中非常常用的方法,用于获取元素相对于视口(viewport)的尺寸和位置。

  • xy 都是范围 -11 的值,中心是 0,代表鼠标在容器里的相对位置。
  • 这个范围让后续计算方便做正负方向运动。

模拟相机视角变化

js 复制代码
logo.value.style.transform = `rotateX(${-y * 20}deg) rotateY(${x * 20}deg)`;

这一步的主要目的是让整体结构跟着鼠标动,模拟相机视角变化。最简单的就是通过CSS的transform实现3D位置变化。

这里的 20 是旋转最大角度,让效果不会太夸张。

分层错位运动

要想让球体有空间感,就要实现球体视差位移,同样的可以使用transform属性。

js 复制代码
spheres.value.forEach((sphere, index) => {
  const sphereX = x * 35 * Math.cos(index * 0.8);
  const sphereY = y * 35 * Math.sin(index * 0.8);
  const sphereZ = Math.abs(x * y) * 70 + index * 8;
  
  sphere.style.transform = `translate3d(${sphereX}px, ${sphereY}px, ${sphereZ}px)`;
});

"视差"的核心是让每个球体产生不同速度和运动方向:

  • 每个球体的 X 轴偏移 sphereX = 鼠标横向偏移 * 35 * 一个和球序号有关的余弦函数。
    → 余弦函数让每个球的偏移方向和幅度有差异,避免动作统一死板。
  • Y 轴偏移 sphereY 类似,用正弦函数产生错落。
  • Z 轴偏移 sphereZ 通过鼠标位置和球的索引控制,让不同球体在"深度"上产生不同位移,制造层次感。
  • translate3d 是 CSS 3D 变换,直接告诉浏览器把球体往三个方向移动。

当然,在鼠标离开时,我们需要所有元素慢慢回到原点,避免停留在中间状态。

ini 复制代码
logo.value.style.transform = 'rotateX(0) rotateY(0)';
spheres.value.forEach(sphere => {
  sphere.style.transform = 'translate3d(0, 0, 0)';
});

开源库

市面上有一些专门做视差或3D交互的库,可以帮我们快速实现效果。

Parallax.js

  • 经典轻量级视差库,支持基于鼠标移动的多层元素视差效果。
  • 用法简单,适合基础悬停视差。
  • GitHub 地址

Vanilla-tilt.js

  • 类似 Tilt.js,功能更全面,支持触摸事件和更多配置。
  • 纯原生 JS,无依赖,性能好。
  • GitHub 地址

react-parallax-tilt (React 生态)

  • React 版本的Tilt效果库,适合 React 项目。
  • 支持自定义视差和旋转,易于集成。
  • GitHub 地址
相关推荐
少云清几秒前
【功能测试】6_Web端抓包 _Fiddler抓包工具的应用
前端·功能测试·fiddler
豐儀麟阁贵9 分钟前
8.5在方法中抛出异常
java·开发语言·前端·算法
zengyuhan50339 分钟前
Windows BLE 开发指南(Rust windows-rs)
前端·rust
醉方休42 分钟前
Webpack loader 的执行机制
前端·webpack·rust
前端老宋Running1 小时前
一次从“卡顿地狱”到“丝般顺滑”的 React 搜索优化实战
前端·react.js·掘金日报
隔壁的大叔1 小时前
如何自己构建一个Markdown增量渲染器
前端·javascript
用户4445543654261 小时前
Android的自定义View
前端
WILLF1 小时前
HTML iframe 标签
前端·javascript
枫,为落叶1 小时前
Axios使用教程(一)
前端
小章鱼学前端1 小时前
2025 年最新 Fabric.js 实战:一个完整可上线的图片选区标注组件(含全部源码).
前端·vue.js