View.js 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388
  1. import * as THREE from "../../libs/three.js/build/three.module.js";
  2. import {transitions, easing, lerp} from '../utils/transitions.js'
  3. import math from '../utils/math.js'
  4. import Common from '../utils/Common'
  5. let sid = 0
  6. export class View extends THREE.EventDispatcher{
  7. constructor () {
  8. super()
  9. this.position = new THREE.Vector3(0, 0, 0);
  10. this.yaw = 0//Math.PI / 4; // = 4dkk lon + 90
  11. this._pitch = 0//-Math.PI / 4; //上下旋转 = 4dkk lat
  12. this.radius = 1;
  13. this.maxPitch = Math.PI / 2;
  14. this.minPitch = -Math.PI / 2;
  15. this.sid = sid++
  16. this.LookTransition = 'LookTransition'+this.sid
  17. }
  18. //add------
  19. applyToCamera(camera){
  20. camera.position.copy(this.position);
  21. camera.rotation.copy(this.rotation)
  22. camera.updateMatrix();
  23. camera.updateMatrixWorld();
  24. camera.matrixWorldInverse.copy(camera.matrixWorld).invert();
  25. }
  26. get rotation(){
  27. var rotation = new THREE.Euler;
  28. rotation.order = "ZXY";
  29. rotation.x = Math.PI / 2 + this.pitch;
  30. rotation.z = this.yaw;
  31. return rotation
  32. }
  33. set rotation(rotation){
  34. //因为 rotation的y不一定是0 , 所以不能直接逆着写。
  35. this.direction = new THREE.Vector3(0,0,-1).applyEuler(rotation)
  36. }
  37. copy(a){
  38. Common.CopyClassObject(this, a)
  39. }
  40. clone () {
  41. /* let c = new View();
  42. c.yaw = this.yaw;
  43. c._pitch = this.pitch;
  44. c.radius = this.radius;
  45. c.maxPitch = this.maxPitch;
  46. c.minPitch = this.minPitch;
  47. return c; */
  48. return Common.CloneClassObject(this)
  49. }
  50. //----------
  51. get pitch () {
  52. return this._pitch;
  53. }
  54. set pitch (angle) {
  55. this._pitch = Math.max(Math.min(angle, this.maxPitch), this.minPitch);
  56. }
  57. get direction () {
  58. let dir = new THREE.Vector3(0, 1, 0);
  59. dir.applyAxisAngle(new THREE.Vector3(1, 0, 0), this.pitch);
  60. dir.applyAxisAngle(new THREE.Vector3(0, 0, 1), this.yaw);
  61. return dir;
  62. }
  63. set direction (dir) {
  64. //if(dir.x === dir.y){
  65. if(dir.x === 0 && dir.y === 0){
  66. this.pitch = Math.PI / 2 * Math.sign(dir.z);
  67. }else{
  68. let yaw = Math.atan2(dir.y, dir.x) - Math.PI / 2;
  69. let pitch = Math.atan2(dir.z, Math.sqrt(dir.x * dir.x + dir.y * dir.y));
  70. this.yaw = yaw;
  71. this.pitch = pitch;
  72. }
  73. }
  74. lookAt(t){
  75. let V;
  76. if(arguments.length === 1){
  77. V = new THREE.Vector3().subVectors(t, this.position);
  78. }else if(arguments.length === 3){
  79. V = new THREE.Vector3().subVectors(new THREE.Vector3(...arguments), this.position);
  80. }
  81. let radius = V.length();
  82. let dir = V.normalize();
  83. this.radius = radius;
  84. this.direction = dir;
  85. }
  86. getPivot () {
  87. return new THREE.Vector3().addVectors(this.position, this.direction.multiplyScalar(this.radius));
  88. }
  89. getSide () {
  90. let side = new THREE.Vector3(1, 0, 0);
  91. side.applyAxisAngle(new THREE.Vector3(0, 0, 1), this.yaw);
  92. return side;
  93. }
  94. /* pan (x, y) {
  95. let dir = new THREE.Vector3(0, 1, 0);
  96. dir.applyAxisAngle(new THREE.Vector3(1, 0, 0), this.pitch);
  97. dir.applyAxisAngle(new THREE.Vector3(0, 0, 1), this.yaw);
  98. // let side = new THREE.Vector3(1, 0, 0);
  99. // side.applyAxisAngle(new THREE.Vector3(0, 0, 1), this.yaw);
  100. let side = this.getSide();
  101. let up = side.clone().cross(dir);
  102. let pan = side.multiplyScalar(x).add(up.multiplyScalar(y));
  103. this.position = this.position.add(pan);
  104. // this.target = this.target.add(pan);
  105. } */
  106. pan (x, y) { //发现pan其实就是translate
  107. this.translate(x, 0, y)
  108. }
  109. translate (x, y, z) {
  110. let dir = new THREE.Vector3(0, 1, 0);
  111. dir.applyAxisAngle(new THREE.Vector3(1, 0, 0), this.pitch);
  112. dir.applyAxisAngle(new THREE.Vector3(0, 0, 1), this.yaw);
  113. let side = new THREE.Vector3(1, 0, 0);
  114. side.applyAxisAngle(new THREE.Vector3(0, 0, 1), this.yaw);
  115. let up = side.clone().cross(dir);
  116. let t = side.multiplyScalar(x)
  117. .add(dir.multiplyScalar(y))
  118. .add(up.multiplyScalar(z));
  119. this.position = this.position.add(t);
  120. this.restrictPos()
  121. }
  122. translateWorld (x, y, z) {
  123. this.position.x += x;
  124. this.position.y += y;
  125. this.position.z += z;
  126. this.restrictPos()
  127. }
  128. restrictPos(){//add
  129. if(this.limitBound){
  130. this.position.clamp(this.limitBound.min, this.limitBound.max)
  131. }
  132. }
  133. setCubeView(dir) {
  134. switch(dir) {
  135. case "front":
  136. this.yaw = 0;
  137. this.pitch = 0;
  138. break;
  139. case "back":
  140. this.yaw = Math.PI;
  141. this.pitch = 0;
  142. break;
  143. case "left":
  144. this.yaw = -Math.PI / 2;
  145. this.pitch = 0;
  146. break;
  147. case "right":
  148. this.yaw = Math.PI / 2;
  149. this.pitch = 0;
  150. break;
  151. case "top":
  152. this.yaw = 0;
  153. this.pitch = -Math.PI / 2;
  154. break;
  155. case "bottom":
  156. this.yaw = -Math.PI;
  157. this.pitch = Math.PI / 2;
  158. break;
  159. }
  160. }
  161. isFlying(){
  162. return transitions.getById(this.LookTransition).length > 0
  163. }
  164. cancelFlying(){//外界只能通过这个来cancel
  165. transitions.cancelById(this.LookTransition, true );
  166. }
  167. setView( info = {}){
  168. // position, target, duration = 0, callback = null, onUpdate = null, Easing='', cancelFun
  169. this.cancelFlying()
  170. let done = ()=>{
  171. if(endTarget){
  172. this.lookAt(endTarget); //compute radius for orbitcontrol
  173. }else if(endQuaternion){
  174. this.rotation = new THREE.Euler().setFromQuaternion(endQuaternion)
  175. }
  176. let f = ()=>{
  177. info.callback && info.callback()
  178. this.dispatchEvent('flyingDone')
  179. }
  180. if(info.duration){
  181. setTimeout(f,1)//延迟是为了使isFlying先为false
  182. }else{
  183. f() //有的需要迅速执行回调
  184. }
  185. }
  186. let endPosition = new THREE.Vector3().copy(info.position)
  187. const startPosition = this.position.clone();
  188. const startTarget = this.getPivot();
  189. let startQuaternion = math.getQuaFromPosAim(startPosition,startTarget)
  190. let endTarget = null, endQuaternion ;
  191. if(info.target ){
  192. endTarget = new THREE.Vector3().copy(info.target)
  193. endQuaternion = math.getQuaFromPosAim(endPosition,endTarget)
  194. }else if(info.quaternion){
  195. endQuaternion = info.quaternion.clone()
  196. }
  197. if(!info.duration){
  198. this.position.copy(endPosition);
  199. this.restrictPos()
  200. info.onUpdate && info.onUpdate(1)
  201. done()
  202. }else{
  203. transitions.start(lerp.vector(this.position, endPosition, (pos, progress)=>{
  204. let t = progress
  205. if(endQuaternion){
  206. let quaternion = (new THREE.Quaternion()).copy(startQuaternion)
  207. lerp.quaternion(quaternion, endQuaternion)(progress),
  208. this.rotation = new THREE.Euler().setFromQuaternion(quaternion)
  209. }
  210. this.restrictPos()
  211. info.onUpdate && info.onUpdate(t)//add
  212. }), info.duration, done, 0, info.Easing ? easing[info.Easing] : easing.easeInOutSine /*easeInOutQuad */,null, this.LookTransition, info.cancelFun);
  213. }
  214. }
  215. //平移Ortho相机
  216. moveOrthoCamera(viewport, info, duration, easeName){//boundSize优先于endZoom。
  217. let camera = viewport.camera
  218. let startZoom = camera.zoom
  219. let endPosition = info.endPosition
  220. let boundSize = info.boundSize
  221. let endZoom = info.endZoom
  222. let margin = info.margin || {x:0,y:0}/* 200 */ //像素
  223. if(info.bound){//需要修改boundSize以适应相机的旋转,当相机不在xy水平面上朝向z时
  224. endPosition = endPosition || info.bound.getCenter(new THREE.Vector3())
  225. let matrixRot = new THREE.Matrix4().makeRotationFromEuler(this.rotation)
  226. matrixRot.getInverse(matrixRot)
  227. let boundingBox = info.bound.clone().applyMatrix4(matrixRot)
  228. boundSize = boundingBox.getSize(new THREE.Vector3())
  229. }
  230. this.setView({ position:endPosition, duration,
  231. callback:()=>{//done
  232. },
  233. onUpdate:(progress)=>{
  234. if(boundSize || endZoom){
  235. if(boundSize){
  236. let aspect = boundSize.x / boundSize.y
  237. let w, h;
  238. if(camera.aspect > aspect){//视野更宽则用bound的纵向来决定
  239. h = boundSize.y
  240. endZoom = (viewport.resolution.y - margin.x) / h //注意,要在resolution不为0时执行
  241. }else{
  242. w = boundSize.x;
  243. endZoom = (viewport.resolution.x - margin.y) / w
  244. }
  245. //onUpdate时更新endzoom是因为画布大小可能更改
  246. }
  247. camera.zoom = endZoom * progress + startZoom * (1 - progress)
  248. camera.updateProjectionMatrix()
  249. }
  250. },
  251. Easing:easeName
  252. })
  253. }
  254. zoomOrthoCamera(camera, endZoom, pointer, duration, onProgress){//定点缩放
  255. let startZoom = camera.zoom
  256. let pointerPos = new THREE.Vector3(pointer.x, pointer.y,0.5);
  257. transitions.start(( progress)=>{
  258. let oldPos = pointerPos.clone().unproject(camera);
  259. camera.zoom = endZoom * progress + startZoom * (1 - progress)
  260. camera.updateProjectionMatrix()
  261. let newPos = pointerPos.clone().unproject(camera);
  262. //定点缩放, 恢复一下鼠标所在位置的位置改变量
  263. let moveVec = new THREE.Vector3().subVectors(newPos, oldPos)
  264. camera.position.sub(moveVec)
  265. this.position.copy(camera.position)
  266. onProgress && onProgress()
  267. } , duration, null/* done */, 0, easing.easeInOutSine, null, "zoomInView"/* , info.cancelFun */);
  268. }
  269. };