PanoramaControls.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326
  1. function PanoramaControls(camera, domElement) {
  2. // fyz 相机放大缩小
  3. this.activationThreshold = 1.1;
  4. this.scrollZoomSpeed = 0.08;
  5. this.scrollZoomSta = true;
  6. this.zoomMin = 0.7;
  7. this.zoomMax = 1.5;
  8. this.baseFov = 70;
  9. this.camera = camera;
  10. this.camera.fov = this.baseFov;
  11. this.domElement = domElement;
  12. this.camera.controls = this;
  13. this.enabled = true;
  14. this.target = new THREE.Vector3(0, 0, 0);
  15. this.lookVector = new THREE.Vector3;
  16. this.aimFrom = this.camera.position;
  17. this.lat = 0;
  18. this.latMin = -60//-40;
  19. this.latMax = 60//40;
  20. this.lon = 0;
  21. this.phi = 0;
  22. this.theta = 0;
  23. this.lookSpeed = 0.05;
  24. this.rotationAcc = new THREE.Vector2;
  25. this.rotationSpeed = new THREE.Vector2;
  26. this.rotationHistory = [];
  27. this.rotationDifference = new THREE.Vector2;
  28. this.pointerDragOn = !1;
  29. this.pointer = new THREE.Vector3(0, 0, -1);
  30. this.pointerDragStart = new THREE.Vector3(0, 0, -1);
  31. this._wheel = 0;
  32. this.zoomLevel = 1;
  33. this.translationWorldDelta = new THREE.Vector3
  34. this.bindEvents()
  35. }
  36. PanoramaControls.prototype.bindEvents = function() {
  37. window.addEventListener("mousemove", this.onMouseMove.bind(this));
  38. this.domElement.addEventListener("mousedown", this.onMouseDown.bind(this));
  39. window.addEventListener("mouseup", this.onMouseUp.bind(this));
  40. this.domElement.addEventListener("mouseover", (event) => this.pointerDragOn && 0 === event.which && this.onMouseUp(event));
  41. this.domElement.addEventListener("touchstart", this.onTouchStart.bind(this));
  42. this.domElement.addEventListener("touchmove", this.onTouchMove.bind(this));
  43. this.domElement.addEventListener("touchend", this.onTouchEnd.bind(this));
  44. this.domElement.addEventListener("wheel", this.onMouseWheel.bind(this)); // fyz wheel事件代替mousewheel事件
  45. this.domElement.addEventListener("DOMMouseScroll", this.onMouseWheel.bind(this));
  46. this.domElement.addEventListener("contextmenu", (event) => event.preventDefault());
  47. //document.addEventListener("keydown", this.onKeyDown.bind(this));
  48. //document.addEventListener("keyup", this.onKeyUp.bind(this));
  49. }
  50. PanoramaControls.prototype.lookAt = function(point) {
  51. var directionNegative = this.camera.position.clone().sub(point),
  52. theta = Math.atan(directionNegative.z / directionNegative.x);
  53. theta += directionNegative.x < 0 ? Math.PI : 0;
  54. theta += directionNegative.x > 0 && directionNegative.z < 0 ? 2 * Math.PI : 0;
  55. this.lon = THREE.Math.radToDeg(theta) + 180;
  56. let projectorR = Math.sqrt(directionNegative.x * directionNegative.x + directionNegative.z * directionNegative.z),
  57. phi = Math.atan(directionNegative.y / projectorR);
  58. this.lat = -THREE.Math.radToDeg(phi)
  59. }
  60. PanoramaControls.prototype.startRotationFrom = function(screenX, screenY) {
  61. this.updatePointer(screenX, screenY);
  62. this.pointerDragOn = true;
  63. this.pointerDragStart.copy(this.pointer);
  64. //TODO
  65. //this.pointerDragStartIntersect = this.player.getMouseIntersect(this.pointer.clone(), [this.scene.skybox]).point;
  66. this.rotationHistory = [];
  67. this.rotationSpeed.set(0, 0);
  68. }
  69. PanoramaControls.prototype.onTouchStart = function(event) {
  70. if (this.enabled) {
  71. event.preventDefault();
  72. event.stopPropagation();
  73. this.startRotationFrom(event.changedTouches[0].clientX, event.changedTouches[0].clientY);
  74. }
  75. }
  76. PanoramaControls.prototype.onMouseDown = function(event) {
  77. if (this.enabled) {
  78. event.preventDefault();
  79. event.stopPropagation()
  80. switch (event.button) {
  81. case 0:
  82. this.startRotationFrom(event.clientX, event.clientY);
  83. break;
  84. case 2:
  85. }
  86. }
  87. }
  88. PanoramaControls.prototype.updateRotation = function( ) {
  89. if (this.enabled && this.pointerDragOn) {
  90. var pointerDragStart3D = new THREE.Vector3(this.pointerDragStart.x, this.pointerDragStart.y, 1).unproject(this.camera);
  91. var pointer3D = new THREE.Vector3(this.pointer.x, this.pointer.y, 1).unproject(this.camera);
  92. //两交互点分别到原点的长度
  93. var pointerDragStart3DLength = Math.sqrt(pointerDragStart3D.x * pointerDragStart3D.x + pointerDragStart3D.z * pointerDragStart3D.z);
  94. var pointer3DLength = Math.sqrt(pointer3D.x * pointer3D.x + pointer3D.z * pointer3D.z);
  95. //通过Math.atan2计算在XY面上与X轴的夹角弧度。
  96. //注:因为 z = -1,所以两者到原点的长度近似为x分量(数值的大小也不需要绝对对应)
  97. var anglePointerDragStart3DToX = Math.atan2(pointerDragStart3D.y, pointerDragStart3DLength); //近似为 anglePointerDragStart3DToX = Math.atan2( pointerDragStart3D.y, pointerDragStart3D.x )
  98. var anglePointer3DToX = Math.atan2(pointer3D.y, pointer3DLength); //近似为 anglePointer3DToX = Math.atan2( pointer3D.y, pointer3D.x )
  99. //算出两者角度差,作为竖直方向角度差值(rotationDifference.y)
  100. this.rotationDifference.y = THREE.Math.radToDeg(anglePointerDragStart3DToX - anglePointer3DToX);
  101. //y分量清零,原向量等价于在XZ轴上的投影向量
  102. pointerDragStart3D.y = 0;
  103. pointer3D.y = 0;
  104. //归一化(/length),求两者夹角作为
  105. //判断方向,最后记为水平方向角度差值(rotationDifference.x)
  106. var anglePointerDragStart3DToPointer3D = Math.acos(pointerDragStart3D.dot(pointer3D) / pointerDragStart3D.length() / pointer3D.length());
  107. if (!isNaN(anglePointerDragStart3DToPointer3D)) {
  108. this.rotationDifference.x = THREE.Math.radToDeg(anglePointerDragStart3DToPointer3D);
  109. if (this.pointerDragStart.x < this.pointer.x) {
  110. this.rotationDifference.x *= -1;
  111. }
  112. }
  113. //更新pointerDragStart记录当前帧坐标,用于下一帧求帧差值
  114. this.pointerDragStart.copy(this.pointer);
  115. }
  116. }
  117. PanoramaControls.prototype.onMouseMove = function(event) {
  118. this.updatePointer(event.clientX, event.clientY);
  119. }
  120. PanoramaControls.prototype.onTouchMove = function(event) {
  121. this.updatePointer(event.changedTouches[0].clientX, event.changedTouches[0].clientY)
  122. }
  123. PanoramaControls.prototype.updatePointer = function(screenX, screenY) {
  124. this.pointer.x = screenX / this.domElement.clientWidth * 2 - 1; // 屏幕坐标换算相对于canvas的父级
  125. this.pointer.y = 2 * -(screenY / this.domElement.clientHeight) + 1;
  126. }
  127. PanoramaControls.prototype.endRotation = function() {
  128. this.pointerDragOn = false;
  129. try{
  130. var rotationHistoryAverage = averageVectors(this.rotationHistory);
  131. }catch(e){
  132. console.error(e)
  133. }
  134. this.rotationSpeed.set(rotationHistoryAverage.x * 30, rotationHistoryAverage.y * 30);
  135. }
  136. PanoramaControls.prototype.onTouchEnd = function(event) {
  137. if (this.enabled) {
  138. event.preventDefault();
  139. event.stopPropagation();
  140. this.endRotation()
  141. }
  142. }
  143. PanoramaControls.prototype.onMouseUp = function(event) {
  144. if (this.enabled) {
  145. event.preventDefault();
  146. event.stopPropagation();
  147. this.endRotation()
  148. }
  149. }
  150. PanoramaControls.prototype.update = function(deltaTime) {
  151. if (this.enabled) {
  152. this.updateRotation();
  153. for (this.rotationHistory.push(this.rotationDifference.clone()); this.rotationHistory.length > 5;) {
  154. this.rotationHistory.shift();
  155. }
  156. this.lon += this.rotationDifference.x;
  157. this.lat += this.rotationDifference.y;
  158. this.rotationDifference.set(0, 0);
  159. this.rotationSpeed.x = this.rotationSpeed.x * (1 - 0.05) + this.rotationAcc.x * 4.5;
  160. this.rotationSpeed.y = this.rotationSpeed.y * (1 - 0.05) + this.rotationAcc.y * 4.5;
  161. this.lon += this.rotationSpeed.x * deltaTime;
  162. this.lat += this.rotationSpeed.y * deltaTime;
  163. this.lat = Math.max(this.latMin, Math.min(this.latMax, this.lat));
  164. this.phi = THREE.Math.degToRad(90 - this.lat);
  165. this.theta = THREE.Math.degToRad(this.lon);
  166. this.lookVector.x = Math.sin(this.phi) * Math.cos(this.theta);
  167. this.lookVector.y = Math.cos(this.phi);
  168. this.lookVector.z = Math.sin(this.phi) * Math.sin(this.theta);
  169. this.camera.position.add(this.translationWorldDelta)
  170. this.translationWorldDelta.multiplyScalar(0.9);
  171. this.target.copy(this.lookVector).add(this.aimFrom);
  172. this.camera.lookAt(this.target)
  173. }
  174. }
  175. PanoramaControls.prototype.onMouseWheel = function(event) {
  176. /* if (this.enabled) {
  177. // let z = void 0 !== event['wheelDelta'] ? event['wheelDelta'] : 0 !== event.detail && -event.detail;
  178. // this.flyDirection(new THREE.Vector3(0, 0, -z).normalize());
  179. this._wheel = Math.floor(event['wheelDeltaY'] / 120);
  180. this._wheel = Math.abs(this._wheel) > 0.1 ? Math.sign(this._wheel) : 0;
  181. if (this._wheel !== 0 && this.scrollZoomSta) {
  182. this._wheel > 0 ? this._wheel = 1 + this.scrollZoomSpeed : this._wheel = 1 - this.scrollZoomSpeed;
  183. let curZoomLevel = this._wheel * this.zoomLevel;
  184. this.zoomTo(curZoomLevel);
  185. }
  186. } */
  187. let delta
  188. if (event.wheelDelta !== undefined) { // WebKit / Opera / Explorer 9
  189. delta = event.wheelDelta;
  190. } else if (event.detail !== undefined) { // Firefox
  191. delta = -event.detail;
  192. }
  193. if(delta != void 0){//滚轮缩放
  194. if(delta == 0)return //mac
  195. let direction = new THREE.Vector3(0,0,-1).applyQuaternion(this.camera.quaternion)
  196. let moveSpeed = 0.1
  197. if(delta < 0) moveSpeed *=-1
  198. this.translationWorldDelta.add(direction.multiplyScalar(moveSpeed))
  199. }
  200. }
  201. PanoramaControls.prototype.zoomTo = function(curZoomLevel) {
  202. curZoomLevel < this.zoomMin && (curZoomLevel = this.zoomMin);
  203. curZoomLevel > this.zoomMax && (curZoomLevel = this.zoomMax);
  204. this.zoomLevel = curZoomLevel;
  205. this.camera.fov = this.baseFov*(1/this.zoomLevel);
  206. this.camera.updateProjectionMatrix();
  207. }
  208. /*
  209. reset() {
  210. this.stop()
  211. }
  212. stop() {
  213. this.rotationAcc.set(0, 0);
  214. this.rotationSpeed.set(0, 0);
  215. }
  216. */
  217. //-------------copyFromPlayer
  218. PanoramaControls.prototype.handleControlScroll = function(e) {
  219. e > 0 ? e = 1 + this.scrollZoomSpeed : e < 0 && (e = 1 - this.scrollZoomSpeed);
  220. 0 !== e && this.zoomBy(e)
  221. }
  222. PanoramaControls.prototype.zoomBy = function(e) {
  223. this.zoomTo(this.zoomLevel * e);
  224. }
  225. function averageVectors(e, t) {
  226. var i = new THREE.Vector3();
  227. if (0 === e.length) return i;
  228. for (var r = 0, o = 0; o < e.length; o++) {
  229. var a = t ? e[o][t] : e[o];
  230. i.add(a), r++;
  231. }
  232. return i.divideScalar(r);
  233. }