1
0

TrackballControls.js 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829
  1. import {
  2. Controls,
  3. MathUtils,
  4. MOUSE,
  5. Quaternion,
  6. Vector2,
  7. Vector3
  8. } from 'three';
  9. const _changeEvent = { type: 'change' };
  10. const _startEvent = { type: 'start' };
  11. const _endEvent = { type: 'end' };
  12. const _EPS = 0.000001;
  13. const _STATE = { NONE: - 1, ROTATE: 0, ZOOM: 1, PAN: 2, TOUCH_ROTATE: 3, TOUCH_ZOOM_PAN: 4 };
  14. const _v2 = new Vector2();
  15. const _mouseChange = new Vector2();
  16. const _objectUp = new Vector3();
  17. const _pan = new Vector3();
  18. const _axis = new Vector3();
  19. const _quaternion = new Quaternion();
  20. const _eyeDirection = new Vector3();
  21. const _objectUpDirection = new Vector3();
  22. const _objectSidewaysDirection = new Vector3();
  23. const _moveDirection = new Vector3();
  24. class TrackballControls extends Controls {
  25. constructor( object, domElement = null ) {
  26. super( object, domElement );
  27. // API
  28. this.enabled = true;
  29. this.screen = { left: 0, top: 0, width: 0, height: 0 };
  30. this.rotateSpeed = 1.0;
  31. this.zoomSpeed = 1.2;
  32. this.panSpeed = 0.3;
  33. this.noRotate = false;
  34. this.noZoom = false;
  35. this.noPan = false;
  36. this.staticMoving = false;
  37. this.dynamicDampingFactor = 0.2;
  38. this.minDistance = 0;
  39. this.maxDistance = Infinity;
  40. this.minZoom = 0;
  41. this.maxZoom = Infinity;
  42. this.keys = [ 'KeyA' /*A*/, 'KeyS' /*S*/, 'KeyD' /*D*/ ];
  43. this.mouseButtons = { LEFT: MOUSE.ROTATE, MIDDLE: MOUSE.DOLLY, RIGHT: MOUSE.PAN };
  44. this.state = _STATE.NONE;
  45. this.keyState = _STATE.NONE;
  46. this.target = new Vector3();
  47. // internals
  48. this._lastPosition = new Vector3();
  49. this._lastZoom = 1;
  50. this._touchZoomDistanceStart = 0;
  51. this._touchZoomDistanceEnd = 0;
  52. this._lastAngle = 0;
  53. this._eye = new Vector3();
  54. this._movePrev = new Vector2();
  55. this._moveCurr = new Vector2();
  56. this._lastAxis = new Vector3();
  57. this._zoomStart = new Vector2();
  58. this._zoomEnd = new Vector2();
  59. this._panStart = new Vector2();
  60. this._panEnd = new Vector2();
  61. this._pointers = [];
  62. this._pointerPositions = {};
  63. // event listeners
  64. this._onPointerMove = onPointerMove.bind( this );
  65. this._onPointerDown = onPointerDown.bind( this );
  66. this._onPointerUp = onPointerUp.bind( this );
  67. this._onPointerCancel = onPointerCancel.bind( this );
  68. this._onContextMenu = onContextMenu.bind( this );
  69. this._onMouseWheel = onMouseWheel.bind( this );
  70. this._onKeyDown = onKeyDown.bind( this );
  71. this._onKeyUp = onKeyUp.bind( this );
  72. this._onTouchStart = onTouchStart.bind( this );
  73. this._onTouchMove = onTouchMove.bind( this );
  74. this._onTouchEnd = onTouchEnd.bind( this );
  75. this._onMouseDown = onMouseDown.bind( this );
  76. this._onMouseMove = onMouseMove.bind( this );
  77. this._onMouseUp = onMouseUp.bind( this );
  78. // for reset
  79. this._target0 = this.target.clone();
  80. this._position0 = this.object.position.clone();
  81. this._up0 = this.object.up.clone();
  82. this._zoom0 = this.object.zoom;
  83. if ( domElement !== null ) {
  84. this.connect();
  85. this.handleResize();
  86. }
  87. // force an update at start
  88. this.update();
  89. }
  90. connect() {
  91. window.addEventListener( 'keydown', this._onKeyDown );
  92. window.addEventListener( 'keyup', this._onKeyUp );
  93. this.domElement.addEventListener( 'pointerdown', this._onPointerDown );
  94. this.domElement.addEventListener( 'pointercancel', this._onPointerCancel );
  95. this.domElement.addEventListener( 'wheel', this._onMouseWheel, { passive: false } );
  96. this.domElement.addEventListener( 'contextmenu', this._onContextMenu );
  97. this.domElement.style.touchAction = 'none'; // disable touch scroll
  98. }
  99. disconnect() {
  100. window.removeEventListener( 'keydown', this._onKeyDown );
  101. window.removeEventListener( 'keyup', this._onKeyUp );
  102. this.domElement.removeEventListener( 'pointerdown', this._onPointerDown );
  103. this.domElement.removeEventListener( 'pointermove', this._onPointerMove );
  104. this.domElement.removeEventListener( 'pointerup', this._onPointerUp );
  105. this.domElement.removeEventListener( 'pointercancel', this._onPointerCancel );
  106. this.domElement.removeEventListener( 'wheel', this._onMouseWheel );
  107. this.domElement.removeEventListener( 'contextmenu', this._onContextMenu );
  108. this.domElement.style.touchAction = 'auto'; // disable touch scroll
  109. }
  110. dispose() {
  111. this.disconnect();
  112. }
  113. handleResize() {
  114. const box = this.domElement.getBoundingClientRect();
  115. // adjustments come from similar code in the jquery offset() function
  116. const d = this.domElement.ownerDocument.documentElement;
  117. this.screen.left = box.left + window.pageXOffset - d.clientLeft;
  118. this.screen.top = box.top + window.pageYOffset - d.clientTop;
  119. this.screen.width = box.width;
  120. this.screen.height = box.height;
  121. }
  122. update() {
  123. this._eye.subVectors( this.object.position, this.target );
  124. if ( ! this.noRotate ) {
  125. this._rotateCamera();
  126. }
  127. if ( ! this.noZoom ) {
  128. this._zoomCamera();
  129. }
  130. if ( ! this.noPan ) {
  131. this._panCamera();
  132. }
  133. this.object.position.addVectors( this.target, this._eye );
  134. if ( this.object.isPerspectiveCamera ) {
  135. this._checkDistances();
  136. this.object.lookAt( this.target );
  137. if ( this._lastPosition.distanceToSquared( this.object.position ) > _EPS ) {
  138. this.dispatchEvent( _changeEvent );
  139. this._lastPosition.copy( this.object.position );
  140. }
  141. } else if ( this.object.isOrthographicCamera ) {
  142. this.object.lookAt( this.target );
  143. if ( this._lastPosition.distanceToSquared( this.object.position ) > _EPS || this._lastZoom !== this.object.zoom ) {
  144. this.dispatchEvent( _changeEvent );
  145. this._lastPosition.copy( this.object.position );
  146. this._lastZoom = this.object.zoom;
  147. }
  148. } else {
  149. console.warn( 'THREE.TrackballControls: Unsupported camera type.' );
  150. }
  151. }
  152. reset() {
  153. this.state = _STATE.NONE;
  154. this.keyState = _STATE.NONE;
  155. this.target.copy( this._target0 );
  156. this.object.position.copy( this._position0 );
  157. this.object.up.copy( this._up0 );
  158. this.object.zoom = this._zoom0;
  159. this.object.updateProjectionMatrix();
  160. this._eye.subVectors( this.object.position, this.target );
  161. this.object.lookAt( this.target );
  162. this.dispatchEvent( _changeEvent );
  163. this._lastPosition.copy( this.object.position );
  164. this._lastZoom = this.object.zoom;
  165. }
  166. _panCamera() {
  167. _mouseChange.copy( this._panEnd ).sub( this._panStart );
  168. if ( _mouseChange.lengthSq() ) {
  169. if ( this.object.isOrthographicCamera ) {
  170. const scale_x = ( this.object.right - this.object.left ) / this.object.zoom / this.domElement.clientWidth;
  171. const scale_y = ( this.object.top - this.object.bottom ) / this.object.zoom / this.domElement.clientWidth;
  172. _mouseChange.x *= scale_x;
  173. _mouseChange.y *= scale_y;
  174. }
  175. _mouseChange.multiplyScalar( this._eye.length() * this.panSpeed );
  176. _pan.copy( this._eye ).cross( this.object.up ).setLength( _mouseChange.x );
  177. _pan.add( _objectUp.copy( this.object.up ).setLength( _mouseChange.y ) );
  178. this.object.position.add( _pan );
  179. this.target.add( _pan );
  180. if ( this.staticMoving ) {
  181. this._panStart.copy( this._panEnd );
  182. } else {
  183. this._panStart.add( _mouseChange.subVectors( this._panEnd, this._panStart ).multiplyScalar( this.dynamicDampingFactor ) );
  184. }
  185. }
  186. }
  187. _rotateCamera() {
  188. _moveDirection.set( this._moveCurr.x - this._movePrev.x, this._moveCurr.y - this._movePrev.y, 0 );
  189. let angle = _moveDirection.length();
  190. if ( angle ) {
  191. this._eye.copy( this.object.position ).sub( this.target );
  192. _eyeDirection.copy( this._eye ).normalize();
  193. _objectUpDirection.copy( this.object.up ).normalize();
  194. _objectSidewaysDirection.crossVectors( _objectUpDirection, _eyeDirection ).normalize();
  195. _objectUpDirection.setLength( this._moveCurr.y - this._movePrev.y );
  196. _objectSidewaysDirection.setLength( this._moveCurr.x - this._movePrev.x );
  197. _moveDirection.copy( _objectUpDirection.add( _objectSidewaysDirection ) );
  198. _axis.crossVectors( _moveDirection, this._eye ).normalize();
  199. angle *= this.rotateSpeed;
  200. _quaternion.setFromAxisAngle( _axis, angle );
  201. this._eye.applyQuaternion( _quaternion );
  202. this.object.up.applyQuaternion( _quaternion );
  203. this._lastAxis.copy( _axis );
  204. this._lastAngle = angle;
  205. } else if ( ! this.staticMoving && this._lastAngle ) {
  206. this._lastAngle *= Math.sqrt( 1.0 - this.dynamicDampingFactor );
  207. this._eye.copy( this.object.position ).sub( this.target );
  208. _quaternion.setFromAxisAngle( this._lastAxis, this._lastAngle );
  209. this._eye.applyQuaternion( _quaternion );
  210. this.object.up.applyQuaternion( _quaternion );
  211. }
  212. this._movePrev.copy( this._moveCurr );
  213. }
  214. _zoomCamera() {
  215. let factor;
  216. if ( this.state === _STATE.TOUCH_ZOOM_PAN ) {
  217. factor = this._touchZoomDistanceStart / this._touchZoomDistanceEnd;
  218. this._touchZoomDistanceStart = this._touchZoomDistanceEnd;
  219. if ( this.object.isPerspectiveCamera ) {
  220. this._eye.multiplyScalar( factor );
  221. } else if ( this.object.isOrthographicCamera ) {
  222. this.object.zoom = MathUtils.clamp( this.object.zoom / factor, this.minZoom, this.maxZoom );
  223. if ( this._lastZoom !== this.object.zoom ) {
  224. this.object.updateProjectionMatrix();
  225. }
  226. } else {
  227. console.warn( 'THREE.TrackballControls: Unsupported camera type' );
  228. }
  229. } else {
  230. factor = 1.0 + ( this._zoomEnd.y - this._zoomStart.y ) * this.zoomSpeed;
  231. if ( factor !== 1.0 && factor > 0.0 ) {
  232. if ( this.object.isPerspectiveCamera ) {
  233. this._eye.multiplyScalar( factor );
  234. } else if ( this.object.isOrthographicCamera ) {
  235. this.object.zoom = MathUtils.clamp( this.object.zoom / factor, this.minZoom, this.maxZoom );
  236. if ( this._lastZoom !== this.object.zoom ) {
  237. this.object.updateProjectionMatrix();
  238. }
  239. } else {
  240. console.warn( 'THREE.TrackballControls: Unsupported camera type' );
  241. }
  242. }
  243. if ( this.staticMoving ) {
  244. this._zoomStart.copy( this._zoomEnd );
  245. } else {
  246. this._zoomStart.y += ( this._zoomEnd.y - this._zoomStart.y ) * this.dynamicDampingFactor;
  247. }
  248. }
  249. }
  250. _getMouseOnScreen( pageX, pageY ) {
  251. _v2.set(
  252. ( pageX - this.screen.left ) / this.screen.width,
  253. ( pageY - this.screen.top ) / this.screen.height
  254. );
  255. return _v2;
  256. }
  257. _getMouseOnCircle( pageX, pageY ) {
  258. _v2.set(
  259. ( ( pageX - this.screen.width * 0.5 - this.screen.left ) / ( this.screen.width * 0.5 ) ),
  260. ( ( this.screen.height + 2 * ( this.screen.top - pageY ) ) / this.screen.width ) // screen.width intentional
  261. );
  262. return _v2;
  263. }
  264. _addPointer( event ) {
  265. this._pointers.push( event );
  266. }
  267. _removePointer( event ) {
  268. delete this._pointerPositions[ event.pointerId ];
  269. for ( let i = 0; i < this._pointers.length; i ++ ) {
  270. if ( this._pointers[ i ].pointerId == event.pointerId ) {
  271. this._pointers.splice( i, 1 );
  272. return;
  273. }
  274. }
  275. }
  276. _trackPointer( event ) {
  277. let position = this._pointerPositions[ event.pointerId ];
  278. if ( position === undefined ) {
  279. position = new Vector2();
  280. this._pointerPositions[ event.pointerId ] = position;
  281. }
  282. position.set( event.pageX, event.pageY );
  283. }
  284. _getSecondPointerPosition( event ) {
  285. const pointer = ( event.pointerId === this._pointers[ 0 ].pointerId ) ? this._pointers[ 1 ] : this._pointers[ 0 ];
  286. return this._pointerPositions[ pointer.pointerId ];
  287. }
  288. _checkDistances() {
  289. if ( ! this.noZoom || ! this.noPan ) {
  290. if ( this._eye.lengthSq() > this.maxDistance * this.maxDistance ) {
  291. this.object.position.addVectors( this.target, this._eye.setLength( this.maxDistance ) );
  292. this._zoomStart.copy( this._zoomEnd );
  293. }
  294. if ( this._eye.lengthSq() < this.minDistance * this.minDistance ) {
  295. this.object.position.addVectors( this.target, this._eye.setLength( this.minDistance ) );
  296. this._zoomStart.copy( this._zoomEnd );
  297. }
  298. }
  299. }
  300. }
  301. function onPointerDown( event ) {
  302. if ( this.enabled === false ) return;
  303. if ( this._pointers.length === 0 ) {
  304. this.domElement.setPointerCapture( event.pointerId );
  305. this.domElement.addEventListener( 'pointermove', this._onPointerMove );
  306. this.domElement.addEventListener( 'pointerup', this._onPointerUp );
  307. }
  308. //
  309. this._addPointer( event );
  310. if ( event.pointerType === 'touch' ) {
  311. this._onTouchStart( event );
  312. } else {
  313. this._onMouseDown( event );
  314. }
  315. }
  316. function onPointerMove( event ) {
  317. if ( this.enabled === false ) return;
  318. if ( event.pointerType === 'touch' ) {
  319. this._onTouchMove( event );
  320. } else {
  321. this._onMouseMove( event );
  322. }
  323. }
  324. function onPointerUp( event ) {
  325. if ( this.enabled === false ) return;
  326. if ( event.pointerType === 'touch' ) {
  327. this._onTouchEnd( event );
  328. } else {
  329. this._onMouseUp();
  330. }
  331. //
  332. this._removePointer( event );
  333. if ( this._pointers.length === 0 ) {
  334. this.domElement.releasePointerCapture( event.pointerId );
  335. this.domElement.removeEventListener( 'pointermove', this._onPointerMove );
  336. this.domElement.removeEventListener( 'pointerup', this._onPointerUp );
  337. }
  338. }
  339. function onPointerCancel( event ) {
  340. this._removePointer( event );
  341. }
  342. function onKeyUp() {
  343. if ( this.enabled === false ) return;
  344. this.keyState = _STATE.NONE;
  345. window.addEventListener( 'keydown', this._onKeyDown );
  346. }
  347. function onKeyDown( event ) {
  348. if ( this.enabled === false ) return;
  349. window.removeEventListener( 'keydown', this._onKeyDown );
  350. if ( this.keyState !== _STATE.NONE ) {
  351. return;
  352. } else if ( event.code === this.keys[ _STATE.ROTATE ] && ! this.noRotate ) {
  353. this.keyState = _STATE.ROTATE;
  354. } else if ( event.code === this.keys[ _STATE.ZOOM ] && ! this.noZoom ) {
  355. this.keyState = _STATE.ZOOM;
  356. } else if ( event.code === this.keys[ _STATE.PAN ] && ! this.noPan ) {
  357. this.keyState = _STATE.PAN;
  358. }
  359. }
  360. function onMouseDown( event ) {
  361. if ( this.state === _STATE.NONE ) {
  362. switch ( event.button ) {
  363. case this.mouseButtons.LEFT:
  364. this.state = _STATE.ROTATE;
  365. break;
  366. case this.mouseButtons.MIDDLE:
  367. this.state = _STATE.ZOOM;
  368. break;
  369. case this.mouseButtons.RIGHT:
  370. this.state = _STATE.PAN;
  371. break;
  372. }
  373. }
  374. const state = ( this.keyState !== _STATE.NONE ) ? this.keyState : this.state;
  375. if ( state === _STATE.ROTATE && ! this.noRotate ) {
  376. this._moveCurr.copy( this._getMouseOnCircle( event.pageX, event.pageY ) );
  377. this._movePrev.copy( this._moveCurr );
  378. } else if ( state === _STATE.ZOOM && ! this.noZoom ) {
  379. this._zoomStart.copy( this._getMouseOnScreen( event.pageX, event.pageY ) );
  380. this._zoomEnd.copy( this._zoomStart );
  381. } else if ( state === _STATE.PAN && ! this.noPan ) {
  382. this._panStart.copy( this._getMouseOnScreen( event.pageX, event.pageY ) );
  383. this._panEnd.copy( this._panStart );
  384. }
  385. this.dispatchEvent( _startEvent );
  386. }
  387. function onMouseMove( event ) {
  388. const state = ( this.keyState !== _STATE.NONE ) ? this.keyState : this.state;
  389. if ( state === _STATE.ROTATE && ! this.noRotate ) {
  390. this._movePrev.copy( this._moveCurr );
  391. this._moveCurr.copy( this._getMouseOnCircle( event.pageX, event.pageY ) );
  392. } else if ( state === _STATE.ZOOM && ! this.noZoom ) {
  393. this._zoomEnd.copy( this._getMouseOnScreen( event.pageX, event.pageY ) );
  394. } else if ( state === _STATE.PAN && ! this.noPan ) {
  395. this._panEnd.copy( this._getMouseOnScreen( event.pageX, event.pageY ) );
  396. }
  397. }
  398. function onMouseUp() {
  399. this.state = _STATE.NONE;
  400. this.dispatchEvent( _endEvent );
  401. }
  402. function onMouseWheel( event ) {
  403. if ( this.enabled === false ) return;
  404. if ( this.noZoom === true ) return;
  405. event.preventDefault();
  406. switch ( event.deltaMode ) {
  407. case 2:
  408. // Zoom in pages
  409. this._zoomStart.y -= event.deltaY * 0.025;
  410. break;
  411. case 1:
  412. // Zoom in lines
  413. this._zoomStart.y -= event.deltaY * 0.01;
  414. break;
  415. default:
  416. // undefined, 0, assume pixels
  417. this._zoomStart.y -= event.deltaY * 0.00025;
  418. break;
  419. }
  420. this.dispatchEvent( _startEvent );
  421. this.dispatchEvent( _endEvent );
  422. }
  423. function onContextMenu( event ) {
  424. if ( this.enabled === false ) return;
  425. event.preventDefault();
  426. }
  427. function onTouchStart( event ) {
  428. this._trackPointer( event );
  429. switch ( this._pointers.length ) {
  430. case 1:
  431. this.state = _STATE.TOUCH_ROTATE;
  432. this._moveCurr.copy( this._getMouseOnCircle( this._pointers[ 0 ].pageX, this._pointers[ 0 ].pageY ) );
  433. this._movePrev.copy( this._moveCurr );
  434. break;
  435. default: // 2 or more
  436. this.state = _STATE.TOUCH_ZOOM_PAN;
  437. const dx = this._pointers[ 0 ].pageX - this._pointers[ 1 ].pageX;
  438. const dy = this._pointers[ 0 ].pageY - this._pointers[ 1 ].pageY;
  439. this._touchZoomDistanceEnd = this._touchZoomDistanceStart = Math.sqrt( dx * dx + dy * dy );
  440. const x = ( this._pointers[ 0 ].pageX + this._pointers[ 1 ].pageX ) / 2;
  441. const y = ( this._pointers[ 0 ].pageY + this._pointers[ 1 ].pageY ) / 2;
  442. this._panStart.copy( this._getMouseOnScreen( x, y ) );
  443. this._panEnd.copy( this._panStart );
  444. break;
  445. }
  446. this.dispatchEvent( _startEvent );
  447. }
  448. function onTouchMove( event ) {
  449. this._trackPointer( event );
  450. switch ( this._pointers.length ) {
  451. case 1:
  452. this._movePrev.copy( this._moveCurr );
  453. this._moveCurr.copy( this._getMouseOnCircle( event.pageX, event.pageY ) );
  454. break;
  455. default: // 2 or more
  456. const position = this._getSecondPointerPosition( event );
  457. const dx = event.pageX - position.x;
  458. const dy = event.pageY - position.y;
  459. this._touchZoomDistanceEnd = Math.sqrt( dx * dx + dy * dy );
  460. const x = ( event.pageX + position.x ) / 2;
  461. const y = ( event.pageY + position.y ) / 2;
  462. this._panEnd.copy( this._getMouseOnScreen( x, y ) );
  463. break;
  464. }
  465. }
  466. function onTouchEnd( event ) {
  467. switch ( this._pointers.length ) {
  468. case 0:
  469. this.state = _STATE.NONE;
  470. break;
  471. case 1:
  472. this.state = _STATE.TOUCH_ROTATE;
  473. this._moveCurr.copy( this._getMouseOnCircle( event.pageX, event.pageY ) );
  474. this._movePrev.copy( this._moveCurr );
  475. break;
  476. case 2:
  477. this.state = _STATE.TOUCH_ZOOM_PAN;
  478. for ( let i = 0; i < this._pointers.length; i ++ ) {
  479. if ( this._pointers[ i ].pointerId !== event.pointerId ) {
  480. const position = this._pointerPositions[ this._pointers[ i ].pointerId ];
  481. this._moveCurr.copy( this._getMouseOnCircle( position.x, position.y ) );
  482. this._movePrev.copy( this._moveCurr );
  483. break;
  484. }
  485. }
  486. break;
  487. }
  488. this.dispatchEvent( _endEvent );
  489. }
  490. export { TrackballControls };
粤ICP备19079148号