index.js 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258
  1. const info = wx.getSystemInfoSync();
  2. const dpi = info.pixelRatio;
  3. const width = info.windowWidth * dpi;
  4. const height = info.windowHeight * dpi;
  5. import CONFIG from './config.js';
  6. const ROOT_DURATIONS = [
  7. 3000, 4500, 6000, Infinity
  8. ];
  9. const ROOT_DELAYS = [
  10. 2000, 1500, 1000, 0
  11. ];
  12. const ROOT_AMBIENTS = [
  13. 0.05, 0.15, 0.4, 1
  14. ];
  15. const ROOT_BLURS = [
  16. 32, 32, 16, 0
  17. ];
  18. const REAL_BLURS = [
  19. 0, 32, 48, 96
  20. ];
  21. function showFinalChoose(content, callback) {
  22. wx.showModal({
  23. title: '做出选择',
  24. content,
  25. showCancel: true,
  26. confirmText: '正视现实',
  27. cancelText: '我不要!',
  28. success: (res) => {
  29. if (res.cancel) {
  30. showFinalChoose('不能逃避!', callback);
  31. } else {
  32. callback();
  33. setTimeout(() => {
  34. wx.showToast({
  35. title: '勇敢一些\n不要怯懦',
  36. duration: 5000,
  37. icon: 'none'
  38. })
  39. }, 500);
  40. }
  41. }
  42. });
  43. }
  44. Component({
  45. scene: null,
  46. properties: {
  47. nextAction: {
  48. type: String,
  49. value: '',
  50. observer(fromWhat, old) {
  51. if (fromWhat === 'item') {
  52. this.remainItems -= 1;
  53. if (this.remainItems === 0) {
  54. this.disable3DTouch = false;
  55. this.setData({subStep: true});
  56. return;
  57. }
  58. this.switchSide(false);
  59. this.triggerEvent('requireLight', {state: 'idle'});
  60. return;
  61. }
  62. if (fromWhat === 'char') {
  63. const {texts} = this.config.steps[this.data.step];
  64. this.requireDialog({texts, from: 'step'});
  65. return;
  66. }
  67. if (fromWhat === 'step' || fromWhat === 'intro') {
  68. const step = this.data.step + 1;
  69. if (step === 4) {
  70. wx.showModal({
  71. title: '最后的信',
  72. content: this.config.letter.texts.join('\n'),
  73. showCancel: false,
  74. confirmText: '做出选择',
  75. complete: () => {
  76. showFinalChoose('', () => this.switchSide(false, 0));
  77. }
  78. });
  79. return;
  80. }
  81. if (step === 3) {
  82. this.bgm.stop();
  83. this.bgm2.play();
  84. }
  85. this.remainItems = this.config.steps[step].itemCount;
  86. this.setData({step, subStep: false, ambient: ROOT_AMBIENTS[step]});
  87. this.switchSide(false);
  88. this.triggerEvent('requireLight', {state: 'idle'});
  89. return;
  90. }
  91. if (fromWhat === 'light') {
  92. this.switchSide(true);
  93. this.disable3DTouch = false;
  94. this.lightDuration = ROOT_DURATIONS[this.data.step];
  95. this.lightDelay = ROOT_DELAYS[this.data.step];
  96. }
  97. }
  98. }
  99. },
  100. data: {
  101. step: -1,
  102. subStep: false,
  103. ambient: 0,
  104. width: width,
  105. height: height,
  106. loaded: false,
  107. arReady: false
  108. },
  109. lifetimes: {
  110. detached() {
  111. this.bgm.stop();
  112. this.bgm2.stop();
  113. wx.setKeepScreenOn({keepScreenOn: false});
  114. }
  115. },
  116. methods: {
  117. handleReady({detail}) {
  118. this.scene = detail.value;
  119. this.scene.event.add('tick', this.handleTick.bind(this));
  120. this.inited = false;
  121. this.disable3DTouch = false;
  122. this.remainItems = 0;
  123. this.lightDuration = 0;
  124. this.lightDelay = 0;
  125. this.bgm = wx.createInnerAudioContext({});
  126. this.bgm.src = 'https://mmbizwxaminiprogram-1258344707.cos.ap-guangzhou.myqcloud.com/xr-frame/demo/beside-edge/bgm1.mp3';
  127. this.bgm.loop = true;
  128. this.bgm2 = wx.createInnerAudioContext({});
  129. this.bgm2.src = 'https://mmbizwxaminiprogram-1258344707.cos.ap-guangzhou.myqcloud.com/xr-frame/demo/beside-edge/bgm2.mp3';
  130. this.bgm2.loop = true;
  131. },
  132. handleARReady() {
  133. wx.setKeepScreenOn({keepScreenOn: true});
  134. this.setData({arReady: true});
  135. },
  136. handleAssetsLoaded: function ({detail}) {
  137. console.log('assets loaded', detail.value);
  138. this.config = CONFIG;
  139. this.setData({loaded: true});
  140. },
  141. handleTick(dt) {
  142. const mainCamera = this.scene.getNodeById('main-camera');
  143. const light = this.scene.getNodeById('light');
  144. if (mainCamera && light) {
  145. light.position.set(mainCamera.position);
  146. light.rotation.set(mainCamera.rotation);
  147. light.rotation.x = Math.PI - light.rotation.x;
  148. light.rotation.y += Math.PI;
  149. }
  150. if (this.inited) {
  151. const nextDuration = Math.max(this.lightDuration - dt, 0);
  152. if (this.lightDuration > 0 && nextDuration === 0) {
  153. this.disable3DTouch = true;
  154. this.switchSide(false);
  155. this.triggerEvent('requireLight', {state: 'cd', wait: 1});
  156. } else if (this.lightDuration === 0) {
  157. const nextDelay = Math.max(this.lightDelay - dt, 0);
  158. if (this.lightDelay > 0 && nextDelay === 0) {
  159. this.triggerEvent('requireLight', {state: 'idle'});
  160. } else if (this.lightDelay > 0) {
  161. this.triggerEvent('requireLight', {state: 'cd', wait: nextDelay / ROOT_DELAYS[this.data.step]});
  162. }
  163. this.lightDelay = nextDelay;
  164. }
  165. this.lightDuration = nextDuration;
  166. return;
  167. }
  168. if (!this.data.arReady || !this.data.loaded) {
  169. return;
  170. }
  171. const setItem = this.scene.getNodeById('setitem');
  172. setItem.position.set(mainCamera.position);
  173. setItem.position.y = 1;
  174. this.inited = true;
  175. setTimeout(() => {
  176. this.switchSide(true, ROOT_BLURS[0]);
  177. const {texts} = this.config.intro;
  178. this.requireDialog({texts, from: 'intro'});
  179. this.bgm.play();
  180. }, 1000);
  181. },
  182. switchSide(virtual, blur) {
  183. const setItem = this.scene.getNodeById('setitem');
  184. setItem.visible = virtual;
  185. const blurAsset = this.scene.assets.getAsset('post-process', 'blur');
  186. if (virtual) {
  187. blurAsset.data.radius = blur === undefined ? ROOT_BLURS[this.data.step] : blur;
  188. this.triggerEvent('requireLight', {state: 'hide'});
  189. } else {
  190. blurAsset.data.radius = blur === undefined ? REAL_BLURS[this.data.step] : blur;
  191. }
  192. },
  193. handleResume() {
  194. if (this.inited) {
  195. this.data.step === 3 ? this.bgm2.play() : this.bgm2.play();
  196. }
  197. },
  198. handleTouchObj({detail}) {
  199. if (!this.inDistance(detail)) {
  200. return;
  201. }
  202. const id = detail.value.target.id;
  203. const {texts} = this.config.items[id];
  204. this.requireDialog({texts, from: 'item'});
  205. },
  206. handleTouchChar({detail}) {
  207. if (!this.inDistance(detail)) {
  208. return;
  209. }
  210. const id = this.data.step === 3 ? 'final' : detail.value.target.id;
  211. const {name, texts} = this.config.chars[id];
  212. this.requireDialog({name, texts, from: 'char'});
  213. },
  214. requireDialog(info) {
  215. this.disable3DTouch = true;
  216. this.lightDuration = Infinity;
  217. this.triggerEvent('requireLight', {state: 'hide'});
  218. info.name = info.name || '某个声音';
  219. this.triggerEvent('requireDialog', info);
  220. },
  221. inDistance(detail) {
  222. if (detail.value.camera.el.id !== 'main-camera') {
  223. return false;
  224. }
  225. if (this.disable3DTouch) {
  226. return false;
  227. }
  228. const xrSystem = wx.getXrFrameSystem();
  229. const {camera, target} = detail.value;
  230. const camTrs = camera.el.getComponent(xrSystem.Transform);
  231. const targetTrs = target.getComponent(xrSystem.Transform);
  232. const diff = camTrs.worldPosition.sub(targetTrs.worldPosition);
  233. return Math.sqrt(diff.x * diff.x + diff.z * diff.z) < 3;
  234. }
  235. }
  236. })