tpanorama.js 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333
  1. import * as t from 'three';
  2. const {
  3. Vector2, PerspectiveCamera, Vector3, OrthographicCamera,
  4. Scene, SphereGeometry, Mesh, MeshBasicMaterial, Raycaster,
  5. ImageUtils, WebGLRenderer, Texture, SpriteMaterial, Sprite,
  6. TextureLoader, Math: tMath,
  7. } = t;
  8. var _camera, _scene, _renderer;
  9. var _cameraOrtho, _sceneOrtho;
  10. var _fov = 75;
  11. var _pRadius = 1000;
  12. var _raycaster;
  13. var _container;
  14. var _isUserInteracting = false;
  15. var _lon = 0, _lat = 0;
  16. var _onPointerDownLon = 0, _onPointerDownLat = 0;
  17. var _onPointerDownPointerX = 0, _onPointerDownPointerY = 0;
  18. var _mouse = new Vector2();
  19. var _clickableObjects = [];
  20. var _sprites = [];
  21. var _lables = [];
  22. var options = {
  23. container: 'panoramaConianer',//容器
  24. url: 'resources/img/panorama/pano-7.jpg',//全景图路径
  25. lables: [],//标记 {position:{lon:114,lat:38},logoUrl:'lableLogo.png',text:'我是一个标记'}
  26. widthSegments: 60,//水平切段数
  27. heightSegments: 40,//垂直切段数(值小粗糙速度快,值大精细速度慢)
  28. pRadius: 1000,//全景球的半径,推荐使用默认值
  29. minFocalLength: 1,//镜头最小拉近距离
  30. maxFocalLength: 100,//镜头最大拉近距离
  31. showlable: 'show' // show,click
  32. }
  33. function tpanorama(opt) {
  34. this.render(opt);
  35. }
  36. tpanorama.prototype = {
  37. constructor: this,
  38. def: {},
  39. render: function (opt) {
  40. this.def = extend(options, opt, true);
  41. document.getElementById(this.def.container).innerHTML = '';
  42. _lables = [];
  43. initContainer(this.def.container);
  44. initCamera();
  45. initRaycaster();
  46. makePanorama(this.def.pRadius, this.def.widthSegments, this.def.heightSegments, this.def.url);
  47. initRenderer();
  48. initLable(this.def.lables, this.def.showlable);
  49. _container.addEventListener('mousedown', onDocumentMouseDown, false);
  50. _container.addEventListener('mousemove', onDocumentMouseMove, false);
  51. _container.addEventListener('mouseup', onDocumentMouseUp, false);
  52. _container.addEventListener('mousewheel', (e) => {
  53. onDocumentMouseWheel(e, this.def.minFocalLength, this.def.maxFocalLength);
  54. }, false);
  55. _container.addEventListener('DOMMouseScroll', (e) => {
  56. onDocumentMouseWheel(e, this.def.minFocalLength, this.def.maxFocalLength);
  57. }, false);
  58. _container.addEventListener('click', onDocumentMouseClick, false);
  59. global.addEventListener('resize', onWindowResize, false);
  60. animate();
  61. }
  62. }
  63. function extend(o, n, override) {
  64. for (var key in n) {
  65. if (n.hasOwnProperty(key) && (!o.hasOwnProperty(key) || override)) {
  66. o[key] = n[key];
  67. }
  68. }
  69. return o;
  70. }
  71. function initContainer(c) {
  72. _container = document.getElementById(c);
  73. }
  74. function initCamera() {
  75. _camera = new PerspectiveCamera(_fov, window.innerWidth / window.innerHeight, 1, 1100);
  76. _camera.target = new Vector3(0, 0, 0);
  77. _cameraOrtho = new OrthographicCamera(-window.innerWidth / 2, window.innerWidth / 2, window.innerHeight / 2, -window.innerHeight / 2, 1, 10);
  78. _cameraOrtho.position.z = 10;
  79. _scene = new Scene();
  80. _sceneOrtho = new Scene();
  81. }
  82. function initRaycaster() {
  83. _raycaster = new Raycaster();
  84. }
  85. function makePanorama(pRadius, widthSegments, heightSegments, u) {
  86. var mesh = new Mesh(new SphereGeometry(pRadius, widthSegments, heightSegments),
  87. new MeshBasicMaterial(
  88. { map: ImageUtils.loadTexture(u) }
  89. ));
  90. mesh.scale.x = -1;
  91. _scene.add(mesh);
  92. }
  93. function initRenderer() {
  94. _renderer = new WebGLRenderer();
  95. _renderer.setSize(window.innerWidth, window.innerHeight);
  96. _renderer.autoClear = false;
  97. _container.appendChild(_renderer.domElement);
  98. }
  99. function onDocumentMouseDown(event) {
  100. event.preventDefault();
  101. _isUserInteracting = true;
  102. _onPointerDownPointerX = event.clientX;
  103. _onPointerDownPointerY = event.clientY;
  104. _onPointerDownLon = _lon;
  105. _onPointerDownLat = _lat;
  106. }
  107. function onDocumentMouseMove(event) {
  108. if (_isUserInteracting) {
  109. _lon = (_onPointerDownPointerX - event.clientX) * 0.1 + _onPointerDownLon;
  110. _lat = (event.clientY - _onPointerDownPointerY) * 0.1 + _onPointerDownLat;
  111. }
  112. }
  113. function onDocumentMouseUp() {
  114. _isUserInteracting = false;
  115. }
  116. function onDocumentMouseClick(event) {
  117. _mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
  118. _mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;
  119. _raycaster.setFromCamera(_mouse, _cameraOrtho);
  120. var intersects = _raycaster.intersectObjects(_clickableObjects);
  121. intersects.forEach(function (element) {
  122. alert("Intersection: " + element.object.name);
  123. });
  124. }
  125. function onDocumentMouseWheel(ev, minFocalLength, maxFocalLength) {
  126. var ev = ev || window.event;
  127. var down = true;
  128. var m = _camera.getFocalLength();
  129. down = ev.wheelDelta ? ev.wheelDelta < 0 : ev.detail > 0;
  130. if (down) {
  131. if (m > minFocalLength) {
  132. m -= m * 0.05
  133. _camera.setFocalLength(m);
  134. }
  135. } else {
  136. if (m < maxFocalLength) {
  137. m += m * 0.05
  138. _camera.setFocalLength(m);
  139. }
  140. }
  141. if (ev.preventDefault) {
  142. ev.preventDefault();
  143. }
  144. return false;
  145. }
  146. function onWindowResize() {
  147. _camera.aspect = window.innerWidth / window.innerHeight;
  148. _camera.projectionMatrix.makePerspective(_fov, _camera.aspect, 1, 1100);
  149. _camera.updateProjectionMatrix();
  150. _cameraOrtho.left = -window.innerWidth / 2;
  151. _cameraOrtho.right = window.innerWidth / 2;
  152. _cameraOrtho.top = window.innerHeight / 2;
  153. _cameraOrtho.bottom = -window.innerHeight / 2;
  154. _cameraOrtho.updateProjectionMatrix();
  155. _renderer.setSize(window.innerWidth, window.innerHeight);
  156. }
  157. function initLable(lables, showlable) {
  158. if (showlable == 'show') {
  159. for (var i = 0; i < lables.length; i++) {
  160. _lables.push(createLableSprite(_sceneOrtho, lables[i].text, lables[i].position));
  161. }
  162. } else if (showlable == 'click') {
  163. for (var i = 0; i < lables.length; i++) {
  164. _sprites.push(createSprite(lables[i].position, lables[i].logoUrl, lables[i].text));
  165. }
  166. }
  167. }
  168. function createLableSprite(scene, name, position) {
  169. var canvas1 = document.createElement('canvas');
  170. var context1 = canvas1.getContext('2d');
  171. var metrics = context1.measureText(name);
  172. var width = metrics.width * 1.5;
  173. context1.font = "10px 宋体";
  174. context1.fillStyle = "rgba(0,0,0,0.95)"; // white border
  175. context1.fillRect(0, 0, width + 8, 20 + 8);
  176. context1.fillStyle = "rgba(0,0,0,0.2)"; // black filler
  177. context1.fillRect(2, 2, width + 4, 20 + 4);
  178. context1.fillStyle = "rgba(255,255,255,0.95)"; // text color
  179. context1.fillText(name, 4, 20);
  180. var texture1 = new Texture(canvas1);
  181. texture1.needsUpdate = true;
  182. var spriteMaterial = new SpriteMaterial({ map: texture1 });
  183. var sprite1 = new Sprite(spriteMaterial);
  184. sprite1.scale.set(1.0, 1.0, 1.0);
  185. sprite1.position.set(0, 0, 0);
  186. sprite1.name = name;
  187. var lable = {
  188. name: name,
  189. pos: position,
  190. canvas: canvas1,
  191. context: context1,
  192. texture: texture1,
  193. sprite: sprite1
  194. };
  195. _sceneOrtho.add(lable.sprite);
  196. return lable;
  197. }
  198. function createSprite(position, url, name) {
  199. var textureLoader = new TextureLoader();
  200. var ballMaterial = new SpriteMaterial({
  201. map: textureLoader.load(url)
  202. });
  203. var sp1 = {
  204. pos: position,
  205. name: name,
  206. sprite: new Sprite(ballMaterial)
  207. };
  208. sp1.sprite.scale.set(32, 32, 1.0);
  209. sp1.sprite.position.set(0, 0, 0);
  210. sp1.sprite.name = name;
  211. _sceneOrtho.add(sp1.sprite);
  212. _clickableObjects.push(sp1.sprite);
  213. return sp1;
  214. }
  215. function animate() {
  216. requestAnimationFrame(animate);
  217. render();
  218. }
  219. function render() {
  220. calPosition();
  221. addSprites();
  222. runRender();
  223. }
  224. function calPosition() {
  225. _lat = Math.max(-85, Math.min(85, _lat));
  226. var phi = tMath.degToRad(90 - _lat);
  227. var theta = tMath.degToRad(_lon);
  228. _camera.target.x = _pRadius * Math.sin(phi) * Math.cos(theta);
  229. _camera.target.y = _pRadius * Math.cos(phi);
  230. _camera.target.z = _pRadius * Math.sin(phi) * Math.sin(theta);
  231. _camera.lookAt(_camera.target);
  232. }
  233. function addSprites() {
  234. if (typeof (_sprites) != "undefined") {
  235. for (var i = 0; i < _sprites.length; i++) {
  236. var wp = geoPosition2World(_sprites[i].pos.lon, _sprites[i].pos.lat);
  237. var sp = worldPostion2Screen(wp, _camera);
  238. var test = wp.clone();
  239. test.project(_camera);
  240. if (test.x > -1 && test.x < 1 && test.y > -1 && test.y < 1 && test.z > -1 && test.z < 1) {
  241. _sprites[i].sprite.scale.set(32, 32, 32);
  242. _sprites[i].sprite.position.set(sp.x, sp.y, 1);
  243. }
  244. else {
  245. _sprites[i].sprite.scale.set(1.0, 1.0, 1.0);
  246. _sprites[i].sprite.position.set(0, 0, 0);
  247. }
  248. }
  249. }
  250. if (typeof (_lables) != "undefined") {
  251. for (var i = 0; i < _lables.length; i++) {
  252. var wp = geoPosition2World(_lables[i].pos.lon, _lables[i].pos.lat);
  253. var sp = worldPostion2Screen(wp, _camera);
  254. var test = wp.clone();
  255. test.project(_camera);
  256. if (test.x > -1 && test.x < 1 && test.y > -1 && test.y < 1 && test.z > -1 && test.z < 1) {
  257. var metrics = _lables[i].context.measureText(_lables[i].name);
  258. var width = metrics.width * 3.5;
  259. _lables[i].sprite.scale.set(400, 150, 1.0);
  260. _lables[i].sprite.position.set(sp.x + width, sp.y - 40, 1);
  261. }
  262. else {
  263. _lables[i].sprite.scale.set(1.0, 1.0, 1.0);
  264. _lables[i].sprite.position.set(0, 0, 0);
  265. }
  266. }
  267. }
  268. }
  269. function geoPosition2World(lon, lat) {
  270. lat = Math.max(-85, Math.min(85, lat));
  271. var phi = tMath.degToRad(90 - lat);
  272. var theta = tMath.degToRad(lon);
  273. var result = {
  274. x: _pRadius * Math.sin(phi) * Math.cos(theta),
  275. y: _pRadius * Math.cos(phi),
  276. z: _pRadius * Math.sin(phi) * Math.sin(theta)
  277. }
  278. return new Vector3(result.x, result.y, result.z);
  279. }
  280. function worldPostion2Screen(world_vector, camera) {
  281. var vector = world_vector.clone();
  282. vector.project(camera);
  283. var result = {
  284. x: Math.round((vector.x + 1) * window.innerWidth / 2 - window.innerWidth / 2),
  285. y: Math.round(window.innerHeight / 2 - (-vector.y + 1) * window.innerHeight / 2),
  286. z: 0
  287. };
  288. return new Vector3(result.x, result.y, result.z);
  289. }
  290. function runRender() {
  291. _renderer.clear();
  292. _renderer.render(_scene, _camera);
  293. _renderer.clearDepth();
  294. _renderer.render(_sceneOrtho, _cameraOrtho);
  295. }
  296. window.tpanorama = tpanorama;
  297. module.exports = tpanorama;