Ver código fonte

第一单元

lanxin 1 mês atrás
commit
687ffc9538
100 arquivos alterados com 38735 adições e 0 exclusões
  1. 12 0
      .editorconfig
  2. 23 0
      .gitignore
  3. 2 0
      .npmrc
  4. 11 0
      .prettierrc.js
  5. 3 0
      README.md
  6. 10 0
      config-overrides.js
  7. 30435 0
      package-lock.json
  8. 64 0
      package.json
  9. 8 0
      path.tsconfig.json
  10. BIN
      public/favicon.ico
  11. 41 0
      public/index.html
  12. 7 0
      public/krpano.js
  13. 80 0
      public/myData/f-video.js
  14. 4572 0
      public/myData/hot.js
  15. 1 0
      public/myData/jsmpeg.min.js
  16. 128 0
      public/myData/myData.js
  17. BIN
      public/myData/play.ts
  18. BIN
      public/myData/qfk.TTF
  19. 5 0
      public/plugins/bingmaps.js
  20. 516 0
      public/plugins/combobox.xml
  21. 48 0
      public/plugins/doubleclick_style.xml
  22. 28 0
      public/plugins/fps.xml
  23. 5 0
      public/plugins/googlemaps.js
  24. 5 0
      public/plugins/gyro2.js
  25. 5 0
      public/plugins/pp_blur.js
  26. 5 0
      public/plugins/pp_light.js
  27. 5 0
      public/plugins/pp_sharpen.js
  28. 19 0
      public/plugins/scrollarea.js
  29. 212 0
      public/plugins/showtext.xml
  30. 5 0
      public/plugins/soundinterface.js
  31. 5 0
      public/plugins/videoplayer.js
  32. 5 0
      public/plugins/webvr.js
  33. 942 0
      public/plugins/webvr.xml
  34. BIN
      public/plugins/webvr_handcursor.png
  35. BIN
      public/plugins/webvr_laser.png
  36. BIN
      public/plugins/webvr_light.png
  37. BIN
      public/plugins/webvr_vrcursor.png
  38. BIN
      public/skin/rotate_device.png
  39. 33 0
      public/skin/videointerface.xml
  40. BIN
      public/skin/vtourskin.png
  41. BIN
      public/skin/vtourskin_light.png
  42. 177 0
      src/App.tsx
  43. 124 0
      src/AppTemp.tsx
  44. BIN
      src/assets/img/A0_down.png
  45. BIN
      src/assets/img/A0_right.png
  46. BIN
      src/assets/img/A2_quanwen.png
  47. BIN
      src/assets/img/A2_wenwu.png
  48. BIN
      src/assets/img/A5_gesture.png
  49. BIN
      src/assets/img/A5_line.png
  50. BIN
      src/assets/img/A5_lineAc.png
  51. BIN
      src/assets/img/A5_reset.png
  52. BIN
      src/assets/img/A5_size.png
  53. BIN
      src/assets/img/A5_sizeAc.png
  54. BIN
      src/assets/img/A5_tapian.png
  55. BIN
      src/assets/img/A5_tapianAc.png
  56. BIN
      src/assets/img/A5_zoomin.png
  57. BIN
      src/assets/img/A5_zoomout.png
  58. BIN
      src/assets/img/IMGerror.png
  59. BIN
      src/assets/img/beie.png
  60. BIN
      src/assets/img/bg_base.png
  61. BIN
      src/assets/img/bg_home.png
  62. BIN
      src/assets/img/btn_ModalTxt_bg.png
  63. BIN
      src/assets/img/btn_ModalTxt_bg_ac.png
  64. BIN
      src/assets/img/btn_back.png
  65. BIN
      src/assets/img/btn_home.png
  66. BIN
      src/assets/img/btn_menu.png
  67. BIN
      src/assets/img/btn_tab.png
  68. BIN
      src/assets/img/btn_tabAc.png
  69. BIN
      src/assets/img/butterflyL.png
  70. BIN
      src/assets/img/butterflyR.png
  71. BIN
      src/assets/img/close.png
  72. BIN
      src/assets/img/closeWithTxt.png
  73. BIN
      src/assets/img/detailTxt_l.png
  74. BIN
      src/assets/img/detailTxt_r.png
  75. BIN
      src/assets/img/guideVideo.png
  76. BIN
      src/assets/img/highlight.png
  77. BIN
      src/assets/img/highlight_ac.png
  78. BIN
      src/assets/img/home.png
  79. BIN
      src/assets/img/icon_dot1.png
  80. BIN
      src/assets/img/icon_knowledge.png
  81. BIN
      src/assets/img/icon_paint.png
  82. BIN
      src/assets/img/icon_poster.png
  83. BIN
      src/assets/img/interaction.png
  84. BIN
      src/assets/img/interactionBg.png
  85. BIN
      src/assets/img/loading.gif
  86. BIN
      src/assets/img/menuSider.png
  87. BIN
      src/assets/img/menuSiderBtn.png
  88. BIN
      src/assets/img/tip.png
  89. BIN
      src/assets/img/titleLogo.png
  90. BIN
      src/assets/img/tooltipBg.png
  91. 294 0
      src/assets/styles/base.css
  92. 290 0
      src/assets/styles/base.less
  93. 21 0
      src/components/AsyncSpinLoding/index.module.scss
  94. 15 0
      src/components/AsyncSpinLoding/index.tsx
  95. 214 0
      src/components/BaseImg/index.module.scss
  96. 168 0
      src/components/BaseImg/index.tsx
  97. 82 0
      src/components/BaseVideo/index.module.scss
  98. 89 0
      src/components/BaseVideo/index.tsx
  99. 21 0
      src/components/BtnRight.tsx
  100. 0 0
      src/components/CatVideo/index.module.scss

+ 12 - 0
.editorconfig

@@ -0,0 +1,12 @@
+root = true # 控制配置文件 .editorconfig 是否生效的字段
+ 
+[**] # 匹配全部文件
+indent_style = space # 缩进风格,可选space|tab
+indent_size = 2 # 缩进的空格数
+charset = utf-8 # 设置字符集
+trim_trailing_whitespace = true # 删除一行中的前后空格
+insert_final_newline = true # 设为true表示使文件以一个空白行结尾
+end_of_line = lf
+ 
+[**.md] # 匹配md文件
+trim_trailing_whitespace = false

+ 23 - 0
.gitignore

@@ -0,0 +1,23 @@
+# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
+
+# dependencies
+/node_modules
+/.pnp
+.pnp.js
+
+# testing
+/coverage
+
+# production
+/build
+
+# misc
+.DS_Store
+.env.local
+.env.development.local
+.env.test.local
+.env.production.local
+
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*

+ 2 - 0
.npmrc

@@ -0,0 +1,2 @@
+registry=https://registry.npmmirror.com/
+@dage:registry=http://192.168.20.245:4873/

+ 11 - 0
.prettierrc.js

@@ -0,0 +1,11 @@
+module.exports = {
+  printWidth: 100, // 一行的字符数,如果超过会进行换行
+  tabWidth: 2, // 一个tab代表几个空格数,默认就是2
+  useTabs: false, // 是否启用tab取代空格符缩进,.editorconfig设置空格缩进,所以设置为false
+  semi: false, // 行尾是否使用分号,默认为true
+  singleQuote: true, // 字符串是否使用单引号
+  trailingComma: "none", // 对象或数组末尾是否添加逗号 none| es5| all
+  jsxSingleQuote: true, // 在jsx里是否使用单引号,你看着办
+  bracketSpacing: true, // 对象大括号直接是否有空格,默认为true,效果:{ foo: bar }
+  arrowParens: "avoid", // 箭头函数如果只有一个参数则省略括号
+};

+ 3 - 0
README.md

@@ -0,0 +1,3 @@
+1.使用 yarn。 npm 有问题
+
+2.开发时请修改 public/myData.js 的 baseUrlLoc(dev 的时候另起一个服务运行静态资源) 和 myBaseUrl

+ 10 - 0
config-overrides.js

@@ -0,0 +1,10 @@
+const path = require('path')
+const { override, addWebpackAlias } = require('customize-cra')
+
+// 添加 @ 别名
+const webpackAlias = addWebpackAlias({
+  '@': path.resolve(__dirname, 'src'),
+})
+
+// 导出要进行覆盖的 webpack 配置
+module.exports = override(webpackAlias)

Diferenças do arquivo suprimidas por serem muito extensas
+ 30435 - 0
package-lock.json


+ 64 - 0
package.json

@@ -0,0 +1,64 @@
+{
+  "name": "demo2",
+  "version": "0.1.0",
+  "private": true,
+  "dependencies": {
+    "@ant-design/cssinjs": "^1.5.6",
+    "@dage/krpano": "^2.4.1",
+    "@dage/utils": "^1.0.2",
+    "@testing-library/jest-dom": "^5.16.5",
+    "@testing-library/react": "^13.4.0",
+    "@testing-library/user-event": "^13.5.0",
+    "@types/jest": "^27.5.2",
+    "@types/node": "^16.18.3",
+    "@types/react": "^18.0.24",
+    "@types/react-dom": "^18.0.8",
+    "antd": "^5.8.3",
+    "antd-mobile": "^5.30.0",
+    "axios": "^1.7.9",
+    "react": "^18.2.0",
+    "react-dom": "^18.2.0",
+    "react-redux": "^8.0.4",
+    "react-router-dom": "5.3",
+    "react-scripts": "5.0.1",
+    "react-sortablejs": "^6.1.4",
+    "redux": "^4.2.0",
+    "redux-devtools-extension": "^2.13.9",
+    "redux-thunk": "^2.4.1",
+    "sass": "^1.55.0",
+    "typescript": "^4.8.4",
+    "vconsole": "^3.15.1",
+    "web-vitals": "^2.1.4"
+  },
+  "scripts": {
+    "dev": "react-app-rewired start",
+    "build": "react-app-rewired build",
+    "test": "react-app-rewired test",
+    "eject": "react-scripts eject"
+  },
+  "eslintConfig": {
+    "extends": [
+      "react-app",
+      "react-app/jest"
+    ]
+  },
+  "browserslist": {
+    "production": [
+      ">0.2%",
+      "not dead",
+      "not op_mini all"
+    ],
+    "development": [
+      "last 1 chrome version",
+      "last 1 firefox version",
+      "last 1 safari version"
+    ]
+  },
+  "devDependencies": {
+    "@types/history": "^5.0.0",
+    "@types/react-router-dom": "^5.3.3",
+    "customize-cra": "^1.0.0",
+    "react-app-rewired": "^2.2.1"
+  },
+  "homepage": "."
+}

+ 8 - 0
path.tsconfig.json

@@ -0,0 +1,8 @@
+{
+    "compilerOptions": {
+      "baseUrl": "./",
+      "paths": {
+        "@/*": ["src/*"]
+      }
+    }
+  }

BIN
public/favicon.ico


+ 41 - 0
public/index.html

@@ -0,0 +1,41 @@
+<!DOCTYPE html>
+<html lang="zh">
+  <head>
+    <meta charset="utf-8" />
+    <meta name="viewport" content="width=device-width, initial-scale=1" />
+    <meta name="theme-color" content="#000000" />
+    <meta name="description" content="Web site created using create-react-app" />
+    <link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
+    <link rel="shortcut icon" href="./favicon.ico" type="image/x-icon" />
+    <script src="./myData/myData.js"></script>
+    <script src="./myData/hot.js"></script>
+
+    <script src="./myData/jsmpeg.min.js"></script>
+    <script src="./myData/f-video.js"></script>
+
+    <style>
+      @font-face {
+        font-family: 'qfk';
+        src: url('./myData/qfk.TTF');
+      }
+    </style>
+    <title>河南-邀你至汉家</title>
+  </head>
+  <body>
+    <noscript>You need to enable JavaScript to run this app.</noscript>
+    <div id="root"></div>
+  </body>
+
+  <script src="./krpano.js"></script>
+
+  <!-- <script>
+    window.onload = function () {
+      var script = document.createElement('script')
+      script.src = 'https://cdn.bootcss.com/eruda/1.5.4/eruda.min.js'
+      document.body.appendChild(script)
+      script.onload = function () {
+        eruda.init()
+      }
+    }
+  </script> -->
+</html>

Diferenças do arquivo suprimidas por serem muito extensas
+ 7 - 0
public/krpano.js


+ 80 - 0
public/myData/f-video.js

@@ -0,0 +1,80 @@
+/* f-video.js */
+let F_Video
+;(function () {
+  F_Video = function (url, option) {
+    const u = window.navigator.userAgent.toLowerCase()
+    const isAndroid = u.indexOf('android') > -1
+    let player = new Object()
+
+    let newCanvas = document.createElement('canvas')
+    let params = {
+      canvas: newCanvas,
+      loop: option.loop || false,
+      autoplay: option.autoplay || false,
+      onEnded: () => {
+        option.onEnded && option.onEnded()
+        player.currentTime = 0
+      }
+    }
+
+    newCanvas.style.width = '100%'
+    newCanvas.style.height = '100%'
+    newCanvas.style.objectFit = option.objectFit || 'cover'
+
+    player = new JSMpeg.Player(url.replace('.mp4', '.ts'), {
+      ...option,
+      ...params
+    })
+    player.domElement = newCanvas
+
+    // if (isAndroid) {
+    //   let newCanvas = document.createElement("canvas");
+    //   let params = {
+    //     canvas: newCanvas,
+    //     loop: option.loop || false,
+    //     autoplay: option.autoplay || false,
+    //     onEnded: () => {
+    //       option.onEnded && option.onEnded();
+    //       player.currentTime = 0;
+    //     },
+    //   };
+
+    //   newCanvas.style.width = "100%";
+    //   newCanvas.style.height = "100%";
+    //   newCanvas.style.objectFit = option.objectFit || "cover";
+
+    //   player = new JSMpeg.Player(url.replace(".mp4", ".ts"), {
+    //     ...option,
+    //     ...params,
+    //   });
+    //   player.domElement = newCanvas;
+    // } else {
+    //   let newVideo = document.createElement("video");
+    //   newVideo.setAttribute("x5-video-player-type", "h5");
+    //   newVideo.setAttribute("x-webkit-airplay", "true");
+    //   newVideo.setAttribute("airplay", "allow");
+    //   newVideo.setAttribute("playsinline", "");
+    //   newVideo.setAttribute("webkit-playsinline", "");
+    //   newVideo.setAttribute("src", url);
+    //   option.loop && newVideo.setAttribute("loop", "loop");
+    //   !option.autoplay && newVideo.setAttribute("preload", "auto");
+    //   option.autoplay &&
+    //     window.WeixinJSBridge &&
+    //     window.WeixinJSBridge.invoke("getNetworkType", {}, (e) => {
+    //       player.play();
+    //     });
+
+    //   newVideo.style.width = "100%";
+    //   newVideo.style.height = "100%";
+    //   newVideo.style.objectFit = option.objectFit || "cover";
+
+    //   player = newVideo;
+    //   player.domElement = newVideo;
+
+    //   option.onPlay && player.addEventListener("play", option.onPlay);
+    //   option.onPause && player.addEventListener("pause", option.onPause);
+    //   option.onEnded && player.addEventListener("ended", option.onEnded);
+    // }
+    return player
+  }
+})()

Diferenças do arquivo suprimidas por serem muito extensas
+ 4572 - 0
public/myData/hot.js


Diferenças do arquivo suprimidas por serem muito extensas
+ 1 - 0
public/myData/jsmpeg.min.js


Diferenças do arquivo suprimidas por serem muito extensas
+ 128 - 0
public/myData/myData.js


BIN
public/myData/play.ts


BIN
public/myData/qfk.TTF


Diferenças do arquivo suprimidas por serem muito extensas
+ 5 - 0
public/plugins/bingmaps.js


+ 516 - 0
public/plugins/combobox.xml

