View.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395
  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.js'
  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){//setPivot
  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, forceHorizon ) {
  110. //相机方向
  111. let dir = new THREE.Vector3(0, 1, 0);
  112. dir.applyAxisAngle(new THREE.Vector3(1, 0, 0), forceHorizon ? 0 : this.pitch); //上下角度
  113. dir.applyAxisAngle(new THREE.Vector3(0, 0, 1), this.yaw);//水平角度
  114. let side = new THREE.Vector3(1, 0, 0);
  115. side.applyAxisAngle(new THREE.Vector3(0, 0, 1), this.yaw); //垂直于相机当前水平朝向的 左右方向
  116. let up = side.clone().cross(dir); //垂直于相机当前水平朝向的 向上方向
  117. let t = side.multiplyScalar(x) //x影响 左右分量
  118. .add(dir.multiplyScalar(y)) //y影响 前后分量
  119. .add(up.multiplyScalar(z)); //z影响 上下分量
  120. this.position = this.position.add(t);
  121. this.restrictPos()
  122. }
  123. translateWorld (x, y, z) {
  124. this.position.x += x;
  125. this.position.y += y;
  126. this.position.z += z;
  127. this.restrictPos()
  128. }
  129. restrictPos(){//add
  130. if(this.limitBound){
  131. this.position.clamp(this.limitBound.min, this.limitBound.max)
  132. }
  133. }
  134. setCubeView(dir) {
  135. switch(dir) {
  136. case "front":
  137. this.yaw = 0;
  138. this.pitch = 0;
  139. break;
  140. case "back":
  141. this.yaw = Math.PI;
  142. this.pitch = 0;
  143. break;
  144. case "left":
  145. this.yaw = -Math.PI / 2;
  146. this.pitch = 0;
  147. break;
  148. case "right":
  149. this.yaw = Math.PI / 2;
  150. this.pitch = 0;
  151. break;
  152. case "top":
  153. this.yaw = 0;
  154. this.pitch = -Math.PI / 2;
  155. break;
  156. case "bottom":
  157. this.yaw = -Math.PI;
  158. this.pitch = Math.PI / 2;
  159. break;
  160. }
  161. }
  162. isFlying(){
  163. return transitions.getById(this.LookTransition).length > 0
  164. }
  165. cancelFlying(){//外界只能通过这个来cancel
  166. transitions.cancelById(this.LookTransition, true );
  167. }
  168. setView( info = {}){
  169. // position, target, duration = 0, callback = null, onUpdate = null, Easing='', cancelFun
  170. this.cancelFlying()
  171. let done = ()=>{
  172. if(endTarget){
  173. this.lookAt(endTarget); //compute radius for orbitcontrol
  174. }else if(endQuaternion){
  175. this.rotation = new THREE.Euler().setFromQuaternion(endQuaternion)
  176. }
  177. let f = ()=>{
  178. info.callback && info.callback()
  179. this.dispatchEvent('flyingDone')
  180. }
  181. if(info.duration){
  182. setTimeout(f,1)//延迟是为了使isFlying先为false
  183. }else{
  184. f() //有的需要迅速执行回调
  185. }
  186. }
  187. let endPosition = new THREE.Vector3().copy(info.position)
  188. let startPosition = this.position.clone();
  189. let startQuaternion, endQuaternion, endTarget = null ;
  190. if(info.target ){
  191. endTarget = new THREE.Vector3().copy(info.target)
  192. endQuaternion = math.getQuaFromPosAim(endPosition,endTarget)
  193. }else if(info.quaternion){
  194. endQuaternion = info.quaternion.clone()
  195. }
  196. if(endQuaternion){
  197. startQuaternion = new THREE.Quaternion().setFromEuler(this.rotation)
  198. /* const startTarget = this.getPivot();
  199. let startQuaternion = math.getQuaFromPosAim(startPosition,startTarget) */
  200. }
  201. if(!info.duration){
  202. this.position.copy(endPosition);
  203. this.restrictPos()
  204. info.onUpdate && info.onUpdate(1)
  205. done()
  206. }else{
  207. transitions.start(lerp.vector(this.position, endPosition, (pos, progress)=>{
  208. let t = progress
  209. if(endQuaternion){
  210. let quaternion = (new THREE.Quaternion()).copy(startQuaternion)
  211. lerp.quaternion(quaternion, endQuaternion)(progress),
  212. this.rotation = new THREE.Euler().setFromQuaternion(quaternion)
  213. }
  214. this.restrictPos()
  215. info.onUpdate && info.onUpdate(t)//add
  216. }), info.duration, done, 0, info.Easing ? easing[info.Easing] : easing.easeInOutSine /*easeInOutQuad */,null, this.LookTransition, info.cancelFun);
  217. }
  218. }
  219. //平移Ortho相机
  220. moveOrthoCamera(viewport, info, duration, easeName){//boundSize优先于endZoom。
  221. let camera = viewport.camera
  222. let startZoom = camera.zoom
  223. let endPosition = info.endPosition
  224. let boundSize = info.boundSize
  225. let endZoom = info.endZoom
  226. let margin = info.margin || {x:0,y:0}/* 200 */ //像素
  227. if(info.bound){//需要修改boundSize以适应相机的旋转,当相机不在xy水平面上朝向z时
  228. endPosition = endPosition || info.bound.getCenter(new THREE.Vector3())
  229. let matrixRot = new THREE.Matrix4().makeRotationFromEuler(this.rotation).invert()
  230. let boundingBox = info.bound.clone().applyMatrix4(matrixRot)
  231. boundSize = boundingBox.getSize(new THREE.Vector3())
  232. }
  233. if(boundSize && boundSize.x == 0 && boundSize.y == 0){
  234. boundSize.set(1,1) //避免infinity
  235. }
  236. this.setView({ position:endPosition, duration,
  237. callback:()=>{//done
  238. },
  239. onUpdate:(progress)=>{
  240. if(boundSize || endZoom){
  241. if(boundSize){
  242. let aspect = boundSize.x / boundSize.y
  243. let w, h;
  244. if(camera.aspect > aspect){//视野更宽则用bound的纵向来决定
  245. h = boundSize.y
  246. endZoom = (viewport.resolution.y - margin.y) / h //注意,要在resolution不为0时执行
  247. }else{
  248. w = boundSize.x;
  249. endZoom = (viewport.resolution.x - margin.x) / w
  250. }
  251. //onUpdate时更新endzoom是因为画布大小可能更改
  252. }
  253. camera.zoom = endZoom * progress + startZoom * (1 - progress)
  254. camera.updateProjectionMatrix()
  255. }
  256. },
  257. Easing:easeName
  258. })
  259. }
  260. zoomOrthoCamera(camera, endZoom, pointer, duration, onProgress){//定点缩放
  261. let startZoom = camera.zoom
  262. let pointerPos = new THREE.Vector3(pointer.x, pointer.y,0.5);
  263. transitions.start(( progress)=>{
  264. let oldPos = pointerPos.clone().unproject(camera);
  265. camera.zoom = endZoom * progress + startZoom * (1 - progress)
  266. camera.updateProjectionMatrix()
  267. let newPos = pointerPos.clone().unproject(camera);
  268. //定点缩放, 恢复一下鼠标所在位置的位置改变量
  269. let moveVec = new THREE.Vector3().subVectors(newPos, oldPos)
  270. camera.position.sub(moveVec)
  271. this.position.copy(camera.position)
  272. onProgress && onProgress()
  273. } , duration, null/* done */, 0, easing.easeInOutSine, null, "zoomInView"/* , info.cancelFun */);
  274. }
  275. };