main.js 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582
  1. import { scene, camera, controls, renderer } from './common/scene';
  2. import { setEvents } from './common/setEvents';
  3. import { convertToXYZ, getEventCenter, geodecoder } from './common/geoHelpers';
  4. import { mapTexture } from './common/mapTexture';
  5. import { getTween, memoize } from './common/utils';
  6. import topojson from 'topojson';
  7. import THREE from 'THREE';
  8. import d3 from 'd3';
  9. import fdage from 'fdage';
  10. var root,rotate;
  11. var getIndex = function(elem,index){
  12. index = index || 'index';
  13. return elem.getAttributeNode(index)&&elem.getAttributeNode(index).nodeValue;
  14. }
  15. var setIndex = function(elem,name,index){
  16. index = index || 'index';
  17. elem.setAttribute(index,name);
  18. }
  19. var getByIndex = function(elem,name,index){
  20. var result;
  21. for(var i=0; i<elem.children.length; i++){
  22. if(getIndex(elem.children[i],index)===name){
  23. result = elem.children[i];
  24. }
  25. }
  26. if(result) return result;
  27. else for(var i=0; i<elem.children.length; i++){
  28. result = result || getByIndex(elem.children[i],name,index);
  29. }
  30. return result;
  31. }
  32. var body = d3.select('body').node();
  33. d3.json('data/world.json', function (err, data) {
  34. d3.select("#loading").transition().duration(500)
  35. .style("opacity", 0).remove();
  36. var currentCountry,currentChosed,currentChName, overlay, chosedOverlay;
  37. var pointDown = 0;//鼠标按下为true,抬起为false
  38. var draged = 0; //是否才拖拽过
  39. var segments = 155; // number of vertices. Higher = better mouse accuracy
  40. var isMobile = /Android|iPhone|iPod|iPad|Windows Phone|IEMobile|BlackBerry|webOS/.test(navigator.userAgent);
  41. var countryObj;
  42. // Setup cache for country textures
  43. var countries = topojson.feature(data, data.objects.countries);
  44. var geo = geodecoder(countries.features);
  45. var textureCache = memoize(function (cntryID, color) {
  46. var country = geo.find(cntryID);
  47. return mapTexture(country, color);
  48. });
  49. window.mapTexture = mapTexture;
  50. // Base globe with blue "water"
  51. /* let blueMaterial = new THREE.MeshPhongMaterial({color: '#0889d5', transparent: true}); //2B3B59
  52. let sphere = new THREE.SphereGeometry(400, segments, segments);
  53. let baseGlobe = new THREE.Mesh(sphere, blueMaterial);
  54. baseGlobe.rotation.y = Math.PI;
  55. baseGlobe.addEventListener('click', onGlobeClick);
  56. baseGlobe.addEventListener('mousemove', onGlobeMousemove); */
  57. //baseGlobe.addEventListener('mousemove', onGlobeMousemove);
  58. var texture2DLoader = new THREE.TextureLoader();
  59. var earthMap, alphaGrayMap;
  60. earthMap=texture2DLoader.load("data/newEarth.jpg"/* ,function(t){earthMap = t;} */);//newEarth.png
  61. let mapMat0 = new THREE.MeshPhongMaterial({map: earthMap,/* alphaMap:alphaGrayMap, */transparent: true});
  62. var outerLayer = new THREE.Mesh(new THREE.SphereGeometry(400, segments, segments), mapMat0);
  63. outerLayer.rotation.y = Math.PI;
  64. outerLayer.addEventListener('click', onGlobeClick);
  65. outerLayer.addEventListener('mousemove', onGlobeMousemove);
  66. //add base map layer with all countries
  67. /* let worldTexture = mapTexture(countries, '#647089');
  68. let mapMaterial = new THREE.MeshPhongMaterial({map: worldTexture,transparent: true});
  69. var baseMap = new THREE.Mesh(new THREE.SphereGeometry(400, segments, segments), mapMaterial);
  70. baseMap.rotation.y = Math.PI; */
  71. // create a container node and add the two meshes
  72. root = new THREE.Object3D();
  73. root.scale.set(1.2, 1.2, 1.2);
  74. //root.add(baseGlobe);
  75. //root.add(baseMap);
  76. root.add(outerLayer);
  77. scene.add(root);
  78. var bind = function(elem, event, func, bool) {
  79. bool = bool || false;
  80. if (elem.addEventListener)
  81. elem.addEventListener(event, func, bool);
  82. else if (elem.attachEvent)
  83. elem.attachEvent('on' + event, func);
  84. }
  85. var unbind = function(elem, event, func, bool) {
  86. bool = bool || false;
  87. if (elem.removeEventListener)
  88. elem.removeEventListener(event, func, bool);
  89. else if (elem.detachEvent)
  90. elem.detachEvent('on' + event, func);
  91. }
  92. var canvas = d3.select("#canvas").node();
  93. //地球滑动绑定:
  94. window.lastMouse = {};
  95. bind(canvas,"mousedown",function(event){
  96. window.lastMouse.x = event.offsetX;
  97. window.lastMouse.y = event.offsetY;
  98. })
  99. //地球初始转动绑定:
  100. rotate = true;
  101. var endRotate = function(){
  102. rotate = false;
  103. unbind(canvas,"mousedown",endRotate);
  104. }
  105. bind(canvas,"mousedown",endRotate);
  106. function onGlobeClick(event,originEvent) {//mouseup 在canvas上mouseup不一定会执行这个
  107. if(lastMouse.x){
  108. var x = originEvent.offsetX;
  109. var y = originEvent.offsetY;
  110. if((Math.abs(window.lastMouse.x - x)+Math.abs(window.lastMouse.y - y))>10){
  111. window.lastMouse.x = null;
  112. window.lastMouse.y = null;
  113. return;
  114. }
  115. }
  116. window.lastMouse.x = null;
  117. window.lastMouse.y = null;
  118. // Get pointc, convert to latitude/longitude
  119. var latlng = getEventCenter.call(this, event,400);//radius = 150
  120. latlng[1]+=root.rotation.y*180/Math.PI;
  121. // Get new camera position
  122. var temp = new THREE.Mesh();
  123. temp.position.copy(convertToXYZ(latlng, 980));
  124. temp.lookAt(root.position);
  125. temp.rotateY(Math.PI);
  126. for (let key in temp.rotation) {
  127. if (temp.rotation[key] - camera.rotation[key] > Math.PI) {
  128. temp.rotation[key] -= Math.PI * 2;
  129. } else if (camera.rotation[key] - temp.rotation[key] > Math.PI) {
  130. temp.rotation[key] += Math.PI * 2;
  131. }
  132. }
  133. var tweenPos = getTween.call(camera, 'position', temp.position);
  134. d3.timer(tweenPos);
  135. var tweenRot = getTween.call(camera, 'rotation', temp.rotation);
  136. d3.timer(tweenRot);
  137. onGlobeMousemove(event,null,this);
  138. }
  139. //document.title = "asas"
  140. function onGlobeMousemove(event,originEvent,_this) { //为什么mousedown的时候会立即触发mousemove?
  141. if(window.lastMouse.x){
  142. return;
  143. }
  144. var map, material;
  145. // Get pointc, convert to latitude/longitude
  146. var latlng = getEventCenter.call(this||_this, event, 400);
  147. // Look for country at that latitude/longitude
  148. var country = geo.search(latlng[0], latlng[1]);
  149. if(country && _this && country.code !== currentChosed){//_this表明是由click的触发的
  150. currentChosed = country.code;
  151. currentChName = country.chName;
  152. d3.select("#introduce").select("h2").html(currentChName);
  153. d3.select("#introduce").select(".below").html(currentChosed);
  154. if(d3.select("#introduce").node().style.display === "none"){
  155. d3.select("#introduce").node().style.display = "";
  156. rePos({text:true,earth:true});
  157. }else{
  158. rePos({text:true});
  159. }
  160. //map = isMobile ? mapTexture(geo.find(country.code), '#FF0011'):textureCache(country.code, '#FF0011');
  161. //if(isMobile) document.title = "isMobile"
  162. map = textureCache(country.code, '#FF0000');
  163. material = new THREE.MeshPhongMaterial({map: map,opacity:.5, transparent: true});
  164. if (!chosedOverlay) {
  165. chosedOverlay = new THREE.Mesh(new THREE.SphereGeometry(405, 30, 30), material);
  166. chosedOverlay.rotation.y = Math.PI;
  167. root.add(chosedOverlay);
  168. }else{
  169. chosedOverlay.material = material;
  170. }
  171. }
  172. if (!isMobile && country !== null && country.code !== currentCountry) {
  173. currentCountry = country.code;
  174. map = textureCache(country.code, '#FFFF00');
  175. //map = isMobile ? mapTexture(geo.find(country.code), '#FFF900') : textureCache(country.code, '#FFF900');
  176. material = new THREE.MeshPhongMaterial({map: map,opacity:.5, transparent: true});
  177. if (!overlay) {
  178. overlay = new THREE.Mesh(new THREE.SphereGeometry(405, 30, 30), material);
  179. overlay.rotation.y = Math.PI;
  180. root.add(overlay);
  181. } else {
  182. overlay.material = material;
  183. }
  184. }else if(country == null && _this){
  185. d3.select("#introduce").node().style.display = "none";
  186. }
  187. }
  188. setEvents(camera,canvas, [outerLayer], 'click');
  189. setEvents(camera,canvas, [outerLayer], 'mousemove', 10);
  190. function rePos(o) {//introduce重新定位
  191. //var onlyTitle = (d3.select('#introMenu').node().style.display==="none");
  192. var intro = d3.select("#introduce").node();
  193. var infoH = intro.clientHeight;
  194. if(infoH){
  195. if(o.earth){
  196. var e = Math.min(window.innerWidth/2,window.innerHeight*0.95);
  197. var offset = Math.max(0, window.innerHeight - e)/2;
  198. canvas.style.top = offset + "px";
  199. renderer.setSize(e, e);
  200. }
  201. if(o.text){
  202. var infoH = intro.clientHeight;
  203. var earthH = canvas.clientHeight || window.innerWidth*0.3;//(onlyTitle?0.1:0.3)
  204. if(infoH){
  205. var f1 = parseFloat(intro.style["font-size"]);
  206. if(f1!=f1) f1 = 1; //NAN
  207. var ratio = earthH/infoH/1.2;
  208. var size = Math.min(Math.max(1, f1 * ratio ), 1.5);
  209. intro.style["font-size"] = size + "em";
  210. (size<1.1)?(addClass(intro,"weight")):(removeClass(intro,"weight"));
  211. }
  212. }
  213. }
  214. }
  215. var canvasToImg = function(canvas){
  216. var type = 'png';
  217. var imgData = canvas.toDataURL(type);
  218. return imgData;
  219. }
  220. d3.json('data/worldInfo.json', function (err, introData) {//和info跳转有关
  221. //跳转绑定:
  222. var intro = d3.select("#introduce").node();
  223. var menu = d3.select("#introMenu").node();
  224. var artifact = d3.select("#artifact").node();
  225. for(var i=0; i<3; i++){//三个菜单的点击
  226. bind(menu.children[i],"click",function(i){
  227. return function(){
  228. countryObj = introData.countries[currentChosed];
  229. if(!countryObj)return;//没有改国家的展览
  230. //if(state[state.length-1] == "earth")hangUpEarth();
  231. var type = getIndex(this);
  232. switch(type){
  233. /* case "countryIntro":
  234. showCountryIntro(); break;
  235. case "culture":
  236. showCultureIntro(); break; */
  237. case "artifact":
  238. showArtifactIntro(); break;
  239. /* case "artifactIntro":
  240. artifactIntro(); break; */
  241. /* case "interview":
  242. artifactInterview(); break;
  243. case "music":
  244. artifactMusic(); break; */
  245. default:
  246. console.log("其他");
  247. }
  248. }
  249. }(i))
  250. }
  251. var hangUpEarth = function(){
  252. renderer.render(scene, camera);
  253. var img = document.createElement('img');
  254. img.id = "ToEarth";
  255. img.src = canvasToImg(canvas);
  256. img.style.top = canvas.style.top;
  257. img.style.cursor = "pointer";
  258. img.style.height = img.style.width = canvas.style.height;
  259. img.className = "canvasArea";
  260. img.style['box-shadow'] = "none";
  261. canvas.parentElement.appendChild(img);
  262. canvas.style.display = "none";
  263. setTimeout(function(){
  264. img.style.width = img.style.height = "50px";
  265. img.style.top = "0";
  266. bind(img, "click", backToEarth);
  267. },30)
  268. }
  269. var dropDownEarth = function(){
  270. var img = d3.select('#ToEarth').node();
  271. unbind(img, "click", backToEarth);
  272. img.style.width = "";
  273. var e = Math.min(window.innerWidth/2,window.innerHeight*0.95);
  274. var offset = Math.max(0, window.innerHeight - e)/2;
  275. img.style.height = img.style.width = e + "px";
  276. img.style.top = offset + "px";
  277. setTimeout(function(){
  278. img.parentElement.removeChild(img);
  279. canvas.style.display = "";
  280. rePos({text:true,earth:true});
  281. },400)
  282. }
  283. var backBtn = d3.select("#back").node();
  284. bind(backBtn,"click",function(){
  285. var from = state[state.length-1];
  286. var to = state[state.length-2];
  287. if(!to) return;
  288. state.pop();
  289. switch(from){
  290. case "countryIntro":
  291. exitCountryIntro(); break;
  292. case "culture":
  293. exitCultureIntro(); break;
  294. case "artifact":
  295. exitArtifactIntro(); break;
  296. case "itemDisplay":
  297. exitItemDisplay(); break;
  298. /* case "artifactIntro":
  299. exitArtifactIntro(); break; */
  300. case "interview":
  301. exitArtifactInterview(); break;
  302. case "music":
  303. exitArtifactMusic(); break;
  304. default:
  305. console.log("其他");
  306. }
  307. switch(to){
  308. case "earth":
  309. backToEarth(); break;
  310. case "countryIntro":
  311. showCountryIntro(); break;
  312. case "culture":
  313. showCultureIntro(); break;
  314. case "artifact":
  315. showArtifactIntro(true); break;
  316. /* case "artifactIntro":
  317. artifactIntro(); break; */
  318. case "interview":
  319. artifactInterview(); break;
  320. case "music":
  321. artifactMusic(); break;
  322. default:
  323. console.log("其他");
  324. }
  325. })
  326. function backToEarth(){//确保清除所有,因为包括退了一级的和直接回到地球的
  327. state = ["earth"];
  328. artifact.style.display = "none";
  329. //intro.children[0].innerHTML = currentChName;
  330. menu.removeChild(menu.children[0])
  331. menu.children[0].innerHTML = '<span class="number">NO.1</span>国家背景<span class="EngPostfix">National Background</span>';
  332. menu.children[1].innerHTML = '<span class="number">NO.2</span>器物介绍<span class="EngPostfix">Instrument Introduction</span>';
  333. menu.children[2].innerHTML = '<span class="number">NO.3</span>文化传承<span class="EngPostfix">Cultural Heritage</span>';
  334. menu.children[2].style.display = "";
  335. menu.style.display = "";
  336. setIndex(menu.children[0],"countryIntro");
  337. setIndex(menu.children[1],"artifact");
  338. setIndex(menu.children[2],"culture");
  339. removeClass(intro,"largeBelow");
  340. intro.children[1].innerHTML = currentChosed;
  341. var modelArea = d3.select("#modelArea").node();//将模型和图片展示清除
  342. var imageArea = d3.select("#imageArea").node();
  343. if(modelArea){
  344. modelArea.parentElement.removeChild(modelArea);
  345. }
  346. if(imageArea){
  347. imageArea.parentElement.removeChild(imageArea);
  348. }
  349. dropDownEarth();
  350. }
  351. function exitItemDisplay(){//从单个展示到列表
  352. var modelArea = d3.select("#modelArea").node();
  353. var imageArea = d3.select("#imageArea").node();
  354. if(modelArea){
  355. modelArea.style.display = "none";
  356. }
  357. if(imageArea){
  358. imageArea.style.display = "none";
  359. }
  360. }
  361. function exitArtifactIntro(){//从文字介绍到 单个展示三个菜单
  362. }
  363. function showArtifactIntro(backHere){//back表示是从下一级后退到这里的,此时不用重新生成列表.
  364. menu.style.display = "none";
  365. //menu.innerHTML = '<span>乐器介绍</span><div index="artifactIntro"></div><a index="interview">专家访谈</a><a index="music">音频欣赏</a>';
  366. if(!backHere){
  367. //menu.children[0].innerHTML = "乐器介绍";
  368. hangUpEarth();
  369. var add = document.createElement('div'); //新增一个不可点击的选项:乐器介绍
  370. add.innerHTML = '<span class="title"><span class="point">·</span>乐器介绍<span class="EngPostfix">Instrument Introduction</span></span><span class="instrumentText" index="artifactIntro"></span>'
  371. menu.insertBefore(add,menu.children[0]);
  372. menu.children[1].innerHTML = '<span class="point">·</span>专家访谈<span class="EngPostfix">Experts Interview</span>';
  373. menu.children[2].innerHTML = '<span class="point">·</span>音频欣赏<span class="EngPostfix">Music Appreciation</span>';
  374. menu.children[3].style.display = "none";
  375. //setIndex(menu.children[0],"artifactIntro");
  376. setIndex(menu.children[1],"interview");
  377. setIndex(menu.children[2],"music");
  378. setIndex(menu.children[3],"");
  379. state.push("artifact");
  380. intro.children[1].innerHTML = "器物介绍";
  381. addClass(intro,"largeBelow");
  382. var len1 = countryObj.artifact.length;
  383. var len2 = artifact.children.length;
  384. for(var i=0; i<len1; i++){
  385. if(i>len2-1){
  386. var item = document.createElement('div');
  387. var inner = document.createElement('div');
  388. addClass(item,"item");
  389. artifact.appendChild(item);
  390. item.appendChild(inner);
  391. setIndex(item,i);
  392. bind(item,"click",clickItem)//绑定点击事件
  393. }
  394. else{
  395. item = artifact.children[i];
  396. }
  397. item.style.display = "";//解除隐藏
  398. var itemObj = countryObj.artifact[i];
  399. if(itemObj.type === "image"){
  400. item.children[0].style['background-image'] = "url(data/display/600/"+itemObj.name+".jpg)";
  401. }
  402. }
  403. if(len2>len1){//多余的隐藏
  404. for(var i=len1; i<len2; i++){
  405. artifact.children[i].style.display = "none";
  406. }
  407. }
  408. }
  409. artifact.style.display = "";
  410. }
  411. function clickItem(){//点击列表
  412. state.push('itemDisplay');
  413. var itemObj = countryObj.artifact[getIndex(this)];
  414. menu.style.display = "";
  415. getByIndex(menu,"artifactIntro").innerText = itemObj.intro;
  416. rePos({text:true})
  417. if(itemObj.type === "model"){
  418. var modelArea = d3.select("#modelArea").node();
  419. if(!modelArea){
  420. modelArea = document.createElement("div");
  421. addClass(modelArea,"canvasArea");
  422. modelArea.id = "modelArea";
  423. body.appendChild(modelArea);
  424. }else{
  425. if(getIndex(modelArea)!==itemObj.name){//虽已有,但展示的是别的模型,要删除重新创建
  426. modelArea.parentElement.removeChild(modelArea);
  427. modelArea = document.createElement("div");
  428. addClass(modelArea,"canvasArea");
  429. modelArea.id = "modelArea";
  430. body.appendChild(modelArea);
  431. }
  432. modelArea.style.display = "";
  433. }
  434. if(getIndex(modelArea)!==itemObj.name){
  435. fdage.embed( "data/display/"+itemObj.name+".4dage", { root:modelArea, backgroundColor:[238/255,238/255,238/255],ifPlain:true, width: 800, height: 600, autoStart: true, fullFrame: true, pagePreset: false } );
  436. setIndex(modelArea,itemObj.name);
  437. }
  438. }else{
  439. var imageArea = d3.select("#imageArea").node();
  440. if(!imageArea){
  441. imageArea = document.createElement("div");
  442. addClass(imageArea,"canvasArea");
  443. imageArea.id = "imageArea";
  444. body.appendChild(imageArea);
  445. var inner = document.createElement("div");
  446. imageArea.appendChild(inner);
  447. inner.style.width = inner.style.height = "100%";
  448. }else{
  449. var inner = imageArea.children[0];
  450. imageArea.style.display = "";
  451. }
  452. inner.style["background-image"] = "url(data/display/2048/"+itemObj.name+".jpg"+")";
  453. }
  454. }
  455. });
  456. });
  457. function animate() {
  458. controls.update();
  459. if(root && rotate)root.rotation.y+=0.002;
  460. requestAnimationFrame(animate);
  461. renderer.render(scene, camera);
  462. }
  463. animate();