@@ -0,0 +1,516 @@
+<krpano>
+
+	<!--
+		combobox.xml
+		krpano 1.21 
+		
+		https://krpano.com/plugins/combobox/
+
+		Combobox Plugin
+		- This plugin converts <combobox> elements in the current xml
+		  into <layer> container, scrollarea and textfield elements.
+		- Additionally it's also possible to add and remove combobox
+		  elements also dynamically.
+		- The full xml implementation allows many ways of customizing
+		  for own needs - custom designs/styles, custom functionality.
+
+
+		Syntax for Static XML Code:
+
+			<combobox name="..." design="..." ...any layer settings...>
+				<item name="..." caption="..." onclick="..." />
+				<item name="..." caption="..." onclick="..." />
+			</combobox>
+
+		Syntax for Dynamic XML Code:
+
+		 - Global Actions:
+
+			addComboboxLayer(cbname, design*)
+			removeComboboxLayer(cbname);
+
+		 - Combobox Layer Actions:
+
+			layer[cbname].addItem(caption, onclick)
+			layer[cbname].addNamedItem(name, caption, onclick)
+			layer[cbname].addIdItem(name, caption, onclick);       same as addNamedItem (for combobox.js compatibility)
+			layer[cbname].selectItem(caption)
+			layer[cbname].selectItemByName(name_or_index)
+			layer[cbname].selectIdItem(name_or_index)              same as selectItemByName (for combobox.js compatibility)
+			layer[cbname].removeAll()
+			layer[cbname].openList()
+			layer[cbname].closeList()
+
+		 - Events/Callbacks:
+
+			layer[cbname].onChange
+
+		- Combobox Layer Attributes:
+
+			layer[cbname].item              - krpano Array of the items
+			layer[cbname].selecteditemindex - current selected item index
+	-->
+
+
+	<!-- core internal layer styles -->
+	<style name="combobox_container_style" type="container" maskchildren="true" bgcapture="true" visible="false" onclick="combobox_onclick_event();" alpha="1.0" />
+	<style name="combobox_marker_style" type="text" align="righttop" edge="center" html="▼" havemarkersize="false" onautosized="set(havemarkersize,true);" alpha="1.0" />
+	<style name="combobox_item_style" type="text" wordwrap="false" vcenter="true" align="lefttop" onover="if(!combbox_item_pressed,onoveritem());asyncloop(hovering,,if(!combbox_item_pressed,onoutitem()));" ondown="onoveritem(); set(combbox_item_pressed,true);" onup="onoutitem(); set(combbox_item_pressed,false);" onoveritem="set(bg,true);" onoutitem="set(bg,false);" alpha="1.0" />
+
+	<!-- several pre-defined designs -->
+	<combobox_design name="default" margin="2" open_close_speed="0.25">
+		<!-- default design - white box with black text and blue selection -->
+		<style name="combobox_container_style" bgalpha="1.0" bgcolor="0xFFFFFF" bgborder="1 0xFFFFFF 0.5" bgroundedge="1" bgshadow="0 1 3 0x000000 1.0" />
+		<style name="combobox_marker_style" css="color:#FFFFFF;" bg="false" txtshadow="0 0 2 0x000000 1" />
+		<style name="combobox_item_style" css="color:#222222;" padding="4 4" bg="false" bgcolor="0xC7E4FC" bgalpha="1.0" bgroundedge="1" txtshadow="0 0 1 0xFFFFFF 1.0" />
+	</combobox_design>
+
+	<combobox_design name="vtour" margin="4" open_close_speed="0.25">
+		<!-- default vtourskin.xml design -->
+		<style name="combobox_container_style" bgalpha="0.8" bgcolor="0x2D3E50" bgborder="0" bgroundedge="1" bgshadow="0 4 10 0x000000 0.3" />
+		<style name="combobox_marker_style" css="color:#FFFFFF;" bg="false" txtshadow="0 0 2 0x000000 1" />
+		<style name="combobox_item_style" css="color:#FFFFFF;" padding="4 4" bg="false" bgcolor="0xFFFFFF" bgalpha="0.5" bgroundedge="0" txtshadow="0 0 2 0x000000 1" />
+	</combobox_design>
+
+
+	
+	
+
+	<!-- internal events -->
+	<events name="combobox_xml_plugin_events" keep="true"
+	        onresize="combobox_closelist();"
+	        />
+
+	<!-- krpano version check -->
+	<action name="combobox_versioncheck" autorun="preinit">
+		if(build LT '2022-01-01',
+			error('combobox.xml - too old krpano version!');
+			set(events[combobox_xml_plugin_events].name, null);
+			set(action[addComboboxLayer].content, '');
+			set(action[removeComboboxLayer].content, '');
+		  ,
+			combobox_xml_init();
+		);
+	</action>
+
+	<action name="combobox_xml_init">
+		<!-- set auto call again on next xml load -->
+		set(action[combobox_xml_init].autorun, onload);
+		
+		combobox_parse_xml_elements();
+	</action>
+	
+
+	<!-- convert all <combobox> elements to layers -->
+	<action name="combobox_parse_xml_elements" scope="localonly">
+		if(global.combobox,
+			copy(combobox_src, global.combobox);
+			delete(global.combobox);
+			def(i, integer, 0);
+			def(cnt, integer, get(combobox_src.count));
+			if(cnt GT 0, loop(i LT cnt,
+				copy(cb, combobox_src[get(i)]);
+				if(cb AND cb.name AND cb.parsed != true,
+					set(cb.parsed, true);
+					addComboboxLayer(get(cb.name), get(cb.design));
+					copy(ly, global.layer[get(cb.name)]);
+					copyattributes(get(ly), get(cb));
+					set(ly.keep, true);
+					def(item_cnt, integer, get(cb.item.count));
+					if(item_cnt GT 0,
+						def(item_i, integer, 0);
+						loop(item_i LT  item_cnt,
+							combobox_additem(get(ly.name), get(cb.item[get(item_i)].name), get(cb.item[get(item_i)].caption), get(cb.item[get(item_i)].onclick), get(cb.item[get(item_i)].oninit));
+							inc(item_i);
+						);
+					);
+				);
+				inc(i);
+			));
+		);
+	</action>
+
+
+	<!-- dynamically add a combobox layer -->
+	<action name="addComboboxLayer" scope="localonly" args="cbname, design">
+		<!-- create the layer -->
+		addlayer(get(cbname));
+		copy(cb, global.layer[get(cbname)]);
+		set(cb.keep, true);
+		set(cb.maxopenheight, 1000);
+		
+		<!-- copy the design settings (or set defaults) -->
+		if(!global.combobox_design[get(design)].name, set(design,'default'));
+		copy(cb.cbdesign, global.combobox_design[get(design)]);
+		calc(cb.margin, cb.cbdesign.margin !== null ? cb.cbdesign.margin : 2);
+		calc(cb.open_close_speed, cb.cbdesign.open_close_speed !== null ? cb.cbdesign.open_close_speed : 0.25);
+		<!-- load the styles and copy the design style settings -->
+		cb.loadstyle(combobox_container_style);
+		copyattributes(get(cb), get(cb.cbdesign.style[combobox_container_style]));
+
+		<!-- add/build/map actions -->
+		calc(cb.addItem,          'combobox_additem(' + cbname + ', null, "%%1", "%%2");');
+		calc(cb.addNamedItem,     'combobox_additem(' + cbname + ', "%%1", "%%2", "%%3");');
+		calc(cb.addIdItem,        'combobox_additem(' + cbname + ', "%%1", "%%2", "%%3");');
+		calc(cb.selectItem,       'combobox_finditem(' + cbname + ', "%%1", __cb_fi); if(__cb_fi GE 0, combobox_selectitem(' + cbname + ', get(__cb_fi))); delete(__cb_fi);');
+		calc(cb.selectItemByName, 'combobox_selectitem(' + cbname + ', "%%1");');
+		calc(cb.selectIdItem,     'combobox_selectitem(' + cbname + ', "%%1");');
+		calc(cb.removeAll,        'combobox_removeitems(' + cbname + ');');
+		calc(cb.openList,         'combobox_openlist(' + cbname + ');');
+		calc(cb.closeList,        'combobox_closelist(' + cbname + ');');
+
+		<!-- create sub-layers -->
+		calc(saname, 'combobox_' + cbname + '_scrollarea');
+		addlayer(get(saname));
+		copy(sa, global.layer[get(saname)]);
+		copy(sa.parent, cbname);
+		copy(sa.keep, true);
+		copy(sa.align, lefttop);
+		set(sa.type, 'scrollarea');
+		set(sa.direction, v);
+		set(sa.enabled, false);
+		set(sa.width, 100%);
+		set(sa.height, 100%);
+		copy(cb.scrollarea, sa);
+
+		calc(mkname, 'combobox_' + cbname + '_marker');
+		addlayer(get(mkname));
+		copy(mk, global.layer[get(mkname)]);
+		copy(mk.parent, saname);
+		copy(mk.keep, true);
+		mk.loadstyle(combobox_marker_style);
+		copyattributes(get(mk), get(cb.cbdesign.style[combobox_marker_style]));
+		copy(cb.marker, mk);
+
+		<!-- item data array -->
+		cb.createarray('item');
+
+		<!-- item autosizing information -->
+		set(cb.autosize_i, 0);
+		set(cb.autosize_cnt, 0);
+		set(cb.autosize_max_w, 0);
+		set(cb.autosize_max_h, 0);
+
+		set(cb.lastselecteditemindex, 0);
+		set(cb.selecteditemindex, 0);
+	</action>
+
+
+	<!-- dynamically remove a combobox element -->
+	<action name="removeComboboxLayer" scope="localonly" args="cbname">
+		if(global.layer[get(cbname)],
+			copy(cb, global.layer[get(cbname)]);
+			if(cb === global.openedcombobox, delete(global.openedcombobox));
+			if(cb,
+				removelayer(get(cbname), true);
+			);
+		);
+	</action>
+
+
+	<!-- default onclick event for combobox elements: open the list -->
+	<action name="combobox_onclick_event">
+		combobox_openlist(get(name));
+	</action>
+
+
+	<!-- dynamically add items -->
+	<action name="combobox_additem" scope="localonly" args="cbname, itemname, itemcaption, itemonclick, itemoninit">
+		copy(cb, global.layer[get(cbname)]);
+		
+		<!-- when no item name is set, generate an automatic one -->
+		if(itemname === null, calc(itemname, 'autoname_' + cb.item.count); );
+		
+		<!-- save the item caption and onclick event -->
+		copy(cb.item[get(itemname)].caption, itemcaption);
+		copy(cb.item[get(itemname)].onclick, itemonclick);
+
+		inc(cb.autosize_cnt);
+
+		<!-- create the item layer/textfield -->
+		calc(itemlayername, 'comboboxitem_' + cbname + '_' + itemname);
+		addlayer(get(itemlayername));
+		copy(li, global.layer[get(itemlayername)]);
+		li.loadstyle(combobox_item_style);
+		copyattributes(get(li), get(cb.cbdesign.style[combobox_item_style]));
+		copy(li.parent, cb.scrollarea.name);
+		copy(li.keep, true);
+		copy(li.cblayername, cb.name);
+		copy(li.itemname, itemname);
+		copy(li.html, itemcaption);
+		set(li.onautosized, delayedcall(0,combobox_item_autosize_update()) );
+		set(li.onclick, combobox_item_onclick() );
+		if (isset(itemoninit), callwith(li, itemoninit));
+
+		copy(cb.item[get(itemname)].itemlayername, itemlayername);
+		copy(cb.item[get(itemname)].itemlayer, li);
+	</action>
+
+
+	<!-- onautosized callback from the item textfield -->
+	<action name="combobox_item_autosize_update" scope="localonly">
+		copy(cb, global.layer[get(caller.cblayername)]);
+		inc(cb.autosize_i);
+		Math.max(cb.autosize_max_w, caller.width);
+		Math.max(cb.autosize_max_h, caller.height);
+		delayedcall(calc(cb.name + '_combobox_align_items'), 0.01, calc('combobox_align_items('+cb.name+')') );
+	</action>
+
+
+	<!-- align the image and set the combobox size -->
+	<action name="combobox_align_items" scope="localonly" args="cbname">
+		copy(cb, global.layer[get(cbname)]);
+		if(cb.marker.havemarkersize == false OR cb.scrollarea.loaded == false,
+			<!-- wait until everything is ready -->
+			delayedcall(calc(cb.name + '_waitformarkersize'), 0.01, combobox_align_items(get(cbname)) );
+		  ,
+			<!-- set the item positions and the combobox size -->
+			if(global.openedcombobox === cb, combobox_closelist() );
+			copy(sa, cb.scrollarea);
+			calc(itemwidth, cb.margin GT 0 ? -2 * cb.margin : '100%');
+			copy(mk_w, cb.marker.width);
+			copy(item_cnt, cb.autosize_cnt);
+
+			for(def(item_i, integer, 0), item_i LT item_cnt, inc(item_i),
+				copy(li, global.layer[get(cb.item[get(item_i)].itemlayername)]);
+				set(li.x, get(cb.margin));
+				copy(li.width, itemwidth);
+				copy(li.height, cb.autosize_max_h);
+				calc(li.y, cb.margin + item_i * (cb.autosize_max_h + cb.margin));
+			);
+
+			if(cb.width == null OR cb.width == cb.lastautosizedwidth,
+				<!-- no combobox width (or an autosized width) set - set the largest item width -->
+				calc(cb.width, cb.margin + cb.autosize_max_w + 2 + mk_w + cb.margin);
+				copy(cb.lastautosizedwidth, cb.width);
+			);
+
+			calc(cb.height, 2*cb.margin + cb.autosize_max_h);
+			calc(sa.height, cb.margin + item_cnt*(cb.margin+cb.autosize_max_h));
+			calc(sa.y, -(cb.selecteditemindex * (cb.autosize_max_h + cb.margin)));
+			calc(cb.marker.x, cb.margin + mk_w/2);
+			tween(global.layer[get(cb.name)].marker.y, calc(cb.margin + cb.selecteditemindex*(cb.autosize_max_h + cb.margin) + cb.autosize_max_h/2), 0.1);
+
+			<!-- when all is done, show the combobox -->
+			delayedcall(0.1, set(global.layer[get(cb.name)].visible,true); );
+		);
+	</action>
+
+
+	<!-- helper action for calling a plugin event-code with 'global' and 'caller' scope -->
+	<action name="combobox_do_event_call" scope="local" args="cb, eventcode">
+		if(eventcode !== null, callwith(cb, get(eventcode) ); );
+	</action>
+	
+
+	<!-- default onclick event for items: select the current item, close the list and call the item onclick event -->
+	<action name="combobox_item_onclick" scope="localonly">
+		copy(cb, global.layer[get(caller.cblayername)]);
+		copy(itemname, caller.itemname);
+		combobox_selectitem(get(cb.name), get(itemname));
+
+		if(global.openedcombobox === cb, combobox_closelist() );
+
+		if(cb.item[get(itemname)].onclick,
+			if(cb.callonclickafterclose === false,
+				<!-- call instantly -->
+				combobox_do_event_call(get(cb), get(cb.item[get(itemname)].onclick));
+			  ,
+				<!-- call the onclick event after the combobox has closed -->
+				delayedcall(get(cb.open_close_speed),
+					copy(cb.curitem, cb.item[get(itemname)]);
+					combobox_do_event_call(get(cb), get(cb.item[get(itemname)].onclick));
+				);
+			);
+		);
+	</action>
+
+
+	<!-- select an item -->
+	<action name="combobox_selectitem" scope="localonly" args="cbname, itemname">
+		if(global.combbox_item_pressed != true,
+			copy(cb, global.layer[get(cbname)]);
+			copy(cb.lastselecteditemindex, cb.selecteditemindex);
+			copy(cb.selecteditemindex, cb.item[get(itemname)].index);
+			<!-- call onchange event on selection change -->
+			if(cb.lastselecteditemindex != cb.selecteditemindex AND cb.onchange,
+				combobox_do_event_call(get(cb), get(cb.onchange));
+			);
+			if(global.openedcombobox === cb,
+				<!-- when opened, just close to the selected item -->
+				combobox_closelist();
+			  ,
+				if(global.layer[get(cbname)].scrollarea.loaded AND cb.autosize_max_h GT 0,
+					global.layer[get(cbname)].scrollarea.stopscrolling();
+					calc(offset, cb.selecteditemindex*(cb.autosize_max_h + cb.margin));
+					tween(global.layer[get(cbname)].marker.y, calc(cb.margin + offset + cb.autosize_max_h/2), 0);
+					tween(global.layer[get(cbname)].scrollarea.y, calc(-offset), 0, default, global.layer[get(cbname)].scrollarea.update(); );
+				);
+			);
+		);
+	</action>
+
+
+	<!-- find an item by its caption, the global variable defined in 'returnvariable' will contain the index  -->
+	<action name="combobox_finditem" scope="localonly" args="cbname, itemcaption, returnvariable">
+		copy(cb, global.layer[get(cbname)]);
+		copy(item_cnt, cb.item.count);
+		set(calc('global.' + returnvariable), -1);
+		for(def(item_i, integer, 0), item_i LT  item_cnt, inc(item_i),
+			if(cb.item[get(item_i)].caption == itemcaption,
+				copy(calc('global.' + returnvariable), item_i);
+				copy(item_i, item_cnt);
+			);
+		);
+	</action>
+
+
+	<!-- remove all items (to be able to add new ones) -->
+	<action name="combobox_removeitems" scope="localonly" args="cbname">
+		copy(cb, global.layer[get(cbname)]);
+		if(global.openedcombobox === cb, combobox_closelist() );
+
+		<!-- remove all item layers -->
+		calc(item_i, cb.item.count - 1);
+		loop(item_i GE 0,
+			removelayer(get(cb.item[get(item_i)].itemlayername));
+			dec(item_i);
+		);
+
+		<!-- reset the item information -->
+		set(cb.item.count, 0);
+		set(cb.autosize_i,0);
+		set(cb.autosize_cnt, 0);
+		set(cb.autosize_max_w, 0);
+		set(cb.autosize_max_h, 0);
+		set(cb.selecteditemindex, 0);
+		set(cb.lastselecteditemindex, 0);
+		if(cb.width == cb.lastautosizedwidth, set(cb.width, null));
+	</action>
+
+
+	<!-- open the combobox list -->
+	<action name="combobox_openlist" scope="localonly" args="cbname">
+		<!-- if another combobox is already open, close that one first -->
+		if(global.openedcombobox !== null, combobox_closelist() );
+
+		copy(cb, global.layer[get(cbname)]);
+		copy(global.openedcombobox, cb);
+		
+		<!-- move to top -->
+		copy(cb.backupzorder, cb.zorder);
+		set(cb.zorder, 999);
+
+		<!-- find the available screen space above or below the combobox -->
+		calc(cbheight, 2*cb.margin + cb.autosize_max_h);
+		set(lx1, 0);
+		set(ly1, 0);
+		copy(lx2, cb.pixelwidth);
+		copy(ly2, cbheight);
+		layertoscreen(get(cbname), lx1,ly1, lx1,ly1);
+		layertoscreen(get(cbname), lx2,ly2, lx2,ly2);
+		calc(space_above, ly1 - global.area.pixely);
+		calc(space_below, global.area.pixelheight - (ly2 - global.area.pixely));
+
+		<!-- the required space for full opening: -->
+		calc(openheight, cb.margin + cb.autosize_cnt*(cb.margin+cb.autosize_max_h) );
+		
+		<!-- vertical centered alignment? -->
+		calc(cb_edge, cb.edge ? cb.edge : cb.align);
+		calc(iscentered, cb_edge == 'left' OR cb_edge == 'center' OR cb_edge == 'right');
+		if(iscentered,
+			calc(openheight_max, space_above + space_below);
+		  ,
+			Math.max(openheight_max, space_above, space_below);
+		);
+
+		<!-- limit the height to the available space (minus some margin) -->
+		Math.min(openheight, calc(openheight_max + cbheight - 20));
+
+		clamp(openheight, 0, get(cb.maxopenheight));
+		
+		<!-- need vertical offset? (depending on the available space and the align/edge setting) -->
+		set(yoffset, null);
+		calc(top_overflow, -ly1 + global.area.pixely + openheight/2);
+		calc(bottom_overflow, ly2 - global.area.pixely + openheight/2 - global.area.pixelheight);
+		
+		if(cb.parentobject AND cb.parentobject.autoheight == false,
+			<!-- no vertical offset inside other layers, do only a height clipping -->
+			Math.max(max_overflow, top_overflow, bottom_overflow, 0);
+			sub(openheight, max_overflow);
+		  ,
+			if(iscentered,
+				if(openheight GE (global.area.pixelheight - 20),
+					set(yoffset,0);
+				  ,
+					if(top_overflow GT 0, calc(yoffset, cb.y + top_overflow); );
+					if(bottom_overflow GT 0, calc(yoffset, cb.y - bottom_overflow); );
+				);
+			,
+				indexoftxt(isbottomalign, get(cb_edge), 'bottom');
+				if(space_above GT space_below,
+					if(isbottomalign LT 0, calc(yoffset, cb.y - openheight + cbheight); );
+				  ,
+					if(isbottomalign GE 0, calc(yoffset, cb.y - openheight + cbheight); );
+				);
+			);
+		);
+		if(yoffset != null,
+			copy(cb.ybackup, cb.y);
+			tween(global.layer[get(cbname)].y, calc(yoffset), get(cb.open_close_speed));
+		);
+
+		<!-- center the opened list at the selected item -->
+		if( indexof(cb_edge,'top') GE 0,
+			calc(centeritem_y, -1 * (cb.margin + cb.selecteditemindex*(cb.margin+cb.autosize_max_h) - cb.margin));
+			clamp(centeritem_y, calc(openheight - cb.scrollarea.height), 0);
+		  ,
+			indexof(cb_edge,'bottom') GE 0,
+			calc(centeritem_y, -1 * (cb.margin + cb.selecteditemindex*(cb.margin+cb.autosize_max_h) + cb.autosize_max_h - openheight + cb.margin));
+			clamp(centeritem_y, calc(openheight - cb.scrollarea.height), 0);
+		  ,
+			calc(centeritem_y, -1 * (cb.margin + cb.selecteditemindex*(cb.margin+cb.autosize_max_h) + cb.autosize_max_h/2 - openheight/2));
+			clamp(centeritem_y, calc(openheight - cb.scrollarea.height), 0);
+		);
+
+		<!-- apply the changes now -->
+		tween(global.layer[get(cbname)].height, get(openheight), get(cb.open_close_speed));
+		tween(global.layer[get(cbname)].scrollarea.y, get(centeritem_y), get(cb.open_close_speed), default, global.layer[get(cbname)].scrollarea.update(); );
+
+		tween(global.layer[get(cbname)].marker.rotate, 90, get(cb.open_close_speed));
+		
+		<!-- enable the scrollarea to allow the user to drag it -->
+		set(cb.scrollarea.enabled, true);
+
+		<!-- install a global onmousedown event to close the list when clicking at the pano -->
+		set(global.events[combobox_xml_plugin_events].onmousedown, combobox_closelist() );
+	</action>
+
+
+	<!-- close the current open list -->
+	<action name="combobox_closelist" scope="localonly">
+		if(global.openedcombobox !== null,
+			copy(cb, global.openedcombobox);
+			delete(global.openedcombobox);
+			
+			<!-- restore zorder -->
+			copy(cb.zorder, cb.backupzorder);
+			
+			<!-- clear the global onmousedown event -->
+			set(global.events[combobox_xml_plugin_events].onmousedown, null);
+
+			<!-- disable the dragging -->
+			set(cb.scrollarea.enabled, false);
+
+			<!-- closing animations -->
+			calc(offset, cb.selecteditemindex*(cb.autosize_max_h + cb.margin));
+			if(cb.ybackup !== null, tween(cb.y, get(cb.ybackup), get(cb.open_close_speed)));
+			global.layer[get(cb.name)].scrollarea.stopscrolling();
+			tween(global.layer[get(cb.name)].height, calc(2*cb.margin + cb.autosize_max_h), get(cb.open_close_speed));
+			tween(global.layer[get(cb.name)].scrollarea.y, calc(-offset), get(cb.open_close_speed), default, global.layer[get(cb.name)].scrollarea.update(); );
+			tween(global.layer[get(cb.name)].marker.y, calc(cb.margin + offset + cb.autosize_max_h/2), get(cb.open_close_speed));
+			tween(global.layer[get(cb.name)].marker.rotate, 0, get(cb.open_close_speed));
+		);
+	</action>
+
+</krpano>

