manage.js 43 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295
  1. var g_ProjectName = window.location.pathname.substring(window.location.pathname.indexOf("/") + 1, window.location.pathname.lastIndexOf("/"));
  2. var g_Prefix = "https://super.4dage.com/";
  3. // var g_Prefix="http://project.4dage.com:8017/";//有自己的大场景编辑后台的 - 线上场景调试
  4. // var g_Prefix=window.origin+'/';//有自己的大场景编辑后台的 - 打包
  5. // var g_Prefix=window.location.href.substring(0,window.location.href.indexOf("/index.html")+1);
  6. var s = window.location.href.split('/');
  7. s.pop();
  8. //var g_Prefix = s.join('/');
  9. var g_index = null;
  10. var g_modeldata = null;
  11. var g_weixinTitle = null;
  12. var g_Hots = null;
  13. var g_HotMeshes = [];
  14. var g_HotMeshSize = {
  15. g_HotMeshWidth: 0.3,
  16. g_HotMeshHeight: 0.3
  17. };
  18. //add表示添加,delete表示删除
  19. var g_HotStatus = null;
  20. var g_newHot = []; //存储新加热点
  21. var g_HotImage = {
  22. "point": "https://super.4dage.com/images/4dagePoint2.png",
  23. "point2": "https://super.4dage.com/images/4dagePoint.png"
  24. };
  25. var g_saveHot = false;
  26. var g_TextColor = 0x7777ff;
  27. var g_Text = null;
  28. var g_TextPlaneMesh = [];
  29. var g_TextIconMesh = [];
  30. var g_TextIcon = "./images/text.png";
  31. var g_SelectTextIndex = null;
  32. var g_TextShow = null;
  33. var g_audioPlay = false;
  34. var g_background = null;
  35. var g_roof = null;
  36. var g_data2 = null; //加载的data2.js的内容
  37. var g_bgAudio = null; //背景音乐
  38. var g_tourAudio = null; //导览音乐
  39. var g_play = 1; //表示播放图标状态
  40. var g_playAudio = null; //当前在播放或当继续播放时应该播放的,是g_bgAudio或g_tourAudio
  41. var g_currentHot = null; //当前打开的热点
  42. //var g_Texture=null;
  43. //var g_ChunknameTexture={}; //chunkname和贴图名称对应
  44. var g_NormalTexture = false;
  45. var g_SpecularTexture = false;
  46. var g_DirectionalLight = null;
  47. var g_snapShotWidth = 200; //截图下载图片的大小
  48. var g_snapShotHeight = 140;
  49. //微信分享
  50. var g_weixinObj = {
  51. "title": document.querySelector("head title").innerHTML,
  52. "lineLink": window.location.href,
  53. "imgUrl": "https://www.4dmodel.com/SuperPanoramic/images/weixintitle.jpg",
  54. "desc": "四维时代提供技术支持",
  55. }
  56. var settings = {
  57. hotClickEvent: {
  58. video: {
  59. playAndPause: true,
  60. examine: false,
  61. openHot: false
  62. },
  63. photo: {
  64. examine: false,
  65. openHot: false
  66. },
  67. shine: {
  68. examine: true,
  69. openHot: true
  70. }
  71. },
  72. //默认的:
  73. teleportTime: 1500, //瞬间过渡的时间
  74. /* flytimeDistanceMultiplier:150,
  75. flyTime:750, */
  76. tourRotTime: 2, //默认停留2秒
  77. //dontExamHot:true
  78. transparentBg: false,
  79. bgImg: null
  80. }
  81. if (window.number == '725' || window.number == '724') {
  82. settings.mobileNavHigh = true
  83. }
  84. //共用函数:
  85. window.common = null;
  86. window.MathLight = null;
  87. window.math = null
  88. window.easing = null
  89. window.lerp = null
  90. window.transitions = null
  91. window.browser = null
  92. window.momentTourBlackNewType = 0 //= number == 'TEST'//true
  93. var dealMap = (map) => { //使不resize when image is not power of two
  94. map.wrapS = map.wrapT = THREE.ClampToEdgeWrapping;
  95. map.minFilter = THREE.LinearFilter;
  96. map.magFilter = THREE.LinearFilter;
  97. map.generateMipmaps = true;
  98. }
  99. var dom = {
  100. getOffset: function (type, element, parent) {
  101. left = (type == "left") ? element.offsetLeft : element.offsetTop;
  102. if (!parent) parent = $("body")[0];
  103. while (element = element.offsetParent) {
  104. if (element == parent) break;
  105. left += (type == "left") ? element.offsetLeft : element.offsetTop;
  106. }
  107. return left;
  108. }
  109. };
  110. var getTransformSid = function () {
  111. var name
  112. if (player.mode == 'panorama') {
  113. name = player.currentPano ? player.currentPano.id : 'outside'
  114. } else {
  115. name = 'outside'
  116. }
  117. return name
  118. }
  119. var convertTool = {
  120. getPos2d: function (point, camera, dom) { //获取一个三维坐标对应屏幕中的二维坐标
  121. var camera = camera || player.camera;
  122. var dom = dom || player.domElement;
  123. var pos = point.clone().project(camera) //比之前hotspot的计算方式写得简单 project用于3转2(求法同shader); unproject用于2转3 :new r.Vector3(e.x, e.y, -1).unproject(this.camera);
  124. var x, y;
  125. x = (pos.x + 1) / 2 * dom.clientWidth;
  126. y = (1 - (pos.y + 1) / 2) * dom.clientHeight;
  127. var inSight = x <= dom.clientWidth && x >= 0 //是否在屏幕中
  128. &&
  129. y <= dom.clientHeight && y >= 0
  130. return {
  131. pos: new THREE.Vector2(x, y), // 屏幕像素坐标
  132. vector: pos, //(范围 -1 ~ 1)
  133. trueSide: pos.z < 1, //trueSide为false时,即使在屏幕范围内可见,也是反方向的另一个不可以被渲染的点 参见Tag.update
  134. inSight: inSight //在屏幕范围内可见
  135. };
  136. },
  137. ifShelter: function (pos3d) { //检测某点在视线中是否被mesh遮挡
  138. var ori = player.position
  139. var dir = pos3d.clone().sub(ori).normalize()
  140. var ray = new THREE.Raycaster(ori, dir) //由外向里 因为模型从内侧是可见的所以从外侧
  141. /* if(config.isEdit && publicObjectSet.editor.mainDesign.editing){
  142. var o = ray.intersectObjects(publicObjectSet.editor.mainDesign.wallMeshes);
  143. }else{ */
  144. var o = ray.intersectObjects(player.model.colliders);
  145. //}
  146. var len = pos3d.distanceTo(ori);
  147. if (o && o.length) {
  148. for (var i = 0; i < o.length; i++) {
  149. if (o[i].distance < len) {
  150. return true;
  151. } //有遮挡
  152. }
  153. }
  154. },
  155. /*
  156. 拖拽时,获取鼠标在拖拽面上的位置(需要借助另一个intersectPlane面来计算,即和相机方向一样的面,可保证铺满屏幕)
  157. 但是不一定能获取到,比如鼠标射线不朝向拖拽面时,即使获取也会是一个意外的反方向的交点。
  158. */
  159. getPosAtPlane: function (pos, info /* , mouse, camera */ ) { //pos:与intersectPlane的交点 见笔记
  160. var A = pos;
  161. var player = player;
  162. var mouse = player.mouse;
  163. var O = new THREE.Vector3(mouse.x, mouse.y, -1).unproject(player.camera);
  164. if (info.y != void 0) { //地面线的
  165. var y = info.y;
  166. if (player.mode == "floorplan" /* || Math.abs(O.x-pos.x)<0.0001 && Math.abs(O.z-pos.z)<0.0001) */ ) {
  167. //intersectPlane和地面平行,无交点
  168. var x = pos.x,
  169. z = pos.z;
  170. } else {
  171. if (y < player.camera.position.y && O.y <= A.y /* || y>player.camera.position.y && O.y >= A.y */ ) return null; //鼠标射线向上。因为相机一定位于地面以上(地面不会抬高到相机上吧?),所以无交点。
  172. if (O.y == A.y) {
  173. console.log('一样??');
  174. return;
  175. }
  176. if (A.y == y) {
  177. console.log('一样2??');
  178. return;
  179. }
  180. var r = (O.y - y) / (A.y - y);
  181. var x = (r * A.x - O.x) / (r - 1);
  182. var z = (r * A.z - O.z) / (r - 1);
  183. }
  184. } else { //垂直的也有越过消失点以后反向变化的情况,但使用时影响不大
  185. var N = info.normalVec;
  186. var P = info.pullPos;
  187. if (N.y != 0) {
  188. console.log('N.y != 0');
  189. return;
  190. } //仅仅支持垂直于地面的的墙壁,目前都是
  191. if (O.z == A.z) {
  192. console.log('O.z==A.z?');
  193. return;
  194. }
  195. if (N.z != 0 && N.x != 0) { //直接用这个通用的也可以,支持斜线的墙
  196. //console.log('N.z==0 && N.x == 0?');
  197. var c = (N.x * (A.x - O.x) + N.y * (A.y - O.y) + N.z * (A.z - O.z));
  198. if (c == 0) {
  199. console.log("分母为0?? return;");
  200. return;
  201. }
  202. var t = -((N.x * O.x + N.y * O.y + N.z * O.z) - (P.x * N.x + P.y * N.y + P.z * N.z)) / c
  203. var x = t * (A.x - O.x) + O.x;
  204. var y = t * (A.y - O.y) + O.y;
  205. var z = t * (A.z - O.z) + O.z;
  206. /*原理: 已知空间直线L:(x-a)/m=(x-b)/n=(z-c)/p和空间平面π:Ax+By+Cz+D=0;
  207. 求直线L与平面π的交点的坐标。
  208. 把直线方程改写成参数形式:设(x-a)/m=(x-b)/n=(z-c)/p=t;
  209. 则x=mt+a;y=nt+b;z=pt+c;代入平面π的方程得:
  210. A(mt+a)+B(nt+b)+C(pt+c)+D=0
  211. 由此解得t=-(Aa+Bb+Cc+D)/(Am+Bn+Cp)
  212. 再代入参数方程即得交点的坐标(x,y,z). */
  213. } else if (N.x == 0) { //z与pullPos相等
  214. var z = P.z;
  215. if (O.y == A.y) {
  216. console.log('一样??');
  217. return;
  218. }
  219. if (A.y == y) {
  220. console.log('一样2??');
  221. return;
  222. }
  223. if (A.z == z) {
  224. console.log('一样3??');
  225. return;
  226. }
  227. var r = (O.z - z) / (A.z - z);
  228. var x = (r * A.x - O.x) / (r - 1);
  229. var y = (r * A.y - O.y) / (r - 1);
  230. } else if (N.z == 0) { //x与pullPos相等
  231. var x = P.x;
  232. if (O.y == A.y) {
  233. console.log('一样??');
  234. return;
  235. }
  236. if (A.y == y) {
  237. console.log('一样2??');
  238. return;
  239. }
  240. if (A.x == x) {
  241. console.log('一样3??');
  242. return;
  243. }
  244. var r = (O.x - x) / (A.x - x);
  245. var y = (r * A.y - O.y) / (r - 1);
  246. var z = (r * A.z - O.z) / (r - 1);
  247. }
  248. }
  249. return new THREE.Vector3(x, y, z);
  250. },
  251. getMouseIntersect: function (camera, meshes, mouse) { //获取鼠标和meshes交点
  252. var raycaster = new THREE.Raycaster;
  253. camera.updateMatrixWorld();
  254. var origin = new THREE.Vector3(mouse.x, mouse.y, -1).unproject(camera),
  255. end = new THREE.Vector3(mouse.x, mouse.y, 1).unproject(camera);
  256. var dir = end.sub(origin).normalize()
  257. raycaster.set(origin, dir);
  258. var n = raycaster.intersectObjects(meshes);
  259. if (0 === n.length)
  260. return null;
  261. return n[0];
  262. },
  263. ifIntersectChunks: function (A, B, options = {}) { //获取某个线段/射线和meshes的交点
  264. var dir = B.clone().sub(A).normalize();
  265. var len = options.InfinityLen ? Infinity : A.distanceTo(B) + (options.extLen || 0);
  266. var ray = new THREE.Raycaster(A.clone(), dir, 0, len);
  267. var o = ray.intersectObjects(options.model || player.model.colliders);
  268. if (o && o.length) return o;
  269. if (options.throughWidth) { //允许最小宽度,防止穿过极小的缝隙导致撞墙感
  270. var normal = math.getNormal({
  271. points: [{
  272. x: A.x,
  273. y: A.z
  274. }, {
  275. x: B.x,
  276. y: B.z
  277. }]
  278. }); //线段法线
  279. normal.multiplyScalar(options.throughWidth)
  280. var normalVec3 = new THREE.Vector3(normal.x, 0, normal.y);
  281. var A2 = A.clone().add(normalVec3)
  282. ray.set(A2, dir);
  283. var o2 = ray.intersectObjects(options.model || player.model.colliders);
  284. ray.set(A.clone().add(normalVec3.negate()), dir);
  285. if (o2 && o2.length) return o2;
  286. var o3 = ray.intersectObjects(options.model || player.model.colliders);
  287. if (o3 && o3.length) return o3;
  288. }
  289. return null;
  290. },
  291. getPosAtSphere: function (pos3d, toPanoPos) {
  292. var dir = pos3d.clone().sub(toPanoPos);
  293. dir.normalize(); //然后计算在球中
  294. dir.multiplyScalar(Constants.skyRadius);
  295. dir.add(toPanoPos);
  296. return dir;
  297. },
  298. getScaleForConstantSize: function (op = {}) { //获得规定二维大小的mesh的scale值。可以避免因camera的projection造成的mesh视觉大小改变。 来源:tag.updateDisc
  299. var w;
  300. var i = new THREE.Vector3,
  301. o = new THREE.Vector3,
  302. l = new THREE.Vector3,
  303. c = new THREE.Vector3,
  304. h = new THREE.Vector3
  305. if (op.width2d) w = op.width2d //如果恒定二维宽度
  306. else { //否则考虑上距离,加一丢丢近大远小的效果
  307. var currentDis, nearBound, farBound
  308. if (op.camera.type == "OrthographicCamera") {
  309. currentDis = (op.camera.right - op.camera.left) / op.camera.zoom
  310. } else {
  311. currentDis = op.position.distanceTo(op.camera.position);
  312. }
  313. w = op.maxSize - (op.maxSize - op.minSize) * THREE.Math.smoothstep(currentDis, op.nearBound, op.farBound);
  314. //maxSize : mesh要表现的最大像素宽度; nearBound: 最近距离,若比nearBound近,则使用maxSize
  315. }
  316. i.copy(op.position).project(op.camera), //tag中心在屏幕上的二维坐标
  317. o.set(op.resolution.x / 2, op.resolution.y / 2, 1).multiply(i), //转化成px -w/2 到 w/2的范围
  318. l.set(w / 2, 0, 0).add(o), //加上tag宽度的一半
  319. c.set(2 / op.resolution.x, 2 / op.resolution.y, 1).multiply(l), //再转回 -1 到 1的范围
  320. h.copy(c).unproject(op.camera); //再转成三维坐标,求得tag边缘的位置
  321. var g = h.distanceTo(op.position) //就能得到tag的三维半径
  322. return g //可能NAN 当相机和position重叠时
  323. }
  324. }
  325. //--------------------------------------
  326. //管理js文件 获取modeldata.js 判断是否有特殊的字段,如果有就先加载SpecialScene.js 里面有对特殊场景处理的代码 否则就直接加载main
  327. var Manage = function () {
  328. this.weixinURL = "https://res.wx.qq.com/open/js/jweixin-1.2.0.js",
  329. this.time = "?" + new Date().getTime();
  330. this.loadAudio();
  331. this.loadWeixin();
  332. }
  333. //动态加载js文件
  334. Manage.prototype.LoadJs = function (_files, succes) {
  335. /* 已加载文件缓存列表,用于判断文件是否已加载过,若已加载则不再次加载*/
  336. var classcodes = [];
  337. var FileArray = [];
  338. if (typeof _files === "object") {
  339. FileArray = _files;
  340. } else {
  341. /*如果文件列表是字符串,则用,切分成数组*/
  342. if (typeof _files === "string") {
  343. FileArray = _files.split(",");
  344. }
  345. }
  346. if (FileArray != null && FileArray.length > 0) {
  347. var LoadedCount = 0;
  348. for (var i = 0; i < FileArray.length; i++) {
  349. loadFile(FileArray[i], function () {
  350. LoadedCount++;
  351. if (LoadedCount == FileArray.length) {
  352. try {
  353. succes();
  354. } catch (err) {
  355. console.log("err: 您未定义回调");
  356. }
  357. }
  358. })
  359. }
  360. }
  361. /*加载JS文件,url:文件路径,success:加载成功回调函数*/
  362. function loadFile(url, success) {
  363. if (!FileIsExt(classcodes, url)) {
  364. var _ThisType = GetFileType(url);
  365. var ThisType = _ThisType.indexOf("?") == -1 ? _ThisType : _ThisType.substring(0, _ThisType.indexOf("?"));
  366. var fileObj = null;
  367. if (ThisType == ".js") {
  368. fileObj = document.createElement('script');
  369. fileObj.src = url;
  370. } else if (ThisType == ".css") {
  371. fileObj = document.createElement('link');
  372. fileObj.href = url;
  373. fileObj.type = "text/css";
  374. fileObj.rel = "stylesheet";
  375. } else if (ThisType == ".less") {
  376. fileObj = document.createElement('link');
  377. fileObj.href = url;
  378. fileObj.type = "text/css";
  379. fileObj.rel = "stylesheet/less";
  380. }
  381. success = success || function () {};
  382. fileObj.onload = fileObj.onreadystatechange = function () {
  383. if (!this.readyState || 'loaded' === this.readyState || 'complete' === this.readyState) {
  384. success();
  385. classcodes.push(url)
  386. }
  387. }
  388. document.getElementsByTagName('head')[0].appendChild(fileObj);
  389. } else {
  390. success();
  391. }
  392. }
  393. /*获取文件类型,后缀名,小写*/
  394. function GetFileType(url) {
  395. if (url != null && url.length > 0) {
  396. return url.substr(url.lastIndexOf(".")).toLowerCase();
  397. }
  398. return "";
  399. }
  400. /*文件是否已加载*/
  401. function FileIsExt(FileArray, _url) {
  402. if (FileArray != null && FileArray.length > 0) {
  403. var len = FileArray.length;
  404. for (var i = 0; i < len; i++) {
  405. if (FileArray[i] == _url) {
  406. return true;
  407. }
  408. }
  409. }
  410. return false;
  411. }
  412. };
  413. //获取页面url后面的参数
  414. Manage.prototype.number = function (variable) {
  415. var query = window.location.search.substring(1);
  416. var vars = query.split("&");
  417. for (var i = 0; i < vars.length; i++) {
  418. var pair = vars[i].split("=");
  419. if (pair[0] == variable) {
  420. return pair[1];
  421. }
  422. }
  423. return (false);
  424. };
  425. Manage.prototype.loadWeixin = function () {
  426. var that = this;
  427. this.LoadJs(that.weixinURL + that.time, function () {});
  428. }
  429. Manage.prototype.weixinShare = function () {
  430. console.log("weixinShare")
  431. $.ajax({
  432. url: 'https://www.4dage.com/wechat/jssdk/',
  433. type: "post",
  434. data: {
  435. 'url': location.href.split('#')[0]
  436. },
  437. dataType: "jsonp",
  438. jsonpCallback: "success_jsonp",
  439. success: function (data, textStatus) {
  440. console.log("weixinShare success")
  441. console.log(data.appId)
  442. wx.config({
  443. // debug : true,
  444. appId: data.appId,
  445. timestamp: data.timestamp,
  446. nonceStr: data.nonceStr,
  447. signature: data.signature,
  448. jsApiList: ['checkJsApi', 'onMenuShareTimeline',
  449. 'onMenuShareAppMessage', 'onMenuShareQQ',
  450. 'onMenuShareWeibo', 'hideMenuItems',
  451. 'showMenuItems', 'hideAllNonBaseMenuItem',
  452. 'showAllNonBaseMenuItem', 'translateVoice',
  453. 'startRecord', 'stopRecord', 'onRecordEnd',
  454. 'playVoice', 'pauseVoice', 'stopVoice',
  455. 'uploadVoice', 'downloadVoice', 'chooseImage',
  456. 'previewImage', 'uploadImage', 'downloadImage',
  457. 'getNetworkType', 'openLocation', 'getLocation',
  458. 'hideOptionMenu', 'showOptionMenu', 'closeWindow',
  459. 'scanQRCode', 'chooseWXPay',
  460. 'openProductSpecificView', 'addCard', 'chooseCard',
  461. 'openCard'
  462. ]
  463. });
  464. },
  465. error: function (XMLHttpRequest, textStatus, errorThrown) {
  466. console.log("jsonp.error:" + textStatus);
  467. }
  468. });
  469. var success_jsonp = function (json) {
  470. console.log(json);
  471. };
  472. wx.ready(function () {
  473. // config信息验证后会执行ready方法,所有接口调用都必须在config接口获得结果之后,config是一个客户端的异步操作,所以如果需要在页面加载时就调用相关接口,则须把相关接口放在ready函数中调用来确保正确执行〿
  474. //对于用户触发时才调用的接口,则可以直接调用,不需要放在ready函数中〿
  475. //分享到朋友圈
  476. console.log(g_weixinObj)
  477. wx.onMenuShareTimeline({
  478. title: g_weixinObj.title, // 分享标题
  479. link: g_weixinObj.lineLink, // 分享链接
  480. imgUrl: g_weixinObj.imgUrl, // 分享图标
  481. desc: g_weixinObj.desc
  482. });
  483. //获取“分享给朋友”按钮点击状态及自定义分享内容接叿
  484. wx.onMenuShareAppMessage({
  485. title: g_weixinObj.title, // 分享标题
  486. desc: g_weixinObj.desc, // 分享描述
  487. link: g_weixinObj.lineLink, // 分享链接
  488. imgUrl: g_weixinObj.imgUrl, // 分享图标
  489. type: '', // 分享类型,music、video或link,不填默认为link
  490. dataUrl: '' // 如果type是music或video,则要提供数据链接,默认为空
  491. });
  492. wx.onMenuShareWeibo({
  493. title: g_weixinObj.title, // 分享标题
  494. desc: g_weixinObj.desc, // 分享描述
  495. link: g_weixinObj.lineLink, // 分享链接
  496. imgUrl: g_weixinObj.imgUrl, // 分享图标
  497. success: function () {
  498. // 用户确认分享后执行的回调函数
  499. },
  500. cancel: function () {
  501. // 用户取消分享后执行的回调函数
  502. }
  503. });
  504. wx.onMenuShareQZone({
  505. title: g_weixinObj.title, // 分享标题
  506. desc: g_weixinObj.desc, // 分享描述
  507. link: g_weixinObj.lineLink, // 分享链接
  508. imgUrl: g_weixinObj.imgUrl, // 分享图标
  509. success: function () {
  510. // 用户确认分享后执行的回调函数
  511. },
  512. cancel: function () {
  513. // 用户取消分享后执行的回调函数
  514. }
  515. });
  516. wx.onMenuShareQQ({
  517. title: g_weixinObj.title, // 分享标题
  518. desc: g_weixinObj.desc, // 分享描述
  519. link: g_weixinObj.lineLink, // 分享链接
  520. imgUrl: g_weixinObj.imgUrl, // 分享图标
  521. success: function () {
  522. // 用户确认分享后执行的回调函数
  523. },
  524. cancel: function () {
  525. // 用户取消分享后执行的回调函数
  526. }
  527. });
  528. wx.error(function (res) {
  529. // config信息验证失败会执行error函数,如签名过期导致验证失败,具体错误信息可以打开config的debug模式查看,也可以在返回的res参数中查看,对于SPA可以在这里更新签名〿
  530. });
  531. });
  532. }
  533. Manage.prototype.dealURL = function (src, type) {
  534. //music: "///super.4dage.com/data/LYW/edit/20200928_151633415.mp3"
  535. //"https://super.4dage.com/data/LYW/edit/20200928_165319399.jpg"]
  536. if (window.isLocal && settings.localPrefix != void 0) { //本地将线上的前缀替换
  537. var oldPrefix = 'super.4dage.com/'; //最简单的地址写一个,如果有其他完全不一样的地址就用数组逐个判断
  538. var index = src.indexOf(oldPrefix);
  539. if (index > -1) {
  540. var wholeOldPrefix = src.slice(0, index + oldPrefix.length);
  541. return src.replace(wholeOldPrefix, settings.localPrefix)
  542. }
  543. console.error("没有找到合适的本地链接")
  544. return src
  545. } else {
  546. //add https://
  547. var prefix = g_Prefix.replace('https://', '').replace('http://', '')
  548. if (!src.includes('http:/') && !src.includes('https:/') && src.includes(prefix)) {
  549. src = 'https://' + src
  550. }
  551. return src
  552. }
  553. }
  554. Manage.prototype.removeSrcPostMark = function (url) { //去除texture.load时自动加上的'?'
  555. var index = url.indexOf('?')
  556. if (index > -1) {
  557. return url.slice(0, index)
  558. } else return url
  559. }
  560. Manage.prototype.showInfo = function (o) { // ({result:true, title:"发布成功"});
  561. var box = $(".resultBox");
  562. var title = o.title || o || i18n.get('保存成功');
  563. box.children().eq(0).html(title)
  564. //var time = o.time || THREE.Math.clamp((Config.lang=='en') ? title.length*50 : title.length*130 ,1300,5000);
  565. var time = o.time || THREE.Math.clamp(title.length * 130, 1300, 5000);
  566. o.time || console.log("showtime " + time)
  567. //实际有一半的时间在渐变透明度
  568. this.showInfoTimer && clearTimeout(this.showInfoTimer)
  569. box.removeClass("animate"); //如果之后不久又要showinfo一个的话,先停止前面的animate
  570. setTimeout(function () {
  571. box.css({
  572. '-webkit-animation-duration': time + 'ms',
  573. 'animation-duration': time + 'ms'
  574. })
  575. if (o.top) {
  576. box.children().css('top', o.top + "%");
  577. } else {
  578. box.children().css('top', '')
  579. }
  580. box.removeClass("hide");
  581. box.addClass("animate");
  582. if (o.dontInteract) { //遮挡对屏幕的操作
  583. box.css('pointer-events', 'auto')
  584. } else {
  585. box.css('pointer-events', 'none')
  586. }
  587. this.showInfoTimer = setTimeout(function () {
  588. box.removeClass("animate");
  589. box.addClass("hide");
  590. this.showInfoTimer = null;
  591. }.bind(this), time + 20)
  592. }.bind(this), 50) //这个数字太小的话后面触发的没有重新animate的效果 应该要比帧率大吧
  593. } //like: manage.showInfo({title:'a', top:20})
  594. //公用的函数
  595. function getQueryVariable(variable) {
  596. var query = window.location.search.substring(1);
  597. var vars = query.split("&");
  598. for (var i = 0; i < vars.length; i++) {
  599. var pair = vars[i].split("=");
  600. if (pair[0] == variable) {
  601. return pair[1];
  602. }
  603. }
  604. return (false);
  605. }
  606. //隐藏公司Logo
  607. function showLogo() {
  608. $("#myCompany").hide();
  609. $("#loaderCoBrandName").hide();
  610. $("#title-logo").hide();
  611. $(".title-container").css("justify-content", "center")
  612. }
  613. //czj 添加随机的时间
  614. function randomTime() {
  615. return new Date()
  616. }
  617. function matcher(data) {
  618. if (!data || !g_version) return data;
  619. delete data.model.vision_version;
  620. var _data = {
  621. files: {
  622. "templates": ["images/images{{number}}/{{filename}}"]
  623. },
  624. model: {
  625. sid: window.number,
  626. camera_start: data.model.images && data.model.images.length != 0 ? {
  627. camera: {
  628. zoom: "-1",
  629. quaternion: [
  630. JSON.parse(data.model.images[0].metadata).camera_quaternion.z,
  631. JSON.parse(data.model.images[0].metadata).camera_quaternion.w,
  632. JSON.parse(data.model.images[0].metadata).camera_quaternion.x,
  633. JSON.parse(data.model.images[0].metadata).camera_quaternion.y
  634. ]
  635. },
  636. pano: {
  637. uuid: JSON.parse(data.model.images[0].metadata).scan_id
  638. },
  639. mode: "0"
  640. } : ''
  641. },
  642. sid: window.number,
  643. hoticon: {
  644. default: "https://super.4dage.com/images/4dagePoint2.png",
  645. higt: "https://super.4dage.com/images/4dagePoint.png"
  646. },
  647. special: "false",
  648. weixinDesc: ""
  649. };
  650. $.extend(true, data, _data)
  651. return data;
  652. }
  653. function hotMatcher(data = {}) {
  654. //if(!data || !g_version) return data;
  655. if (g_version) {
  656. data.tourAudio = data.audio || {};
  657. } else {
  658. data.tourAudio = {}
  659. }
  660. return data;
  661. }
  662. var GifTexDeal = {
  663. animateObjects: [],
  664. animateTexs: [],
  665. addAnimation: function (texture, owner, info, id) {
  666. /* if(this.animateObjects.find(e=>
  667. e.texture == texture && !ifSame(info, e.info)
  668. )) */
  669. var animation
  670. var tex = this.animateTexs.find(e => e.texture == texture)
  671. if (tex) {
  672. animation = tex
  673. } else {
  674. animation = {
  675. texture,
  676. info
  677. }
  678. this.animateTexs.push(animation)
  679. this.setRepeart(animation)
  680. }
  681. var object = {
  682. animation, //默认相同的texture对应的info是一样的, 对应一个animation
  683. owner,
  684. }
  685. this.animateObjects.push(object)
  686. return object
  687. },
  688. remove: function (object) {
  689. var index = this.animateObjects.indexOf(object)
  690. if (index > -1) {
  691. this.animateObjects.splice(index, 1)
  692. if (!this.animateObjects.find(e => e.animation == object.animation)) {
  693. let i = this.animateTexs.indexOf(object.animation)
  694. this.animateTexs.splice(i, 1)
  695. object.animation.texture.repeat.set(1, 1)
  696. }
  697. this.stop(object)
  698. }
  699. },
  700. setRepeart: function (animation) {
  701. animation.texture.repeat.set(1 / animation.info.cellXcount, 1 / animation.info.cellYcount)
  702. },
  703. start: function (object) {
  704. if (!object || object.started) return;
  705. object.started = true
  706. if (object.animation.started) return;
  707. object.animation.started = true
  708. var info = object.animation.info
  709. var count = info.cellXcount * info.cellYcount - (info.voidCount || 0)
  710. if (count <= 1) return;
  711. transitions.start((progress) => {
  712. var index = Math.floor(count * progress);
  713. var indexX = index % info.cellXcount
  714. var indexY = info.cellYcount - Math.floor(index / info.cellXcount) - 1; //uv.offset.y是从下到上的
  715. object.animation.texture.offset.x = indexX / info.cellXcount;
  716. object.animation.texture.offset.y = indexY / info.cellYcount;
  717. //console.log(object.id + " : "+ object.texture.offset.toArray())
  718. }, info.duration * (-1), null,
  719. /* ()=>{//done (-1):循环
  720. object.started = false
  721. object.texture.offset.x = 0;
  722. object.texture.offset.y = 0;
  723. this.start(object)
  724. }, */
  725. 0, null, object.id, "gif_" + object.animation.texture.id);
  726. },
  727. stop: function (object) {
  728. if (!object || !object.started) return;
  729. object.started = false
  730. //只有该object对应的texture对应的所有object都停止了,才能真的停止动画:
  731. if (this.animateObjects.find(e => e.animation == object.animation && e.started)) return;
  732. transitions.cancelById("gif_" + object.animation.texture.id);
  733. object.animation.texture.offset.set(0, 0)
  734. object.animation.started = false
  735. }
  736. }
  737. var CloneObject = function (copyObj, result, isSimpleCopy, extraReplace) {
  738. //isSimpleCopy只复制最外层
  739. //复制json result的可能:普通数字或字符串、普通数组、复杂对象
  740. if (!copyObj) return copyObj //0 null undefined ''
  741. result = result || {};
  742. if (copyObj instanceof Array) {
  743. /* if (copyObj[0]instanceof Object) {
  744. //不支持含有 [[Object]] 这样二级数组里面还是复杂数据的,普通和复杂的数据混合可能也不支持
  745. console.error("不支持含有 [[Object]] 这样二级数组里面还是复杂数据的...")
  746. }
  747. return copyObj.slice(0);*/ //如果是数组,直接复制返回(排除数组内是object
  748. return copyObj.map(e => {
  749. if (e instanceof Object) {
  750. return CloneObject(e)
  751. } else return e
  752. })
  753. } else {
  754. if (copyObj.clone instanceof Function) { //解决一部分
  755. return copyObj.clone()
  756. }
  757. }
  758. for (var key in copyObj) {
  759. if (copyObj[key] instanceof Object && !isSimpleCopy)
  760. result[key] = CloneObject(copyObj[key]);
  761. else
  762. result[key] = copyObj[key];
  763. //如果是函数类同基本数据,即复制引用
  764. }
  765. return result;
  766. };
  767. var ifSame = function (object1, object2) {
  768. if (object1 == object2) return true // 0 != undefined , 0 == ''
  769. else if (!object1 || !object2) return false
  770. else if (object1.constructor != object2.constructor) {
  771. return false
  772. } else if (object1 instanceof Array) {
  773. if (object1.length != object2.length) return false;
  774. var _object2 = object2.slice(0);
  775. for (let i = 0; i < object1.length; i++) {
  776. var u = _object2.find(e => ifSame(object1[i], e));
  777. if (u == void 0 && !_object2.includes(u) && !object1.includes(u)) return false;
  778. else {
  779. let index = _object2.indexOf(u);
  780. _object2.splice(index, 1);
  781. }
  782. }
  783. return true
  784. } else if (object1.equals instanceof Function) { //复杂数据仅支持这种,其他的可能卡住?
  785. return object1.equals(object2)
  786. } else if (typeof object1 == 'number' || typeof object1 == 'string') {
  787. if (isNaN(object1) && isNaN(object2)) return true
  788. else return object1 == object2
  789. } else if (typeof object1 == "object") {
  790. var keys1 = Object.keys(object1)
  791. var keys2 = Object.keys(object2)
  792. if (!ifSame(keys1, keys2)) return false;
  793. for (let i in object1) {
  794. var same = ifSame(object1[i], object2[i]);
  795. if (!same) return false
  796. }
  797. return true
  798. } else {
  799. console.log('isSame出现例外')
  800. }
  801. }
  802. /* var SoundManager = {//暂不支持同时播放
  803. currentAudio:null,//当前正在播放list中的哪一个
  804. list:[
  805. {
  806. name:"bg",
  807. level : 0,//越大优先级越高
  808. canBeInterrupted : true ,
  809. },
  810. {
  811. name:"boxVideo",
  812. level : 1,
  813. canBeInterrupted : true,
  814. checkIfNeedPlay:function(){
  815. }
  816. },
  817. {
  818. name:"hot",
  819. level : 2,
  820. canBeInterrupted : true
  821. },
  822. {
  823. name:"tour",
  824. level : 3,
  825. canBeInterrupted : true
  826. },
  827. ],
  828. switchPlay:function(){
  829. },
  830. register:function(){
  831. }
  832. }
  833. */
  834. //最好能知道应该播放到的currentTime
  835. var SoundManager = { //暂不支持同时播放
  836. currentAudio: null, //当前正在播放list中的哪一个
  837. enableSound: true, //是否允许有声音
  838. playHistory: [], //被打断的加入播放历史
  839. list: [], //同一级别可以互相打断 //暂时不做多级别
  840. play: function (name, src, currentTime) {
  841. // 点击开始自动导览的时候,手动停止背景音乐播放
  842. window.manage.switchBgmState(false);
  843. var object = this.list.find(e => e.name == name)
  844. if (object) {
  845. if (this.currentAudio) {
  846. this.pause(this.currentAudio.name, false, true)
  847. }
  848. { //将当前要播放的播放历史中清除
  849. let index = this.playHistory.indexOf(object)
  850. if (index > -1) this.playHistory.splice(index, 1);
  851. }
  852. this.currentAudio = object
  853. if (src) {
  854. this.setSrc(name, src)
  855. }
  856. if (currentTime != void 0) {
  857. object.audio.currentTime = currentTime
  858. }
  859. if (object.audio) {
  860. object.audio.load(); // iOS 9 还需要额外的 load 一下, 否则直接 play 无效 *///https://www.cnblogs.com/interdrp/p/4211883.html 部分资料
  861. object.audio.play();
  862. object.callback && object.callback(true)
  863. }
  864. Log(name + ' 播放 ')
  865. }
  866. },
  867. pause: function (name, autoReplayLast, isInterrupt) { //需要能自动恢复上一个被打算的音频。恢复前判断是否还需要播放
  868. var object = this.list.find(e => e.name == name)
  869. if (object) {
  870. this.currentAudio = null
  871. if (object.audio) {
  872. object.audio.pause()
  873. object.callback && object.callback(false)
  874. Log(name + ' 中断音频 ' + "(" + common.getFileNameFromUrl(object.audio.src) + ')')
  875. }
  876. if (isInterrupt) { //一般主动调用不需要加这个
  877. this.playHistory.push(object) //如果是被中断的,加入播放历史,等待恢复播放
  878. }
  879. if (autoReplayLast) { //播放之前的音频。它们是被打断过的。
  880. while (this.playHistory.length) {
  881. var last = this.playHistory.pop();
  882. if (last.src && last.canplay(last.audio)) {
  883. this.play(last.name)
  884. }
  885. }
  886. }
  887. }
  888. },
  889. setSrc: function (name, src) { //不能直接给audio赋src!一定要用这个函数!因为我要拿这里的src来判断有无src,因为貌似audio的src会自动变,''时会变成html的链接
  890. var object = this.list.find(e => e.name == name)
  891. object.src = src
  892. object.audio.src = src
  893. Log(`${object.name} 设置src: ${src}`)
  894. },
  895. createAudio: function (object = {}) { //name, level, canplay
  896. if (!object.fake) {
  897. object.audio = new Audio();
  898. object.audio.loop = !!object.loop;
  899. //object.audio.autoplay = true;
  900. object.audio.addEventListener('ended', () => {
  901. if (object.loop) { //循环
  902. Log(`${object.name} 播放完毕,重新播放`)
  903. object.audio.play()
  904. } else {
  905. this.pause(object.name, true); //停止后的后续处理
  906. }
  907. });
  908. object.audio.oncanplaythrough = () => {
  909. Log(`${object.name} canplaythrough `)
  910. }
  911. }
  912. this.list.push(object)
  913. },
  914. initAutoPlay: function () { //处理设备自动播放限制
  915. let play = function () {
  916. if (this.currentAudio && this.currentAudio.audio) {
  917. this.currentAudio.audio.play()
  918. Log(`${this.currentAudio.name} 自动播放成功`) //即使设置src在这之后好像也能成功播放
  919. } else {
  920. }
  921. document.removeEventListener("touchstart", play);
  922. document.removeEventListener("click", play);
  923. $('#player')[0].removeEventListener("touchstart", play);
  924. }.bind(this);
  925. document.addEventListener("WeixinJSBridgeReady", play, false);
  926. document.addEventListener("touchstart", play); //ios需要加个事件才能播放 不能自动播放;如果还有浏览器不行,换成别的交互事件
  927. document.addEventListener("click", play);
  928. $('#player')[0].addEventListener("touchstart", play);
  929. }
  930. }
  931. function Log(value, color, fontSize) {
  932. color = color || '#13f'
  933. fontSize = fontSize || 14
  934. console.warn(`%c${value}`, `color:${color};font-size:${fontSize}px`)
  935. }
  936. Manage.prototype.loadAudio = function () { //相关:g_tourAudio \ g_playAudio
  937. //热点页面因为挡住了界面,所以暂时不存在别的音频阻止热点音频的可能
  938. //box视频都静音,所以暂时不考虑
  939. SoundManager.createAudio({
  940. name: 'bgm',
  941. level: 0,
  942. src: '',
  943. loop: true,
  944. canplay: (audio) => {
  945. return this.bgmShouldPlay
  946. },
  947. callback: (state) => { //play或pause时随之触发的函数(即使还没开始播放)
  948. if (state) {
  949. console.log('播放背景音乐');
  950. document.querySelector('#openMusic').style.display = 'none'
  951. document.querySelector('#closeMusic').style.display = 'block'
  952. } else {
  953. console.log('暂停背景音乐');
  954. document.querySelector('#openMusic').style.display = 'block'
  955. document.querySelector('#closeMusic').style.display = 'none'
  956. }
  957. }
  958. })
  959. SoundManager.createAudio({
  960. name: 'tour',
  961. level: 0,
  962. src: '',
  963. loop: false,
  964. canplay: (audio) => {
  965. return player.director && player.director.tourIsPlaying && player.director.getAudio()
  966. }
  967. })
  968. SoundManager.createAudio({
  969. name: 'hot',
  970. fake: true, //实际上没有audio. 只是为了来停止和续播其他音频
  971. level: 1,
  972. src: '',
  973. loop: false,
  974. canplay: (audio) => {
  975. }
  976. })
  977. $("#volume").find("a").on("click", () => {
  978. if ($("#volume img")[0].src.indexOf("btn_on.png") > -1) {
  979. this.switchBgmState(true);
  980. } else if ($("#volume img")[0].src.indexOf("btn_off.png") > -1) {
  981. this.switchBgmState(false);
  982. }
  983. })
  984. // this.switchBgmState(true);//初始设置允许播放bgm
  985. // SoundManager.initAutoPlay()
  986. }
  987. Manage.prototype.switchBgmState = function (state) { //按钮的状态完全代表是否应该播放bgm,即使还没加载完
  988. this.bgmShouldPlay = state
  989. if (state) {
  990. SoundManager.play('bgm')
  991. } else {
  992. SoundManager.pause('bgm')
  993. }
  994. /* if(!g_bgAudio || !g_bgAudio.src) return;
  995. var played = function(){
  996. console.log('begin play bgm');
  997. g_play = 1;
  998. g_playAudio = g_bgAudio;
  999. g_tourAudio && g_tourAudio.pause()
  1000. }
  1001. var paused = function(){
  1002. g_play = 0;
  1003. g_playAudio == g_bgAudio && (g_playAudio = null)
  1004. }
  1005. if(state ){
  1006. g_bgAudio.play();
  1007. if(g_bgAudio.paused){
  1008. paused()
  1009. }else{
  1010. played()
  1011. return true
  1012. }
  1013. }else{
  1014. g_bgAudio.pause();
  1015. paused()
  1016. }
  1017. g_bgAudio.pauseByHot = false
  1018. g_bgAudio.pauseByTour = false */
  1019. }
  1020. var manage = new Manage();
  1021. //兼容一代的場景
  1022. //請求地址統一管理
  1023. var g_onePregix = "https://bigscene.4dage.com/" //对应一代 http://www.4dmodel.com/SuperPanoramic/index.html?m=55
  1024. var g_version = manage.number("version");
  1025. g_version === "one" ? g_Prefix = g_onePregix : '';