PanoramaTool.js 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546
  1. //漫游模式相机控制器
  2. /**
  3. * 功能:主要是在漫游模式下控制相机的方向(相机的移动只是做了事件触发,逻辑不在此)
  4. *
  5. * 原理:
  6. * @ 以camera本地坐标系作为参考建立球坐标系,有关球坐标系 https://en.wikipedia.org/wiki/Spherical_coordinate_system
  7. * @ target作为camera的视点通过鼠标交互在球坐标系运动,然后调用THREE的API:camera.LookAt(target)校正相机方向
  8. *
  9. * @param {*} camera 被控制的相机
  10. */
  11. import * as THREE from '../lib/three.module.js'
  12. import math from '../utils/math.js'
  13. import common from '../utils/common.js'
  14. import { MouseButton, ControlEvents } from '../utils/enum.js'
  15. const rotationAfterMoveMultiplier = 40
  16. const rotationAfterMoveHistoryCount = 5
  17. const insideLookLimitUp = 90 //25
  18. const insideLookLimitDown = -90 //-25
  19. const rotationFriction = 0.05
  20. const rotationAccelerationInside = 4.5
  21. class PanoramaTool extends THREE.EventDispatcher {
  22. constructor(application) {
  23. super()
  24. this.application = application
  25. this.name = 'panorama'
  26. this.target = new THREE.Vector3(0, 0, 0) //相机视点,鼠标交互主要影响的对象
  27. this.lookVector = new THREE.Vector3() //相机方向,以单位向量表示
  28. this.lookSpeed = 0.05 //没发现下文用到???
  29. this.rotationAcc = new THREE.Vector2() //旋转角加速度
  30. this.rotationSpeed = new THREE.Vector2() //旋转角速度
  31. this.speed = 1 // 相机拖拽旋转速度
  32. /**
  33. * 球坐标系的相关参数lat,lon 与 phi,theta 两种表示形式
  34. * 注:少了半径参数,因为是用于约束相机的方向,半径长短在此没有意义,单位1即可,体现在方向向量lookVector上
  35. */
  36. this.lat = 0 //纬度,角度表示,直观
  37. this.lon = 0 //经度,角度表示,直观
  38. this.phi = 0 //phi,标准球坐标系的参数,弧度表示,用于进行直接计算
  39. this.theta = 0 //theta,标准球坐标系的参数,弧度表示,用于进行直接计算
  40. this.locked = !1 //是否锁定
  41. /**
  42. * 交互行为相关,有鼠标点击与触摸,点击或触摸的地方在此约定统称为交互点
  43. */
  44. this.pointer = new THREE.Vector2(0, 0) //交互点的屏幕坐标,有别于DOM坐标,在此存放NDC坐标。(NDC,三维常用坐标系,二维坐标,整个屏幕映射范围(-1,1),屏幕中心为原点,+Y朝上,+X朝右)
  45. this.pointersLimit = 2 //触摸事件的触摸点的限制个数
  46. this.pointers = [] //存储交互点的坐标
  47. this.rotationDifference = new THREE.Vector2() //记录帧之间的要进行的旋转量
  48. this.rotationHistory = [] //记录一次拖拽过程中每帧产生的rotationDifference,用于拖拽完成后计算平均值进而得到惯性角速度
  49. this.pointerDragOn = !1 //拖拽的标记,用于处理各种交互行为下的冲突问题
  50. this.pointerDragStart = new THREE.Vector2(0, 0) //拖拽开始位置,也作为两帧之间前一帧的坐标位置
  51. this.pinchDistance = 0 //触控下,“捏合”交互下,两触摸点的距离
  52. this.moveStart = new THREE.Vector2() //交互点移动行为开始的最表
  53. this.moveTolerance = 0.01 //产生拖拽行为的鼠标移动最小阈值,用于解决点击,和其他行为的误触操作
  54. this.translate = new THREE.Vector3()
  55. this._animate = this.animate.bind(this)
  56. this._onPointerDown = this.onPointerDown.bind(this)
  57. this._onPointerMove = this.onPointerMove.bind(this)
  58. this._onPointerUp = this.onPointerUp.bind(this)
  59. this._onMouseWheel = this.onMouseWheel.bind(this)
  60. this._onContextMenu = this.onContextMenu.bind(this)
  61. }
  62. /**
  63. * 启用状态
  64. */
  65. usable() {
  66. return !this.locked
  67. }
  68. activate() {
  69. //add
  70. const application = this.application
  71. const container = application.container
  72. application.addEventListener('animation', this._animate)
  73. container.addEventListener('pointerdown', this._onPointerDown)
  74. container.addEventListener('pointermove', this._onPointerMove)
  75. container.addEventListener('pointerup', this._onPointerUp)
  76. container.addEventListener('wheel', this._onMouseWheel, false)
  77. container.addEventListener('contextmenu', this._onContextMenu, false)
  78. //application.addEventListener('scene', this._onScene)
  79. this.lookAt(null, this.getDirection())
  80. }
  81. deactivate() {
  82. const application = this.application
  83. const container = application.container
  84. //container.removeEventListener('wheel', this._onWheel, false)
  85. application.removeEventListener('animation', this._animate)
  86. container.removeEventListener('pointerdown', this._onPointerDown)
  87. container.removeEventListener('pointermove', this._onPointerMove)
  88. container.removeEventListener('pointerup', this._onPointerUp)
  89. container.removeEventListener('wheel', this._onMouseWheel)
  90. container.removeEventListener('contextmenu', this._onContextMenu, false)
  91. //application.removeEventListener('scene', this._onScene)
  92. }
  93. getDirection() {
  94. //not by lon lat
  95. return new THREE.Vector3(0, 0, -1).applyQuaternion(this.application.camera.quaternion)
  96. }
  97. /**
  98. * 根据新的方向向量计算所指向的球面坐标(lat,lon),用到了笛卡尔坐标系转球面坐标系的数学方法
  99. * 注:THREE 的 Vector3 与 Spherical 两个数学类有互转的方法
  100. * @param {THREE.Vector3} direction 方向向量
  101. */
  102. lookAt(target, dir) {
  103. //compute lon lat
  104. let camera = this.application.camera
  105. dir = dir || camera.position.clone().sub(target)
  106. /**
  107. * 以下全为笛卡尔坐标->球座标,不多赘述
  108. */
  109. var i = Math.atan(dir.y / dir.x)
  110. i += dir.x < 0 ? Math.PI : 0
  111. i += dir.x > 0 && dir.y < 0 ? 2 * Math.PI : 0
  112. this.lon = -THREE.MathUtils.radToDeg(i) - 180
  113. var n = Math.sqrt(dir.x * dir.x + dir.y * dir.y),
  114. o = Math.atan(dir.z / n)
  115. this.lat = THREE.MathUtils.radToDeg(o)
  116. }
  117. /**
  118. * 记录一次拖拽旋转开始时的一些状态
  119. * @param {number} clientX 屏幕坐标
  120. * @param {number} clientY 屏幕坐标
  121. */
  122. startRotationFrom(clientX, clientY) {
  123. //以屏幕中心为原点,得到pointer在屏幕的百分比
  124. var mouse = common.handelPadding(clientX, clientY, this.application.container)
  125. math.convertScreenPositionToNDC(this.pointer, mouse, this.application.container.clientWidth, this.application.container.clientHeight)
  126. this.pointerDragOn = !0
  127. this.pointerDragStart.copy(this.pointer)
  128. this.moveStart.copy(this.pointer)
  129. this.rotationHistory = []
  130. this.rotationSpeed.set(0, 0)
  131. }
  132. onMouseOver(mouseEvent) {
  133. !this.pointerDragOn || (0 !== mouseEvent.which && 0 !== mouseEvent.buttons) || this.onMouseUp(mouseEvent)
  134. }
  135. onTouchStart(pointerEvent) {
  136. if (this.usable()) {
  137. pointerEvent.preventDefault()
  138. pointerEvent.stopPropagation()
  139. switch (pointerEvent.touches.length) {
  140. case 1:
  141. this.startRotationFrom(pointerEvent.touches[0].clientX, pointerEvent.touches[0].clientY)
  142. break
  143. case 2:
  144. var t = (pointerEvent.touches[0].clientX - pointerEvent.touches[1].clientX) / window.innerWidth,
  145. i = (pointerEvent.touches[0].clientY - pointerEvent.touches[1].clientY) / window.innerHeight
  146. this.pinchDistance = Math.sqrt(t * t + i * i)
  147. }
  148. //this.emit(ControlEvents.InputStart, 'touch')
  149. }
  150. }
  151. onPointerDown(pointerEvent) {
  152. if (this.usable()) {
  153. if ('touch' === pointerEvent.pointerType) {
  154. if (this.pointers.length < this.pointersLimit) {
  155. this.pointers.push({
  156. id: pointerEvent.pointerId,
  157. clientX: pointerEvent.clientX,
  158. clientY: pointerEvent.clientY
  159. })
  160. }
  161. pointerEvent.touches = this.pointers
  162. this.onTouchStart(pointerEvent)
  163. //this.emit(ControlEvents.InputStart, 'pointer')
  164. } else {
  165. this.onMouseDown(pointerEvent)
  166. }
  167. }
  168. }
  169. onMouseDown(mouseEvent) {
  170. if (this.usable()) {
  171. mouseEvent.preventDefault()
  172. mouseEvent.stopPropagation()
  173. switch (mouseEvent.button) {
  174. case MouseButton.LEFT:
  175. this.startRotationFrom(mouseEvent.clientX, mouseEvent.clientY)
  176. break
  177. case MouseButton.RIGHT:
  178. this.panStart = true
  179. break
  180. }
  181. //this.emit(ControlEvents.InputStart, 'mouse')
  182. }
  183. }
  184. /**
  185. * 根据两帧交互点坐标之间的差值,计算两帧角度差值(rotationDifference)用于旋转
  186. * 1.将两次交互点坐标分别映射到3D空间
  187. * 2.通过两坐标在XY平面上投影,分别计算与X轴夹角,再求差值作为竖直方向角度差值(rotationDifference.y)
  188. * 3.通过两坐标在XZ平面上投影,分别计算与X轴夹角,再求差值作为水平方向角度差值(rotationDifference.x)
  189. */
  190. updateRotation() {
  191. if (this.usable() && this.pointerDragOn) {
  192. let camera = this.application.camera
  193. camera.matrixWorld = new THREE.Matrix4() //许钟文加 unproject前先把相机置于原点 (player的cameras里的panorama是不更新matrixworld的,只有player的camera才更新。 为了其他的camera加)
  194. //两交互点在3D空间的坐标
  195. var pointerDragStart3D = new THREE.Vector3(this.pointerDragStart.x, this.pointerDragStart.y, -1).unproject(camera),
  196. pointer3D = new THREE.Vector3(this.pointer.x, this.pointer.y, -1).unproject(camera),
  197. //两交互点分别到原点的长度
  198. pointerDragStart3DLength = Math.sqrt(pointerDragStart3D.x * pointerDragStart3D.x + pointerDragStart3D.z * pointerDragStart3D.z),
  199. pointer3DLength = Math.sqrt(pointer3D.x * pointer3D.x + pointer3D.z * pointer3D.z),
  200. //通过Math.atan2计算在XY面上与X轴的夹角弧度。
  201. //注:因为 z = -1,所以两者到原点的长度近似为x分量(数值的大小也不需要绝对对应)
  202. anglePointerDragStart3DToX = Math.atan2(pointerDragStart3D.y, pointerDragStart3DLength), //近似为 anglePointerDragStart3DToX = Math.atan2( pointerDragStart3D.y, pointerDragStart3D.x )
  203. anglePointer3DToX = Math.atan2(pointer3D.y, pointer3DLength) //近似为 anglePointer3DToX = Math.atan2( pointer3D.y, pointer3D.x )
  204. camera.updateMatrix()
  205. camera.updateMatrixWorld()
  206. //算出两者角度差,作为竖直方向角度差值(rotationDifference.y)
  207. this.rotationDifference.y = THREE.MathUtils.radToDeg(anglePointerDragStart3DToX - anglePointer3DToX)
  208. //y分量清零,原向量等价于在XZ轴上的投影向量
  209. pointerDragStart3D.y = 0
  210. pointer3D.y = 0
  211. //归一化(/length),求两者夹角作为
  212. //判断方向,最后记为水平方向角度差值(rotationDifference.x)
  213. var anglePointerDragStart3DToPointer3D = Math.acos(pointerDragStart3D.dot(pointer3D) / pointerDragStart3D.length() / pointer3D.length())
  214. // isNaN(s) || (this.rotationDifference.x = THREE.MathUtils.radToDeg(s),
  215. // this.pointerDragStart.x < this.pointer.x && (this.rotationDifference.x *= -1)),
  216. if (!isNaN(anglePointerDragStart3DToPointer3D)) {
  217. this.rotationDifference.x = THREE.MathUtils.radToDeg(anglePointerDragStart3DToPointer3D)
  218. if (this.pointerDragStart.x < this.pointer.x) {
  219. this.rotationDifference.x *= -1
  220. }
  221. }
  222. this.rotationDifference.multiplyScalar(this.speed)
  223. //console.log(pointerDragStart3DLength,pointer3DLength)
  224. }
  225. }
  226. onMouseMove(mouseEvent) {
  227. if (this.usable()) {
  228. var mouse = common.handelPadding(mouseEvent.clientX, mouseEvent.clientY, this.application.container)
  229. math.convertScreenPositionToNDC(this.pointer, mouse, this.application.container.clientWidth, this.application.container.clientHeight)
  230. /* if (this.pointerDragOn) {
  231. if (Math.abs(this.pointer.x - this.moveStart.x) > this.moveTolerance || Math.abs(this.pointer.y - this.moveStart.y) > this.moveTolerance) {
  232. //this.emit(ControlEvents.Move, 'mouse')
  233. }
  234. } */
  235. //add
  236. this.updateRotation()
  237. if (this.panStart) {
  238. let delta = new THREE.Vector2().subVectors(this.pointer, this.pointerDragStart)
  239. let speed = 30
  240. this.translate.set(-delta.x * speed, -delta.y * speed, 0)
  241. }
  242. //更新pointerDragStart记录当前帧坐标,用于下一帧求帧差值
  243. this.pointerDragStart.copy(this.pointer)
  244. }
  245. }
  246. onTouchMove(pointerEvent) {
  247. if (this.usable()) {
  248. //this.emit(ControlEvents.Move, 'touch')
  249. switch (pointerEvent.touches.length) {
  250. case 1:
  251. var mouse = common.handelPadding(pointerEvent.touches[0].clientX, pointerEvent.touches[0].clientY, this.application.container)
  252. math.convertScreenPositionToNDC(this.pointer, mouse, this.application.container.clientWidth, this.application.container.clientHeight)
  253. break
  254. case 2:
  255. var offsetX = (pointerEvent.touches[0].clientX - pointerEvent.touches[1].clientX) / window.innerWidth,
  256. offsetY = (pointerEvent.touches[0].clientY - pointerEvent.touches[1].clientY) / window.innerHeight,
  257. n = this.pinchDistance - Math.sqrt(offsetX * offsetX + offsetY * offsetY)
  258. if (Math.abs(n) > 0.01) {
  259. //this.emit(ControlEvents.InteractionDirect)
  260. //this.emit(ControlEvents.Pinch, n)
  261. this.pinchDistance -= n
  262. }
  263. }
  264. }
  265. }
  266. onPointerMove(pointerEvent) {
  267. if (this.usable()) {
  268. if ('touch' === pointerEvent.pointerType) {
  269. this.pointers.forEach(function(t) {
  270. if (pointerEvent.pointerId === t.id) {
  271. t.clientX = pointerEvent.clientX
  272. t.clientY = pointerEvent.clientY
  273. }
  274. })
  275. pointerEvent.touches = this.pointers
  276. this.onTouchMove(pointerEvent)
  277. } else {
  278. this.onMouseMove(pointerEvent)
  279. }
  280. }
  281. }
  282. endRotation() {
  283. this.pointerDragOn = !1
  284. var averageVector = math.averageVectors(this.rotationHistory)
  285. this.rotationSpeed.set(averageVector.x * rotationAfterMoveMultiplier, averageVector.y * rotationAfterMoveMultiplier)
  286. }
  287. onTouchEnd(pointerEvent) {
  288. if (this.usable()) {
  289. pointerEvent.preventDefault()
  290. pointerEvent.stopPropagation()
  291. this.endRotation()
  292. }
  293. }
  294. onMouseUp(mouseEvent) {
  295. if (this.usable()) {
  296. mouseEvent.preventDefault()
  297. mouseEvent.stopPropagation()
  298. this.endRotation()
  299. this.panStart = false
  300. }
  301. }
  302. onPointerUp(pointerEvent) {
  303. if (this.usable()) {
  304. if ('touch' === pointerEvent.pointerType) {
  305. this.pointers.forEach(
  306. function(t, i) {
  307. pointerEvent.pointerId === t.id && this.pointers.splice(i, 1)
  308. }.bind(this)
  309. )
  310. pointerEvent.touches = this.pointers
  311. this.onTouchEnd(pointerEvent)
  312. } else {
  313. this.onMouseUp(pointerEvent)
  314. }
  315. }
  316. }
  317. /**
  318. * 主循环更新,主要通过物理上的刚体旋转行为(角位移,角速度,角加速度,摩擦等)计算得到新的相机视点target,主要是每帧瞬时的状态
  319. *
  320. * updateRotation()计算每帧对应的旋转量 rotationDifference
  321. *
  322. * 角位移:rotationDifference与原本lon,lat (等价于phi,theta)累加,得到新的角位移
  323. * 角速度:(rotationDifference数组的平均值 * 速度因子rotationAccelerationInside + 角加速度) - 摩擦rotationFriction。
  324. *
  325. * target坐标:新的角位移计算出新的球坐标,转换计算后的球坐标到笛卡尔坐标系
  326. *
  327. * @param { number } deltaTime 帧间隔时间。 注:关于帧间隔时间,是个有关物理计算的很重要的值,用于保持物理量与绝对时间的对应而不受帧率的的干扰,下文计算角速度用到。更多请见 https://blog.csdn.net/ChinarCSDN/article/details/82914420
  328. */
  329. animate(event = {}) {
  330. let deltaTime = event.delta
  331. if (this.locked) return //if(settings.vrEnabled) return;
  332. const camera = this.application.camera
  333. // 求出新的rotationDifference
  334. //this.updateRotation()
  335. //记录一组rotationDifference 用于求角速度 rotationSpeed。注:见 endRotation()
  336. for (this.rotationHistory.push(this.rotationDifference.clone()); this.rotationHistory.length > rotationAfterMoveHistoryCount; ) {
  337. this.rotationHistory.shift()
  338. }
  339. //计算角位移(交互影响下的)
  340. this.lon += this.rotationDifference.x
  341. this.lat += this.rotationDifference.y
  342. this.rotationDifference.set(0, 0)
  343. //计算角速度
  344. this.rotationSpeed.x = this.rotationSpeed.x * (1 - rotationFriction) + this.rotationAcc.x * rotationAccelerationInside
  345. this.rotationSpeed.y = this.rotationSpeed.y * (1 - rotationFriction) + this.rotationAcc.y * rotationAccelerationInside
  346. //计算角位移(交互后,物理定律影响下的)
  347. this.lon += this.rotationSpeed.x * deltaTime
  348. this.lat += this.rotationSpeed.y * deltaTime
  349. this.lat = Math.max(insideLookLimitDown, Math.min(insideLookLimitUp, this.lat)) //通过预定义的俯仰角最大最小范围(insideLookLimitUp、insideLookLimitDown)来限制俯仰角。 注:这种数学计算很常见,因此API也很常见(clamp),等价于 this.lat = THREE.MathUtils.clamp( this.lat, constants.insideLookLimitDown, settings.insideLookLimitUp );
  350. //转换为标准球坐标参数形式,并最终转换为笛卡尔坐标系下
  351. this.phi = THREE.MathUtils.degToRad(90 - this.lat)
  352. this.theta = THREE.MathUtils.degToRad(this.lon)
  353. this.lookVector.x = -Math.sin(this.phi) * Math.cos(this.theta)
  354. this.lookVector.z = Math.cos(this.phi)
  355. this.lookVector.y = Math.sin(this.phi) * Math.sin(this.theta)
  356. if (this.translate.length() != 0) {
  357. console.log(this.translate.clone())
  358. let vec = this.translate.clone().applyQuaternion(camera.quaternion)
  359. camera.position.add(vec)
  360. this.translate.set(0, 0, 0)
  361. camera.updateMatrix() //update完才能 lookAt
  362. }
  363. //求taget坐标: 当前相机位置 + 方向向量(对于此处旋转来说距离并无意义,方向向量的1即可)
  364. this.target.copy(this.lookVector).add(camera.position)
  365. //THREE的API来更新相机旋转。注:lookAt是四阶矩阵比较常见的API,因此此PanoramaControls计算流程,不算与THREE耦合
  366. camera.lookAt(this.target)
  367. camera.updateMatrix()
  368. this.application.notifyObjectsChanged(camera, this)
  369. }
  370. /**
  371. * 滚轮行为: 触发自定义事件
  372. */
  373. onMouseWheel(wheelEvent) {
  374. if (this.usable()) {
  375. //var t = wheelEvent.wheelDelta || -wheelEvent.detail
  376. //this.emit(ControlEvents.InteractionDirect)
  377. //this.emit(ControlEvents.Scroll, t)
  378. var delta = 0
  379. if (event.wheelDelta) {
  380. // WebKit / Opera / Explorer 9
  381. delta = -event.wheelDelta * 0.005
  382. } else if (event.detail) {
  383. // Firefox
  384. delta = -0.02 * event.detail
  385. }
  386. if (delta !== 0) {
  387. this.translate.set(0, 0, delta)
  388. }
  389. //displayMode
  390. }
  391. }
  392. /**
  393. * 键盘按下:触发自定义事件
  394. */
  395. onKeyDown(keyboardEvent) {
  396. if (!this.player.$app.config.useShortcutKeys) {
  397. return
  398. }
  399. if (this.usable()) {
  400. if (keyboardEvent.metaKey || keyboardEvent.ctrlKey) {
  401. } else {
  402. keyboardEvent.preventDefault()
  403. this.handleKeyDown(keyboardEvent.which)
  404. }
  405. }
  406. }
  407. handleKeyDown(keyValue) {
  408. var t = function(e, t) {
  409. this.rotationAcc[e] = t
  410. }.bind(this)
  411. //this.emit(ControlEvents.InteractionKey)
  412. var i = !0
  413. switch (keyValue) {
  414. case Keys.LEFTARROW:
  415. case Keys.J:
  416. t('x', -1)
  417. break
  418. case Keys.RIGHTARROW:
  419. case Keys.L:
  420. t('x', 1)
  421. break
  422. case Keys.I:
  423. t('y', 1)
  424. break
  425. case Keys.K:
  426. t('y', -1)
  427. break
  428. default:
  429. i = !1
  430. }
  431. //i && this.emit(ControlEvents.Move, 'key')
  432. }
  433. onKeyUp(keyboardEvent) {
  434. if (this.usable()) {
  435. keyboardEvent.preventDefault()
  436. keyboardEvent.stopPropagation()
  437. this.handleKeyUp(keyboardEvent.which)
  438. }
  439. }
  440. handleKeyUp(keyValue) {
  441. switch (keyValue) {
  442. case Keys.LEFTARROW:
  443. case Keys.J:
  444. case Keys.RIGHTARROW:
  445. case Keys.L:
  446. this.rotationAcc.x = 0
  447. break
  448. case Keys.I:
  449. case Keys.K:
  450. this.rotationAcc.y = 0
  451. }
  452. }
  453. onContextMenu(event) {
  454. event.preventDefault()
  455. }
  456. /**
  457. * 给定角加速度,使开始旋转。 注:类似给定力推
  458. */
  459. startRotating(e, t) {
  460. e && (this.rotationAcc.x = e)
  461. t && (this.rotationAcc.y = t)
  462. }
  463. /**
  464. * 通过物理定律来终止旋转
  465. */
  466. stopRotating(e) {
  467. e && (this.rotationSpeed.x = this.rotationSpeed.y = 0)
  468. this.rotationAcc.set(0, 0)
  469. }
  470. reset() {
  471. this.pointerDragOn = !1
  472. this.rotationAcc.set(0, 0)
  473. this.rotationSpeed.set(0, 0)
  474. this.pointers = []
  475. }
  476. }
  477. export { PanoramaTool }