• 基于 Three.js 的图形操纵控件


    一、背景

    Three.js 是一个前端三维图形展示库。它自带了一个三维图形控件,OrbitControls.js,可以控制三维图形的平移、缩放、旋转。

    OrbitControls 是通过控制”照相机“(也就是三维形体的观察者)的位置,以实现对三维形体的平移、缩放,和旋转的。

    也就是说:

    • 通过平移照相机的位置,实现三维形体在显示屏幕上的平移效果
    • 通过调整照相机的远近,实现三维形体在显示屏幕上的缩放效果
    • 通过旋转照相机的镜头角度和朝向,实现三维形体在显示屏幕上的旋转

    但在实际使用中,这样带来一个问题:

    • 在平移了屏幕上的形体之后,相当于把”照相机“平移到了形体侧方
    • 这样,照相机镜头的指向,即照相机的正视线,不再对准形体中心的,而是被平移到了形体旁边的位置
    • 这时,再进行旋转操作,就是把照相机对准形体旁边的位置旋转,造成形体在屏幕上旋转的轨迹非常混乱

    为了解决这个问题,我们需要自己开发一套控件,来实现形体平移之后,仍能围绕形体中心进行旋转的效果。

    二、方案

    基本思路如下:

    1. 借鉴 OrbitControls 的事件处理框架,接收鼠标操作和触摸屏上的手势,判断用户是要进行旋转、平移,还是缩放。
    2. 然后通过操作形体,而不是操作照相机的方式,实现旋转、平移、缩放的效果。

    2.1 事件处理框架

    借用 OrbitControls 的事件处理框架:

    • 监听 pointerdown 事件,收到该事件后
      • 把该 pointer (也就是鼠标的某一个键,或者触摸屏上的某一个手指)加入活动 pointer 列表 
      • 进行 touchstart 处理(触摸屏)
        • 如果只有一个活动的 pointer (单指) - 旋转
          • handleTouchStartRotate() – 记录当前单指的坐标
        • 如果有两个活动的 pointer(双指)- 平移 + 缩放 - handleTouchStartDollyPan()
          • handleTouchStartDolly() - 记录当前两指之间的距离
          • handleTouchStartPan() - 记录当前两指中间的坐标
      • 进行 mousedown 处理(鼠标)
      • 开始监听 pointermove 事件,收到该事件后
        • 进行 touchmove 处理 (触摸屏)
          • 记录该 pointer 的位置
            • handleTouchMoveRotate
              • 根据当前 pointer 和初始 pointer 的坐标差值,控制旋转
                • rotateLeft
                • rotateUp
              • 用当前 pointer 的坐标值,更新初始坐标值的位置
            • handleTouchMoveDollyPan
              • handleTouchMoveDolly
                • 根据当前两指间距离和初始两指间距离的比例,计算缩放比例
                • dolly()
                • 用当前两指间距离,更新初始的两指间距离
              • handleTouchMovePan
                • 计算当前两指的平均坐标值
                • 根据当前坐标和初始坐标,计算移动位置
                • pan()
                • 用当前两指的平均坐标值,更新初始的两指坐标值
        • 进行 mousemove 处理(鼠标)
      • 开始监听 pointerup 事件,收到该事件后
        • 把该事件相关的 pointer 从 pointer 列表中删除 
        • 如果当前没有活动的 pointer (鼠标所有按键都松开了,触摸屏上没有手指活动)
          • 不再监听 pointermove
          • 不再监听 pointerup
    • 监听 pointercancel 事件
      • 移动浏览器会自动发送 pointercancel 事件,这时我们需要在三维图形的画布 (canvas) 的 CSS 中,把  touch-action 属性设为 none

    2.2 功能指标

    2.2.1 前提

    支持的设备

    支持 鼠标操作 触摸屏操作

    • 鼠标:左键拖动 = 旋转,右键拖动 = 平移,滚轮 = 缩放;按住 ctrl / shift / meta 三个键中的任意一个,可以切换左、右键的效果。
    • 触摸屏:单指拖动 = 旋转,双指拖动 = 平移,双指 Pinch = 缩放

    目前暂时不支持键盘

    支持的投影模式

    支持 正交照相机 (orthographic camera) 和 透视照相机 (perspective camera) 两种模式

    正交照相机没有 “近大远小” 的效果,不能靠移动照相机的位置进行图形的缩放。

    2.2.2 缩放

    操作:

    • 鼠标:滚轮
    • 触摸屏:双指 Pinch

    要求:缩放效果均匀,不会随着放大或者缩小变化得特别快或者特别慢。

    限制:最多放大 5 倍,最小缩到 1/5

    2.2.3 平移

    操作:

    • 鼠标:右键拖动  (或者按住 ctrl / shift / meta 三键之一,同时左键拖动)
    • 触摸屏:双指拖动

    要求:形体跟随 pointer (鼠标或手指)移动,跟随性良好,

    限制:形体不得移出 pointer 控制范围之外,不得移到屏幕之外完全不可见。

    2.2.4 旋转

    操作:

    • 鼠标:左键拖动(或者按住 ctrl / shift / meta 三键之一,同时右键拖动)
    • 触摸屏:单指拖动

    要求:绕形体中心点旋转,形体跟随 pointer ( 鼠标或手指)转动,跟随性好。

    限制:上下旋转不超过 2Pi, 左右旋转不超过 2Pi

    注意:

    • 双指操作时,Pinch 和 拖动可能同时进行,因此,可能产生 “既平移,又缩放” 的效果
    • 一些笔记本电脑的触控板,比如 Dell 大多数型号的笔记本电脑,并不支持实际意义上的多点触控,上面的 pinch 实际上产生的是 mousewheel (鼠标滚轮)事件,而对双指拖动没有任何反应。

    三、实现

    3.1 缩放

    正交相机 Orthographic透视相机 Perspective
    Mouse

    每次事件缩放固定的比例

    • deltaY>0,滚轮向下:  缩小
    • deltaY<0,滚轮向上:  放大

    比例 = 0.95^zoomSpeed

    也可以直接考虑设置 zoom 参数
    Touch

    根据两次事件指尖距离的比例 (r),
    以 r^zoomSpeed 进行缩放

    • 指尖距离变大,r>1,放大
    • 指尖距离变小,r<1,缩小
    统一考虑调整 zoom 参数

    3.2 平移

    根据 camera.zoom 的比例,结合 pointer 在屏幕上的位置变化向量,计算物体移动的距离。

    物体移动(x, -y) = Pointer(dx, dy) * 视场(x,y) / 画布 (x,y) / camera.zoom

    正交相机  视场(x,y) = camera.right - camera.left , camera.top - camera.bottom

    透视相机  视场(x,y) = tan(camera.fov/2)*camera.postion.z*2, tan(camera.fov/2)*camera.postion.z*2*camera.aspect

    3.3 旋转

    每次事件只做步长为 0.05 的旋转,根据 Pointer(x, y) 的移动方向,进行左右,或者上下旋转。

    学习网站参考
    Discover three.js!

    Three.js – JavaScript 3D Library

  • 相关阅读:
    Spring:核心容器(6)
    EMQX Enterprise 5.5 发布:新增 Elasticsearch 数据集成
    【Linux】在Xilinx平台上实现UVC Gadget(1)
    【学习笔记】在 windows 下创建多线程 C++
    (附源码)springboot苔藓植物科普网站 毕业设计 345641
    Day17-购物车页面-商品列表-实现滑动删除功能
    mysql基础学习笔记
    MySQL索引
    288.【华为OD机试】AI面板识别(排序算法—Java&Python&C++&JS实现)
    Serverless 数仓技术与挑战(内含 PPT 下载)
  • 原文地址:https://blog.csdn.net/yf18040578780/article/details/133179818