+ 48 - 0
public/plugins/doubleclick_style.xml

@@ -0,0 +1,48 @@
+<krpano>
+	
+	<!--
+		doubleclick_style.xml
+		krpano 1.21
+	
+		A helper style for single/double-click detection for layer and hotspot elements.
+		
+		Example:
+		
+		<layer ...
+		       style="doubleclick"
+		       onsingleclick="trace('-single click-');"
+		       ondoubleclick="trace('-double click-');"
+		       />
+	-->
+	
+	<style name="doubleclick"
+	       downx="0"
+	       downy="0"
+	       clicks="0"
+	       onsingleclick=""
+	       ondoubleclick=""
+	       onclick.addevent="doubleclick_style_onclick();"
+	       />
+
+	<action name="doubleclick_style_onclick">
+	    inc(clicks);
+	    if(clicks == 2,
+	        set(clicks,0); 
+	        stopdelayedcall(doubleclickdetector);
+	        if((((mouse.x-downx)^2 + (mouse.y-downy)^2)^0.5) LT (device.touch ? 10 : 2),
+	            ondoubleclick();
+	          ,
+	            onsingleclick();
+	            onsingleclick();
+	        );
+	      ,
+	        copy(downx, mouse.x); 
+	        copy(downy, mouse.y);
+	        delayedcall(doubleclickdetector, 0.3, 
+	            set(clicks,0); 
+	            onsingleclick();
+	        );
+	    );
+	</action>
+	
+</krpano>

+ 28 - 0
public/plugins/fps.xml

@@ -0,0 +1,28 @@
+<krpano>
+
+	<!--
+		FPS (frames per second) performance measuring tool.
+		This tools shows the current average rendering frame-rate in the left-top corner.
+	-->
+
+	<action name="fps_install" autorun="preinit" scope="local">
+		delayedcall(0.5,
+			addlayer(fps_display);
+			set(layer[fps_display],
+				type=text,
+				keep=true,
+				align='lefttop',
+				css=calc('font-size:'+(device.mobile AND stagescale LT 1.0 ? 24 : 12)+'px; color:#FFFFFF;'),
+				txtshadow='0 0 1 0x000000 1.0',
+				bg=false,
+				parent=STAGE,
+				vr=true,
+				enabled=false
+			);
+			setinterval(fps_plugin, 0.5,
+				calc(layer[fps_display].html, 'FPS: ' + ((display.currentfps+0.5) BOR 0));
+			);
+		);
+	</action>
+
+</krpano>

Diferenças do arquivo suprimidas por serem muito extensas
+ 5 - 0
public/plugins/googlemaps.js


Diferenças do arquivo suprimidas por serem muito extensas
+ 5 - 0
public/plugins/gyro2.js


Diferenças do arquivo suprimidas por serem muito extensas
+ 5 - 0
public/plugins/pp_blur.js


Diferenças do arquivo suprimidas por serem muito extensas
+ 5 - 0
public/plugins/pp_light.js


Diferenças do arquivo suprimidas por serem muito extensas
+ 5 - 0
public/plugins/pp_sharpen.js


Diferenças do arquivo suprimidas por serem muito extensas
+ 19 - 0
public/plugins/scrollarea.js


+ 212 - 0
public/plugins/showtext.xml

@@ -0,0 +1,212 @@
+<krpano>
+	<!--
+		showtext() xml plugin
+		- showtext() and <textstyle> support for HTML5
+		- docu: https://krpano.com/plugins/showtext/
+		- krpano 1.21
+	-->
+
+
+	<!-- predefine a DEFAULT textstyle element -->
+	<textstyle name="DEFAULT" />
+	
+	
+
+
+	<!-- the automatic running (autorun=preinit) install action -->
+	<action name="showtext_install" autorun="preinit" scope="private:showtext">
+		<!-- remove the built-in 'showtext' function to use the 'showtext' <action> instead: -->
+		delete(global.showtext);
+
+		<!-- initialize internal variables -->
+		set(showtext_style, DEFAULT);
+		set(showtext_prevstyle, null);
+		set(showtext_text, '');
+		set(showtext_prevtext, '');
+		set(showtext_timeout, 0.1);
+		set(showtext_fadeout, 0.0);
+		set(showtext_clipping, false);
+	</action>
+	
+
+	<action name="showtext" scope="private:showtext" args="text, style">
+		if(!style, set(style, DEFAULT));
+		
+		if(global.textstyle[get(style)], 
+			copy(showtext_style, style);
+		  ,
+			warning("showtext() - there is no textstyle '", style, "' defined!");
+			global.textstyle.createarrayitem(get(style));
+		);
+
+		copy(showtext_text, text);
+		
+		if(showtext_text != showtext_prevtext,
+			copy(showtext_prevtext, showtext_text);
+			showtext_createnewtext();
+		  ,
+			delayedcall(showtext_timer, get(showtext_timeout), showtext_hide() );
+		  );
+	</action>
+	
+
+	<action name="showtext_createnewtext" scope="private:showtext">
+		<!-- stop running mouse and alpha update calls -->
+		stopdelayedcall(showtext_mouseupdates);
+		stoptween(global.layer[showtext_tf].alpha);
+
+		<!-- remove the old textfield when the style has changed -->
+		if(showtext_style != showtext_prevstyle,
+			copy(showtext_prevstyle, showtext_style);
+			removelayer(showtext_tf);
+		  );
+		  
+		<!-- create a new textfield plugin layer -->
+		addlayer(showtext_tf);
+
+		<!-- create 'shortcut' variables (tf,ts) for faster access -->
+		copy(tf, global.layer[showtext_tf]);
+		copy(ts, global.textstyle[get(showtext_style)]);
+
+		<!-- get the position settings -->
+		if(ts.origin  !== null, copy(ts_origin,  ts.origin),  set(ts_origin, 'cursor'));
+		if(ts.edge    !== null, copy(ts_edge,    ts.edge),    set(ts_edge, 'bottom'));
+		if(ts.xoffset !== null, copy(ts_xoffset, ts.xoffset), set(ts_xoffset, 0));
+		if(ts.yoffset !== null, copy(ts_yoffset, ts.yoffset), set(ts_yoffset, -3));
+
+		<!-- set the position settings -->
+		if(ts_origin == 'cursor',
+			set(tf.align, 'lefttop');
+			showtext_movetomouse();
+		  ,
+			copy(tf.align, ts_origin);
+		  );
+		copy(tf.edge, ts_edge);
+		copy(tf.ox, ts_xoffset);
+		copy(tf.oy, ts_yoffset);
+
+		<!-- get the font settings -->
+		if(ts.font      !== null, copy(ts_font,      ts.font),      set(ts_font, 'Times'));
+		if(ts.fontsize  !== null, copy(ts_fontsize,  ts.fontsize),  set(ts_fontsize, 12.0));
+		if(ts.bold      !== null, copy(ts_bold,      ts.bold),      set(ts_bold, true));
+		if(ts.italic    !== null, copy(ts_italic,    ts.italic),    set(ts_italic, false));
+		if(ts.textcolor !== null, copy(ts_textcolor, ts.textcolor), set(ts_textcolor, 0x000000));
+		if(ts.textalign !== null, copy(ts_textalign, ts.textalign), set(ts_textalign, 'left'));
+
+		<!-- use the font settings to build the CSS style -->
+		set(tf_css, '');
+		tohex(ts_textcolor, '#', 6);
+		txtadd(tf_css, 'font-family:',get(ts_font),'; font-size:',get(ts_fontsize),'px; color:',get(ts_textcolor),'; ');
+		if(ts_textalign != 'none', txtadd(tf_css, get(tf_css), 'text-align:',get(ts_textalign),'; '));
+		if(ts_bold,   txtadd(tf_css, 'font-weight:bold; '));
+		if(ts_italic, txtadd(tf_css, 'font-style:italic; '));
+		if(ts.css !== null, txtadd(tf_css, get(ts.css)));
+		copy(tf.css, tf_css);
+
+		<!-- size settings -->
+		if(ts.width   !== null AND ts.width   !== '', copy(tf.width,   ts.width));
+		if(ts.height  !== null AND ts.height  !== '', copy(tf.height,  ts.height));
+		if(ts.vcenter !== null AND ts.vcenter !== '', copy(tf.vcenter, ts.vcenter));
+		if(ts.padding !== null AND ts.padding !== '', copy(tf.padding, ts.padding), set(tf.padding,1));
+
+		<!-- background, border, shadow settings -->
+		if(ts.background      !== null, copy(tf.background,      ts.background));
+		if(ts.backgroundcolor !== null, copy(tf.backgroundcolor, ts.backgroundcolor));
+		if(ts.backgroundalpha !== null, copy(tf.backgroundalpha, ts.backgroundalpha));
+		if(ts.border          !== null, copy(tf.border,          ts.border), set(tf.border,true));
+		if(ts.bordercolor     !== null, copy(tf.bordercolor,     ts.bordercolor));
+		if(ts.borderalpha     !== null, copy(tf.borderalpha,     ts.borderalpha));
+		if(ts.borderwidth     !== null, copy(tf.borderwidth,     ts.borderwidth));
+		if(ts.roundedge       !== null, copy(tf.roundedge,       ts.roundedge));
+		if(ts.shadow          !== null, copy(tf.shadow,          ts.shadow));
+		if(ts.shadowrange     !== null, copy(tf.shadowrange,     ts.shadowrange));
+		if(ts.shadowangle     !== null, copy(tf.shadowangle,     ts.shadowangle));
+		if(ts.shadowcolor     !== null, copy(tf.shadowcolor,     ts.shadowcolor));
+		if(ts.shadowalpha     !== null, copy(tf.shadowalpha,     ts.shadowalpha));
+		if(ts.textshadow      !== null, copy(tf.textshadow,      ts.textshadow));
+		if(ts.textshadowrange !== null, copy(tf.textshadowrange, ts.textshadowrange));
+		if(ts.textshadowangle !== null, copy(tf.textshadowangle, ts.textshadowangle));
+		if(ts.textshadowcolor !== null, copy(tf.textshadowcolor, ts.textshadowcolor));
+		if(ts.textshadowalpha !== null, copy(tf.textshadowalpha, ts.textshadowalpha));
+
+		<!-- showing settings -->
+		if(ts.alpha      !== null, copy(ts_alpha,      ts.alpha),      set(ts_alpha, 1.0));
+		if(ts.showtime   !== null, copy(ts_showtime,   ts.showtime),   set(ts_showtime, 0.1));
+		if(ts.fadetime   !== null, copy(ts_fadetime,   ts.fadetime),   set(ts_fadetime, 0.0));
+		if(ts.fadeintime !== null, copy(ts_fadeintime, ts.fadeintime), set(ts_fadeintime, 0.0));
+		copy(showtext_timeout, ts_showtime);
+		copy(showtext_fadeout, ts_fadetime);
+		if(ts_fadeintime GT 0,
+			set(tf.alpha, 0.0);
+			tween(global.layer[showtext_tf].alpha, get(ts_alpha), get(ts_fadeintime), linear);
+		  ,
+			copy(tf.alpha, ts_alpha);
+		  );
+
+		if(ts.noclip !== null, copy(showtext_clipping,ts.noclip), set(showtext_clipping, true));
+		
+		if(showtext_clipping,
+			set(tf.onloaded,    showtext_do_clipping() );
+			set(tf.onautosized, showtext_do_clipping() );
+		  );
+
+		<!-- special flash-only settings -->
+		if(ts.embeddedfonts !== null, copy(tf.embeddedfonts, ts.embeddedfonts));
+		if(ts.effect        !== null, copy(tf.effect,        ts.effect));
+		if(ts.blendmode     !== null, copy(tf.blendmode,     ts.blendmode));
+
+		<!-- set the text and the basic settings to start showing the textfield -->
+		copy(tf.html, showtext_text);
+		set(tf.enabled, false);
+		set(tf.zorder, 999999);
+		if(ts.parent, copy(tf.parent, ts.parent) );
+		set(tf.type, 'text');
+
+		<!-- start the text-hiding timer -->
+		delayedcall(showtext_timer, get(showtext_timeout), showtext_hide() );
+	</action>
+
+
+	<action name="showtext_do_clipping" scope="private:showtext">
+		if(showtext_clipping,
+			global.layer[showtext_tf].updatepos();
+			copy(tf_px, global.layer[showtext_tf].pixelx);
+			copy(tf_py, global.layer[showtext_tf].pixely);
+			if(tf_px LT 0,
+				sub(global.layer[showtext_tf].x, tf_px);
+			  ,
+				add(tf_rightedge, tf_px, global.layer[showtext_tf].pixelwidth);
+				if(tf_rightedge GE global.stagewidth, sub(tf_rightedge,global.stagewidth); sub(global.layer[showtext_tf].x,tf_rightedge); );
+			  );
+			if(tf_py LT 0,
+				sub(global.layer[showtext_tf].y,  tf_py);
+			  ,
+				add(tf_bottomedge, tf_py, global.layer[showtext_tf].pixelheight);
+				if(tf_bottomedge GE global.stageheight, sub(tf_bottomedge,global.stageheight); sub(global.layer[showtext_tf].y,tf_bottomedge); );
+			  );
+		  );
+	</action>
+
+
+	<action name="showtext_movetomouse" scope="private:showtext">
+		copy(global.layer[showtext_tf].x, global.mouse.stagex);
+		copy(global.layer[showtext_tf].y, global.mouse.stagey);
+
+		showtext_do_clipping();
+
+		delayedcall(showtext_mouseupdates, 0, showtext_movetomouse() );
+	</action>
+
+
+	<action name="showtext_hide" scope="private:showtext">
+		if(global.layer[showtext_tf],
+			tween(global.layer[showtext_tf].alpha, 0.0, get(showtext_fadeout), linear,
+					stopdelayedcall(showtext_mouseupdates);
+					removelayer(showtext_tf);
+					set(showtext_text, '');
+					set(showtext_prevtext, '');
+				);
+		  );
+	</action>
+
+</krpano>

