Three.js 的魔法手势:让 3D 世界在指尖起舞

在数字艺术的奇幻森林里,Three.js 就是那位手持魔杖的魔法师,它能将枯燥的代码编织成令人惊叹的 3D 世界。不过,当这个虚拟世界只停留在屏幕上,像个高冷的艺术品,难免有些遗憾。别担心!今天我们要给 Three.js 的魔法书里添上关键一页 ------ 用手势交互打破次元壁,让用户能亲手触摸、旋转、缩放这个奇妙的 3D 世界。

一、Three.js 的底层魔法与交互困境

Three.js 的底层就像一个精密的魔法工厂,它利用 WebGL 技术直接与显卡对话,将顶点数据、纹理信息等原材料,通过一系列复杂的 "魔法咒语"(也就是数学变换和着色器渲染),快速组装成 3D 模型呈现在浏览器中。然而,在这个 3D 世界里,传统的鼠标点击和键盘操作就像隔着一层玻璃与世界互动,在移动端更是显得格格不入。想象一下,你捧着手机,却只能眼巴巴地看着精美的 3D 模型,无法像在现实中那样用手指摆弄它,是不是很憋屈?这时候,我们就需要引入 Hammer.js 这个 "交互小精灵",来解锁 Three.js 的手势交互新姿势。

二、召唤 Hammer.js 小精灵

Hammer.js 就像 Three.js 的得力助手,它能精准捕捉移动端的各种触摸手势,把用户手指的滑动、捏合等动作,翻译成 Three.js 能听懂的指令。

首先,我们要在项目中引入 Hammer.js。这就好比给我们的魔法团队招来新成员,在 HTML 文件里,通过

xml 复制代码
<script src="https://cdnjs.cloudflare.com/ajax/libs/hammer.js/2.0.8/hammer.min.js"></script>

或者如果你使用的是现代的前端构建工具,比如 npm,也可以用一句咒语把它召唤到项目里:

复制代码
npm install hammerjs

然后在 JavaScript 文件中引入:

javascript 复制代码
import Hammer from 'hammerjs';

三、给 3D 世界赋予手势魔法

接下来,我们要把 Hammer.js 和 Three.js 这两个魔法高手结合起来,给 3D 场景加上手势交互的超能力。

假设我们已经搭建好了一个简单的 Three.js 场景,有一个立方体在场景中:

ini 复制代码
// 创建场景、相机和渲染器
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
const renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
// 创建一个立方体
const geometry = new THREE.BoxGeometry();
const material = new THREE.MeshBasicMaterial({ color: 0x00ff00 });
const cube = new THREE.Mesh(geometry, material);
scene.add(cube);
// 设置相机位置
camera.position.z = 5;

现在,我们要给这个场景加上捏合缩放和滑动旋转的手势交互。

1. 滑动旋转:让模型跟着手指跳舞

我们先给场景绑定一个滑动手势事件,当用户在屏幕上滑动时,让立方体跟着旋转。这就好比给立方体系上一根无形的绳子,用户一拉绳子,它就开始旋转:

ini 复制代码
const hammertime = new Hammer(renderer.domElement);
hammertime.on('pan', function (ev) {
    cube.rotation.x += 0.01 * ev.deltaY;
    cube.rotation.y += 0.01 * ev.deltaX;
});

在这段代码里,我们首先创建了一个 Hammer 实例,并把 Three.js 渲染器的 DOM 元素作为参数传进去,这就相当于给 Hammer 划定了一个 "工作范围",让它只关注这个区域内的手势操作。然后,我们监听pan事件,也就是滑动事件。当用户滑动时,ev.deltaX和ev.deltaY分别记录了水平和垂直方向的滑动距离,我们根据这个距离,按一定比例(这里是 0.01)来更新立方体的旋转角度,这样立方体就会随着用户的滑动优雅地旋转起来。

2. 捏合缩放:像变魔术一样放大缩小

接下来是更神奇的捏合缩放功能。当用户用两根手指在屏幕上捏合或张开时,我们要让 3D 模型相应地缩小或放大,这就像在施展一个大小变化的魔法:

ini 复制代码
hammertime.on('pinch', function (ev) {
    const factor = ev.scale;
    cube.scale.x *= factor;
    cube.scale.y *= factor;
    cube.scale.z *= factor;
});

