index.js 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791
  1. Component({
  2. behaviors: [require('../common/share-behavior').default],
  3. properties: {
  4. markerImg: {
  5. type: String
  6. },
  7. },
  8. data: {
  9. loaded: false,
  10. arReady: false,
  11. isStartPlay1: false,
  12. adlScale: '0 0 0',
  13. adlPos: '-0.1 -0.5 -0.05',
  14. sunReady: false,
  15. tigerReady: false
  16. },
  17. lifetimes: {
  18. attached() {
  19. },
  20. detached() {
  21. this.stopSunRotation();
  22. this.stopTiger();
  23. }
  24. },
  25. methods: {
  26. handleReady({
  27. detail
  28. }) {
  29. const xrScene = this.scene = detail.value;
  30. // console.log('xr-scene', xrScene);
  31. },
  32. handleAssetsProgress: function ({
  33. detail
  34. }) {
  35. // console.log('assets progress', detail.value);
  36. },
  37. handleAssetsLoaded: function ({
  38. detail
  39. }) {
  40. console.log('assets loaded', detail.value);
  41. this.setData({
  42. loaded: true
  43. });
  44. },
  45. handleARReady: function ({
  46. detail
  47. }) {
  48. console.log('arReady');
  49. this.setData({
  50. arReady: true
  51. })
  52. },
  53. handleItem1Loaded({ detail }) {
  54. const el = detail.value.target;
  55. const animator = el.getComponent("animator");
  56. this.animator1 = animator
  57. console.log('animator1', animator)
  58. const gltf = el.getComponent("gltf");
  59. this.bgGltf = gltf;
  60. this.sunMeshes = this.resolveSunMeshesFromBG(gltf);
  61. this.setData({ sunReady: !!(this.sunMeshes && this.sunMeshes.length) });
  62. if (this.sunShouldRun) this.startSunRotation();
  63. this.tigerMeshes = this.resolveTigerMeshesFromBG(gltf);
  64. this.setData({ tigerReady: !!(this.tigerMeshes && this.tigerMeshes.length) });
  65. if (this.tigerShouldRun && this.tigerStartRequested) this.scheduleTigerStart();
  66. },
  67. handleItem2Loaded({ detail }) {
  68. const el = detail.value.target;
  69. const animator = el.getComponent("animator");
  70. this.animator2 = animator;
  71. },
  72. handleARTrackerState1({
  73. detail
  74. }) {
  75. // 事件的值即为`ARTracker`实例
  76. const tracker = detail.value;
  77. // 获取当前状态和错误信息
  78. console.log('tracker', tracker)
  79. const {
  80. state,
  81. } = tracker;
  82. if (state == 2) {
  83. this.play()
  84. } else {
  85. this.pause()
  86. }
  87. },
  88. play() {
  89. if (!this.data.loaded) return
  90. this.sunShouldRun = true;
  91. this.startSunRotation();
  92. this.tigerShouldRun = true;
  93. this.tigerStartRequested = false;
  94. this.prepareTigerAudio();
  95. this.prepareTigerFrames();
  96. if (!this.data.isStartPlay1) {
  97. this.setData({
  98. isStartPlay1: true
  99. }, () => {
  100. this.playitem1Action()
  101. })
  102. }
  103. },
  104. pause() {
  105. this.sunShouldRun = false;
  106. this.stopSunRotation();
  107. this.tigerShouldRun = false;
  108. this.tigerStartRequested = false;
  109. this.stopTiger();
  110. },
  111. resolveSunMeshesFromBG(gltf) {
  112. if (!gltf || !this.scene) return [];
  113. const candidates = this.getSunNameCandidates();
  114. const uniqueCandidates = [];
  115. for (const name of candidates) {
  116. if (name && !uniqueCandidates.includes(name)) uniqueCandidates.push(name);
  117. }
  118. for (const name of uniqueCandidates) {
  119. if (typeof gltf.getPrimitivesByNodeName === 'function') {
  120. const meshes = gltf.getPrimitivesByNodeName(name);
  121. if (meshes && meshes.length) return meshes;
  122. }
  123. if (typeof gltf.getPrimitivesByMeshName === 'function') {
  124. const meshes = gltf.getPrimitivesByMeshName(name);
  125. if (meshes && meshes.length) return meshes;
  126. }
  127. }
  128. if (typeof gltf.getMeshes === 'function') {
  129. const meshes = gltf.getMeshes() || [];
  130. const meshNames = meshes.map(m => m && (m.name || m.el && m.el.name)).filter(Boolean);
  131. console.warn('sun mesh not found, available mesh names:', meshNames);
  132. } else {
  133. console.warn('sun mesh not found');
  134. }
  135. return [];
  136. },
  137. getSunNameCandidates() {
  138. const defaultCandidates = ['Sun', 'sun', 'SUN', '太阳', 'Taiyang', 'taiyang'];
  139. if (!this.scene) return defaultCandidates;
  140. const gltfAsset = this.scene.assets && this.scene.assets.getAsset && this.scene.assets.getAsset('gltf', 'bg');
  141. const jsonRaw = gltfAsset && gltfAsset.jsonRaw;
  142. if (!jsonRaw) return defaultCandidates;
  143. const names = [];
  144. const addIfMatch = (n) => {
  145. if (!n || typeof n !== 'string') return;
  146. if (/Sun/i.test(n) || n.includes('太阳')) names.push(n);
  147. };
  148. if (Array.isArray(jsonRaw.nodes)) {
  149. for (const node of jsonRaw.nodes) addIfMatch(node && node.name);
  150. }
  151. if (Array.isArray(jsonRaw.meshes)) {
  152. for (const mesh of jsonRaw.meshes) addIfMatch(mesh && mesh.name);
  153. }
  154. return names.length ? names.concat(defaultCandidates) : defaultCandidates;
  155. },
  156. startSunRotation() {
  157. if (!this.scene) return;
  158. if (!this.sunShouldRun) return;
  159. if (!this.sunMeshes || !this.sunMeshes.length) return;
  160. if (this.sunRotationTimer) return;
  161. this.sunFrameCount = 159;
  162. this.sunFrameIndex = this.sunFrameIndex || 1;
  163. this.sunTextureCacheLimit = 12;
  164. if (!this.sunTextureCache) this.sunTextureCache = new Map();
  165. if (!this.sunTextureQueue) this.sunTextureQueue = [];
  166. this.updateSunFrame();
  167. this.sunRotationTimer = setInterval(() => this.updateSunFrame(), 80);
  168. },
  169. stopSunRotation() {
  170. if (this.sunRotationTimer) {
  171. clearInterval(this.sunRotationTimer);
  172. this.sunRotationTimer = null;
  173. }
  174. this.sunFrameLoading = false;
  175. if (this.scene && this.scene.assets && this.scene.assets.releaseAsset && this.sunTextureQueue) {
  176. for (const assetId of this.sunTextureQueue) {
  177. this.scene.assets.releaseAsset('texture', assetId);
  178. }
  179. }
  180. this.sunTextureCache = null;
  181. this.sunTextureQueue = null;
  182. },
  183. async updateSunFrame() {
  184. if (this.sunFrameLoading) return;
  185. if (!this.scene || !this.sunMeshes || !this.sunMeshes.length) return;
  186. if (!this.sunShouldRun) return;
  187. this.sunFrameLoading = true;
  188. try {
  189. const frame = this.sunFrameIndex;
  190. this.sunFrameIndex = frame >= this.sunFrameCount ? 1 : frame + 1;
  191. const frameStr = String(frame).padStart(3, '0');
  192. const assetId = `sun-${frameStr}`;
  193. const src = `https://ossxiaoan.4dage.com/hq-eduction-vr/sun/Sun_${frameStr}.png`;
  194. let texture = this.sunTextureCache && this.sunTextureCache.get(assetId);
  195. if (!texture) {
  196. const result = await this.scene.assets.loadAsset({ type: 'texture', assetId, src });
  197. texture = result && result.value;
  198. if (texture) {
  199. if (this.sunTextureCache) this.sunTextureCache.set(assetId, texture);
  200. if (this.sunTextureQueue) this.sunTextureQueue.push(assetId);
  201. while (this.sunTextureQueue && this.sunTextureQueue.length > this.sunTextureCacheLimit) {
  202. const oldAssetId = this.sunTextureQueue.shift();
  203. if (oldAssetId) {
  204. this.scene.assets.releaseAsset('texture', oldAssetId);
  205. if (this.sunTextureCache) this.sunTextureCache.delete(oldAssetId);
  206. }
  207. }
  208. }
  209. }
  210. if (texture) {
  211. for (const mesh of this.sunMeshes) {
  212. if (mesh && mesh.material && typeof mesh.material.setTexture === 'function') {
  213. mesh.material.setTexture('u_baseColorMap', texture);
  214. }
  215. }
  216. }
  217. } catch (e) {
  218. console.warn('updateSunFrame failed', e);
  219. } finally {
  220. this.sunFrameLoading = false;
  221. }
  222. },
  223. resolveTigerMeshesFromBG(gltf) {
  224. if (!gltf || !this.scene) return [];
  225. const candidates = this.getTigerNameCandidates();
  226. const uniqueCandidates = [];
  227. for (const name of candidates) {
  228. if (name && !uniqueCandidates.includes(name)) uniqueCandidates.push(name);
  229. }
  230. for (const name of uniqueCandidates) {
  231. if (typeof gltf.getPrimitivesByNodeName === 'function') {
  232. const meshes = gltf.getPrimitivesByNodeName(name);
  233. if (meshes && meshes.length) return meshes;
  234. }
  235. if (typeof gltf.getPrimitivesByMeshName === 'function') {
  236. const meshes = gltf.getPrimitivesByMeshName(name);
  237. if (meshes && meshes.length) return meshes;
  238. }
  239. }
  240. if (typeof gltf.getMeshes === 'function') {
  241. const meshes = gltf.getMeshes() || [];
  242. const meshNames = meshes.map(m => m && (m.name || m.el && m.el.name)).filter(Boolean);
  243. console.warn('tiger mesh not found, available mesh names:', meshNames);
  244. } else {
  245. console.warn('tiger mesh not found');
  246. }
  247. return [];
  248. },
  249. getTigerNameCandidates() {
  250. const defaultCandidates = ['Tiger', 'tiger', 'TIGER', '老虎', '虎', 'Laohu', 'laohu', 'hu'];
  251. if (!this.scene) return defaultCandidates;
  252. const gltfAsset = this.scene.assets && this.scene.assets.getAsset && this.scene.assets.getAsset('gltf', 'bg');
  253. const jsonRaw = gltfAsset && gltfAsset.jsonRaw;
  254. if (!jsonRaw) return defaultCandidates;
  255. const names = [];
  256. const addIfMatch = (n) => {
  257. if (!n || typeof n !== 'string') return;
  258. if (/tiger/i.test(n) || /laohu/i.test(n) || n.includes('虎')) names.push(n);
  259. };
  260. if (Array.isArray(jsonRaw.nodes)) {
  261. for (const node of jsonRaw.nodes) addIfMatch(node && node.name);
  262. }
  263. if (Array.isArray(jsonRaw.meshes)) {
  264. for (const mesh of jsonRaw.meshes) addIfMatch(mesh && mesh.name);
  265. }
  266. return names.length ? names.concat(defaultCandidates) : defaultCandidates;
  267. },
  268. scheduleTigerStart() {
  269. if (!this.scene) return;
  270. if (!this.tigerShouldRun) return;
  271. if (!this.tigerStartRequested) return;
  272. if (this.tigerCompleted) return;
  273. if (this.tigerRotationTimer) return;
  274. if (!this.tigerMeshes || !this.tigerMeshes.length) return;
  275. if (this.tigerAppearTimer) return;
  276. this.tigerAppearTimer = setInterval(() => {
  277. if (!this.tigerShouldRun || this.tigerCompleted) {
  278. this.stopTigerAppearWatch();
  279. return;
  280. }
  281. const visibleMeshes = this.getVisibleTigerMeshes();
  282. if (!visibleMeshes.length) return;
  283. this.stopTigerAppearWatch();
  284. this.tigerTargetMeshes = visibleMeshes;
  285. this.startTigerAudio();
  286. this.startTigerAnimation();
  287. }, 100);
  288. },
  289. stopTigerAppearWatch() {
  290. if (!this.tigerAppearTimer) return;
  291. clearInterval(this.tigerAppearTimer);
  292. this.tigerAppearTimer = null;
  293. },
  294. isTigerVisible() {
  295. return this.getVisibleTigerMeshes().length > 0;
  296. },
  297. getVisibleTigerMeshes() {
  298. if (!this.tigerMeshes || !this.tigerMeshes.length) return [];
  299. const visible = [];
  300. for (const mesh of this.tigerMeshes) {
  301. const el = mesh && mesh.el;
  302. if (!el || typeof el.getComponent !== 'function') continue;
  303. try {
  304. const transform = el.getComponent('transform');
  305. const scale = transform && transform.scale;
  306. if (!scale) continue;
  307. if (typeof scale === 'string') {
  308. const parts = scale.split(/\s+/).map(Number);
  309. const maxV = Math.max(...parts.filter(n => Number.isFinite(n)));
  310. if (Number.isFinite(maxV) && maxV > 0.001) visible.push(mesh);
  311. continue;
  312. }
  313. if (typeof scale === 'object') {
  314. const sx = typeof scale.x === 'number' ? scale.x : undefined;
  315. const sy = typeof scale.y === 'number' ? scale.y : undefined;
  316. const sz = typeof scale.z === 'number' ? scale.z : undefined;
  317. const maxV = Math.max(sx || 0, sy || 0, sz || 0);
  318. if (maxV > 0.001) visible.push(mesh);
  319. }
  320. } catch (e) {
  321. }
  322. }
  323. return visible;
  324. },
  325. startTigerAnimation() {
  326. if (!this.scene) return;
  327. if (!this.tigerShouldRun) return;
  328. if (!this.tigerMeshes || !this.tigerMeshes.length) return;
  329. if (this.tigerCompleted) return;
  330. if (this.tigerRotationTimer) return;
  331. this.tigerFrameStart = 1;
  332. this.tigerFrameEnd = 460;
  333. this.tigerFrameCount = 460;
  334. const skipFrames = 29;
  335. this.tigerPlayedFrames = Math.min(skipFrames, this.tigerFrameCount);
  336. this.tigerTextureCacheLimit = 48;
  337. if (!this.tigerTextureCache) this.tigerTextureCache = new Map();
  338. if (!this.tigerTextureQueue) this.tigerTextureQueue = [];
  339. this.prepareTigerFrames();
  340. this.updateTigerFrame();
  341. const intervalMs = this.getTigerFrameIntervalMs();
  342. this.tigerRotationTimer = setInterval(() => this.updateTigerFrame(), intervalMs);
  343. },
  344. getTigerFrameIntervalMs() {
  345. const durationMs = Number.isFinite(this.tigerAudioDurationMs) && this.tigerAudioDurationMs > 0
  346. ? this.tigerAudioDurationMs
  347. : 46000;
  348. const frameCount = typeof this.tigerFrameCount === 'number' && this.tigerFrameCount > 0 ? this.tigerFrameCount : 460;
  349. const raw = Math.round(durationMs / frameCount);
  350. return Math.min(500, Math.max(16, raw));
  351. },
  352. stopTiger() {
  353. this.stopTigerAppearWatch();
  354. if (this.tigerRotationTimer) {
  355. clearInterval(this.tigerRotationTimer);
  356. this.tigerRotationTimer = null;
  357. }
  358. this.tigerFrameLoading = false;
  359. this.tigerTargetMeshes = null;
  360. if (this.scene && this.scene.assets && this.scene.assets.releaseAsset && this.tigerTextureQueue) {
  361. for (const assetId of this.tigerTextureQueue) {
  362. if (assetId === this.tigerLastAssetId) continue;
  363. this.scene.assets.releaseAsset('texture', assetId);
  364. }
  365. }
  366. this.tigerTextureCache = null;
  367. this.tigerTextureQueue = null;
  368. this.stopTigerAudio();
  369. this.stopTigerFramesPreload();
  370. },
  371. prepareTigerFrames() {
  372. if (this.tigerFramesPrepared || this.tigerFramesPreparing) return;
  373. this.tigerFramesPreparing = true;
  374. this.tigerFramesAbort = false;
  375. if (!this.tigerFrameTempPaths) this.tigerFrameTempPaths = Object.create(null);
  376. if (!this.tigerFrameDownloadTasks) this.tigerFrameDownloadTasks = Object.create(null);
  377. const startNo = 39;
  378. const endNo = 460;
  379. const maxConcurrent = 6;
  380. let nextNo = startNo;
  381. let inFlight = 0;
  382. const pump = () => {
  383. if (this.tigerFramesAbort) return;
  384. while (inFlight < maxConcurrent && nextNo <= endNo && !this.tigerFramesAbort) {
  385. const frameNo = nextNo++;
  386. const frameStr = String(frameNo).padStart(3, '0');
  387. const assetId = `tigerx-${frameStr}`;
  388. if (this.tigerFrameTempPaths[assetId]) continue;
  389. inFlight += 1;
  390. const task = wx.downloadFile({
  391. url: `https://ossxiaoan.4dage.com/hq-eduction-vr/tiger/Tigerx_${frameStr}.png`,
  392. success: (res) => {
  393. if (this.tigerFramesAbort) return;
  394. const tempPath = res && res.statusCode === 200 ? res.tempFilePath : '';
  395. if (tempPath) this.tigerFrameTempPaths[assetId] = tempPath;
  396. },
  397. complete: () => {
  398. inFlight -= 1;
  399. if (this.tigerFrameDownloadTasks) delete this.tigerFrameDownloadTasks[assetId];
  400. if (nextNo > endNo && inFlight <= 0) {
  401. this.tigerFramesPrepared = true;
  402. this.tigerFramesPreparing = false;
  403. return;
  404. }
  405. pump();
  406. }
  407. });
  408. if (task && typeof task.abort === 'function') this.tigerFrameDownloadTasks[assetId] = task;
  409. }
  410. };
  411. pump();
  412. },
  413. stopTigerFramesPreload() {
  414. this.tigerFramesAbort = true;
  415. this.tigerFramesPrepared = false;
  416. this.tigerFramesPreparing = false;
  417. if (this.tigerFrameDownloadTasks) {
  418. for (const k of Object.keys(this.tigerFrameDownloadTasks)) {
  419. const task = this.tigerFrameDownloadTasks[k];
  420. if (task && typeof task.abort === 'function') {
  421. try {
  422. task.abort();
  423. } catch (e) {
  424. }
  425. }
  426. }
  427. }
  428. this.tigerFrameDownloadTasks = null;
  429. this.tigerFrameTempPaths = null;
  430. },
  431. async updateTigerFrame() {
  432. if (this.tigerFrameLoading) return;
  433. if (!this.scene || !this.tigerMeshes || !this.tigerMeshes.length) return;
  434. if (!this.tigerShouldRun) return;
  435. if (this.tigerCompleted) return;
  436. this.tigerFrameLoading = true;
  437. try {
  438. const frameCount = typeof this.tigerFrameCount === 'number' && this.tigerFrameCount > 0 ? this.tigerFrameCount : 460;
  439. const durationMs = Number.isFinite(this.tigerAudioDurationMs) && this.tigerAudioDurationMs > 0
  440. ? this.tigerAudioDurationMs
  441. : 46000;
  442. const played = this.tigerPlayedFrames || 0;
  443. let desiredPlayed = played + 1;
  444. const audioCtx = this.tigerAudioCtx;
  445. if (audioCtx && this.tigerAudioStarted) {
  446. const currentTime = Number(audioCtx.currentTime);
  447. if (Number.isFinite(currentTime) && currentTime >= 0) {
  448. const p = Math.floor((currentTime * 1000 / durationMs) * frameCount) + 1;
  449. desiredPlayed = Math.max(desiredPlayed, p);
  450. }
  451. }
  452. if (desiredPlayed <= played) return;
  453. if (desiredPlayed >= frameCount) desiredPlayed = frameCount;
  454. if (played >= frameCount) {
  455. this.completeTigerAnimation();
  456. return;
  457. }
  458. const start = typeof this.tigerFrameStart === 'number' ? this.tigerFrameStart : 1;
  459. const end = typeof this.tigerFrameEnd === 'number' ? this.tigerFrameEnd : frameCount;
  460. const rangeLen = Math.max(1, end - start + 1);
  461. const frameNo = start + ((desiredPlayed - 1) % rangeLen);
  462. this.tigerPlayedFrames = desiredPlayed;
  463. const frameStr = String(frameNo).padStart(3, '0');
  464. const assetId = `tigerx-${frameStr}`;
  465. const localSrc = this.tigerFrameTempPaths && this.tigerFrameTempPaths[assetId];
  466. const src = localSrc || `https://ossxiaoan.4dage.com/hq-eduction-vr/tiger/Tigerx_${frameStr}.png`;
  467. let texture = this.tigerTextureCache && this.tigerTextureCache.get(assetId);
  468. if (!texture) {
  469. const result = await this.scene.assets.loadAsset({ type: 'texture', assetId, src });
  470. texture = result && result.value;
  471. if (texture) {
  472. if (this.tigerTextureCache) this.tigerTextureCache.set(assetId, texture);
  473. if (this.tigerTextureQueue) this.tigerTextureQueue.push(assetId);
  474. while (this.tigerTextureQueue && this.tigerTextureQueue.length > this.tigerTextureCacheLimit) {
  475. const oldAssetId = this.tigerTextureQueue.shift();
  476. if (oldAssetId) {
  477. this.scene.assets.releaseAsset('texture', oldAssetId);
  478. if (this.tigerTextureCache) this.tigerTextureCache.delete(oldAssetId);
  479. }
  480. }
  481. }
  482. }
  483. if (texture) {
  484. this.tigerLastAssetId = assetId;
  485. const targets = (this.tigerTargetMeshes && this.tigerTargetMeshes.length) ? this.tigerTargetMeshes : this.tigerMeshes;
  486. for (const mesh of targets) {
  487. if (mesh && mesh.material && typeof mesh.material.setTexture === 'function') {
  488. mesh.material.setTexture('u_baseColorMap', texture);
  489. }
  490. }
  491. }
  492. if (this.tigerPlayedFrames >= this.tigerFrameCount) {
  493. this.completeTigerAnimation();
  494. }
  495. } catch (e) {
  496. console.warn('updateTigerFrame failed', e);
  497. } finally {
  498. this.tigerFrameLoading = false;
  499. }
  500. },
  501. completeTigerAnimation() {
  502. if (this.tigerRotationTimer) {
  503. clearInterval(this.tigerRotationTimer);
  504. this.tigerRotationTimer = null;
  505. }
  506. this.tigerCompleted = true;
  507. },
  508. prepareTigerAudio() {
  509. if (this.tigerAudioCtx || this.tigerAudioPreparing) return;
  510. const url = 'https://ossxiaoan.4dage.com/hq-eduction-vr/tiger/tiger.mp3';
  511. this.tigerAudioPrepared = false;
  512. this.tigerAudioStartPending = !!this.tigerAudioStartPending;
  513. this.tigerAudioStarted = false;
  514. this.tigerAudioPreparing = true;
  515. const createAudioCtx = (src) => {
  516. const audioCtx = wx.createInnerAudioContext();
  517. audioCtx.src = src;
  518. audioCtx.loop = false;
  519. audioCtx.volume = 0;
  520. audioCtx.onCanplay(() => {
  521. if (this.tigerAudioPrepared) return;
  522. this.tigerAudioPrepared = true;
  523. if (this.tigerAudioStarted) return;
  524. const updateDuration = () => {
  525. if (!this.tigerAudioCtx) return;
  526. const d = Number(audioCtx.duration);
  527. if (Number.isFinite(d) && d > 0) this.tigerAudioDurationMs = Math.round(d * 1000);
  528. };
  529. updateDuration();
  530. setTimeout(updateDuration, 300);
  531. try {
  532. audioCtx.volume = 0;
  533. } catch (e) {
  534. }
  535. try {
  536. audioCtx.play();
  537. } catch (e) {
  538. }
  539. setTimeout(() => {
  540. if (!this.tigerAudioCtx) return;
  541. if (this.tigerAudioStarted) return;
  542. try {
  543. audioCtx.pause();
  544. } catch (e) {
  545. }
  546. try {
  547. if (Number(audioCtx.currentTime) > 0.1) audioCtx.seek(0);
  548. } catch (e) {
  549. }
  550. if (this.tigerAudioStartPending) this.startTigerAudio();
  551. }, 200);
  552. });
  553. audioCtx.onEnded(() => {
  554. this.stopTigerAudio();
  555. this.completeTigerAnimation();
  556. });
  557. this.tigerAudioCtx = audioCtx;
  558. this.tigerAudioPreparing = false;
  559. try {
  560. audioCtx.play();
  561. } catch (e) {
  562. }
  563. };
  564. if (this.tigerAudioTempPath) {
  565. createAudioCtx(this.tigerAudioTempPath);
  566. return;
  567. }
  568. wx.downloadFile({
  569. url,
  570. success: (res) => {
  571. const tempPath = res && res.statusCode === 200 ? res.tempFilePath : '';
  572. if (tempPath) this.tigerAudioTempPath = tempPath;
  573. createAudioCtx(tempPath || url);
  574. },
  575. fail: () => {
  576. createAudioCtx(url);
  577. }
  578. });
  579. },
  580. startTigerAudio() {
  581. if (this.tigerAudioStarted) return;
  582. if (!this.tigerAudioCtx) {
  583. this.tigerAudioStartPending = true;
  584. this.prepareTigerAudio();
  585. }
  586. if (!this.tigerAudioCtx) return;
  587. if (this.tigerAudioStarted) return;
  588. const audioCtx = this.tigerAudioCtx;
  589. const requestedOffsetSec = typeof this.tigerAudioStartOffsetSec === 'number' ? this.tigerAudioStartOffsetSec : 2;
  590. const startOffsetSec = Math.max(0, Math.min(3, requestedOffsetSec));
  591. const startNow = () => {
  592. if (!this.tigerAudioCtx) return;
  593. if (this.tigerAudioStarted) return;
  594. try {
  595. audioCtx.volume = 1;
  596. } catch (e) {
  597. }
  598. try {
  599. const t = Number(audioCtx.currentTime);
  600. if (startOffsetSec > 0) {
  601. if (!Number.isFinite(t) || Math.abs(t - startOffsetSec) > 0.2) audioCtx.seek(startOffsetSec);
  602. } else {
  603. if (Number.isFinite(t) && t > 0.1) audioCtx.seek(0);
  604. }
  605. } catch (e) {
  606. }
  607. try {
  608. audioCtx.play();
  609. this.tigerAudioStarted = true;
  610. } catch (e) {
  611. this.tigerAudioStarted = false;
  612. }
  613. };
  614. if (this.tigerAudioPrepared) {
  615. this.tigerAudioStartPending = false;
  616. startNow();
  617. return;
  618. }
  619. this.tigerAudioStartPending = true;
  620. startNow();
  621. },
  622. stopTigerAudio() {
  623. if (!this.tigerAudioCtx) return;
  624. this.tigerAudioCtx.stop();
  625. this.tigerAudioCtx.destroy();
  626. this.tigerAudioCtx = null;
  627. this.tigerAudioPrepared = false;
  628. this.tigerAudioStartPending = false;
  629. this.tigerAudioStarted = false;
  630. this.tigerAudioPreparing = false;
  631. this.tigerAudioDurationMs = null;
  632. },
  633. playitem1Action() {
  634. if (!this.animator1 || !this.animator2) return;
  635. console.log('start animator1');
  636. this.animator1.play('All Animations', {
  637. loop: 0
  638. });
  639. setTimeout(() => {
  640. console.log('start animator2');
  641. this.animator2.pause();
  642. const duration = 1500;
  643. const startTime = Date.now();
  644. const startScale = 0;
  645. const endScale = 0.38;
  646. const startPX = -0.1
  647. const endPX = 0
  648. const startPY = -0.5
  649. const endPY = 0
  650. const startPZ = -0.05
  651. const endPZ = 0
  652. const step = () => {
  653. const now = Date.now();
  654. let t = (now - startTime) / duration;
  655. if (t > 1) t = 1;
  656. const s = startScale + (endScale - startScale) * t;
  657. const scaleStr = `${s} ${s} ${s}`;
  658. const p = `${startPX + (endPX - startPX) * t} ${startPY + (endPY - startPY) * t} ${startPZ + (endPZ - startPZ) * t}`;
  659. if (t >= 0.5 && !this.isStartPlay2) {
  660. this.isStartPlay2 = true;
  661. this.animator2.play('All Animations', {
  662. loop: 0
  663. });
  664. }
  665. this.setData({
  666. adlScale: scaleStr,
  667. adlPos: p
  668. });
  669. if (t < 1) {
  670. setTimeout(step, 16); // 60fps
  671. } else {
  672. const audioUrl = 'https://houseoss.4dkankan.com/project/hq-eduction-vr/public/%E5%AE%89%E5%BE%B7%E7%83%88.mp3';
  673. if (!this.audioCtx) {
  674. const audioCtx = wx.createInnerAudioContext();
  675. audioCtx.src = audioUrl;
  676. audioCtx.play();
  677. audioCtx.onEnded(() => {
  678. const hideDuration = 1000;
  679. const hideStartTime = Date.now();
  680. const startScaleHide = 0.38;
  681. const endScaleHide = 0;
  682. const hideStep = () => {
  683. const now = Date.now();
  684. let t = (now - hideStartTime) / hideDuration;
  685. if (t > 1) t = 1;
  686. const s = startScaleHide + (endScaleHide - startScaleHide) * t;
  687. const scaleStr = `${s} ${s} ${s}`;
  688. this.setData({
  689. adlScale: scaleStr
  690. });
  691. if (t < 1) {
  692. setTimeout(hideStep, 16);
  693. } else {
  694. this.animator2 = null;
  695. if (this.audioCtx) {
  696. this.audioCtx.destroy();
  697. this.audioCtx = null;
  698. }
  699. this.tigerStartRequested = true;
  700. this.scheduleTigerStart();
  701. }
  702. };
  703. hideStep();
  704. });
  705. }
  706. }
  707. };
  708. this.setData({
  709. adlScale: '0 0 0'
  710. });
  711. step();
  712. }, 1500);
  713. }
  714. }
  715. })