Diferenças do arquivo suprimidas por serem muito extensas
+ 5 - 0
public/plugins/soundinterface.js


Diferenças do arquivo suprimidas por serem muito extensas
+ 5 - 0
public/plugins/videoplayer.js


Diferenças do arquivo suprimidas por serem muito extensas
+ 5 - 0
public/plugins/webvr.js


+ 942 - 0
public/plugins/webvr.xml

@@ -0,0 +1,942 @@
+<krpano>
+	
+	<!--
+		webvr.xml
+		krpano 1.21
+		
+		https://krpano.com/plugins/xmlextensions/#webvr
+		https://krpano.com/plugins/webvr/
+	-->
+	
+
+	<!-- load the WebVR plugin and assign it to a global 'webvr' variable -->
+	<plugin name="webvr" devices="html5" keep="true"
+	        url="webvr.js"
+	        mobilevr_support="true"
+	        mobilevr_touch_support="true"
+	        mobilevr_fake_support="true"
+	        mobilevr_profile.normal="90|60|42|0|0|0"
+	        mobilevr_profile.mobile="80|60|42|35|0.441|0.156"
+	        mobilevr_wakelock="true"
+	        fullscreen_mirroring="false"
+	        mouse_pointerlock="true"
+	        vr_cursor_onover="if(handcursor, tween(hotspot[vr_cursor].scale,0.4,0.1); vr_auto_click(get(vr_timeout)); );"
+	        vr_cursor_onout="tween(hotspot[vr_cursor].scale,0.3,0.1);"
+	        onavailable="webvr_onavailable();"
+	        onunavailable=""
+	        onunknowndevice="webvr_onunknowndevice();"
+	        onvrcontrollers="webvr_onvrcontrollers();"
+	        onentervr="webvr_onentervr();"
+	        onexitvr="webvr_onexitvr();"
+	        ondenied="webvr_ondenied();"
+	        />
+
+
+
+	<!-- the VR cursor hotspot -->
+	<style name="vr_cursor_style" 
+		url="webvr_vrcursor.png"
+		visible="false"
+		enabled="false"
+		distorted="true"
+		crop="0|0|80|80"
+		scale="0.3"
+		depth="120"
+		/>
+	
+	<action name="webvr_load_vr_cursor_hs" scope="local">
+		addhotspot('vr_cursor', hs);
+		hs.loadstyle(vr_cursor_style);
+		set(hs.keep, true);
+		set(webvr.vr_cursor, 'hotspot[vr_cursor]');
+	</action>
+	
+	<action name="webvr_load_vr_controller_hs" scope="private:VRCONTROLLERS" args="controllerstyle">
+		removehotspot('vr_controller_l');
+		removehotspot('vr_controller_r');
+		addhotspot('vr_controller_l', vr_ctrl_l);
+		addhotspot('vr_controller_r', vr_ctrl_r);
+		set(vr_ctrl_l.keep, true);
+		set(vr_ctrl_r.keep, true);
+		vr_ctrl_l.loadstyle(calc(controllerstyle ? controllerstyle : (global.display.depthbuffer ? 'vrcontroller_laser' : 'vrcontroller_light_and_point')));
+		vr_ctrl_r.loadstyle(calc(controllerstyle ? controllerstyle : (global.display.depthbuffer ? 'vrcontroller_laser' : 'vrcontroller_light_and_point')));
+
+		<!-- optional: vibrate the controllers on hovering:
+		vr_ctrl_l.addevent('onover', pulse(1.0, 0.25) );
+		vr_ctrl_r.addevent('onover', pulse(1.0, 0.25) ); -->
+		if(!global.webvr.iswebxr,
+			vr_ctrl_l.addevent('onvrcontrollerbutton', 'if(vrbuttonindex == 3 AND vrbuttonstate == "up", vrsetup_open(); );' );
+			vr_ctrl_r.addevent('onvrcontrollerbutton', 'if(vrbuttonindex == 3 AND vrbuttonstate == "up", vrsetup_open(); );' );
+		);
+		
+		set(global.webvr.vr_controller, 'vr_controller_l,vr_controller_r');
+		set(global.have_vr_controllers, true);
+		
+		if(global.webvr.isvrbrowser AND global.webvr.vrcontrollers[0].buttons.length LE 2,
+			<!-- when there are only two buttons on the VR controller (e.g. Oculus Go) use an extra hotspot for the VR-setup -->
+			addhotspot('webvr_vrsetup', vr_setup_hs);
+			vr_setup_hs.loadstyle('webvr_button_style');
+			set(vr_setup_hs, keep=true, ath=0, atv=90, depth=160, zorder=999, torigin=view, html='VR SETUP', oversampling=3, scale=0.2, onclick='vrsetup_open();', onloaded='renderloop( copy(ath,view.hlookat); );');
+		);
+	</action>
+	
+	<action name="webvr_onvrcontrollers" scope="private:VRCONTROLLERS">
+		if(!global.have_vr_controllers, webvr_load_vr_controller_hs(); );
+	</action>
+		
+	<style name="vrcontroller_laser" 
+			url="webvr_laser.png"
+			distorted="true"
+			enabled="false"
+			visible="false"
+			width="0.5" height="1000" edge="bottom" oref="1" oy="0"
+			torigin="world" tx="0" ty="0" tz="0" depth="0"
+			zorder="99999"
+			depthbuffer="true"
+			targethitd.number="0"
+			onover="copy(targethitd, target.hitd);"
+			onout="set(targethitd, 0);"
+			onloaded="asyncloop(loaded, calc(height, (targethitd GT 0 ? targethitd : (hitd GT 0 ? hitd : 1000)) / display.hotspotworldscale); );"
+			/>
+			
+	<style name="vrcontroller_light_and_point" 
+			url="webvr_light.png"
+			distorted="true"
+			enabled="false"
+			visible="false"
+			width="0.5" height="18" edge="bottom" oref="1" oy="0"
+			torigin="world" tx="0" ty="0" tz="0" depth="0"
+			zorder="99999"
+			depthbuffer="true"
+			onloaded="vrcontroller_target_point();"
+			/>
+	
+	<style name="vrcontroller_handcursor_and_point" 
+			url="webvr_handcursor.png"
+			distorted="true"
+			enabled="false"
+			visible="false"
+			width="10" height="10" edge="center" oref="1" oy="-1"
+			torigin="world" tx="0" ty="0" tz="0" depth="0"
+			zorder="99999"
+			depthbuffer="true"
+			onloaded="vrcontroller_target_point();"
+			/>
+	
+	<action name="vrcontroller_target_point" scope="localonly">
+		addhotspot(auto, hs);
+		set(hs, keep=true, type=text, bgcolor=0xFFFFFF, bgalpha=1.0, width=10, height=10, bgroundedge=5, bgborder='1 0x000000 1.0', oversampling=2,
+			scale=0.4, torigin=world, depth=0, distorted=false, zoom=true, zorder=99998, enabled=false
+		  );
+		renderloop(
+			if(!caller.loaded,
+				removehotspot(get(hs.name));
+				stoprenderloop();
+			  ,
+				if(global.display.havedepthmap,
+					<!-- use the laser for depthmap panos -->
+					removehotspot(get(hs.name));
+					stoprenderloop();
+					webvr_load_vr_controller_hs('vrcontroller_laser');
+				  ,
+					calc(hs.bgcolor, caller.pressed ? 0x049AFF : (caller.hovering ? 0x00FF00 : 0xFFFFFF));
+					calc(distance, (caller.target AND caller.target.hitd GT 0 ? caller.target.hitd : (caller.hitd GT 0 ? caller.hitd : 1000)));
+					calc(hs.scale, 0.4 * (distance GT 1000 ? distance / 1000 : (distance LT 200 ? 0.25 : (0.25 + (distance - 200)/800 * 0.75))));
+					calc(hs.tx, caller.tx + caller.dx * distance);
+					calc(hs.ty, caller.ty + caller.dy * distance);
+					calc(hs.tz, caller.tz + caller.dz * distance);
+				);
+			);
+		);
+	</action>
+	
+
+
+
+	<!-- vr_auto_click() - call this action in the onover event of a
+	     hotspot to trigger automatically a click after some time.  -->
+	<action name="vr_auto_click" scope="local" args="vr_aclk_timeout">
+		if(webvr.isenabled,
+			if(vr_aclk_timeout == null, set(vr_aclk_timeout, 2000));
+			copy(vr_aclk_t1, timertick);
+			set(vr_aclk_waiting, true);
+			copy(webvr.vr_aclk_hotspot, caller.name);
+			set(hotspot[vr_cursor].crop,'0|0|80|80');
+
+			asyncloop(vr_aclk_waiting AND webvr.vr_aclk_hotspot == caller.name,
+				sub(dt, timertick, vr_aclk_t1);
+
+				if(!caller.hovering,
+					set(vr_aclk_waiting, false);
+					set(hotspot[vr_cursor].crop,'0|0|80|80');
+				  ,
+					div(f, dt, vr_aclk_timeout);
+					mul(f, 16);
+					roundval(f);
+					Math.min(f, 16);
+					mul(f, 80);
+
+					txtadd(hotspot[vr_cursor].crop,get(f),'|0|80|80');
+
+					<!-- wait another 100ms delay after finishing the animation before doing the click -->
+					sub(dt, 100);
+					if(dt GT vr_aclk_timeout,
+						set(vr_aclk_waiting,false);
+						set(hotspot[vr_cursor].crop,'0|0|80|80');
+						<!-- call onclick -->
+						callwith(caller, scope(global, ondown();onup();onclick(); ); );
+					);
+				);
+			);
+		);
+	</action>
+
+
+	<!-- by pressing SPACE the headset could be re-centered -->
+	<events name="webvr_events" devices="html5" keep="true"
+	        onmousedown="if(webvr AND webvr.isenabled, webvr_showbuttons() );"
+	        />
+
+
+
+	<!-- when WebVR support is available show an EnterVR button -->
+	<action name="webvr_onavailable">
+		webvr.loadsettings();
+		if(layer[webvr_enterbutton], delayedcall(0.5, tween(layer[webvr_enterbutton].alpha,1.0); ); );
+	</action>
+	
+	
+	<action name="webvr_onunknowndevice">
+		if(webvr.isfake AND device.desktop AND webvr.havesettings == false,
+			<!-- set the 'no distortion' headset for fake desktop usage -->
+			set(webvr.mobilevr_lens_overlap, 1.0);
+			set(webvr.mobilevr_lens_fov, 96.0);
+			set(webvr.mobilevr_lens_dist, 0.0);
+			set(webvr.mobilevr_lens_dist2, '1|0|0|0');
+			set(webvr.mobilevr_lens_ca, 0.0);
+			set(webvr.mobilevr_lens_vign, 100);
+		  );
+	</action>
+
+
+	<action name="webvr_onentervr">
+		if(layer[webvr_enterbutton], tween(layer[webvr_enterbutton].alpha,0,0); );
+
+		webvr_showbuttons();
+		webvr_hide_all_non_vr_layers();
+
+		if(webvr.isfake, webvr_show_fakemode_info(true); );
+		
+		webvr_load_vr_cursor_hs();
+	</action>
+
+
+	<action name="webvr_onexitvr">
+		removehotspot('vr_cursor');
+		removehotspot('vr_controller_l');
+		removehotspot('vr_controller_r');
+		set(have_vr_controllers, false);
+		
+		stopdelayedcall(vr_button_fadeout);
+
+		if(layer[webvr_enterbutton], tween(layer[webvr_enterbutton].alpha,1); );
+		tween(layer[webvr_exitbutton].alpha,0);
+		tween(layer[webvr_setupbutton].alpha,0);
+		
+		webvr_show_fakemode_info(false);
+
+		webvr_restore_layers();
+	</action>
+	
+	
+	<action name="webvr_ondenied" scope="local">
+		addlayer(webvr_ondenied_info);
+		layer[webvr_ondenied_info].loadstyle(webvr_button_style);
+		set(layer[webvr_ondenied_info],
+			align='center',
+			html='Entering VR mode was denied!',
+			onclick='set(enabled,false); tween(alpha,0,0.5,default,removelayer(get(name)));'
+		);
+		delayedcall(2.0,
+			callwith(layer[webvr_ondenied_info], onclick(); );
+		);
+	</action>
+
+
+	<action name="webvr_hide_all_non_vr_layers" scope="local">
+		for(set(i,0), i LT layer.count, inc(i),
+			copy(lr, layer[get(i)]);
+			if(lr.vr !== true,
+				copy(lr.vr_backup_visible, lr.visible);
+				set(lr.visible, false);
+			);
+		);
+	</action>
+
+	<action name="webvr_restore_layers" scope="local">
+		for(set(i,0), i LT layer.count, inc(i),
+			copy(lr, layer[get(i)]);
+			if(lr.vr_backup_visible,
+				copy(lr.visible, lr.vr_backup_visible);
+				delete(lr.vr_backup_visible);
+			);
+		);
+	</action>
+	
+	<action name="webvr_show_fakemode_info" scope="local" args="show">
+		if(show == true,
+			addlayer(webvr_fakemode_info);
+			set(layer[webvr_fakemode_info],
+				type='text',
+				keep=true,
+				align='bottom',
+				y=80,
+				bg=false,
+				css='color:#FFFFFF;text-align:center;',
+				html='[i][u]Simulated WebVR Mode![/u][br]For real WebVR with headset tracking use a [a href="http://webvr.info" target="_blank" style="color:#FFFFFF;"]WebVR-capable[/a] browser or a mobile device and a VR headset.[/i]'
+			);
+		  ,
+			removelayer(webvr_fakemode_info);
+		);
+	</action>
+	
+	
+	<!-- ensure the same scaling on mobiles (regardless if mobilescale is 0.5 or 1.0) -->
+	<krpano webvr_setup_scale="calc:(1.0 + 1.0*(device.mobile AND stagescale LT 1.0)) / (1.0 + 1.0*device.mobile)"
+	        webvr_button_scale.normal="1.0"
+	        webvr_button_scale.mobile="1.6"
+	        />
+
+	
+	<!-- the EnterVR/ExitVR and SetupVR buttons -->
+	<style name="webvr_button_style"
+	       type="text"
+	       bgcolor="0x000000"
+	       bgalpha="0.5"
+	       bgroundedge="0"
+	       css="calc:'color:#FFFFFF;font-size:' + 20*webvr_setup_scale*webvr_button_scale + 'px;'"
+	       padding="calc:6*webvr_setup_scale*webvr_button_scale + ' ' + 10*webvr_setup_scale*webvr_button_scale"
+	       />
+	
+	<layer name="webvr_enterbutton" keep="true" vr="true"
+	       style="webvr_button_style"
+	       html="Enter VR"
+	       align="top" y="24"
+	       autoalpha="true" alpha="0.0"
+	       onclick="webvr.enterVR();"
+	       />
+
+	<layer name="webvr_exitbutton" keep="true" vr="true"
+	       style="webvr_button_style"
+	       html="Exit VR"
+	       align="top" y="24"
+	       autoalpha="true" alpha="0.0"
+	       onclick="webvr.exitVR();"
+	       />
+
+	<layer name="webvr_setupbutton" keep="true" vr="true"
+	       style="webvr_button_style"
+	       html="VR Setup"
+	       align="bottom" y="24"
+	       autoalpha="true" alpha="0.0"
+	       onclick="vrsetup_open();"
+	       />
+
+
+	<action name="webvr_showbuttons">
+		stopdelayedcall(vr_button_fadeout);
+		tween(layer[webvr_exitbutton].alpha|layer[webvr_setupbutton].alpha, 1.0|1.0, 0.25);
+		delayedcall(vr_button_fadeout, 3.0, tween(layer[webvr_exitbutton].alpha|layer[webvr_setupbutton].alpha, 0.0|0.0, 1.0); );
+	</action>
+
+	
+		
+	<!-- VR SETUP -->
+	
+	
+	<mobilevr_presets>
+		<headset name="cbv1" caption="Cardboard V1"  profile="80|60|42|35|0.441|0.156" />
+		<headset name="cbv2" caption="Cardboard V2"  profile="120|64|39|35|0.34|0.55" />
+		<headset name="dydm" caption="Daydream"      profile="104|60|41|35|0.42|0.51" />
+		<headset name="nodt" caption="No Distortion" profile="90|60|42|0|0|0" />
+	</mobilevr_presets>
+
+	<action name="vrsetup_open">
+		if(!vrsetup_open_js, vrsetup_init(); );
+		vrsetup_open_js();
+	</action>
+		
+
+	<action name="vrsetup_init" type="Javascript"><![CDATA[
+		
+		var webvr = krpano.webvr;
+		var padding = 20;
+		
+		function vrsetup_dlg_create(type)
+		{
+			var dlg = {type:type, bg:null, y:0, scale:1, elements:[]};
+		
+			if (type == 'layer')
+			{
+				dlg.bg = krpano.addlayer();
+				dlg.bg.keep = true;
+				dlg.bg.type = 'container';
+				dlg.bg.align = 'center';
+			}
+			else	// 'hotspot'
+			{
+				dlg.scale = 0.15;
+				dlg.bg = krpano.addhotspot();
+				dlg.bg.keep = true;
+				dlg.bg.type = 'text';
+				dlg.bg.distorted = true;
+				dlg.bg.ath = krpano.view.hlookat;
+				dlg.bg.atv = 0;
+				dlg.bg.depth = 150;
+				dlg.bg.torigin = 'world';
+				dlg.bg.tx = krpano.view.tx;
+				dlg.bg.ty = krpano.view.ty;
+				dlg.bg.tz = krpano.view.tz;
+			}
+			
+			dlg.bg.bgcolor = 0x000000;
+			dlg.bg.bgalpha = 0.5;
+			dlg.bg.bgcapture = true;
+			dlg.bg.handcursor = false;
+			dlg.bg.capture = false;
+			dlg.bg.zorder = 100;
+			dlg.bg.visible = false;
+			
+			dlg.y = 0;
+			
+			return dlg;
+		}
+		
+		function vrsetup_dlg_addline(dlg, linetext, customcss, onclick)
+		{
+			var txt;
+			
+			if (dlg.type == 'layer')
+			{
+				txt = krpano.addlayer();
+				txt.keep = true;
+				txt.type = 'text';
+				txt.align = 'center';
+				txt.zorder = 101;
+			}
+			else	// 'hotspot'
+			{
+				txt = krpano.addhotspot();
+				txt.keep = true;
+				txt.type = 'text';
+				txt.distorted = true;
+				txt.zorder = 101;
+				txt.ath = dlg.bg.ath;
+				txt.atv = dlg.bg.atv;
+				txt.depth = dlg.bg.depth - 1;
+				txt.oversampling = 2;
+				txt.scale = dlg.scale;
+				txt.torigin = dlg.bg.torigin;
+				txt.tx = dlg.bg.tx;
+				txt.ty = dlg.bg.ty;
+				txt.tz = dlg.bg.tz;
+			}
+			
+			txt.onautosized = function()
+			{
+				txt.havesize = true;
+			}
+			
+			txt.edge = 'top';
+			txt.visible = false;
+			txt.bg = false;
+			txt.html = "" + linetext;
+			txt.css = 'font-size:32px;font-weight:bold;color:#FFFFFF; line-height:90%;' + (customcss || '');
+			
+			if (onclick)
+			{
+				txt.onclick = onclick;
+			}
+			else
+			{
+				txt.enabled = false;
+			}
+			
+			dlg.elements.push( txt );
+			
+			return txt;
+		}
+		
+		function vrsetup_dlg_addctrl(dlg, changedelay, callback)
+		{
+			var txt = vrsetup_dlg_addline(dlg, callback(0) );
+			
+			var inc;
+			var dec;
+			
+			if(dlg.type == 'layer')
+			{
+				inc = krpano.addlayer();
+				inc.keep = true;
+				inc.type = 'text';
+				inc.align = 'center';
+				inc.zorder = 101;
+				
+				dec = krpano.addlayer();
+				dec.keep = true;
+				dec.type = 'text';
+				dec.align = 'center';
+				dec.zorder = 101;
+			}
+			else	// 'hotspot'
+			{
+				inc = krpano.addhotspot();
+				inc.keep = true;
+				inc.type = 'text';
+				inc.distorted = true;
+				inc.zorder = 101;
+				inc.ath = dlg.bg.ath;
+				inc.atv = dlg.bg.atv;
+				inc.depth = dlg.bg.depth - 1;
+				inc.oversampling = 2;
+				inc.scale = dlg.scale;
+				inc.torigin = dlg.bg.torigin;
+				inc.tx = dlg.bg.tx;
+				inc.ty = dlg.bg.ty;
+				inc.tz = dlg.bg.tz;
+				
+				dec = krpano.addhotspot();
+				dec.keep = true;
+				dec.type = 'text';
+				dec.distorted = true;
+				dec.zorder = 101;
+				dec.ath = dlg.bg.ath;
+				dec.atv = dlg.bg.atv;
+				dec.depth = dlg.bg.depth - 1;
+				dec.oversampling = 2;
+				dec.scale = dlg.scale;
+				dec.torigin = dlg.bg.torigin;
+				dec.tx = dlg.bg.tx;
+				dec.ty = dlg.bg.ty;
+				dec.tz = dlg.bg.tz;
+			}
+			
+			inc.edge = 'top';
+			inc.visible = false;
+			inc.bg = false;
+			inc.html = '&#62;';
+			inc.css = 'font-size:32px;font-weight:bold;color:#FFFFFF; line-height:90%;';
+			inc.padding = '0 10';
+					
+			dec.edge = 'top';
+			dec.visible = false;
+			dec.bg = false;
+			dec.html = '&#60;';
+			dec.css = 'font-size:32px;font-weight:bold;color:#FFFFFF; line-height:90%;';
+			dec.padding = '0 10';
+			
+			inc.vr_timeout = changedelay * 1000;
+			dec.vr_timeout = changedelay * 1000;
+			inc.ondown = function(){ txt.html = ""+callback(+1); inc.enabled = false; setTimeout(function(){ inc.enabled = true; },0); };
+			dec.ondown = function(){ txt.html = ""+callback(-1); dec.enabled = false; setTimeout(function(){ dec.enabled = true; },0); };
+			
+			txt.ctrlchilds = [inc,dec];
+			
+			txt.updateControl = function()
+			{
+				txt.html = callback(0);
+			}
+			
+			return txt;
+		}
+		
+		function vrsetup_dlg_addspace(dlg, customspace)
+		{
+			dlg.elements.push(customspace|| padding);
+		}
+		
+		function vrsetup_dlg_finish(dlg)
+		{
+			var i, w=0, h=0;
+			var waitforsizes=false;
+			
+			for (i=0; i < dlg.elements.length; i++)
+			{
+				var e = dlg.elements[i];
+				if ( isNaN(e) )
+				{
+					if ( e.havesize )
+					{
+						w = Math.max( w, e.width * 1);
+						h += e.height * 1;
+					}
+					else
+					{
+						waitforsizes = true;
+						break;
+					}
+				}
+				else
+				{
+					h += e;
+				}
+			}
+			
+			if (waitforsizes)
+			{
+				setTimeout( function(){ vrsetup_dlg_finish(dlg); }, 16 ); 
+			}
+			else
+			{
+				var y = 0;
+				
+				dlg.bg.width = Math.ceil((w + padding*2)*dlg.scale);
+				dlg.bg.height = Math.ceil((h + padding*2)*dlg.scale);
+					
+				for (i=0; i < dlg.elements.length; i++)
+				{
+					var e = dlg.elements[i];
+					if ( isNaN(e) )
+					{
+						e.oy = Math.round((-h/2 + y) * dlg.scale);
+						e.visible = true;
+						
+						y += e.height * 1;
+						
+						if (e.ctrlchilds )
+						{
+							for (var j=0; j < e.ctrlchilds.length; j++)
+							{
+								var sube = e.ctrlchilds[j];
+								sube.ox = Math.round(((j&1)-0.5) * (-w) * dlg.scale);
+								sube.oy = e.oy;
+								sube.visible = true;
+							}
+						}
+					}
+					else
+					{
+						y += e;
+					}
+				}
+				
+				dlg.bg.visible = true;
+			}
+		}
+		
+		function vrsetup_dlg_remove(dlg)
+		{
+			var removefu = dlg.type == 'layer' ? krpano.removelayer : krpano.removehotspot;
+			
+			var i,j;
+			for (i=0; i < dlg.elements.length; i++)
+			{
+				var e = dlg.elements[i];
+				if ( isNaN(e) )
+				{
+					if (e.ctrlchilds )
+					{
+						for (j=0; j < e.ctrlchilds.length; j++)
+						{
+							var sube = e.ctrlchilds[j];
+							removefu(sube.name);
+						}
+					}
+					
+					removefu(e.name);
+				}
+			}
+			
+			removefu(dlg.bg.name);
+		}
+		
+		
+		function vrsetup_webvr_dialog()
+		{
+			// WebVR API rendering
+			var dlg = vrsetup_dlg_create('hotspot');
+			vrsetup_dlg_addline(dlg, 'WebVR Setup');
+			vrsetup_dlg_addspace(dlg);
+			
+			vrsetup_dlg_addline(dlg, 'Oversampling:', 'font-size:16px;')
+			var ctrl_ss = vrsetup_dlg_addctrl(dlg, 1.0, function(change)
+			{
+				var p = webvr.oversampling;
+				
+				if (change < 0) { p = Math.max(0.2, Number(p) - 0.1); webvr.oversampling = p;  } else 
+				if (change > 0) { p = Math.min(4.0, Number(p) + 0.1); webvr.oversampling = p;  }
+				
+				krpano.actions.delayedcall(0.3, function()
+				{
+					renderres.html = webvr.renderwidth + "x" + webvr.renderheight;
+				});
+				
+				return p.toFixed(1);
+			});
+			vrsetup_dlg_addspace(dlg,8);
+			vrsetup_dlg_addline(dlg, 'Rendering Resolution:', 'font-size:16px;');
+			var renderres = vrsetup_dlg_addline(dlg, '');
+			vrsetup_dlg_addspace(dlg);
+			vrsetup_dlg_addline(dlg, 'CLOSE', '', vrsetup_close);
+			vrsetup_dlg_finish(dlg);
+			
+			return dlg;
+		}
+		
+		function vrsetup_mobilevr_dialog()
+		{
+			// MobileVR / Cardboard rendering
+			var dlg = vrsetup_dlg_create(0 ? 'hotspot' : 'layer');
+			vrsetup_dlg_addline(dlg, 'MobileVR SETUP');
+			vrsetup_dlg_addspace(dlg);
+			vrsetup_dlg_addline(dlg, 'Screensize (inch):', 'font-size:16px;')
+			vrsetup_dlg_addctrl(dlg, 1.0, function(change){ var ss = Number(webvr.mobilevr_screensize); if (isNaN(ss)) ss = 5.0; if (change < 0) { ss = Math.max(4.0, ss - 0.1); webvr.mobilevr_screensize = ss; } else if (change > 0) { ss = Math.min(10.0, ss + 0.1); webvr.mobilevr_screensize = ss; } return ss.toFixed(1); });
+			vrsetup_dlg_addspace(dlg);
+			vrsetup_dlg_addline(dlg, 'VR Headset Preset:', 'font-size:16px;')
+			var ctrl_ps = vrsetup_dlg_addctrl(dlg, 1.0, function(change)
+			{
+				var preset_index = -1;
+				var i;
+				
+				var profile = webvr.mobilevr_profile;
+				var presets = krpano.get("mobilevr_presets.headset");
+				if (presets)
+				{
+					presets = presets.getArray();
+					
+					for (i=0; i < presets.length; i++)
+					{
+						if ( profile == presets[i].profile )
+						{
+							preset_index = i;
+							break;
+						}
+					}
+					
+					if (change < 0)
+					{
+						preset_index--;
+						if (preset_index < 0)
+							preset_index = presets.length - 1;
+								
+						webvr.mobilevr_profile = presets[preset_index].profile;
+					}
+					else if (change > 0)
+					{
+						preset_index++;
+						if (preset_index >= presets.length)
+							preset_index = 0;
+						
+						webvr.mobilevr_profile = presets[preset_index].profile;
+					}
+				}
+					
+				if (preset_index >= 0)
+				{
+					return presets[preset_index].caption;
+				}
+				
+				return 'Custom';
+			});
+			vrsetup_dlg_addspace(dlg);
+			vrsetup_dlg_addline(dlg, 'Customize Headset', 'font-size:25px;', function()
+			{
+				vrsetup_dlg_remove(dlg); 
+				dlg=null; 
+				
+				vrsetup_dialog = vrsetup_mobilevr_interactive_dialog();
+				
+			});
+			vrsetup_dlg_addline(dlg, '(Interactive Adjustment in VR)', 'font-size:12px;');
+			vrsetup_dlg_addspace(dlg);
+			vrsetup_dlg_addline(dlg, 'CLOSE', '', vrsetup_close);
+			vrsetup_dlg_finish(dlg);
+			
+			return dlg;
+		}
+		
+		function vrsetup_mobilevr_interactive_dialog()
+		{
+			// MobileVR / Cardboard rendering
+			var ctrl_preset, ctrl_fov, ctrl_ild, ctrl_stl, ctrl_ttl, ctrl_k1, ctrl_k2, ctrl_os;
+			
+			var dlg = vrsetup_dlg_create('hotspot');
+			vrsetup_dlg_addline(dlg, 'MobileVR SETUP');
+			vrsetup_dlg_addspace(dlg);
+			vrsetup_dlg_addline(dlg, 'Preset:', 'font-size:16px;')
+			
+			ctrl_preset = vrsetup_dlg_addctrl(dlg, 1.0, function(change)
+			{
+				var preset_index = -1;
+				var i;
+				
+				var profile = webvr.mobilevr_profile;
+				var presets = krpano.get("mobilevr_presets.headset");
+				if (presets)
+				{
+					presets = presets.getArray();
+					
+					for (i=0; i < presets.length; i++)
+					{
+						if ( profile == presets[i].profile )
+						{
+							preset_index = i;
+							break;
+						}
+					}
+					
+					if (change < 0)
+					{
+						preset_index--;
+						if (preset_index < 0)
+							preset_index = presets.length - 1;
+								
+						webvr.mobilevr_profile = presets[preset_index].profile;
+					}
+					else if (change > 0)
+					{
+						preset_index++;
+						if (preset_index >= presets.length)
+							preset_index = 0;
+						
+						webvr.mobilevr_profile = presets[preset_index].profile;
+					}
+					
+					if (change != 0)
+					{
+						ctrl_fov.updateControl();
+						ctrl_ild.updateControl();
+						ctrl_stl.updateControl();
+						if (ctrl_ttl) ctrl_ttl.updateControl();
+						ctrl_k1.updateControl();
+						ctrl_k2.updateControl();
+					}
+				}
+			
+				if (preset_index >= 0)
+				{
+					return presets[preset_index].caption;
+				}
+				
+				return 'Custom';
+			});
+			
+			vrsetup_dlg_addspace(dlg);
+			vrsetup_dlg_addline(dlg, 'Lens-Field-of-View:', 'font-size:16px;');
+			ctrl_fov = vrsetup_dlg_addctrl(dlg, 0.25, function(change){ var p = webvr.mobilevr_profile.split("|"); if (change < 0) { p[0] = Number(p[0]) - 1.0; webvr.mobilevr_profile = p.join("|"); } else if (change > 0) { p[0] = Number(p[0]) + 1.0; webvr.mobilevr_profile = p.join("|"); } if (change != 0) ctrl_preset.updateControl(); return Number(p[0]).toFixed(0); });
+			vrsetup_dlg_addspace(dlg, 8);
+			vrsetup_dlg_addline(dlg, 'Inter-Lens-Distance (mm):', 'font-size:16px;');
+			ctrl_ild = vrsetup_dlg_addctrl(dlg, 0.25, function(change){ var p = webvr.mobilevr_profile.split("|"); if (change < 0) { p[1] = Number(p[1]) - 1.0; webvr.mobilevr_profile = p.join("|"); } else if (change > 0) { p[1] = Number(p[1]) + 1.0; webvr.mobilevr_profile = p.join("|"); } if (change != 0) ctrl_preset.updateControl(); return Number(p[1]).toFixed(0); });
+			vrsetup_dlg_addspace(dlg, 8);
+			vrsetup_dlg_addline(dlg, 'Screen-to-Lens-Distance (mm):', 'font-size:16px;');
+			ctrl_stl = vrsetup_dlg_addctrl(dlg, 0.25, function(change){ var p = webvr.mobilevr_profile.split("|"); if (change < 0) { p[2] = Number(p[2]) - 1.0; webvr.mobilevr_profile = p.join("|"); } else if (change > 0) { p[2] = Number(p[2]) + 1.0; webvr.mobilevr_profile = p.join("|"); } if (change != 0) ctrl_preset.updateControl(); return Number(p[2]).toFixed(0); });
+			vrsetup_dlg_addspace(dlg, 8);
+			if (!webvr.isfake)
+			{
+				vrsetup_dlg_addline(dlg, 'Tray-to-Lens-Center-Distance: (mm):', 'font-size:16px;');
+				ctrl_ttl = vrsetup_dlg_addctrl(dlg, 0.25, function(change){ var p = webvr.mobilevr_profile.split("|"); if (change < 0) { p[3] = Number(p[3]) - 1.0; webvr.mobilevr_profile = p.join("|"); } else if (change > 0) { p[3] = Number(p[3]) + 1.0; webvr.mobilevr_profile = p.join("|"); } if (change != 0) ctrl_preset.updateControl(); return Number(p[3]).toFixed(0); });
+				vrsetup_dlg_addspace(dlg, 8);
+			}
+			vrsetup_dlg_addline(dlg, 'Lens-Distortion Coefficients:', 'font-size:16px;');
+			ctrl_k1 = vrsetup_dlg_addctrl(dlg, 0.1, function(change){ var p = webvr.mobilevr_profile.split("|"); if (change < 0) { p[4] = Number(p[4]) - 0.01; webvr.mobilevr_profile = p.join("|"); } else if (change > 0) { p[4] = Number(p[4]) + 0.01; webvr.mobilevr_profile = p.join("|"); } if (change != 0) ctrl_preset.updateControl(); return Number(p[4]).toFixed(3); });
+			ctrl_k2 = vrsetup_dlg_addctrl(dlg, 0.1, function(change){ var p = webvr.mobilevr_profile.split("|"); if (change < 0) { p[5] = Number(p[5]) - 0.01; webvr.mobilevr_profile = p.join("|"); } else if (change > 0) { p[5] = Number(p[5]) + 0.01; webvr.mobilevr_profile = p.join("|"); } if (change != 0) ctrl_preset.updateControl(); return Number(p[5]).toFixed(3); });
+			vrsetup_dlg_addspace(dlg);
+			vrsetup_dlg_addline(dlg, 'Oversampling:', 'font-size:16px;');
+			ctrl_os = vrsetup_dlg_addctrl(dlg, 0.25, function(change){ var p = webvr.oversampling; if (change < 0) { p = Math.max(0.2, Number(p) - 0.1); webvr.oversampling = p;  } else if (change > 0) { p = Math.min(4.0, Number(p) + 0.1); webvr.oversampling = p;  } return p.toFixed(1); });
+			vrsetup_dlg_addspace(dlg);
+			vrsetup_dlg_addline(dlg, 'CLOSE', '', vrsetup_close);
+			vrsetup_dlg_finish(dlg);
+			
+			return dlg;
+		}
+		
+		var vrsetup_dialog = null;
+		var vrsetup_events = null;
+		
+		function vrsetup_close()
+		{
+			if (vrsetup_dialog)
+			{
+				webvr.savesettings();
+			
+				vrsetup_dlg_remove(vrsetup_dialog); 
+				vrsetup_dialog = null;
+			}
+		
+			if (vrsetup_events)
+			{
+				krpano.events.removeItem(vrsetup_events.name);
+				vrsetup_events = null;
+			}
+		}
+		
+		krpano.vrsetup_close = function()
+		{
+			vrsetup_close();
+		}
+		
+		krpano.vrsetup_open_js = function()
+		{
+			if (vrsetup_dialog != null)
+			{
+				// already open...
+				return;
+			}
+				
+			if (webvr.iswebvr)
+			{
+				vrsetup_dialog = vrsetup_webvr_dialog();
+			}
+			else
+			{
+				vrsetup_dialog = vrsetup_mobilevr_dialog();
+			}
+			
+			vrsetup_events = krpano.events.createItem("auto");
+			vrsetup_events.keep = true;
+			
+			vrsetup_events.webvr_onexitvr = function()
+			{
+				vrsetup_close();
+			}
+		}
+		
+	]]></action>
+	
+
+	<!-- A list of devices and their screensize -->
+	<mobilevr_device_database>
+		<device name="iPhone 5/5S/SE" screen="320x568x2" size="4.0" bevel="3" />
+		<device name="iPhone 6/6S/7/8" screen="375x667x2" size="4.7" />
+		<device name="iPhone 6/6S/7/8 Plus" screen="414x736" size="5.5" />
+		<device name="iPhone 6/6S/7/8 Plus (zoomed)" screen="375x667x3" size="5.5" />
+		<device name="iPhone X/XS/11Pro" screen="375x812x3" size="5.85|5.33" />
+		<device name="iPhone XS/11Pro Max" screen="414x896x3" size="6.46|5.95" />
+		<device name="iPhone XR/11" screen="414x896x2" size="6.06|5.58" />
+		<device name="iPhone 12/12Pro" screen="390x844" size="6.1" />
+		<device name="iPhone 12Pro Max" screen="428x926" size="6.7" />
+		<device name="Samsung S6" ua="sm-g930" size="5.1" />
+		<device name="Samsung S6 Edge" ua="sm-g925" size="5.1" />
+		<device name="Samsung S6 Edge Plus" ua="sm-g928" size="5.7" />
+		<device name="Samsung S7" ua="sm-g930" size="5.1" />
+		<device name="Samsung S7 Edge" ua="sm-g935" size="5.5" />
+		<device name="Samsung S8" ua="sm-g950" size="5.8" />
+		<device name="Samsung S8+" ua="sm-g955" size="6.2" />
+		<device name="Samsung S9" ua="sm-g960" size="5.8" />
+		<device name="Samsung S9+" ua="sm-g965" size="6.2" />
+		<device name="Samsung S10e" ua="sm-g970" size="5.8" />
+		<device name="Samsung S10" ua="sm-g973" size="6.1" />
+		<device name="Samsung S10+" ua="sm-g975" size="6.4" />
+		<device name="Samsung S10 5G" ua="sm-g977" size="6.7" />
+		<device name="Samsung Note 7" ua="sm-n930" size="5.7" />
+		<device name="Samsung Note 8" ua="sm-n950" size="6.3" />
+		<device name="Samsung Note 9" ua="sm-n960" size="6.4" />
+		<device name="Samsung Note 10" ua="sm-n970" size="6.3" />
+		<device name="Samsung Note 10 5G" ua="sm-n971" size="6.3" />
+		<device name="Samsung Note 10+" ua="sm-n975" size="6.8" />
+		<device name="Samsung Note 10+ 5G" ua="sm-n976" size="6.8" />
+		<device name="Huawei P10 Lite" ua="HUAWEIWAS-TL10" size="5.2" />
+		<device name="Huawei P20 Lite" ua="HUAWEIANE-LX1" size="5.84" />
+	</mobilevr_device_database>
+
+</krpano>

