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

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


视差悬停效果

最近在 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 地址
相关推荐
m0_471199632 分钟前
【自动化】前端开发,如何将 Jenkins 与 Gitee 结合实现自动化的持续集成(构建)和持续部署(发布)
前端·gitee·自动化·jenkins
w***95493 分钟前
spring-boot-starter和spring-boot-starter-web的关联
前端
Moment7 分钟前
富文本编辑器技术选型,到底是 Prosemirror 还是 Tiptap 好 ❓❓❓
前端·javascript·面试
xkxnq12 分钟前
第二阶段:Vue 组件化开发(第 18天)
前端·javascript·vue.js
晓得迷路了13 分钟前
栗子前端技术周刊第 112 期 - Rspack 1.7、2025 JS 新星榜单、HTML 状态调查...
前端·javascript·html
怕浪猫16 分钟前
React从入门到出门 第五章 React Router 配置与原理初探
前端·javascript·react.js
jinmo_C++16 分钟前
从零开始学前端 · HTML 基础篇(一):认识 HTML 与页面结构
前端·html·状态模式
飞哥数智坊16 分钟前
3位实战分享、6个案例展示,TRAE Friends@济南第二场圆满完成
人工智能·ai编程·trae
鹏多多22 分钟前
前端2025年终总结:借着AI做大做强再创辉煌
前端·javascript
小Tomkk31 分钟前
⭐️ StarRocks Web 使用介绍与实战指南
前端·ffmpeg