第2.8篇手势交互——拖拽旋转 3D 模型难度⭐⭐⭐ 高级前置知识2.7 3D 模型渲染入门涉及源文件products/default/src/main/ets/pages/ModelViewerPage.ets前言在上一篇中我们成功使用 ArkGraphics3D 加载并渲染了一个 3D 模型。但静态的模型只能展示一个角度在实际应用中用户通常需要从各个方向观察模型。本篇将在模型渲染的基础上实现两种交互方式手指拖拽旋转和按钮步进旋转并深入理解四元数Quaternion在 3D 旋转中的运用。1. 旋转状态管理首先定义三个关键变量来追踪旋转状态privaterotationAngle:number0;// 当前旋转角度弧度privatedragStartX:number0;// 拖拽起始 X 坐标privatedragStartAngle:number0;// 拖拽起始时的旋转角度rotationAngle模型绕 Y 轴的总旋转量单位为弧度。dragStartX用户手指按下时的屏幕 X 坐标。dragStartAngle手指按下时当前的旋转角度用于增量计算。弧度与角度换算角度 弧度 × 57.3或弧度 × 180 / π。代码中用Math.round(rotationAngle * 57.3)将弧度转换为角度显示。2. onTouch 手势监听onTouch是 ArkUI 提供的基础手势事件接口可以监听Down、Move、Up等触摸事件。privatehandleRenderTouch(event:TouchEvent):void{if(event.touches.length0){return;}if(event.typeTouchType.Down){// 记录触摸起点和当前角度this.dragStartXevent.touches[0].x;this.dragStartAnglethis.rotationAngle;}elseif(event.typeTouchType.Move){// 计算水平滑动的偏移量constdeltaX:numberevent.touches[0].x-this.dragStartX;// 偏移量 → 旋转角度乘以 0.01 做归一化this.rotationAnglethis.dragStartAngledeltaX*0.01;// 应用旋转到模型this.applyModelRotation();// 更新状态显示this.status拖动旋转Math.round(this.rotationAngle*57.3).toString()°;}}手势逻辑拆解事件类型行为TouchType.Down记录起始 X 坐标和当前角度作为增量计算的基准TouchType.Move计算水平位移deltaX累加到起始角度上TouchType.Up无需处理角度已实时更新参数归一化deltaX * 0.01中的0.01是一个归一化因子它将屏幕像素差值映射到弧度值屏幕滑动 100px → 旋转 1 弧度约 57.3°屏幕滑动 360px → 旋转 3.6 弧度约 206°这个值可以根据交互体验需求调整数值越大同样滑动距离产生的旋转角度越大。3. 四元数Quaternion旋转变换3.1 为什么用四元数在 3D 图形学中表示旋转有三种主要方式方式优点缺点欧拉角Euler直观三个角度万向锁问题旋转矩阵通用占用空间大插值困难四元数Quaternion无万向锁插值平滑不够直观ArkGraphics3D 的Node.rotation属性使用四元数避免了欧拉角的万向锁问题。3.2 绕 Y 轴旋转的四元数privateapplyModelRotation():void{if(this.modelRootnull){return;}consthalfAngle:numberthis.rotationAngle/2;constrotation:Quaternion{x:0,y:Math.sin(halfAngle),// 绕 Y 轴旋转z:0,w:Math.cos(halfAngle)};this.modelRoot.rotationrotation;this.requestRenderFrame();}四元数原理绕任意轴旋转的四元数公式为Q (cos(θ/2), axis_x * sin(θ/2), axis_y * sin(θ/2), axis_z * sin(θ/2))当绕 Y 轴旋转时axis (0, 1, 0)因此Q (cos(θ/2), 0, sin(θ/2), 0)对应到代码中分量公式代码wcos(θ/2)Math.cos(halfAngle)x00ysin(θ/2)Math.sin(halfAngle)z00注意如果希望绕 X 轴上下旋转将x设为sin(halfAngle)y设为0绕 Z 轴同理。3.3 触发渲染更新应用旋转后调用renderFrame通知渲染引擎刷新画面privaterequestRenderFrame():void{if(this.activeScenenull){return;}constparams:RenderParameters{alwaysRender:true};this.activeScene.renderFrame(params);}4. 按钮步进旋转与复位除了拖拽还提供了按钮操作方式实现固定角度的步进旋转和一键复位。4.1 步进旋转privaterotateByStep(deltaAngle:number):void{this.rotationAngledeltaAngle;this.applyModelRotation();this.status已旋转Math.round(this.rotationAngle*57.3).toString()°;}按钮调用示例每次旋转约 20°// 左转 -0.35 弧度约 -20°this.rotateByStep(-0.35);// 右转 0.35 弧度约 20°this.rotateByStep(0.35);4.2 复位privateresetRotation():void{this.rotationAngle0;this.applyModelRotation();this.status视角已复位;}4.3 按钮 UI示意代码Row(){Button(左转).onClick((){this.rotateByStep(-0.35);})Button(复位).onClick((){this.resetRotation();})Button(右转).onClick((){this.rotateByStep(0.35);})}5. 交互流程图┌─────────────────────────────────────────┐ │ 交互流程总览 │ ├─────────────────────────────────────────┤ │ │ │ 手指拖拽 │ │ ┌─────────┐ ┌──────────────┐ │ │ │ Down │ → │ 记录起始状态 │ │ │ └─────────┘ └──────────────┘ │ │ ┌─────────┐ ┌──────────────┐ │ │ │ Move │ → │ deltaX * 0.01│ │ │ └─────────┘ └──────┬───────┘ │ │ ▼ │ │ ┌──────────────────────────────────┐ │ │ │ rotationAngle start deltaX │ │ │ └──────────────┬───────────────────┘ │ │ ▼ │ │ ┌──────────────────────────────────┐ │ │ │ applyModelRotation() │ │ │ │ → Quaternion(0, sin(θ/2), 0, │ │ │ │ cos(θ/2)) │ │ │ │ → modelRoot.rotation Q │ │ │ │ → renderFrame() │ │ │ └──────────────────────────────────┘ │ │ │ │ 按钮步进 │ │ rotateByStep(deltaAngle) │ │ → rotationAngle deltaAngle │ │ → applyModelRotation() │ │ │ │ 复位 │ │ resetRotation() │ │ → rotationAngle 0 │ │ → applyModelRotation() │ │ │ └─────────────────────────────────────────┘6. 关键参数调优指南参数作用推荐值范围调大效果deltaX * 0.01触摸灵敏度0.005 ~ 0.02滑动更灵敏旋转更快rotateByStep(-0.35)步进角度0.17 ~ 0.70每次旋转角度更大Quaternion.y sin(θ/2)旋转轴设为1为绕 Y 轴改为 X 轴实现上下旋转小结本篇我们实现了 3D 模型的交互式旋转onTouch 手势监听通过TouchType.Down记录起始状态TouchType.Move计算位移并更新角度。触摸坐标 → 旋转角度deltaX * 0.01实现屏幕像素到弧度值的归一化转换。四元数旋转构建绕 Y 轴的Quaternion设置到Node.rotation完成旋转变换。角度累加与状态保存通过rotationAngle变量维持旋转状态支持增量旋转。按钮步进与复位提供精确控制和一键还原能力。renderFrame 渲染更新每次旋转后主动触发重绘。拖拽交互让用户能够自由观察模型的每一个角度是 3D 查看器最基础也是最重要的交互方式之一。下一篇预告第2.9篇视频导出与本地保存——DocumentViewPicker