BIN
public/plugins/webvr_handcursor.png


BIN
public/plugins/webvr_laser.png


BIN
public/plugins/webvr_light.png


BIN
public/plugins/webvr_vrcursor.png


BIN
public/skin/rotate_device.png


+ 33 - 0
public/skin/videointerface.xml

@@ -0,0 +1,33 @@
+<krpano>
+	<action name="skin_video_setup">
+		if(skin_video_setup_done !== true,
+			set(skin_video_setup_done, true);
+
+			<!-- use the video events for getting state updates -->
+			set(plugin[video].onvideoready, jscall(window.onVideoSceneReady()););
+			set(plugin[video].onvideoplay, jscall(window.onVideoScenePlay()););
+			set(plugin[video].onvideopaused, jscall(window.onVideoScenePaused()););
+			set(plugin[video].onvideocomplete, jscall(window.onVideoSceneComplete()););
+			set(plugin[video].onerror, jscall(calc('window.onVideoSceneError("' + videoerror + '")')));
+		  );
+	</action>
+
+	<!-- videointerface_addsource (name, videourl, posterurl)
+		%1 - name
+		%2 - videourl
+		%3 - posterurl
+	-->
+	<action name="videointerface_addsource">
+		<!-- init/setup the video interface skin -->
+		skin_video_setup();
+
+		set(videosources['v%1'].sourcename, '%1');
+		set(videosources['v%1'].videourl, '%2');
+		set(videosources['v%1'].posterurl, '%3');
+	</action>
+
+	<action name="videointerface_play">
+		copy(vs, videosources['v%1']);
+		plugin[video].playvideo(get(vs.videourl), get(vs.posterurl), get(plugin[video].pausedonstart), get(plugin[video].time));
+	</action>
+</krpano>