这里我们监听pinch事件,也就是捏合事件。ev.scale记录了捏合操作的缩放比例,当用户两根手指张开,这个值会大于 1,模型就会放大;当两根手指捏合,这个值会小于 1,模型就会缩小。我们把这个缩放比例分别应用到立方体的x、y、z三个轴向上,这样立方体就能像被施了魔法一样,随着手指的动作自由变换大小。

四、魔法升级:更丝滑的用户体验

为了让交互体验更加流畅自然,我们还可以对代码进行一些优化。比如,给旋转和缩放操作加上一些限制,避免模型旋转过度或者缩放到离谱的大小。我们也可以添加一些过渡动画,让模型的变化更加柔和,就像给魔法加上了特效:

ini 复制代码
// 限制旋转角度
const maxRotation = Math.PI;
hammertime.on('pan', function (ev) {
    const newRotationX = cube.rotation.x + 0.01 * ev.deltaY;
    const newRotationY = cube.rotation.y + 0.01 * ev.deltaX;
    cube.rotation.x = Math.max(-maxRotation, Math.min(maxRotation, newRotationX));
    cube.rotation.y = Math.max(-maxRotation, Math.min(maxRotation, newRotationY));
});
// 添加缩放过渡动画
let targetScale = cube.scale.clone();
const easingFactor = 0.1;
function updateScale() {
    cube.scale.x += (targetScale.x - cube.scale.x) * easingFactor;
    cube.scale.y += (targetScale.y - cube.scale.y) * easingFactor;
    cube.scale.z += (targetScale.z - cube.scale.z) * easingFactor;
    requestAnimationFrame(updateScale);
}
updateScale();
hammertime.on('pinch', function (ev) {
    const factor = ev.scale;
    targetScale.x *= factor;
    targetScale.y *= factor;
    targetScale.z *= factor;
});

在限制旋转角度的代码中,我们设定了一个最大旋转角度maxRotation,每次更新旋转角度时,都用Math.max和Math.min函数来确保旋转角度不会超出范围。在缩放过渡动画的代码中,我们引入了一个targetScale变量来记录目标缩放值,通过一个updateScale函数不断地让当前缩放值向目标缩放值靠近,每次靠近的幅度由easingFactor控制,这样模型的缩放就会变得平滑自然。

五、魔法的边界与未来

虽然我们已经成功给 Three.js 场景加上了炫酷的手势交互,但这只是魔法世界的冰山一角。在实际应用中,我们还会遇到各种挑战,比如不同设备的手势识别差异、复杂 3D 场景的性能优化等等。不过别担心,随着技术的不断发展,Three.js 和 Hammer.js 这些魔法工具也在不断升级,未来我们将能创造出更加逼真、流畅、有趣的 3D 交互体验,让用户真正沉浸在数字魔法的世界里。

现在,快去施展你的代码魔法,让 Three.js 的 3D 世界在用户的指尖上欢快起舞吧!

以上文章详细介绍了 Three.js 结合 Hammer.js 实现手势交互的方法。你对文章的内容深度、讲解方式是否满意?若有其他修改或补充需求,随时和我说。

相关推荐
墨夏16 分钟前
TS 高级类型
前端·typescript
程序猿师兄24 分钟前
若依框架前端调用后台服务报跨域错误
前端
前端小巷子28 分钟前
跨标签页通信(三):Web Storage
前端·面试·浏览器
工呈士28 分钟前
TCP 三次握手与四次挥手详解
前端·后端·面试
BillKu30 分钟前
Vue3 + TypeScript + Element Plus + el-input 输入框列表按回车聚焦到下一行
前端·javascript·typescript
复苏季风30 分钟前
前端程序员unity学习笔记01: 从c#开始的入门,using命名空间,MonoBehaviour,static,public
前端
阿古达木33 分钟前
沉浸式改 bug,步步深入
前端·javascript·github
小泡芙丫34 分钟前
JavaScript 的 Promise:一场关于相亲、结婚与生子的异步人生大戏
javascript
stoneSkySpace42 分钟前
react 自定义状态管理库
前端·react.js·前端框架
堕落年代1 小时前
SpringAI1.0的MCPServer自动暴露Tool
前端