You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于p5.js实现WebGL 3D物体鼠标悬停Y轴180度旋转

解决p5.js WebGL 3D物体悬停旋转及批量重置的方案

核心思路

不用在创建物体阶段提前调用rotate(),而是给每个物体维护独立的旋转状态变量,在draw()循环中根据状态动态应用旋转。初始时状态为0,物体保持静止;鼠标悬停时触发状态变更,驱动平滑旋转过渡;最后检测所有物体的完成状态,统一触发重置动画。

实现步骤与代码示例

1. 定义物体状态结构

用数组存储每个物体的位置、当前旋转角度、目标角度、旋转状态等,确保每个物体的独立性:

let objects = [];
let resetting = false; // 标记是否处于全局重置阶段

function setup() {
  createCanvas(800, 600, WEBGL);
  
  // 初始化4个物体,分布在不同X轴位置
  objects = [
    { x: -200, y: 0, z: 0, currentRot: 0, targetRot: 0, isRotating: false, isCompleted: false },
    { x: -60, y: 0, z: 0, currentRot: 0, targetRot: 0, isRotating: false, isCompleted: false },
    { x: 60, y: 0, z: 0, currentRot: 0, targetRot: 0, isRotating: false, isCompleted: false },
    { x: 200, y: 0, z: 0, currentRot: 0, targetRot: 0, isRotating: false, isCompleted: false }
  ];
}

2. 鼠标悬停检测(3D简化版)

针对平面分布的物体,将3D位置转换为屏幕坐标,通过2D范围判断鼠标是否悬停:

function checkMouseHover(obj) {
  // 把物体3D坐标转成屏幕X坐标
  const screenX = createVector(obj.x, obj.y, obj.z).screenX();
  // 假设物体宽度为100,检测鼠标是否落在投影范围内
  return mouseX > screenX - 50 && mouseX < screenX + 50;
}

如果是不规则复杂3D物体,可改用射线检测(raycast)提升精度,上述简化版足够应对基础场景。

3. 旋转逻辑与物体绘制

在draw()中处理状态更新、平滑旋转过渡,再逐个绘制物体:

function draw() {
  background(220);
  orbitControl(); // 可选:允许拖拽旋转相机查看3D效果
  
  // 检测是否所有物体都完成旋转,触发全局重置
  const allCompleted = objects.every(obj => obj.isCompleted);
  if (allCompleted && !resetting) {
    resetting = true;
    objects.forEach(obj => {
      obj.targetRot = 0;
      obj.isRotating = true;
      obj.isCompleted = false;
    });
  }
  
  // 遍历处理每个物体的旋转与绘制
  objects.forEach(obj => {
    // 鼠标悬停触发旋转(未在旋转、未完成、未处于重置阶段时)
    if (checkMouseHover(obj) && !obj.isRotating && !obj.isCompleted && !resetting) {
      obj.targetRot = PI; // 180度(p5.js使用弧度)
      obj.isRotating = true;
    }
    
    // 平滑过渡旋转角度
    if (obj.isRotating) {
      // 用lerp实现插值,0.05控制旋转速度,数值越大转得越快
      obj.currentRot = lerp(obj.currentRot, obj.targetRot, 0.05);
      // 角度接近目标时,停止旋转并标记状态
      if (abs(obj.currentRot - obj.targetRot) < 0.01) {
        obj.currentRot = obj.targetRot;
        obj.isRotating = false;
        obj.isCompleted = (obj.targetRot === PI);
        // 重置完成后清除全局重置标记
        if (resetting && obj.targetRot === 0) {
          obj.isCompleted = false;
          resetting = objects.some(obj => obj.isRotating);
        }
      }
    }
    
    // 绘制物体:用push/pop隔离每个物体的变换状态
    push();
    translate(obj.x, obj.y, obj.z);
    rotateY(obj.currentRot); // 动态应用当前旋转角度
    drawComplexObject(); // 替换成你的复杂3D物体绘制函数
    pop();
  });
}

// 示例复杂物体(替换成你自己的3D模型/绘制逻辑)
function drawComplexObject() {
  push();
  box(100);
  translate(0, 0, 50);
  cylinder(20, 80);
  pop();
}

关键要点

  • 状态驱动旋转:通过currentRot(当前角度)和targetRot(目标角度)控制旋转,初始时两者均为0,物体静止;悬停时修改目标角度,在draw()循环中逐步插值过渡,实现平滑旋转。
  • 独立状态管理:每个物体的状态单独存储,避免相互干扰。
  • 重置触发逻辑:用every()检测所有物体的完成状态,统一触发重置动画,同样通过插值过渡回到初始角度。
  • 变换隔离:每个物体绘制时用push()和pop()包裹,防止当前物体的旋转、平移操作影响其他物体。

内容的提问来源于stack exchange,提问作者torchic

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.16 20:23:11