BIN
public/skin/vtourskin.png


BIN
public/skin/vtourskin_light.png


+ 177 - 0
src/App.tsx

@@ -0,0 +1,177 @@
+import '@/assets/styles/base.css'
+// 关于路由
+import React, { useCallback, useEffect, useRef } from 'react'
+import { Router, Route, Switch } from 'react-router-dom'
+import history from './utils/history'
+import SpinLoding from './components/SpinLoding'
+
+import NotFound from '@/components/NotFound'
+import store, { RootState } from './store'
+import { baseURL, isLoc, myData } from './utils/http'
+import AsyncSpinLoding from './components/AsyncSpinLoding'
+import { useSelector } from 'react-redux'
+import Zguide from './components/Zguide'
+
+// import Vconsole from 'vconsole'
+// new Vconsole()
+
+// import { isLoc, myData } from './utils/http'
+const A1home = React.lazy(() => import('./pages/A1home'))
+const A1_1base = React.lazy(() => import('./pages/A0base'))
+const A2yblm = React.lazy(() => import('./pages/A2yblm'))
+const A3beie = React.lazy(() => import('./pages/A3beie'))
+const A4quanwen = React.lazy(() => import('./pages/A4quanwen'))
+const A5wenwu = React.lazy(() => import('./pages/A5wenwu'))
+
+declare global {
+  //设置全局属性
+  interface Window {
+    //window对象属性
+    isHH: boolean //加入对象
+    unityOpenHot: (name: string) => void
+    unityBack: () => void
+  }
+}
+
+let tempW = document.documentElement.clientWidth
+let tempH = document.documentElement.clientHeight
+
+let tempMax = tempW >= tempH ? tempW : tempH
+let tempMin = tempW >= tempH ? tempH : tempW
+
+const pageBi = Math.round(Number((tempMax / tempMin).toFixed(2)))
+
+// 设计图按照 844 X 390 来开发
+const planSize = {
+  width: 844,
+  height: Math.round(Number((844 / pageBi).toFixed(0)))
+}
+
+export default function App() {
+  useEffect(() => {
+    // 打包环境 刷新页面从 首页 开始
+    // if (!isLoc && myData.isLdong) {
+    //   if (window.location.hash !== '#/') {
+    //     window.location.href = window.location.origin
+    //   }
+    // }
+  }, [])
+
+  // 根元素
+  const rootRef = useRef<any>(null)
+
+  const pageFullChangeFu = useCallback(() => {
+    let width = document.documentElement.clientWidth
+    let height = document.documentElement.clientHeight
+
+    let isHHTemp = false
+
+    if (width >= height) {
+      if (tempMax - width > 100) return
+
+      //横屏
+      isHHTemp = true
+      const sizeW = width / planSize.width
+      let sizeH = height / planSize.height
+
+      let moveX = (planSize.width - width) / 2
+      let moveY = (planSize.height - height) / 2
+
+      if (width >= planSize.width) moveX = 0
+      rootRef.current.style.left = '0'
+      rootRef.current.style.transform = `translate(${-moveX}px,${-moveY}px) scale(${sizeW},${sizeH}) rotate(0deg)`
+      rootRef.current.style.transformOrigin = 'center'
+
+      store.dispatch({
+        type: 'layout/style',
+        payload: {
+          width: planSize.width,
+          moveX: -moveX,
+          moveY: -moveY,
+          sizeW,
+          sizeH
+        }
+      })
+    } else {
+      if (tempMax - height > 100) return
+
+      // 竖屏
+      isHHTemp = false
+      const temp = width
+      width = height
+      height = temp
+
+      const sizeW = width / planSize.width
+      let sizeH = height / planSize.height
+
+      rootRef.current.style.left = '100%'
+      rootRef.current.style.transform = `rotate(90deg) scale(${sizeW},${sizeH})`
+
+      rootRef.current.style.transformOrigin = 'left top'
+    }
+
+    // 横竖屏变化的时候 刷新页面
+
+    // if (window.isHH !== isHHTemp) {
+    //   window.location.reload()
+    // }
+
+    store.dispatch({ type: 'layout/isHH', payload: isHHTemp })
+  }, [])
+
+  const audioPlayFu = useCallback(() => {
+    // 播放背景音乐
+    const audioDom: HTMLAudioElement = document.querySelector('#bgMp3')!
+    if (audioDom) {
+      if (audioDom.paused) audioDom.play()
+      else rootRef.current.removeEventListener('click', audioPlayFu)
+    }
+  }, [])
+
+  useEffect(() => {
+    rootRef.current = document.querySelector('#root')
+    rootRef.current.style.width = planSize.width + 'px'
+    rootRef.current.style.height = planSize.height + 'px'
+
+    pageFullChangeFu()
+    window.addEventListener('resize', pageFullChangeFu, false)
+
+    // 全局点击都播放背景音乐
+    if (myData.isLdong && !isLoc) rootRef.current.addEventListener('click', audioPlayFu)
+  }, [audioPlayFu, pageFullChangeFu])
+
+  // 分享和点赞
+  const { guideVideo } = useSelector((state: RootState) => state.A0Layout)
+
+  return (
+    <>
+      {/* 关于路由 */}
+      <Router history={history}>
+        <React.Suspense fallback={<SpinLoding />}>
+          <Switch>
+            <Route path='/' component={A1home} exact />
+            <Route path='/base' component={A1_1base} exact />
+            <Route path='/yblm' component={A2yblm} exact />
+            <Route path='/beie' component={A3beie} exact />
+            <Route path='/quanwen' component={A4quanwen} exact />
+            <Route path='/wenwu' component={A5wenwu} exact />
+            <Route path='*' component={NotFound} />
+          </Switch>
+        </React.Suspense>
+      </Router>
+
+      {/* 背景音乐 */}
+      <audio id='bgMp3' loop style={{ display: 'none' }} src={baseURL + 'home/bg.mp3'}></audio>
+
+      {/* 发送请求的加载组件 */}
+      <AsyncSpinLoding />
+      {/* 操作指引视频 */}
+      {guideVideo ? (
+        <Zguide
+          src={guideVideo}
+          closeFu={() => store.dispatch({ type: 'layout/guideVideo', payload: '' })}
+        />
+      ) : null}
+    </>
+  )
+}

+ 124 - 0
src/AppTemp.tsx

@@ -0,0 +1,124 @@
+import '@/assets/styles/base.css'
+// 关于路由
+import React, { useCallback, useEffect, useRef } from 'react'
+import { Router, Route, Switch } from 'react-router-dom'
+import history, { isMobiileFu } from './utils/history'
+import SpinLoding from './components/SpinLoding'
+// import { Image } from 'antd'
+// import { useSelector } from 'react-redux'
+// import store, { RootState } from './store'
+// import MessageCom from './components/Message'
+// import LookDom from './components/LookDom'
+import NotFound from '@/components/NotFound'
+const A1home = React.lazy(() => import('./pages/A1home'))
+
+// 设计图按照 1920 X 919 来开发
+const planSize = {
+  width: isMobiileFu() ? 414 : 1920,
+  height: isMobiileFu() ? 736 : 919
+}
+
+export default function App() {
+  // 从仓库中获取查看图片的信息
+  // const lookBigImg = useSelector((state: RootState) => state.A0Layout.lookBigImg)
+
+  const rootRef = useRef<any>(null)
+
+  // 移动端存下来 最小屏幕数据 防止打开输入框的时候压缩高度
+  const moBaseObj = useRef({
+    num: 0,
+    scaleH: 0,
+    moveY: 0
+  })
+
+  const pageFullChangeFu = useCallback(() => {
+    let width = document.documentElement.clientWidth
+    let height = document.documentElement.clientHeight
+
+    // 屏幕改变的时候改变字体大小,这个项目暂时用不上
+    // // 1920 = 16px
+    // const bei = 1920 / 16;
+    // let res = Math.round(width / bei);
+    // if (res <= 10) res = 10;
+    // rootRef.current.style.setProperty("--fontNum", res + "px");
+
+    // 移动端开发 宽度限制最大500px
+    if (planSize.width <= 500) {
+      width = width >= 500 ? 500 : width
+      rootRef.current.style.margin = '0 auto'
+    }
+
+    const sizeW = width / planSize.width
+    let sizeH = height / planSize.height
+
+    const moveX = (planSize.width - width) / 2
+    let moveY = (planSize.height - height) / 2
+
+    // 移动端
+    if (height < moBaseObj.current.num) {
+      // 打开了键盘
+      document.querySelector('body')!.style.overflow = 'auto'
+      sizeH = moBaseObj.current.scaleH
+      moveY = moBaseObj.current.moveY
+    } else document.querySelector('body')!.style.overflow = 'hidden'
+
+    // console.log("-------", width, moveX);
+    rootRef.current.style.transform = `translate(${-moveX}px,${-moveY}px) scale(${sizeW},${sizeH})`
+  }, [])
+
+  useEffect(() => {
+    rootRef.current = document.querySelector('#root')
+    rootRef.current.style.width = planSize.width + 'px'
+    rootRef.current.style.height = planSize.height + 'px'
+
+    // 移动端
+    if (planSize.width <= 500) {
+      const height = document.documentElement.clientHeight
+
+      moBaseObj.current = {
+        num: height,
+        scaleH: height / planSize.height,
+        moveY: (planSize.height - height) / 2
+      }
+    }
+
+    pageFullChangeFu()
+
+    window.addEventListener('resize', pageFullChangeFu, false)
+  }, [pageFullChangeFu])
+
+  return (
+    <>
+      {/* 关于路由 */}
+      <Router history={history}>
+        <React.Suspense fallback={<SpinLoding />}>
+          <Switch>
+            <Route path='/' component={A1home} exact />
+            <Route path='*' component={NotFound} />
+          </Switch>
+        </React.Suspense>
+      </Router>
+
+      {/* 所有图片点击预览查看大图 */}
+      {/* <Image
+        preview={{
+          visible: lookBigImg.show,
+          src: lookBigImg.url,
+          onVisibleChange: value => {
+            // 清除仓库信息
+            store.dispatch({
+              type: 'layout/lookBigImg',
+              payload: { url: '', show: false }
+            })
+          }
+        }}
+      /> */}
+
+      {/* 查看视频音频 */}
+      {/* <LookDom /> */}
+
+      {/* antd 轻提示 ---兼容360浏览器 */}
+      {/* <MessageCom /> */}
+    </>
+  )
+}

BIN
src/assets/img/A0_down.png


BIN
src/assets/img/A0_right.png


BIN
src/assets/img/A2_quanwen.png


BIN
src/assets/img/A2_wenwu.png


BIN
src/assets/img/A5_gesture.png


BIN
src/assets/img/A5_line.png


BIN
src/assets/img/A5_lineAc.png


BIN
src/assets/img/A5_reset.png


BIN
src/assets/img/A5_size.png


BIN
src/assets/img/A5_sizeAc.png


BIN
src/assets/img/A5_tapian.png


BIN
src/assets/img/A5_tapianAc.png


BIN
src/assets/img/A5_zoomin.png


BIN
src/assets/img/A5_zoomout.png


BIN
src/assets/img/IMGerror.png


BIN
src/assets/img/beie.png


BIN
src/assets/img/bg_base.png


BIN
src/assets/img/bg_home.png


BIN
src/assets/img/btn_ModalTxt_bg.png


BIN
src/assets/img/btn_ModalTxt_bg_ac.png


BIN
src/assets/img/btn_back.png


BIN
src/assets/img/btn_home.png


BIN
src/assets/img/btn_menu.png


BIN
src/assets/img/btn_tab.png


BIN
src/assets/img/btn_tabAc.png


BIN
src/assets/img/butterflyL.png


BIN
src/assets/img/butterflyR.png


BIN
src/assets/img/close.png


BIN
src/assets/img/closeWithTxt.png


BIN
src/assets/img/detailTxt_l.png


BIN
src/assets/img/detailTxt_r.png


BIN
src/assets/img/guideVideo.png


BIN
src/assets/img/highlight.png


BIN
src/assets/img/highlight_ac.png


BIN
src/assets/img/home.png


BIN
src/assets/img/icon_dot1.png


BIN
src/assets/img/icon_knowledge.png


BIN
src/assets/img/icon_paint.png


BIN
src/assets/img/icon_poster.png


BIN
src/assets/img/interaction.png


BIN
src/assets/img/interactionBg.png


BIN
src/assets/img/loading.gif


BIN
src/assets/img/menuSider.png


BIN
src/assets/img/menuSiderBtn.png


BIN
src/assets/img/tip.png


BIN
src/assets/img/titleLogo.png


BIN
src/assets/img/tooltipBg.png


+ 294 - 0
src/assets/styles/base.css

@@ -0,0 +1,294 @@
+* {
+  margin: 0;
+  padding: 0;
+  box-sizing: border-box;
+  word-wrap: break-word;
+  -webkit-tap-highlight-color: transparent;
+  /* font-family: qfk; */
+}
+/* 全局css变量 */
+:root {
+  --themeColor: #733c00;
+  --themeColor2: #ead98f;
+  --fontNum: 16px;
+}
+html {
+  height: 100%;
+  user-select: none;
+}
+body {
+  position: relative;
+  font: 1em/1.4 'Microsoft Yahei', 'PingFang SC', 'Avenir', 'Segoe UI', 'Hiragino Sans GB', 'STHeiti', 'Microsoft Sans Serif', 'WenQuanYi Micro Hei', sans-serif;
+  font-size: var(--fontNum);
+  height: 100%;
+  color: black;
+  overflow: auto;
+}
+#root {
+  overflow: hidden;
+  margin: auto;
+  position: relative;
+}
+#root > div {
+  width: 100%;
+  height: 100%;
+}
+i {
+  font-style: normal;
+}
+img {
+  max-width: 100%;
+  max-height: 100%;
+  vertical-align: middle;
+  object-fit: cover;
+}
+ul {
+  list-style: none;
+}
+/* 文本域取消下拉 */
+textarea {
+  resize: none !important;
+  min-height: 100px !important;
+}
+/* 找不到页面 */
+.noFindPage {
+  opacity: 0;
+  transition: opacity 0.5s;
+  text-align: center;
+  transform: scale(0.6);
+}
+.noFindPage .ant-result {
+  padding: 20px !important;
+}
+.noFindPage .ant-result .ant-result-subtitle {
+  font-size: 20px;
+}
+[hidden] {
+  display: none !important;
+}
+/* antd图片预览组件 */
+.ant-image {
+  display: none !important;
+}
+.mySorrl::-webkit-scrollbar {
+  /*滚动条整体样式*/
+  width: 5px;
+  /*高宽分别对应横竖滚动条的尺寸*/
+  height: 1px;
+}
+.mySorrl::-webkit-scrollbar-thumb {
+  /*滚动条里面小方块*/
+  border-radius: 10px;
+  -webkit-box-shadow: inset 0 0 5px transparent;
+  background: var(--themeColor);
+}
+.mySorrl::-webkit-scrollbar-track {
+  /*滚动条里面轨道*/
+  -webkit-box-shadow: inset 0 0 5px transparent;
+  border-radius: 10px;
+  background: transparent;
+}
+.ant-image-preview-operations {
+  background-color: rgba(0, 0, 0, 0.8) !important;
+}
+.sizeNo {
+  font-family: 'Microsoft Yahei', 'PingFang SC', 'Avenir', 'Segoe UI', 'Hiragino Sans GB', 'STHeiti', 'Microsoft Sans Serif', 'WenQuanYi Micro Hei', sans-serif !important;
+}
+.adm-auto-center-content {
+  font-family: 'Microsoft Yahei', 'PingFang SC', 'Avenir', 'Segoe UI', 'Hiragino Sans GB', 'STHeiti', 'Microsoft Sans Serif', 'WenQuanYi Micro Hei', sans-serif !important;
+  font-size: 16px;
+  letter-spacing: 3px;
+  text-align: center;
+}
+.hoverD {
+  transition: all 0.3s;
+}
+.hoverD:hover {
+  transform: scale(1.1);
+}
+.yunShan {
+  animation: yunShan 1.5s infinite linear;
+  color: #f1e39e;
+}
+@keyframes yunShan {
+  0% {
+    opacity: 1;
+  }
+  50% {
+    opacity: 0;
+  }
+  100% {
+    opacity: 1;
+  }
+}
+#BtnRight {
+  position: absolute;
+  width: 50px;
+  height: 50px;
+  cursor: pointer;
+  bottom: 15px;
+  right: 20px;
+}
+#BtnRight > img {
+  position: absolute;
+  top: 0;
+  left: 0;
+  width: 100%;
+  height: 100%;
+  object-fit: fill !important;
+  opacity: 1;
+  transition: all 0.3s;
+}
+#BtnRight .BtnRight2 {
+  opacity: 0;
+  pointer-events: none;
+}
+#BtnRight:hover .BtnRight1 {
+  opacity: 0;
+  pointer-events: none;
+}
+#BtnRight:hover .BtnRight2 {
+  opacity: 1;
+  pointer-events: auto;
+}
+@media screen and (min-width: 1200px) {
+  #BtnRight {
+    width: 35px;
+    height: 35px;
+  }
+}
+#HotOpCss {
+  animation: HotOpCss 0.5s linear forwards;
+}
+@keyframes HotOpCss {
+  0% {
+    opacity: 0;
+  }
+  100% {
+    opacity: 1;
+  }
+}
+#bgImgBox {
+  position: absolute;
+  top: 0;
+  left: 0;
+  width: 100%;
+  height: 100%;
+  object-fit: fill !important;
+  pointer-events: none;
+}
+@media screen and (orientation: portrait) {
+  /* 竖屏 */
+  .adm-image-viewer-image-wrapper img {
+    transform: rotate(90deg) !important;
+  }
+  .adm-toast-wrap {
+    transform: rotate(90deg) !important;
+  }
+  .ant-tooltip {
+    /* transform: rotate(90deg) scale(0.7) !important;
+    transform-origin: center center !important; */
+
+  }
+}
+#myIframe {
+  position: fixed;
+  top: 0;
+  left: 0;
+  z-index: 99;
+  width: 100vw;
+  height: 100vh;
+  z-index: 2;
+}
+.HotIconBase {
+  animation: yunShan 2s infinite linear;
+}
+@keyframes yunShan {
+  0% {
+    opacity: 1;
+  }
+  50% {
+    opacity: 0.2;
+  }
+  100% {
+    opacity: 1;
+  }
+}
+.likeImg2 {
+  animation: likeImg2 1.5s linear forwards;
+}
+@keyframes likeImg2 {
+  0% {
+    transform: scale(0.1);
+  }
+  100% {
+    transform: scale(1.5);
+  }
+}
+/* 隐藏静音按钮 */
+
+#root .ant-tooltip {
+  width: 100px !important;
+  height: 100px !important;
+  max-width: 100px !important;
+  .ant-tooltip-content {
+    width: 100px !important;
+    height: 100px !important;
+    .ant-tooltip-content-inner {
+      width: 100px !important;
+      height: 100px !important;
+    }
+  }
+}
+
+#root .ant-tooltip .tooltip_MT {
+  height: 100%;
+  font-size: 16px;
+  line-height: 24px;
+  color: #000;
+  display: flex;
+  flex-direction: column;
+  padding-bottom: 8px;
+  gap: 4px;
+  .top {
+    width: 100%;
+    height: 15px;
+    display: flex;
+    justify-content: space-between;
+    align-items: center;
+    .title {
+      width: 30px;
+      height: 100%;
+      font-size: 10px;
+      line-height: 15px;
+      color: rgba(255, 233, 182, 1);
+    }
+    .close {
+      width: 40px;
+      height: 100%;
+      cursor: pointer;
+      display: flex;
+      justify-content: end;
+      align-items: center;
+      & > img {
+        height: 90%;
+        object-fit: contain;
+      }
+    }
+  }
+  .content {
+    width: 100%;
+    height: calc(100% - 16px);
+    font-size: 7px;
+    line-height: 10px;
+    color: #fff;
+    font-weight: lighter;
+    overflow: auto;
+    &::-webkit-scrollbar {
+      width: 1px;
+    }
+    &::-webkit-scrollbar-thumb {
+      background: rgba(255, 233, 182, 1);
+    }
+  }
+}

+ 290 - 0
src/assets/styles/base.less

@@ -0,0 +1,290 @@
+* {
+  margin: 0;
+  padding: 0;
+  box-sizing: border-box;
+  word-wrap: break-word;
+  -webkit-tap-highlight-color: transparent;
+  font-family: qfk;
+}
+
+/* 全局css变量 */
+:root {
+  --themeColor: #733c00;
+  --themeColor2: #ead98f;
+  --fontNum: 16px;
+}
+
+html {
+  height: 100%;
+  user-select: none;
+}
+
+body {
+  position: relative;
+  font: 1em/1.4 'Microsoft Yahei', 'PingFang SC', 'Avenir', 'Segoe UI', 'Hiragino Sans GB',
+    'STHeiti', 'Microsoft Sans Serif', 'WenQuanYi Micro Hei', sans-serif;
+  font-size: var(--fontNum);
+  height: 100%;
+  color: black;
+  overflow: auto;
+  background-color: #cbc5b7;
+  // background-color: rgba(0, 0, 0, 0.8);
+}
+
+#root {
+  overflow: hidden;
+  margin: auto;
+  position: relative;
+
+  & > div {
+    width: 100%;
+    height: 100%;
+  }
+}
+
+i {
+  font-style: normal;
+}
+
+img {
+  max-width: 100%;
+  max-height: 100%;
+  vertical-align: middle;
+  object-fit: cover;
+}
+
+ul {
+  list-style: none;
+}
+
+/* 文本域取消下拉 */
+textarea {
+  resize: none !important;
+  min-height: 100px !important;
+}
+
+/* 找不到页面 */
+.noFindPage {
+  opacity: 0;
+  transition: opacity 0.5s;
+  text-align: center;
+  transform: scale(0.6);
+  .ant-result {
+    padding: 20px !important;
+    .ant-result-subtitle {
+      font-size: 20px;
+    }
+  }
+}
+
+[hidden] {
+  display: none !important;
+}
+
+/* antd图片预览组件 */
+.ant-image {
+  display: none !important;
+}
+
+// 滚动条
+.mySorrl::-webkit-scrollbar {
+  /*滚动条整体样式*/
+  width: 5px;
+  /*高宽分别对应横竖滚动条的尺寸*/
+  height: 1px;
+}
+
+.mySorrl::-webkit-scrollbar-thumb {
+  /*滚动条里面小方块*/
+  border-radius: 10px;
+  -webkit-box-shadow: inset 0 0 5px transparent;
+  background: var(--themeColor);
+}
+
+.mySorrl::-webkit-scrollbar-track {
+  /*滚动条里面轨道*/
+  -webkit-box-shadow: inset 0 0 5px transparent;
+  border-radius: 10px;
+  background: transparent;
+}
+
+.ant-image-preview-operations {
+  background-color: rgba(0, 0, 0, 0.8) !important;
+}
+
+// 默认字体
+.sizeNo {
+  font-family: 'Microsoft Yahei', 'PingFang SC', 'Avenir', 'Segoe UI', 'Hiragino Sans GB', 'STHeiti',
+    'Microsoft Sans Serif', 'WenQuanYi Micro Hei', sans-serif !important;
+}
+.adm-auto-center-content {
+  font-family: 'Microsoft Yahei', 'PingFang SC', 'Avenir', 'Segoe UI', 'Hiragino Sans GB', 'STHeiti',
+    'Microsoft Sans Serif', 'WenQuanYi Micro Hei', sans-serif !important;
+  font-size: 16px;
+  letter-spacing: 3px;
+  text-align: center;
+}
+
+.hoverD {
+  transition: all 0.3s;
+  &:hover {
+    transform: scale(1.1);
+  }
+}
+
+// 云气图闪动
+.yunShan {
+  animation: yunShan 1.5s infinite linear;
+  color: #f1e39e;
+}
+@keyframes yunShan {
+  0% {
+    opacity: 1;
+  }
+
+  50% {
+    opacity: 0;
+  }
+
+  100% {
+    opacity: 1;
+  }
+}
+
+// 右下角的按钮
+#BtnRight {
+  position: absolute;
+  width: 50px;
+  height: 50px;
+  cursor: pointer;
+  bottom: 15px;
+  right: 20px;
+  & > img {
+    position: absolute;
+    top: 0;
+    left: 0;
+    width: 100%;
+    height: 100%;
+    object-fit: fill !important;
+    opacity: 1;
+    transition: all 0.3s;
+  }
+  .BtnRight2 {
+    opacity: 0;
+    pointer-events: none;
+  }
+  &:hover {
+    .BtnRight1 {
+      opacity: 0;
+      pointer-events: none;
+    }
+    .BtnRight2 {
+      opacity: 1;
+      pointer-events: auto;
+    }
+  }
+}
+
+@media screen and (min-width: 1200px) {
+  #BtnRight {
+    width: 35px;
+    height: 35px;
+  }
+}
+
+// 热点页面打开透明的变化
+#HotOpCss {
+  animation: HotOpCss 0.5s linear forwards;
+}
+@keyframes HotOpCss {
+  0% {
+    opacity: 0;
+  }
+
+  100% {
+    opacity: 1;
+  }
+}
+
+#bgImgBox {
+  position: absolute;
+  top: 0;
+  left: 0;
+  width: 100%;
+  height: 100%;
+  object-fit: fill !important;
+  pointer-events: none;
+}
+
+// antd mo 查看图片
+
+@media screen and (orientation: portrait) {
+  /* 竖屏 */
+  .adm-image-viewer-image-wrapper img {
+    transform: rotate(90deg) !important;
+  }
+  .adm-toast-wrap {
+    transform: rotate(90deg) !important;
+  }
+  .ant-tooltip {
+    transform: rotate(90deg) !important;
+  }
+}
+
+// 嵌套unity
+#myIframe {
+  position: fixed;
+  top: 0;
+  left: 0;
+  z-index: 99;
+  width: 100vw;
+  height: 100vh;
+  z-index: 2;
+}
+
+// 热点图标闪动
+.HotIconBase {
+  animation: yunShan 2s infinite linear;
+}
+@keyframes yunShan {
+  0% {
+    opacity: 1;
+  }
+
+  50% {
+    opacity: 0.2;
+  }
+
+  100% {
+    opacity: 1;
+  }
+}
+
+// 点赞的+1
+.likeImg2 {
+  animation: likeImg2 1.5s linear forwards;
+}
+@keyframes likeImg2 {
+  0% {
+    transform: scale(0.1);
+  }
+
+  100% {
+    transform: scale(1.5);
+  }
+}
+
+/* 隐藏静音按钮 */
+// video::-webkit-media-controls-mute-button {
+//   display: none !important;
+// }
+
+// /* 隐藏音量控制条 */
+// video::-webkit-media-controls-volume-control-container {
+//   display: none !important;
+// }
+
+// /* 隐藏音量调节滑块 */
+// video::-webkit-media-controls-volume-slider {
+//   display: none !important;
+// }

+ 21 - 0
src/components/AsyncSpinLoding/index.module.scss

@@ -0,0 +1,21 @@
+.AsyncSpinLoding {
+  opacity: 0;
+  pointer-events: none;
+  transition: all .5s;
+  position: fixed;
+  z-index: 9998;
+  top: 0;
+  left: 0;
+  width: 100%;
+  height: 100%;
+  // background-color: rgba(0, 0, 0, .6);
+  background-color: transparent;
+  :global{
+    .ant-spin-spinning{
+      position: absolute;
+      top: 50%;
+      left: 50%;
+      transform: translate(-50%,-50%);
+    }
+  }
+}

+ 15 - 0
src/components/AsyncSpinLoding/index.tsx

@@ -0,0 +1,15 @@
+import styles from "./index.module.scss";
+import { Spin } from "antd";
+import React from "react";
+
+function AsyncSpinLoding() {
+  return (
+    <div id="AsyncSpinLoding" className={styles.AsyncSpinLoding}>
+      <Spin size="large" />
+    </div>
+  );
+}
+
+const MemoAsyncSpinLoding = React.memo(AsyncSpinLoding);
+
+export default MemoAsyncSpinLoding;

+ 214 - 0
src/components/BaseImg/index.module.scss

@@ -0,0 +1,214 @@
+.BaseImg {
+  width: 100%;
+  height: 100%;
+  position: absolute;
+  top: 0;
+  left: 0;
+  z-index: 11;
+  opacity: 1;
+  transition: opacity 1s;
+  &::after {
+    content: '';
+    position: absolute;
+    pointer-events: none;
+    width: 100%;
+    height: 100%;
+    background-color: rgba(0, 0, 0, 0.4);
+  }
+
+  :global {
+    .BIcon {
+      position: absolute;
+      top: -60px;
+      left: 50%;
+      transform: translateX(-50%);
+      width: 30px;
+      z-index: 10;
+      cursor: pointer;
+      display: none;
+    }
+
+    .BIlogo {
+      z-index: 5;
+      position: absolute;
+      top: -60px;
+      width: 650px;
+      max-width: 90%;
+      left: 50%;
+      transform: translateX(-50%);
+    }
+
+    .BItxt {
+      text-indent: 2em;
+      position: absolute;
+      top: 35%;
+      left: 50%;
+      transform: translateX(-50%);
+      z-index: 10;
+      width: 60%;
+      height: 160px;
+      background-size: 100% 100%;
+      padding: 26px;
+      background-color: rgba(242, 242, 215, 0.3);
+      backdrop-filter: blur(4px);
+      display: flex;
+      align-items: center;
+      & > img {
+        position: absolute;
+        top: 0;
+        left: 0;
+        width: 100%;
+        object-fit: fill !important;
+        pointer-events: none;
+      }
+      .BItxtImg {
+        top: auto;
+        bottom: 0;
+      }
+      & > div {
+        text-align: justify;
+        width: 100%;
+        max-height: 100%;
+        color: #fffddc;
+        font-size: 11px;
+        letter-spacing: 3px;
+        line-height: 22px;
+        overflow-y: auto;
+
+        &::-webkit-scrollbar {
+          display: none;
+        }
+      }
+    }
+
+    .BIbaseBtn {
+      position: absolute;
+      bottom: 40px;
+      left: 50%;
+      transform: translateX(-50%);
+      cursor: pointer;
+      width: 120px;
+      height: 36px;
+      z-index: 10;
+      background-size: 100% 100%;
+      background-image: url('../../assets/img/btn.png');
+      color: #fffddc;
+      transition: all 0.3s;
+
+      .BIBtxt {
+        font-size: 18px;
+        position: absolute;
+        top: 0;
+        left: 0;
+        width: 100%;
+        height: 100%;
+        display: flex;
+        justify-content: center;
+        align-items: center;
+        font-family: 'Microsoft Yahei', 'PingFang SC', 'Avenir', 'Segoe UI', 'Hiragino Sans GB',
+          'STHeiti', 'Microsoft Sans Serif', 'WenQuanYi Micro Hei', sans-serif !important;
+      }
+      .BIBtxtFont {
+        font-family: 'qfk' !important;
+      }
+
+      &:hover {
+        background-image: url('../../assets/img/btnAc.png');
+        color: #b67150;
+      }
+    }
+
+    // 进度条
+    .BIBxian {
+      position: absolute;
+      bottom: 7%;
+      left: 50%;
+      transform: translateX(-50%);
+      width: 120px;
+      padding: 0 10px;
+      height: 2px;
+      z-index: 10;
+      & > div {
+        width: 100%;
+        height: 100%;
+        background-color: rgba(231, 214, 142, 0.6);
+        & > div {
+          width: 0%;
+          height: 100%;
+
+          background-color: #fffddc;
+        }
+      }
+    }
+    .birdBox {
+      position: absolute;
+      bottom: -122px;
+      left: 70%;
+      transform: translateX(-50%);
+      z-index: 100;
+      width: 250px;
+      height: 260px;
+      transition: all 0.5s;
+      .bird1 {
+        position: absolute;
+        top: 0;
+        left: 0;
+        pointer-events: none;
+        width: 250px;
+        height: 260px;
+        object-fit: fill !important;
+      }
+      .bird2 {
+        position: absolute;
+        bottom: 80px;
+        left: 50%;
+        transform: translateX(-50%);
+        background-image: url('../../assets/img/bird/quan.png');
+        background-size: 100% 100%;
+        z-index: 2;
+        cursor: pointer;
+        width: 90px;
+        height: 90px;
+        color: #fffddc;
+        text-align: center;
+        font-size: 16px;
+        padding-top: 18px;
+        transition: all 0.3s;
+        &:hover {
+          color: #a55b41;
+          background-image: url('../../assets/img/bird/quanAc.png');
+        }
+      }
+      .bird3 {
+        cursor: pointer;
+        position: absolute;
+        left: 50%;
+        transform: translateX(-50%);
+        top: -55px;
+        width: 33px;
+        height: 55px;
+        background-image: url('../../assets/img/bird/ji.png');
+        background-size: 100% 100%;
+        transition: all 0.3s;
+        &:hover {
+          background-image: url('../../assets/img/bird/jiAc.png');
+        }
+      }
+    }
+    #HotOpCss {
+      z-index: 1000;
+    }
+
+    // 屏幕>=1200
+    // @media screen and (min-width: 1200px) {
+    //   .BItxt > div {
+    //     font-size: 11px !important;
+    //   }
+    // }
+  }
+}
+
+.BaseImgHide {
+  opacity: 0;
+  pointer-events: none;
+}

Diferenças do arquivo suprimidas por serem muito extensas
+ 168 - 0
src/components/BaseImg/index.tsx


+ 82 - 0
src/components/BaseVideo/index.module.scss

@@ -0,0 +1,82 @@
+.BaseVideo {
+  width: 100%;
+  height: 100%;
+  position: absolute;
+  z-index: 11;
+  opacity: 1;
+  transition: opacity 0.5s;
+  :global {
+    .BVBIcon {
+      position: absolute;
+      top: -60px;
+      left: 50%;
+      transform: translateX(-50%);
+      width: 30px;
+      z-index: 10;
+      cursor: pointer;
+    }
+
+    .BaseVideoIcon {
+      position: absolute;
+      top: 0;
+      width: 650px;
+      max-width: 90%;
+      left: 50%;
+      transform: translateX(-50%);
+    }
+
+    .BVbaseBtn {
+      position: absolute;
+      bottom: 40px;
+      left: 50%;
+      transform: translateX(-50%);
+      cursor: pointer;
+      width: 100px;
+      height: auto;
+      z-index: 10;
+      & > img {
+        width: 100%;
+      }
+
+      .BVBtxt {
+        position: absolute;
+        top: 0;
+        left: 0;
+        color: #fffddc;
+        width: 100%;
+        height: 100%;
+        display: flex;
+        justify-content: center;
+        align-items: center;
+        transition: color 0.3s;
+        &:hover {
+          color: #ead98f;
+        }
+      }
+      .BVBxian {
+        position: absolute;
+        bottom: -10px;
+        left: 0;
+        width: 100%;
+        padding: 0 10px;
+        height: 2px;
+        & > div {
+          width: 100%;
+          height: 100%;
+          background-color: rgba(231, 214, 142, 0.6);
+          & > div {
+            width: 0%;
+            height: 100%;
+
+            background-color: #fffddc;
+          }
+        }
+      }
+    }
+  }
+}
+
+.BaseVideoHide {
+  opacity: 0;
+  pointer-events: none;
+}

+ 89 - 0
src/components/BaseVideo/index.tsx

@@ -0,0 +1,89 @@
+import React, { useCallback, useEffect, useRef, useState } from 'react'
+import styles from './index.module.scss'
+import classNames from 'classnames'
+import { baseURL, myData } from '@/utils/http'
+import { domDelOwnFu } from '@/utils/utilsSome'
+
+type Props = {
+  isShow: boolean //是否显示
+  imgSrc: string //标题图片的路径
+  videoSrc: string //视频路径
+  parentFu: () => void //点击继续的方法(调用父亲)
+}
+
+function BaseVideo({ isShow, imgSrc, videoSrc, parentFu }: Props) {
+  const baseVideoRef = useRef<HTMLVideoElement>(null)
+
+  useEffect(() => {
+    setTimeout(() => {
+      if (baseVideoRef.current) {
+        baseVideoRef.current.play()
+      }
+    }, 100)
+  }, [])
+
+  const [loding, setLoding] = useState(myData.isLdong ? 0 : 100)
+
+  const timeRR = useRef(-1)
+
+  useEffect(() => {
+    clearInterval(timeRR.current)
+    timeRR.current = window.setInterval(() => {
+      if (loding >= 100) {
+        clearInterval(timeRR.current)
+        return
+      }
+      setLoding(loding + 1)
+    }, 30)
+  }, [loding])
+
+  const btnStartFu = useCallback(() => {
+    if (loding >= 100) {
+      parentFu()
+      // 0.5s之后删除初始视频
+      setTimeout(() => {
+        domDelOwnFu('#BaseVideo')
+      }, 500)
+    }
+  }, [loding, parentFu])
+
+  return (
+    <div
+      id='BaseVideo'
+      className={classNames(styles.BaseVideo, isShow ? '' : styles.BaseVideoHide)}
+    >
+      <img className='BaseVideoIcon' src={imgSrc} alt='' />
+      <div className='BVbaseBtn' onClick={btnStartFu}>
+        {/* anpg动图 */}
+        <div className='BVBIcon'>
+          <img src={`${baseURL}visit/arrow.png`} alt='' />
+        </div>
+
+        <img src={`${baseURL}visit/btn.png`} alt='' />
+
+        <div className='BVBtxt'>{loding >= 100 ? '点击继续' : '加载中'}</div>
+        {loding >= 100 ? null : (
+          <div className='BVBxian'>
+            <div>
+              <div style={{ width: loding + '%' }}></div>
+            </div>
+          </div>
+        )}
+      </div>
+
+      <video
+        ref={baseVideoRef}
+        src={videoSrc}
+        playsInline
+        muted
+        webkit-playsinline='true'
+        x5-video-player-type='h5'
+        loop
+      />
+    </div>
+  )
+}
+
+const MemoBaseVideo = React.memo(BaseVideo)
+
+export default MemoBaseVideo

+ 21 - 0
src/components/BtnRight.tsx

@@ -0,0 +1,21 @@
+import React from 'react'
+import { baseURL } from '@/utils/http'
+
+type Props = {
+  imgName: string
+  clickSon: () => void
+  title: '返回' | '跳过'
+}
+
+function BtnRight({ imgName, clickSon, title }: Props) {
+  return (
+    <div title={title} id='BtnRight' onClick={clickSon}>
+      <img className='BtnRight1' src={`${baseURL}btn/${imgName}.png`} alt='' />
+      <img className='BtnRight2' src={`${baseURL}btn/${imgName}Ac.png`} alt='' />
+    </div>
+  )
+}
+
+const MemoBtnRight = React.memo(BtnRight)
+
+export default MemoBtnRight

+ 0 - 0
src/components/CatVideo/index.module.scss


Alguns arquivos não foram mostrados porque muitos arquivos mudaram nesse diff