shaogen1995 3 hari lalu
induk
melakukan
5b2a37f40c

+ 3 - 0
project/.env

@@ -0,0 +1,3 @@
+# .env.production
+GENERATE_SOURCEMAP = false
+# 关闭映射

TEMPAT SAMPAH
project/.nvmrc


+ 39 - 0
project/.vscode/settings.json

@@ -0,0 +1,39 @@
+{
+  "search.exclude": {
+    "/node_modules": true,
+    "dist": true,
+    "pnpm-lock.sh": true
+  },
+  "editor.formatOnSave": true,
+  "[javascript]": {
+    "editor.defaultFormatter": "esbenp.prettier-vscode"
+  },
+  "[javascriptreact]": {
+    "editor.defaultFormatter": "esbenp.prettier-vscode"
+  },
+  "[typescript]": {
+    "editor.defaultFormatter": "esbenp.prettier-vscode"
+  },
+  "[typescriptreact]": {
+    "editor.defaultFormatter": "esbenp.prettier-vscode"
+  },
+  "[json]": {
+    "editor.defaultFormatter": "vscode.json-language-features"
+  },
+  "[html]": {
+    "editor.defaultFormatter": "esbenp.prettier-vscode"
+  },
+  "[markdown]": {
+    "editor.defaultFormatter": "esbenp.prettier-vscode"
+  },
+  "[css]": {
+    "editor.defaultFormatter": "esbenp.prettier-vscode"
+  },
+  "[less]": {
+    "editor.defaultFormatter": "esbenp.prettier-vscode"
+  },
+  "[scss]": {
+    "editor.defaultFormatter": "esbenp.prettier-vscode"
+  },
+  "liveServer.settings.port": 5502
+}

+ 104 - 0
project/package-lock.json

@@ -28,7 +28,9 @@
         "media-chrome": "^4.15.1",
         "react": "^18.2.0",
         "react-dom": "^18.2.0",
+        "react-grid-layout": "^2.2.2",
         "react-redux": "^8.0.4",
+        "react-resizable": "^3.1.3",
         "react-router-dom": "5.3",
         "react-scripts": "5.0.1",
         "react-sortablejs": "^6.1.4",
@@ -6311,6 +6313,15 @@
         "wrap-ansi": "^7.0.0"
       }
     },
+    "node_modules/clsx": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmmirror.com/clsx/-/clsx-2.1.1.tgz",
+      "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=6"
+      }
+    },
     "node_modules/co": {
       "version": "4.6.0",
       "resolved": "https://registry.npmmirror.com/co/-/co-4.6.0.tgz",
@@ -8880,6 +8891,12 @@
       "resolved": "https://registry.npmmirror.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
       "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="
     },
+    "node_modules/fast-equals": {
+      "version": "4.0.3",
+      "resolved": "https://registry.npmmirror.com/fast-equals/-/fast-equals-4.0.3.tgz",
+      "integrity": "sha512-G3BSX9cfKttjr+2o1O22tYMLq0DPluZnYtq1rXumE1SpL/F/SLIfHx08WYQoWSIpeMYf8sRbJ8++71+v6Pnxfg==",
+      "license": "MIT"
+    },
     "node_modules/fast-glob": {
       "version": "3.2.12",
       "resolved": "https://registry.npmmirror.com/fast-glob/-/fast-glob-3.2.12.tgz",
@@ -15769,11 +15786,43 @@
         "react": "^18.2.0"
       }
     },
+    "node_modules/react-draggable": {
+      "version": "4.5.0",
+      "resolved": "https://registry.npmmirror.com/react-draggable/-/react-draggable-4.5.0.tgz",
+      "integrity": "sha512-VC+HBLEZ0XJxnOxVAZsdRi8rD04Iz3SiiKOoYzamjylUcju/hP9np/aZdLHf/7WOD268WMoNJMvYfB5yAK45cw==",
+      "license": "MIT",
+      "dependencies": {
+        "clsx": "^2.1.1",
+        "prop-types": "^15.8.1"
+      },
+      "peerDependencies": {
+        "react": ">= 16.3.0",
+        "react-dom": ">= 16.3.0"
+      }
+    },
     "node_modules/react-error-overlay": {
       "version": "6.0.11",
       "resolved": "https://registry.npmmirror.com/react-error-overlay/-/react-error-overlay-6.0.11.tgz",
       "integrity": "sha512-/6UZ2qgEyH2aqzYZgQPxEnz33NJ2gNsnHA2o5+o4wW9bLM/JYQitNP9xPhsXwC08hMMovfGe/8retsdDsczPRg=="
     },
+    "node_modules/react-grid-layout": {
+      "version": "2.2.2",
+      "resolved": "https://registry.npmmirror.com/react-grid-layout/-/react-grid-layout-2.2.2.tgz",
+      "integrity": "sha512-yNo9pxQWoxHWRAwHGSVT4DEGELYPyQ7+q9lFclb5jcqeFzva63/2F72CryS/jiTIr/SBIlTaDdyjqH+ODg8oBw==",
+      "license": "MIT",
+      "dependencies": {
+        "clsx": "^2.1.1",
+        "fast-equals": "^4.0.3",
+        "prop-types": "^15.8.1",
+        "react-draggable": "^4.4.6",
+        "react-resizable": "^3.0.5",
+        "resize-observer-polyfill": "^1.5.1"
+      },
+      "peerDependencies": {
+        "react": ">= 16.3.0",
+        "react-dom": ">= 16.3.0"
+      }
+    },
     "node_modules/react-is": {
       "version": "17.0.2",
       "resolved": "https://registry.npmmirror.com/react-is/-/react-is-17.0.2.tgz",
@@ -15830,6 +15879,20 @@
         "node": ">=0.10.0"
       }
     },
+    "node_modules/react-resizable": {
+      "version": "3.1.3",
+      "resolved": "https://registry.npmmirror.com/react-resizable/-/react-resizable-3.1.3.tgz",
+      "integrity": "sha512-liJBNayhX7qA4tBJiBD321FDhJxgGTJ07uzH5zSORXoE8h7PyEZ8mLqmosST7ppf6C4zUsbd2gzDMmBCfFp9Lw==",
+      "license": "MIT",
+      "dependencies": {
+        "prop-types": "15.x",
+        "react-draggable": "^4.5.0"
+      },
+      "peerDependencies": {
+        "react": ">= 16.3",
+        "react-dom": ">= 16.3"
+      }
+    },
     "node_modules/react-router": {
       "version": "5.3.4",
       "resolved": "https://registry.npmmirror.com/react-router/-/react-router-5.3.4.tgz",
@@ -23554,6 +23617,11 @@
         "wrap-ansi": "^7.0.0"
       }
     },
+    "clsx": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmmirror.com/clsx/-/clsx-2.1.1.tgz",
+      "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA=="
+    },
     "co": {
       "version": "4.6.0",
       "resolved": "https://registry.npmmirror.com/co/-/co-4.6.0.tgz",
@@ -25504,6 +25572,11 @@
       "resolved": "https://registry.npmmirror.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
       "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="
     },
+    "fast-equals": {
+      "version": "4.0.3",
+      "resolved": "https://registry.npmmirror.com/fast-equals/-/fast-equals-4.0.3.tgz",
+      "integrity": "sha512-G3BSX9cfKttjr+2o1O22tYMLq0DPluZnYtq1rXumE1SpL/F/SLIfHx08WYQoWSIpeMYf8sRbJ8++71+v6Pnxfg=="
+    },
     "fast-glob": {
       "version": "3.2.12",
       "resolved": "https://registry.npmmirror.com/fast-glob/-/fast-glob-3.2.12.tgz",
@@ -30608,11 +30681,33 @@
         "scheduler": "^0.23.0"
       }
     },
+    "react-draggable": {
+      "version": "4.5.0",
+      "resolved": "https://registry.npmmirror.com/react-draggable/-/react-draggable-4.5.0.tgz",
+      "integrity": "sha512-VC+HBLEZ0XJxnOxVAZsdRi8rD04Iz3SiiKOoYzamjylUcju/hP9np/aZdLHf/7WOD268WMoNJMvYfB5yAK45cw==",
+      "requires": {
+        "clsx": "^2.1.1",
+        "prop-types": "^15.8.1"
+      }
+    },
     "react-error-overlay": {
       "version": "6.0.11",
       "resolved": "https://registry.npmmirror.com/react-error-overlay/-/react-error-overlay-6.0.11.tgz",
       "integrity": "sha512-/6UZ2qgEyH2aqzYZgQPxEnz33NJ2gNsnHA2o5+o4wW9bLM/JYQitNP9xPhsXwC08hMMovfGe/8retsdDsczPRg=="
     },
+    "react-grid-layout": {
+      "version": "2.2.2",
+      "resolved": "https://registry.npmmirror.com/react-grid-layout/-/react-grid-layout-2.2.2.tgz",
+      "integrity": "sha512-yNo9pxQWoxHWRAwHGSVT4DEGELYPyQ7+q9lFclb5jcqeFzva63/2F72CryS/jiTIr/SBIlTaDdyjqH+ODg8oBw==",
+      "requires": {
+        "clsx": "^2.1.1",
+        "fast-equals": "^4.0.3",
+        "prop-types": "^15.8.1",
+        "react-draggable": "^4.4.6",
+        "react-resizable": "^3.0.5",
+        "resize-observer-polyfill": "^1.5.1"
+      }
+    },
     "react-is": {
       "version": "17.0.2",
       "resolved": "https://registry.npmmirror.com/react-is/-/react-is-17.0.2.tgz",
@@ -30643,6 +30738,15 @@
       "resolved": "https://registry.npmmirror.com/react-refresh/-/react-refresh-0.11.0.tgz",
       "integrity": "sha512-F27qZr8uUqwhWZboondsPx8tnC3Ct3SxZA3V5WyEvujRyyNv0VYPhoBg1gZ8/MV5tubQp76Trw8lTv9hzRBa+A=="
     },
+    "react-resizable": {
+      "version": "3.1.3",
+      "resolved": "https://registry.npmmirror.com/react-resizable/-/react-resizable-3.1.3.tgz",
+      "integrity": "sha512-liJBNayhX7qA4tBJiBD321FDhJxgGTJ07uzH5zSORXoE8h7PyEZ8mLqmosST7ppf6C4zUsbd2gzDMmBCfFp9Lw==",
+      "requires": {
+        "prop-types": "15.x",
+        "react-draggable": "^4.5.0"
+      }
+    },
     "react-router": {
       "version": "5.3.4",
       "resolved": "https://registry.npmmirror.com/react-router/-/react-router-5.3.4.tgz",

+ 2 - 0
project/package.json

@@ -23,7 +23,9 @@
     "media-chrome": "^4.15.1",
     "react": "^18.2.0",
     "react-dom": "^18.2.0",
+    "react-grid-layout": "^2.2.2",
     "react-redux": "^8.0.4",
+    "react-resizable": "^3.1.3",
     "react-router-dom": "5.3",
     "react-scripts": "5.0.1",
     "react-sortablejs": "^6.1.4",

+ 1 - 2
project/public/index.html

@@ -1,5 +1,5 @@
 <!doctype html>
-<html lang="en">
+<html lang="zh">
   <head>
     <meta charset="utf-8" />
     <meta
@@ -11,7 +11,6 @@
     <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="./three/data.js"></script>
     <title>革命领袖视察黑龙江纪念馆</title>
   </head>
 

+ 241 - 181
project/public/myData/myData.js

@@ -7,186 +7,246 @@ const isPcTemp = document.documentElement.clientWidth >= 1200
 
 // 一些动态配置的数据
 const myDataTemp = {
-    siderData: [
-        {
-            title: '展馆风采',
-            path: 'home'
-        },
-        {
-            title: '展馆漫游',
-            path: 'scene'
-        },
-        {
-            title: '建筑动画',
-            path: 'architecture'
-        },
-        {
-            title: '构件动画',
-            path: 'member'
-        },
-        {
-            title: '构件赏析',
-            path: 'view'
-        },
-        {
-            title: '前世今生',
-            path: 'life'
-        }
-    ],
-    sceneList: [
-        {
-            id: 'p1',
-            name:'场景名称',
-            thumbUrl: 'myData/img/Atest.png',
-        },
-        {
-            id: 'p2',
-            name:'场景名称2',
-            thumbUrl: '',
-        },
-        {
-            id: 'p3',
-            name:'场景名称3',
-            thumbUrl: '',
-        },
-        {
-            id: 'p4',
-            name:'场景名称4',
-            thumbUrl: '',
-        }
-    ],
-    architectureAnimation: 'myData/media/bgVideo.mp4',
-    memberList: [
-        {
-            name:'台灯',
-            nameEn:'DESK LAMP', 
-            imgSrc: 'myData/img/member1.png',
-            imgSrcLight: 'myData/img/member1_l.png',
-            videoSrc:'myData/media/bgVideo.mp4'
-        },
-        {
-            name:'门头',
-            nameEn:'STOREFRONT', 
-            imgSrc: 'myData/img/member2.png',
-            imgSrcLight: 'myData/img/member2_l.png',
-            videoSrc:'myData/media/bgVideo.mp4'
-        },
-        {
-            name:'壁炉',
-            nameEn:'FIREPLACE', 
-            imgSrc: 'myData/img/member3.png',
-            imgSrcLight: 'myData/img/member3_l.png',
-            videoSrc:'myData/media/bgVideo.mp4'
-        },
-        {
-            name:'吊灯',
-            nameEn:'CHANDELIER', 
-            imgSrc: 'myData/img/member4.png',
-            imgSrcLight: 'myData/img/member4_l.png',
-            videoSrc:'myData/media/bgVideo.mp4'
-        },
-        {
-            name:'门把手',
-            nameEn:'STOREFRONT', 
-            imgSrc: 'myData/img/member5.png',
-            imgSrcLight: 'myData/img/member5_l.png',
-            videoSrc:'myData/media/bgVideo.mp4'
-        },
-        {
-            name:'门厅镜框',
-            nameEn:'CHANDELIER', 
-            imgSrc: 'myData/img/member6.png',
-            imgSrcLight: 'myData/img/member6_l.png',
-            videoSrc:'myData/media/bgVideo.mp4'
-        },
-        {
-            name:'立柱',
-            nameEn:'CHANDELIER', 
-            imgSrc: 'myData/img/member7.png',
-            imgSrcLight: 'myData/img/member7_l.png',
-            videoSrc:'myData/media/bgVideo.mp4'
-        },
-        {
-            name:'门头',
-            nameEn:'STOREFRONT', 
-            imgSrc: 'myData/img/member8.png',
-            imgSrcLight: 'myData/img/member8_l.png',
-            videoSrc:'myData/media/bgVideo.mp4'
-        },
-        {
-            name:'外墙构件',
-            nameEn:'CHANDELIER', 
-            imgSrc: 'myData/img/member9.png',
-            imgSrcLight: 'myData/img/member9_l.png',
-            videoSrc:'myData/media/bgVideo.mp4'
-        },
-        {
-            name:'衣帽间',
-            nameEn:'WARDROBE', 
-            imgSrc: 'myData/img/member10.png',
-            imgSrcLight: 'myData/img/member10_l.png',
-            videoSrc:'myData/media/bgVideo.mp4'
-        },
-        
-    ],
-    lifeList: [
-        {
-            name:'建筑落成',
-            time:'1923年',
-            imgSrc: 'myData/img/Atest.png',
-            videoSrc:'',
-            desc:'建筑落成',
-        },
-        {
-            name:'功能转变',
-            time:'1946年',
-            imgSrc: 'myData/img/Atest.png',
-            videoSrc:'myData/media/bgVideo.mp4',
-            desc:'功能转变',
-        },
-        {
-            name:'革命领袖视察',
-            time:'1950年',
-            imgSrc: 'myData/img/Atest.png',
-            videoSrc:'',
-            desc:'革命领袖视察',
-        },
-        {
-            name:'首次大修',
-            time:'1975年',
-            imgSrc: 'myData/img/Atest.png',
-            videoSrc:'',
-            desc:'首次大修',
-        },
-        {
-            name:'纪念馆设立',
-            time:'1977年',
-            imgSrc: 'myData/img/Atest.png',
-            videoSrc:'',
-            desc:'纪念馆设立',
-        },
-        {
-            name:'文物保护单位',
-            time:'1996年',
-            imgSrc: 'myData/img/Atest.png',
-            videoSrc:'',
-            desc:'文物保护单位',
-        },
-        {
-            name:'数字化保护',
-            time:'2020年',
-            imgSrc: 'myData/img/Atest.png',
-            videoSrc:'',
-            desc:'数字化保护',
-        },
-        {
-            name:'纪录片',
-            time:'纪录片',
-            imgSrc: 'myData/img/Atest.png',
-            videoSrc:'myData/media/bgVideo.mp4',
-            desc:'纪录片',
-        },
-    ]
+  siderData: [
+    {
+      title: '展馆风采',
+      path: 'home'
+    },
+    {
+      title: '展馆漫游',
+      path: 'scene'
+    },
+    {
+      title: '建筑动画',
+      path: 'architecture'
+    },
+    {
+      title: '构件动画',
+      path: 'member'
+    },
+    {
+      title: '构件赏析',
+      path: 'view'
+    },
+    {
+      title: '前世今生',
+      path: 'life'
+    }
+  ],
+  sceneList: [
+    {
+      id: 'p1',
+      name: '场景名称',
+      thumbUrl: 'myData/img/Atest.png'
+    },
+    {
+      id: 'p2',
+      name: '场景名称2',
+      thumbUrl: ''
+    },
+    {
+      id: 'p3',
+      name: '场景名称3',
+      thumbUrl: ''
+    },
+    {
+      id: 'p4',
+      name: '场景名称4',
+      thumbUrl: ''
+    }
+  ],
+  architectureAnimation: 'myData/media/bgVideo.mp4',
+  memberList: [
+    {
+      name: '台灯',
+      nameEn: 'DESK LAMP',
+      imgSrc: 'myData/img/member1.png',
+      imgSrcLight: 'myData/img/member1_l.png',
+      videoSrc: 'myData/media/bgVideo.mp4'
+    },
+    {
+      name: '门头',
+      nameEn: 'STOREFRONT',
+      imgSrc: 'myData/img/member2.png',
+      imgSrcLight: 'myData/img/member2_l.png',
+      videoSrc: 'myData/media/bgVideo.mp4'
+    },
+    {
+      name: '壁炉',
+      nameEn: 'FIREPLACE',
+      imgSrc: 'myData/img/member3.png',
+      imgSrcLight: 'myData/img/member3_l.png',
+      videoSrc: 'myData/media/bgVideo.mp4'
+    },
+    {
+      name: '吊灯',
+      nameEn: 'CHANDELIER',
+      imgSrc: 'myData/img/member4.png',
+      imgSrcLight: 'myData/img/member4_l.png',
+      videoSrc: 'myData/media/bgVideo.mp4'
+    },
+    {
+      name: '门把手',
+      nameEn: 'STOREFRONT',
+      imgSrc: 'myData/img/member5.png',
+      imgSrcLight: 'myData/img/member5_l.png',
+      videoSrc: 'myData/media/bgVideo.mp4'
+    },
+    {
+      name: '门厅镜框',
+      nameEn: 'CHANDELIER',
+      imgSrc: 'myData/img/member6.png',
+      imgSrcLight: 'myData/img/member6_l.png',
+      videoSrc: 'myData/media/bgVideo.mp4'
+    },
+    {
+      name: '立柱',
+      nameEn: 'CHANDELIER',
+      imgSrc: 'myData/img/member7.png',
+      imgSrcLight: 'myData/img/member7_l.png',
+      videoSrc: 'myData/media/bgVideo.mp4'
+    },
+    {
+      name: '门头',
+      nameEn: 'STOREFRONT',
+      imgSrc: 'myData/img/member8.png',
+      imgSrcLight: 'myData/img/member8_l.png',
+      videoSrc: 'myData/media/bgVideo.mp4'
+    },
+    {
+      name: '外墙构件',
+      nameEn: 'CHANDELIER',
+      imgSrc: 'myData/img/member9.png',
+      imgSrcLight: 'myData/img/member9_l.png',
+      videoSrc: 'myData/media/bgVideo.mp4'
+    },
+    {
+      name: '衣帽间',
+      nameEn: 'WARDROBE',
+      imgSrc: 'myData/img/member10.png',
+      imgSrcLight: 'myData/img/member10_l.png',
+      videoSrc: 'myData/media/bgVideo.mp4'
+    }
+  ],
+  lifeList: [
+    {
+      name: '建筑落成',
+      time: '1923年',
+      imgSrc: 'myData/img/Atest.png',
+      videoSrc: '',
+      desc: '建筑落成'
+    },
+    {
+      name: '功能转变',
+      time: '1946年',
+      imgSrc: 'myData/img/Atest.png',
+      videoSrc: 'myData/media/bgVideo.mp4',
+      desc: '功能转变'
+    },
+    {
+      name: '革命领袖视察',
+      time: '1950年',
+      imgSrc: 'myData/img/Atest.png',
+      videoSrc: '',
+      desc: '革命领袖视察'
+    },
+    {
+      name: '首次大修',
+      time: '1975年',
+      imgSrc: 'myData/img/Atest.png',
+      videoSrc: '',
+      desc: '首次大修'
+    },
+    {
+      name: '纪念馆设立',
+      time: '1977年',
+      imgSrc: 'myData/img/Atest.png',
+      videoSrc: '',
+      desc: '纪念馆设立'
+    },
+    {
+      name: '文物保护单位',
+      time: '1996年',
+      imgSrc: 'myData/img/Atest.png',
+      videoSrc: '',
+      desc: '文物保护单位'
+    },
+    {
+      name: '数字化保护',
+      time: '2020年',
+      imgSrc: 'myData/img/Atest.png',
+      videoSrc: '',
+      desc: '数字化保护'
+    },
+    {
+      name: '纪录片',
+      time: '纪录片',
+      imgSrc: 'myData/img/Atest.png',
+      videoSrc: 'myData/media/bgVideo.mp4',
+      desc: '纪录片'
+    }
+  ]
 }
 
-
+const cardNames = [
+  {
+    id: 1,
+    name: '文物的名称1',
+    img: '1.png',
+    obj: { 构件: '构件1', 位置: '位置1', 装饰: '装饰1', 材质: '材质1' }
+  },
+  {
+    id: 2,
+    name: '文物的名称2',
+    img: '2.png',
+    obj: { 构件: '构件2', 位置: '位置2', 装饰: '装饰2', 材质: '材质2' }
+  },
+  {
+    id: 3,
+    name: '文物的名称3',
+    img: '3.png',
+    obj: { 构件: '构件3', 位置: '位置3', 装饰: '装饰3', 材质: '材质3' }
+  },
+  {
+    id: 4,
+    name: '文物的名称4',
+    img: '4.png',
+    obj: { 构件: '构件4', 位置: '位置4', 装饰: '装饰4', 材质: '材质4' }
+  },
+  {
+    id: 5,
+    name: '文物的名称5',
+    img: '5.png',
+    obj: { 构件: '构件5', 位置: '位置5', 装饰: '装饰5', 材质: '材质5' }
+  },
+  {
+    id: 6,
+    name: '文物的名称6',
+    img: '6.png',
+    obj: { 构件: '构件6', 位置: '位置6', 装饰: '装饰6', 材质: '材质6' }
+  },
+  {
+    id: 7,
+    name: '文物的名称7',
+    img: '7.png',
+    obj: { 构件: '构件7', 位置: '位置7', 装饰: '装饰7', 材质: '材质7' }
+  },
+  {
+    id: 8,
+    name: '文物的名称8',
+    img: '8.png',
+    obj: { 构件: '构件8', 位置: '位置8', 装饰: '装饰8', 材质: '材质8' }
+  },
+  {
+    id: 9,
+    name: '文物的名称9',
+    img: '9.png',
+    obj: { 构件: '构件9', 位置: '位置9', 装饰: '装饰9', 材质: '材质9' }
+  },
+  {
+    id: 10,
+    name: '文物的名称10',
+    img: '10.png',
+    obj: { 构件: '构件10', 位置: '位置10', 装饰: '装饰10', 材质: '材质10' }
+  }
+]

TEMPAT SAMPAH
project/public/three/assets/0.jpg


TEMPAT SAMPAH
project/public/three/assets/1.png


TEMPAT SAMPAH
project/public/three/assets/10.png


TEMPAT SAMPAH
project/public/three/assets/2.png


TEMPAT SAMPAH
project/public/three/assets/3.png


TEMPAT SAMPAH
project/public/three/assets/4.png


TEMPAT SAMPAH
project/public/three/assets/5.png


TEMPAT SAMPAH
project/public/three/assets/6.png


TEMPAT SAMPAH
project/public/three/assets/7.png


TEMPAT SAMPAH
project/public/three/assets/8.png


TEMPAT SAMPAH
project/public/three/assets/9.png


project/public/three/img/background.jpg → project/public/three/assets/background.jpg


+ 20 - 21
project/public/three/click.js

@@ -1,34 +1,33 @@
 // 点击
 viewer.addEventListener('clickObject', e => {
-  window.top.clickObject(e.imgName);
+  window.top.clickObject(e.id)
   // 暂停动画
   viewer.setAutoMove(false)
 })
 
 // 继续动画 - 给 父页面调用
-window.stareMove = (val) => {
+window.stareMove = val => {
   viewer.setAutoMove(val)
 }
 
-// let flag = false
+let flag = false
 
-// // 鼠标移入
-// viewer.addEventListener('hoverObject', e => {
-//   // console.log('鼠标移入',e);
-//   flag = true
-//   window.top.hoverObject(e);
-// })
-
-// // 鼠标移出
-// viewer.addEventListener('mouseoutObject', e => {
-//   // console.log('鼠标移出',e);
-//   flag = false
-//   window.top.mouseoutObject(e);
-// })
+// 鼠标移入
+viewer.addEventListener('hoverObject', e => {
+  // console.log('鼠标移入',e);
+  flag = true
+  window.top.hoverObject(e.name)
+})
 
+// 鼠标移出
+viewer.addEventListener('mouseoutObject', e => {
+  // console.log('鼠标移出',e);
+  flag = false
+  window.top.mouseoutObject()
+})
 
-// document.querySelector('#player').onmousemove = (event) => {
-//   if (!flag) return
-//   let e = event || window.event;
-//   window.top.mouseLoc(e.clientX, e.clientY);
-// }
+document.querySelector('#player').onmousemove = event => {
+  if (!flag) return
+  let e = event || window.event
+  window.top.mouseLoc(e.clientX, e.clientY)
+}

+ 0 - 57
project/public/three/data.js

@@ -1,57 +0,0 @@
-    const cardNames = [
-      {
-        id: 1,
-        name: '文物的名称1',
-        img: '1.png',
-        obj: { 构件: '构件1', 位置: '位置1', 装饰: '装饰1', 材质: '材质1' }
-      },
-      {
-        id: 2,
-        name: '文物的名称2',
-        img: '2.png',
-        obj: { 构件: '构件2', 位置: '位置2', 装饰: '装饰2', 材质: '材质2' }
-      },
-      {
-        id: 3,
-        name: '文物的名称3',
-        img: '3.png',
-        obj: { 构件: '构件3', 位置: '位置3', 装饰: '装饰3', 材质: '材质3' }
-      },
-      {
-        id: 4,
-        name: '文物的名称4',
-        img: '4.png',
-        obj: { 构件: '构件4', 位置: '位置4', 装饰: '装饰4', 材质: '材质4' }
-      },
-      {
-        id: 5,
-        name: '文物的名称5',
-        img: '5.png',
-        obj: { 构件: '构件5', 位置: '位置5', 装饰: '装饰5', 材质: '材质5' }
-      },
-      {
-        id: 6,
-        name: '文物的名称6',
-        img: '6.png',
-        obj: { 构件: '构件6', 位置: '位置6', 装饰: '装饰6', 材质: '材质6' }
-      },
-      {
-        id: 7,
-        name: '文物的名称7',
-        img: '7.png',
-        obj: { 构件: '构件7', 位置: '位置7', 装饰: '装饰7', 材质: '材质7' }
-      },
-      {
-        id: 8,
-        name: '文物的名称8',
-        img: '8.png',
-        obj: { 构件: '构件8', 位置: '位置8', 装饰: '装饰8', 材质: '材质8' }
-      },
-      {
-        id: 9,
-        name: '文物的名称9',
-        img: '9.png',
-        obj: { 构件: '构件9', 位置: '位置9', 装饰: '装饰9', 材质: '材质9' }
-      }
-    ]
-

+ 61 - 74
project/public/three/index.html

@@ -1,72 +1,65 @@
-<!DOCTYPE html>
+<!doctype html>
 <html lang="zh">
+  <head>
+    <meta charset="UTF-8" />
+    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
+    <meta name="viewport" content="width=device-width,initial-scale=1.0" />
+    <title>artsandculture</title>
+    <style>
+      #player,
+      body,
+      canvas {
+        width: 100%;
+        height: 100%;
+        position: absolute;
+        overflow: hidden;
+        margin: 0;
+        padding: 0;
+      }
 
-<head>
-  <meta charset="UTF-8">
-  <meta http-equiv="X-UA-Compatible" content="IE=edge">
-  <meta name="viewport" content="width=device-width,initial-scale=1.0">
-  <title>artsandculture</title>
-  <style>
-    #player,
-    body,
-    canvas {
-      width: 100%;
-      height: 100%;
-      position: absolute;
-      overflow: hidden;
-      margin: 0;
-      padding: 0;
-    }
-
-    /*canvas{
+      /*canvas{
           background-image: url(background.jpg); 
           background-repeat: no-repeat;
           background-position: center;
           background-size: cover;
       }*/
-    #consoleLog {
-      width: 120px;
-      height: 153px;
-      position: absolute;
-      left: 0px;
-      bottom: 160px;
-      z-index: 999999;
-      color: black;
-      opacity: 0.9;
-      font-size: 12px;
-    }
-  </style>
-</head>
-
-<body>
-  <div id="player">
-    <canvas></canvas>
-  </div>
-
-  <script type="text/javascript" src="jquery-2.1.1.min.js"></script>
-  <script type="text/javascript" src="three.min.js"></script>
-
-  <script src="./data.js"></script>
-
-  <script>
-
-
-    let vfov = 60 //垂直视角范围度数
-    window.setting = {
-      vfov,
-      cards: {
-        far: 15,
-        beginFadeNear: 7,
-        near: 1,
-        fadeInDur: 2000,
-        highest: Math.tan(THREE.Math.degToRad(vfov / 2)), //当card在1米处时最高可以多少才能在视线内
-
+      #consoleLog {
+        width: 120px;
+        height: 153px;
+        position: absolute;
+        left: 0px;
+        bottom: 160px;
+        z-index: 999999;
+        color: black;
+        opacity: 0.9;
+        font-size: 12px;
+      }
+    </style>
+  </head>
+
+  <body>
+    <div id="player">
+      <canvas></canvas>
+    </div>
+
+    <script src="../myData/myData.js"></script>
+
+    <script type="text/javascript" src="jquery-2.1.1.min.js"></script>
+    <script type="text/javascript" src="three.min.js"></script>
+    <script>
+      let vfov = 60 //垂直视角范围度数
+      window.setting = {
+        vfov,
+        cards: {
+          far: 10,
+          beginFadeNear: 7,
+          near: 1,
+          fadeInDur: 2000,
+          highest: Math.tan(THREE.Math.degToRad(vfov / 2)) //当card在1米处时最高可以多少才能在视线内
+        }
       }
 
-
-    };
-
-    /* var textarea = document.createElement('textarea');
+      /* var textarea = document.createElement('textarea');
     textarea.id = "consoleLog";
 
     document.getElementsByTagName("body")[0].appendChild(textarea);
@@ -85,17 +78,11 @@
     }
 
     */
-  </script>
-
-
-
-
-
-
-  <script type="text/javascript" src="utils.js"></script>
-  <script type="text/javascript" src="PanoramaControls.js"></script>
-  <script type="text/javascript" src="index.js"></script>
-  <script src="./click.js"></script>
-</body>
-
-</html>
+    </script>
+
+    <script type="text/javascript" src="utils.js"></script>
+    <script type="text/javascript" src="PanoramaControls.js"></script>
+    <script type="text/javascript" src="index.js"></script>
+    <script src="./click.js"></script>
+  </body>
+</html>

+ 343 - 384
project/public/three/index.js

@@ -1,118 +1,99 @@
+let camera, scene, renderer
 
-
-let camera, scene, renderer;
-
-const shadowHasAlpha = false  //阴影是否考虑光透过透明材质  
+const shadowHasAlpha = false //阴影是否考虑光透过透明材质
 
 const planeGeo = new THREE.PlaneBufferGeometry(1, 1)
-const raycaster = new THREE.Raycaster(); raycaster.linePrecision = 0;//不检测boxHelper
-const mouse = new THREE.Vector2();
+const raycaster = new THREE.Raycaster()
+raycaster.linePrecision = 0 //不检测boxHelper
+const mouse = new THREE.Vector2()
 
 let needUpdateShadow, needsUpdateScene
 
 var BlurShader = {
-
-    uniforms: {
-
-        "map": { value: null },
-        "blurRadius": { value: new THREE.Vector2(1.0 / 512.0, 1.0 / 512.0) },
-        'opacity': { value: 0 }
-    },
-
-    vertexShader: [
-
-        "varying vec2 vUv;",
-
-        "void main() {",
-
-        "	vUv = uv;",
-        "	gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );",
-
-        "}"
-
-    ].join("\n"),
-
-    fragmentShader: [
-
-        "uniform sampler2D map;",
-        "uniform vec2 resolution;",
-        "uniform float blurRadius;",
-        "uniform float opacity;",
-        "varying vec2 vUv;",
-
-        "void main() {",
-
-
-        "	vec2 offset = blurRadius / resolution; ",
-
-        "	vec4 sum = vec4( 0.0 );",
-
-        "	sum += texture2D( map, vec2( vUv.x - 4.0 * offset.x, vUv.y ) ) * 0.051;",
-        "	sum += texture2D( map, vec2( vUv.x - 3.0 * offset.x, vUv.y ) ) * 0.0918;",
-        "	sum += texture2D( map, vec2( vUv.x - 2.0 * offset.x, vUv.y ) ) * 0.12245;",
-        "	sum += texture2D( map, vec2( vUv.x - 1.0 * offset.x, vUv.y ) ) * 0.1531;",
-        "	sum += texture2D( map, vec2( vUv.x, vUv.y ) ) * 0.1633;",
-        "	sum += texture2D( map, vec2( vUv.x + 1.0 * offset.x, vUv.y ) ) * 0.1531;",
-        "	sum += texture2D( map, vec2( vUv.x + 2.0 * offset.x, vUv.y ) ) * 0.12245;",
-        "	sum += texture2D( map, vec2( vUv.x + 3.0 * offset.x, vUv.y ) ) * 0.0918;",
-        "	sum += texture2D( map, vec2( vUv.x + 4.0 * offset.x, vUv.y ) ) * 0.051;",
-
-        "	sum += texture2D( map, vec2( vUv.x , vUv.y - 4.0 * offset.y ) ) * 0.051;",
-        "	sum += texture2D( map, vec2( vUv.x , vUv.y - 3.0 * offset.y) ) * 0.0918;",
-        "	sum += texture2D( map, vec2( vUv.x , vUv.y - 2.0 * offset.y) ) * 0.12245;",
-        "	sum += texture2D( map, vec2( vUv.x , vUv.y - 1.0 * offset.y ) ) * 0.1531;",
-        "	sum += texture2D( map, vec2( vUv.x , vUv.y ) ) * 0.1633;",
-        "	sum += texture2D( map, vec2( vUv.x , vUv.y + 1.0 * offset.y ) ) * 0.1531;",
-        "	sum += texture2D( map, vec2( vUv.x , vUv.y + 2.0 * offset.y ) ) * 0.12245;",
-        "	sum += texture2D( map, vec2( vUv.x , vUv.y + 3.0 * offset.y ) ) * 0.0918;",
-        "	sum += texture2D( map, vec2( vUv.x , vUv.y + 4.0 * offset.y ) ) * 0.051;",
-
-
-        "	gl_FragColor = sum / 2.0 ;",
-        "	gl_FragColor.a *= opacity;",
-
-
-
-        "}"
-
-    ].join("\n")
-
-};
-
-
-
-
+  uniforms: {
+    map: { value: null },
+    blurRadius: { value: new THREE.Vector2(1.0 / 512.0, 1.0 / 512.0) },
+    opacity: { value: 0 }
+  },
+
+  vertexShader: [
+    'varying vec2 vUv;',
+
+    'void main() {',
+
+    '	vUv = uv;',
+    '	gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );',
+
+    '}'
+  ].join('\n'),
+
+  fragmentShader: [
+    'uniform sampler2D map;',
+    'uniform vec2 resolution;',
+    'uniform float blurRadius;',
+    'uniform float opacity;',
+    'varying vec2 vUv;',
+
+    'void main() {',
+
+    '	vec2 offset = blurRadius / resolution; ',
+
+    '	vec4 sum = vec4( 0.0 );',
+
+    '	sum += texture2D( map, vec2( vUv.x - 4.0 * offset.x, vUv.y ) ) * 0.051;',
+    '	sum += texture2D( map, vec2( vUv.x - 3.0 * offset.x, vUv.y ) ) * 0.0918;',
+    '	sum += texture2D( map, vec2( vUv.x - 2.0 * offset.x, vUv.y ) ) * 0.12245;',
+    '	sum += texture2D( map, vec2( vUv.x - 1.0 * offset.x, vUv.y ) ) * 0.1531;',
+    '	sum += texture2D( map, vec2( vUv.x, vUv.y ) ) * 0.1633;',
+    '	sum += texture2D( map, vec2( vUv.x + 1.0 * offset.x, vUv.y ) ) * 0.1531;',
+    '	sum += texture2D( map, vec2( vUv.x + 2.0 * offset.x, vUv.y ) ) * 0.12245;',
+    '	sum += texture2D( map, vec2( vUv.x + 3.0 * offset.x, vUv.y ) ) * 0.0918;',
+    '	sum += texture2D( map, vec2( vUv.x + 4.0 * offset.x, vUv.y ) ) * 0.051;',
+
+    '	sum += texture2D( map, vec2( vUv.x , vUv.y - 4.0 * offset.y ) ) * 0.051;',
+    '	sum += texture2D( map, vec2( vUv.x , vUv.y - 3.0 * offset.y) ) * 0.0918;',
+    '	sum += texture2D( map, vec2( vUv.x , vUv.y - 2.0 * offset.y) ) * 0.12245;',
+    '	sum += texture2D( map, vec2( vUv.x , vUv.y - 1.0 * offset.y ) ) * 0.1531;',
+    '	sum += texture2D( map, vec2( vUv.x , vUv.y ) ) * 0.1633;',
+    '	sum += texture2D( map, vec2( vUv.x , vUv.y + 1.0 * offset.y ) ) * 0.1531;',
+    '	sum += texture2D( map, vec2( vUv.x , vUv.y + 2.0 * offset.y ) ) * 0.12245;',
+    '	sum += texture2D( map, vec2( vUv.x , vUv.y + 3.0 * offset.y ) ) * 0.0918;',
+    '	sum += texture2D( map, vec2( vUv.x , vUv.y + 4.0 * offset.y ) ) * 0.051;',
+
+    '	gl_FragColor = sum / 2.0 ;',
+    '	gl_FragColor.a *= opacity;',
+
+    '}'
+  ].join('\n')
+}
 
 var Viewer = function (index, dom) {
-    this.index = index;
-    this.dom = dom
-    this.camera = new THREE.PerspectiveCamera(setting.vfov);
-    this.camera.position.set(0, 1, -1.5);
-    this.control = new PanoramaControls(this.camera, this.dom)
-    this.control.latMin = this.control.latMax = 0
-
-
-    //this.control.target.set(0,-1,0)  
-    this.setRenderer()
-
-    this.scene = new THREE.Scene();
-    this.scene.background = common.loadTexture("./img/background.jpg")
-    this.pointerDownPos
-    this.active = false;
-    this.antialias = true;
-    this.clickTime = new Date().getTime();
-    this.updateClock = new THREE.Clock();
-
-
-    this.cardGroup = new THREE.Object3D();
-    this.scene.add(this.cardGroup);
-    this.cardGroup.name = "cardGroup";
-    this.autoMove = true
-
-
-    this.bindEvents()
-    this.preLoadCards()
-    this.animate()
+  this.index = index
+  this.dom = dom
+  this.camera = new THREE.PerspectiveCamera(setting.vfov)
+  this.camera.position.set(0, 1, -1.5)
+  this.control = new PanoramaControls(this.camera, this.dom)
+  this.control.latMin = this.control.latMax = 0
+
+  //this.control.target.set(0,-1,0)
+  this.setRenderer()
+
+  this.scene = new THREE.Scene()
+  this.scene.background = common.loadTexture('./assets/background.jpg')
+  this.pointerDownPos
+  this.active = false
+  this.antialias = true
+  this.clickTime = new Date().getTime()
+  this.updateClock = new THREE.Clock()
+
+  this.cardGroup = new THREE.Object3D()
+  this.scene.add(this.cardGroup)
+  this.cardGroup.name = 'cardGroup'
+  this.autoMove = true
+
+  this.bindEvents()
+  this.preLoadCards()
+  this.animate()
 }
 
 /* 
@@ -121,279 +102,270 @@ var Viewer = function (index, dom) {
  */
 
 Viewer.prototype.preLoadCards = function () {
-
-    let i = 10
-    while (i-- > 0) {
-        this.addCard(true)
-    }
-
-
-    let add = () => {
-        if (document.hidden) return
-        this.addCard()
-        setTimeout(add, 40000 * Math.random() * this.getDensity())  //当前视野中密度越小 添加越频繁
-    }
-    add()
-
-
-
-    document.addEventListener('visibilitychange', (e) => {
-        if (!document.hidden) add()
-        console.log('document.hidden', document.hidden)
-    })
-
+  let i = 10
+  while (i-- > 0) {
+    this.addCard(true)
+  }
+
+  let add = () => {
+    if (document.hidden) return
+    this.addCard()
+    setTimeout(add, 40000 * Math.random() * this.getDensity()) //当前视野中密度越小 添加越频繁
+  }
+  add()
+
+  document.addEventListener('visibilitychange', e => {
+    if (!document.hidden) add()
+    console.log('document.hidden', document.hidden)
+  })
 }
 
 Viewer.prototype.getDensity = function () {
+  let frustumMatrix = new THREE.Matrix4()
+  frustumMatrix.multiplyMatrices(this.camera.projectionMatrix, this.camera.matrixWorldInverse)
 
+  let frustum = new THREE.Frustum()
+  frustum.setFromProjectionMatrix(frustumMatrix)
 
-    let frustumMatrix = new THREE.Matrix4()
-    frustumMatrix.multiplyMatrices(this.camera.projectionMatrix, this.camera.matrixWorldInverse)
-
-    let frustum = new THREE.Frustum();
-    frustum.setFromProjectionMatrix(frustumMatrix)
-
-
-    let count = this.cardGroup.children.filter(card => {
-        return frustum.containsPoint(card.position)
-    }).length
-
+  let count = this.cardGroup.children.filter(card => {
+    return frustum.containsPoint(card.position)
+  }).length
 
-    let density = count / (this.renderer.domElement.width * this.renderer.domElement.height) * 1000
+  let density = (count / (this.renderer.domElement.width * this.renderer.domElement.height)) * 1000
 
-    return density
+  return density
 }
 
-
 Viewer.prototype.addCard = function (around) {
-
-    let cardIndex = Math.floor(cardNames.length * Math.random())
-    common.loadTexture("./assets/" + cardNames[cardIndex].img, (map) => {
-
-        let card = new THREE.Mesh(planeGeo, new THREE.ShaderMaterial({
-            uniforms: {
-                map: { value: map },
-                resolution: { value: new THREE.Vector2(this.renderer.domElement.width, this.renderer.domElement.height) },
-                blurRadius: { value: 0 },//像素
-                opacity: { value: 0 }
-            },
-            vertexShader: BlurShader.vertexShader,
-            fragmentShader: BlurShader.fragmentShader,
-            transparent: true, side: 2
-        }))
-
-        Object.defineProperty(card.material, 'opacity', {
-            get: function () {
-                return card.material.uniforms.opacity.value
-            },
-            set: function (e) {
-                card.material.uniforms.opacity.value = e
-
-                card.material.uniforms.blurRadius.value = math.linearClamp(e, [0, 0.4], [40, 0])
-            }
-        })
-
-        let direction, far = setting.cards.far
-        if (around) {//在四周所有方向都可生成,在一开始时需要
-            let n = 0.6//范围0-1 越大越可能接近相机
-            far = far * (1 - n * Math.random()) //靠近一点  
-            direction = new THREE.Vector3(0, 0, -1).applyEuler(new THREE.Euler(0, Math.PI * 2 * Math.random(), 0))
-        } else {//仅在相机前方生成,因为相机往这个方向移动,最前方空缺
-
-
-            direction = new THREE.Vector3(0, 0, -1).applyQuaternion(this.camera.quaternion).applyEuler(new THREE.Euler(0, this.camera.hfov * (Math.random() - 0.5), 0))
-
-        }
-
-
-        let h = (Math.random() * 2 - 1) * setting.cards.highest * 0.8 // *0.8是因为靠近后就会飞出视线
-        card.position.copy(this.camera.position).add(direction.add(new THREE.Vector3(0, h, 0)).multiplyScalar(far))
-
-        card.scale.set(map.image.width / 500, map.image.height / 500, 1)
-        this.cardGroup.add(card)
-
-
-        card.transition = transitions.start(lerp.property(card.material, 'opacity', 1, (e) => {
-            //console.log(e, card.uuid)
-
-        }), setting.cards.fadeInDur);
-
-
-    })
-}
-
-
-Viewer.prototype.removeCards = function () {//移除超过bound的卡
-    let needRemove = this.cardGroup.children.filter(card => {
-        if (card.disToCam > setting.cards.far) {
-            card.material.dispose()
-            transitions.cancel(card.transition)
-            //console.log('remove一张卡')
-            return true
-        }
+  let cardIndex = Math.floor(cardNames.length * Math.random())
+  common.loadTexture('./assets/' + cardNames[cardIndex].img, map => {
+    /* let card = new THREE.Mesh(planeGeo, new THREE.MeshBasicMaterial({ 
+            map,
+            transparent:true, opacity:0,side:2
+        }))  */
+
+    let card = new THREE.Mesh(
+      planeGeo,
+      new THREE.ShaderMaterial({
+        uniforms: {
+          map: { value: map, id: cardNames[cardIndex].id, name: cardNames[cardIndex].name },
+          resolution: {
+            value: new THREE.Vector2(
+              this.renderer.domElement.width,
+              this.renderer.domElement.height
+            )
+          },
+          blurRadius: { value: 0 }, //像素
+          opacity: { value: 0 }
+        },
+        vertexShader: BlurShader.vertexShader,
+        fragmentShader: BlurShader.fragmentShader,
+        transparent: true,
+        side: 2
+      })
+    )
+
+    Object.defineProperty(card.material, 'opacity', {
+      get: function () {
+        return card.material.uniforms.opacity.value
+      },
+      set: function (e) {
+        card.material.uniforms.opacity.value = e
+
+        card.material.uniforms.blurRadius.value = math.linearClamp(e, [0, 0.4], [40, 0])
+      }
     })
-    needRemove.forEach(card => card.parent.remove(card))
-    //needRemove.length>0 && console.log('当前存在卡数', this.cardGroup.children.length)
-}
-
 
-Viewer.prototype.update = function (deltaTime) {//绘制的时候同时更新 
-
-    this.setSize()
-    this.control.update(deltaTime)
-    transitions.update(deltaTime)
+    let direction,
+      far = setting.cards.far
+    if (around) {
+      //在四周所有方向都可生成,在一开始时需要
+      let n = 0.6 //范围0-1 越大越可能接近相机
+      far = far * (1 - n * Math.random()) //靠近一点
+      direction = new THREE.Vector3(0, 0, -1).applyEuler(
+        new THREE.Euler(0, Math.PI * 2 * Math.random(), 0)
+      )
+    } else {
+      //仅在相机前方生成,因为相机往这个方向移动,最前方空缺
 
-    if (this.autoMove) {
-        let direction = new THREE.Vector3(0, 0, -1).applyQuaternion(this.camera.quaternion)
-        let moveSpeed = 0.8
-        this.camera.position.add(direction.multiplyScalar(deltaTime * moveSpeed))
+      direction = new THREE.Vector3(0, 0, -1)
+        .applyQuaternion(this.camera.quaternion)
+        .applyEuler(new THREE.Euler(0, this.camera.hfov * (Math.random() - 0.5), 0))
     }
-    this.cardGroup.children.forEach(card => {
-        card.quaternion.copy(this.camera.quaternion)
-
-        let dis = card.position.clone().setY(0).distanceTo(this.camera.position.clone().setY(0))
-        if (!card.transition.running) {
-            card.material.opacity = math.linearClamp(dis, [setting.cards.near, setting.cards.beginFadeNear], [0, 1])
-        }
-
-        card.disToCam = dis
-    })
-    this.removeCards()
-
-
-
 
+    let h = (Math.random() * 2 - 1) * setting.cards.highest * 0.8 // *0.8是因为靠近后就会飞出视线
+    card.position
+      .copy(this.camera.position)
+      .add(direction.add(new THREE.Vector3(0, h, 0)).multiplyScalar(far))
+
+    card.scale.set(map.image.width / 500, map.image.height / 500, 1)
+    this.cardGroup.add(card)
+
+    card.transition = transitions.start(
+      lerp.property(card.material, 'opacity', 1, e => {
+        //console.log(e, card.uuid)
+      }),
+      setting.cards.fadeInDur
+    )
+  })
+}
 
-    var needsUpdate = 1;
-    if (needsUpdate) {
-        this.renderer.autoClear = true
-        this.renderer.render(this.scene, this.camera)
+Viewer.prototype.removeCards = function () {
+  //移除超过bound的卡
+  let needRemove = this.cardGroup.children.filter(card => {
+    if (card.disToCam > setting.cards.far) {
+      card.material.dispose()
+      transitions.cancel(card.transition)
+      //console.log('remove一张卡')
+      return true
     }
-
-
+  })
+  needRemove.forEach(card => card.parent.remove(card))
+  //needRemove.length>0 && console.log('当前存在卡数', this.cardGroup.children.length)
 }
 
+Viewer.prototype.update = function (deltaTime) {
+  //绘制的时候同时更新
+
+  this.setSize()
+  this.control.update(deltaTime)
+  transitions.update(deltaTime)
+
+  if (this.autoMove) {
+    let direction = new THREE.Vector3(0, 0, -1).applyQuaternion(this.camera.quaternion)
+    let moveSpeed = 0.8
+    this.camera.position.add(direction.multiplyScalar(deltaTime * moveSpeed))
+  }
+  this.cardGroup.children.forEach(card => {
+    card.quaternion.copy(this.camera.quaternion)
+
+    let dis = card.position.clone().setY(0).distanceTo(this.camera.position.clone().setY(0))
+    if (!card.transition.running) {
+      card.material.opacity = math.linearClamp(
+        dis,
+        [setting.cards.near, setting.cards.beginFadeNear],
+        [0, 1]
+      )
+    }
 
+    card.disToCam = dis
+  })
+  this.removeCards()
 
+  var needsUpdate = 1
+  if (needsUpdate) {
+    this.renderer.autoClear = true
+    this.renderer.render(this.scene, this.camera)
+  }
+}
 
 Viewer.prototype.bindEvents = function () {
-
-    this.renderer.domElement.addEventListener('pointermove', this.onPointerMove.bind(this), false);
-    this.renderer.domElement.addEventListener('pointerdown', this.onPointerDown.bind(this), false);
-    this.renderer.domElement.addEventListener('pointerup', this.onPointerUp.bind(this), false);
-
+  this.renderer.domElement.addEventListener('pointermove', this.onPointerMove.bind(this), false)
+  this.renderer.domElement.addEventListener('pointerdown', this.onPointerDown.bind(this), false)
+  this.renderer.domElement.addEventListener('pointerup', this.onPointerUp.bind(this), false)
 }
 
 Viewer.prototype.setRenderer = function () {
-    try {
-        this.renderer = new THREE.WebGLRenderer({ canvas: $(this.dom).find("canvas")[0], antialias: true }),
-            this.renderer.setPixelRatio(window.devicePixelRatio ? window.devicePixelRatio : 1),
-            this.renderer.autoClear = false
-        this.renderer.setClearColor(0xffffff, 1)
-        console.log("ContextCreated")
-        //this.emit(Events.ContextCreated) 
-    } catch (e) {
-        console.error("Unable to create a WebGL rendering context")
-    }
+  try {
+    ;((this.renderer = new THREE.WebGLRenderer({
+      canvas: $(this.dom).find('canvas')[0],
+      antialias: true
+    })),
+      this.renderer.setPixelRatio(window.devicePixelRatio ? window.devicePixelRatio : 1),
+      (this.renderer.autoClear = false))
+    this.renderer.setClearColor(0xffffff, 1)
+    console.log('ContextCreated')
+    //this.emit(Events.ContextCreated)
+  } catch (e) {
+    console.error('Unable to create a WebGL rendering context')
+  }
 }
 
-
-
-
-Viewer.prototype.hasChanged = function () {//判断画面是否改变了,改变后需要更新一些东西
-    var copy = function () {
-        this.previousState = {
-            projectionMatrix: this.camera.projectionMatrix.clone(),//worldMatrix在control时归零了所以不用了吧,用position和qua也一样
-            position: this.camera.position.clone(),
-            quaternion: this.camera.quaternion.clone(),
-            //mouse: this.mouse.clone(), 
-            fov: this.camera.fov
-        };
-    }.bind(this)
-
-
-    if (!this.previousState) {
-        copy()
-        return { cameraChanged: !0, changed: !0 };
+Viewer.prototype.hasChanged = function () {
+  //判断画面是否改变了,改变后需要更新一些东西
+  var copy = function () {
+    this.previousState = {
+      projectionMatrix: this.camera.projectionMatrix.clone(), //worldMatrix在control时归零了所以不用了吧,用position和qua也一样
+      position: this.camera.position.clone(),
+      quaternion: this.camera.quaternion.clone(),
+      //mouse: this.mouse.clone(),
+      fov: this.camera.fov
     }
-    var cameraChanged =
-        !this.camera.projectionMatrix.equals(this.previousState.projectionMatrix) ||
-        !this.camera.position.equals(this.previousState.position) ||
-        !this.camera.quaternion.equals(this.previousState.quaternion)
-
-
-    var changed = cameraChanged //|| !this.mouse.equals(this.previousState.mouse)  
+  }.bind(this)
 
+  if (!this.previousState) {
     copy()
+    return { cameraChanged: !0, changed: !0 }
+  }
+  var cameraChanged =
+    !this.camera.projectionMatrix.equals(this.previousState.projectionMatrix) ||
+    !this.camera.position.equals(this.previousState.position) ||
+    !this.camera.quaternion.equals(this.previousState.quaternion)
 
-    return { cameraChanged, changed };
-}
-
-Viewer.prototype.setSize = function () {
-    var w, h, pixelRatio;
-    return function () {
-        if (w != this.dom.clientWidth || h != this.dom.clientHeight || pixelRatio != window.devicePixelRatio) {
-            w = this.dom.clientWidth;
-            h = this.dom.clientHeight;
-
-            pixelRatio = window.devicePixelRatio;
-
-            this.camera.aspect = w / h;
-            this.camera.updateProjectionMatrix();
-
-
-            this.renderer.setSize(w, h, false, pixelRatio);
+  var changed = cameraChanged //|| !this.mouse.equals(this.previousState.mouse)
 
-            this.camera.hfov = cameraLight.getHFOVForCamera(this.camera, true)
+  copy()
 
+  return { cameraChanged, changed }
+}
 
-            this.cardGroup.children.forEach(card =>
-                card.material.uniforms.resolution.value.set(w, h)
-            )
-        }
-    }
-}()
-
-
-
-Viewer.prototype.animate = function () {
-    var deltaTime = Math.min(1, this.updateClock.getDelta());
-    this.update(deltaTime)
-    //bus.emit('player/position/change', {x:this.position.x, y:this.position.z, lon: this.cameraControls.controls.panorama.lon})
-
-    window.requestAnimationFrame(this.animate.bind(this));
-},
-
+Viewer.prototype.setSize = (function () {
+  var w, h, pixelRatio
+  return function () {
+    if (
+      w != this.dom.clientWidth ||
+      h != this.dom.clientHeight ||
+      pixelRatio != window.devicePixelRatio
+    ) {
+      w = this.dom.clientWidth
+      h = this.dom.clientHeight
 
-    Viewer.prototype.onPointerMove = function (event) {
+      pixelRatio = window.devicePixelRatio
 
-        if (event.isPrimary === false) return;
+      this.camera.aspect = w / h
+      this.camera.updateProjectionMatrix()
 
-        mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
-        mouse.y = - (event.clientY / window.innerHeight) * 2 + 1;
+      this.renderer.setSize(w, h, false, pixelRatio)
 
-        if (!this.pointerDownPos) this.checkIntersection();
+      this.camera.hfov = cameraLight.getHFOVForCamera(this.camera, true)
 
+      this.cardGroup.children.forEach(card => card.material.uniforms.resolution.value.set(w, h))
     }
+  }
+})()
+;((Viewer.prototype.animate = function () {
+  var deltaTime = Math.min(1, this.updateClock.getDelta())
+  this.update(deltaTime)
+  //bus.emit('player/position/change', {x:this.position.x, y:this.position.z, lon: this.cameraControls.controls.panorama.lon})
+
+  window.requestAnimationFrame(this.animate.bind(this))
+}),
+  (Viewer.prototype.onPointerMove = function (event) {
+    if (event.isPrimary === false) return
+
+    mouse.x = (event.clientX / window.innerWidth) * 2 - 1
+    mouse.y = -(event.clientY / window.innerHeight) * 2 + 1
+
+    if (!this.pointerDownPos) this.checkIntersection()
+  }))
 Viewer.prototype.onPointerDown = function (event) {
-
-    if (event.isPrimary === false) return;
-    mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
-    mouse.y = - (event.clientY / window.innerHeight) * 2 + 1;
-    this.pointerDownPos = mouse.clone()
-    this.pointerDownTime = Date.now()
+  if (event.isPrimary === false) return
+  mouse.x = (event.clientX / window.innerWidth) * 2 - 1
+  mouse.y = -(event.clientY / window.innerHeight) * 2 + 1
+  this.pointerDownPos = mouse.clone()
+  this.pointerDownTime = Date.now()
 }
 
 Viewer.prototype.onPointerUp = function (event) {
-
-    if (event.isPrimary === false) return;
-    mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
-    mouse.y = - (event.clientY / window.innerHeight) * 2 + 1;
-    let now = Date.now()
-    if (mouse.distanceTo(this.pointerDownPos) < 0.006 && now - this.pointerDownTime < 1000) {//click
-
-        //doubleClick
-        /* var time = new Date().getTime();
+  if (event.isPrimary === false) return
+  mouse.x = (event.clientX / window.innerWidth) * 2 - 1
+  mouse.y = -(event.clientY / window.innerHeight) * 2 + 1
+  let now = Date.now()
+  if (mouse.distanceTo(this.pointerDownPos) < 0.006 && now - this.pointerDownTime < 1000) {
+    //click
+
+    //doubleClick
+    /* var time = new Date().getTime();
         if(time - this.clickTime < 300){
             if(this.intersects.length){
                 console.log('doubleClick');
@@ -401,67 +373,54 @@ Viewer.prototype.onPointerUp = function (event) {
                 transitions.start(lerp.vector(this.control.target, this.intersects[0].point), 600, null, 0 , easing.easeInOutQuad, null, Transitions.doubleClick);
             }
         } */
-        if (this.hoveredObject) {
-            this.dispatchEvent({ type: 'clickObject', imgName: this.hoveredObject.material.uniforms.map.value.image.src.split('/').pop() })
-        }
+    if (this.hoveredObject) {
+      this.dispatchEvent({
+        type: 'clickObject',
+        id: this.hoveredObject.material.uniforms.map.id
+      })
     }
+  }
 
-    this.pointerDownPos = null
+  this.pointerDownPos = null
 }
 
-
-
 Viewer.prototype.checkIntersection = function () {
-
-    raycaster.setFromCamera(mouse, this.camera);
-    raycaster.near = 2
-    const intersects = raycaster.intersectObject(this.cardGroup, true);
-    var recover = () => {
-        if (this.hoveredObject) {
-
-
-        }
+  raycaster.setFromCamera(mouse, this.camera)
+  raycaster.near = 2
+  const intersects = raycaster.intersectObject(this.cardGroup, true)
+  var recover = () => {
+    if (this.hoveredObject) {
     }
-    if (intersects.length > 0) {
-
-        const hoveredObject = intersects[0].object;
-        if (this.hoveredObject != hoveredObject) {
-            recover()
-            this.dispatchEvent({ type: "hoverObject", imgName: hoveredObject.material.uniforms.map.value.image.src.split('/').pop() })
-            this.hoveredObject = hoveredObject;
-            this.dom.style.cursor = 'pointer'
-
-        }
-    } else {
-        recover()
-        this.dispatchEvent({ type: "mouseoutObject" })
-        this.dom.style.cursor = ''
-        this.hoveredObject = null
+  }
+  if (intersects.length > 0) {
+    const hoveredObject = intersects[0].object
+    if (this.hoveredObject != hoveredObject) {
+      recover()
+
+      this.dispatchEvent({
+        type: 'hoverObject',
+        name: hoveredObject.material.uniforms.map.name
+      })
+      this.hoveredObject = hoveredObject
+      this.dom.style.cursor = 'pointer'
     }
-    this.intersects = intersects;
+  } else {
+    recover()
+    this.dispatchEvent({ type: 'mouseoutObject' })
+    this.dom.style.cursor = ''
+    this.hoveredObject = null
+  }
+  this.intersects = intersects
 }
 
-
-
-Viewer.prototype.setAutoMove = function (state) {//设置相机飞行状态
-    this.autoMove = !!state
-
+Viewer.prototype.setAutoMove = function (state) {
+  //设置相机飞行状态
+  this.autoMove = !!state
 }
 
-Object.assign(Viewer.prototype, THREE.EventDispatcher.prototype);
+Object.assign(Viewer.prototype, THREE.EventDispatcher.prototype)
 
 //============
 
-var startTime = new Date().getTime();
-var viewer = new Viewer(0, $("#player")[0])
-
-
-
-
-
-
-
-
-
-
-
+var startTime = new Date().getTime()
+var viewer = new Viewer(0, $('#player')[0])

TEMPAT SAMPAH
project/src/assets/three/background.jpg


project/public/three/img/row.png → project/src/assets/three/row.png


+ 35 - 31
project/src/pages/A5view/index.module.scss

@@ -1,40 +1,44 @@
 .A5view {
-  position: relative;
-  overflow: auto;
-  // background-image: url('../../assets/three/background.jpg');
-  // background-size: 100% 100%;
-
-
   :global {
-
     iframe {
+      position: absolute;
+      top: 0;
+      left: 0;
       width: 100%;
       height: 100%;
+      z-index: 1;
     }
 
+    .btn {
+      position: absolute;
+      top: 50px;
+      left: 50px;
+      cursor: pointer;
+      background-color: red;
+      display: flex;
+      justify-content: center;
+      align-items: center;
+      z-index: 2;
+    }
 
-    // .A5row {
-    //   width: 17vw;
-    //   height: 44vh;
-    //   background-image: url('../../assets/three/row.png');
-    //   background-size: 100% 100%;
-    //   display: flex;
-    //   flex-direction: column;
-    //   justify-content: center;
-    //   align-items: center;
-
-    //   h3 {
-    //     font-size: 16px;
-    //     margin-bottom: 3%;
-    //     color: #fff3c5;
-    //     letter-spacing: 2px;
-    //   }
-
-    //   img {
-    //     max-width: 40%;
-    //     max-height: 50%;
-    //   }
-    // }
+    .txt {
+      position: absolute;
+      z-index: 3;
+      top: 0;
+      left: 0;
+      width: 200px;
+      text-align: center;
+      pointer-events: none;
+      font-size: 16px;
+      letter-spacing: 2px;
+      opacity: 0;
+      color: #f9d36d;
+
+      h2 {
+        font-size: 24px;
+        font-weight: 700;
+        margin-bottom: 15px;
+      }
+    }
   }
-
-}
+}

+ 65 - 8
project/src/pages/A5view/index.tsx

@@ -1,17 +1,74 @@
-import React, { useCallback, useEffect, useRef, useState } from 'react'
+import React, { useEffect, useRef, useState } from 'react'
 import styles from './index.module.scss'
 import MenuSider from '@/components/MenuSider'
 
+const fullNum = {
+  width: window.innerWidth,
+  height: window.innerHeight
+}
+
 function A5view() {
+  const iframeDom = useRef<any>(null)
+
+  const [txt, setTxt] = useState({
+    title: '',
+    show: false
+  })
+
+  useEffect(() => {
+    // 点击图片
+    ;(window as any).clickObject = (val: any) => {
+      console.log('000', val)
+    }
+    // 鼠标移入
+    ;(window as any).hoverObject = (name: any) => {
+      setTxt({
+        title: name,
+        show: true
+      })
+    }
+    // 鼠标移出
+    ;(window as any).mouseoutObject = () => {
+      setTxt({
+        title: '',
+        show: false
+      })
+    }
+
+    const txtDom: any = document.querySelector('.txt')
+
+    if (txtDom) {
+      // 获取鼠标坐标
+      ;(window as any).mouseLoc = (x: any, y: any) => {
+        // console.log("ppp", x, y);
+        // 最大X值
+        const maxX = fullNum.width - 200
+        let xRes = x >= maxX ? maxX : x
+        // xRes = xRes - 100 <= 0 ? 0 : xRes - 100;
+        // 最大y值
+        const domHeight = txtDom.clientHeight
+        const maxY = fullNum.height - domHeight
+        let yRes = y >= maxY ? maxY : y
+        // yRes = yRes - domHeight / 2 <= 0 ? 0 : yRes - domHeight / 2;
+        txtDom.style.top = yRes + 'px'
+        txtDom.style.left = xRes + 'px'
+      }
+    }
+  }, [])
+
   return (
     <div className={styles.A5view}>
-      {/* {cardNames.map(item => (
-        <div className='A5row' key={item.id}>
-          <h3>{item.name}</h3>
-          <img src={`./myData/three/assets/${item.img}`} alt='' />
-        </div>
-      ))} */}
-      <iframe id='iframe' src='./three/index.html' frameBorder='0' title='3d'></iframe>
+      <iframe
+        id='iframe'
+        ref={iframeDom}
+        title='3d'
+        src='./three/index.html'
+        frameBorder='0'
+      ></iframe>
+      <div className='txt' style={{ opacity: txt.show ? 0.8 : 0 }}>
+        <h2>{txt.title}</h2>
+      </div>
+
       <MenuSider isSidebarOpen={false} />
     </div>
   )

+ 41 - 3
project/yarn.lock

@@ -3853,6 +3853,11 @@ cliui@^7.0.2:
     strip-ansi "^6.0.0"
     wrap-ansi "^7.0.0"
 
+clsx@^2.1.1:
+  version "2.1.1"
+  resolved "https://registry.npmmirror.com/clsx/-/clsx-2.1.1.tgz"
+  integrity sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==
+
 co@^4.6.0:
   version "4.6.0"
   resolved "https://registry.npmmirror.com/co/-/co-4.6.0.tgz"
@@ -5443,6 +5448,11 @@ fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3:
   resolved "https://registry.npmmirror.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz"
   integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==
 
+fast-equals@^4.0.3:
+  version "4.0.3"
+  resolved "https://registry.npmmirror.com/fast-equals/-/fast-equals-4.0.3.tgz"
+  integrity sha512-G3BSX9cfKttjr+2o1O22tYMLq0DPluZnYtq1rXumE1SpL/F/SLIfHx08WYQoWSIpeMYf8sRbJ8++71+v6Pnxfg==
+
 fast-glob@^3.2.12, fast-glob@^3.2.9:
   version "3.2.12"
   resolved "https://registry.npmmirror.com/fast-glob/-/fast-glob-3.2.12.tgz"
@@ -8508,7 +8518,7 @@ prompts@^2.0.1, prompts@^2.4.2:
     kleur "^3.0.3"
     sisteransi "^1.0.5"
 
-prop-types@^15.6.2, prop-types@^15.8.1:
+prop-types@^15.6.2, prop-types@^15.8.1, prop-types@15.x:
   version "15.8.1"
   resolved "https://registry.npmmirror.com/prop-types/-/prop-types-15.8.1.tgz"
   integrity sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==
@@ -9018,7 +9028,7 @@ react-dev-utils@^12.0.1:
     strip-ansi "^6.0.1"
     text-table "^0.2.0"
 
-react-dom@*, "react-dom@^16.8 || ^17.0 || ^18.0", "react-dom@^16.8.0 || ^17.0.0 || ^18.0.0", react-dom@^18.0.0, react-dom@^18.2.0, react-dom@>=16.0.0, react-dom@>=16.11.0, react-dom@>=16.8.0, react-dom@>=16.9.0:
+react-dom@*, "react-dom@^16.8 || ^17.0 || ^18.0", "react-dom@^16.8.0 || ^17.0.0 || ^18.0.0", react-dom@^18.0.0, react-dom@^18.2.0, "react-dom@>= 16.3", "react-dom@>= 16.3.0", react-dom@>=16.0.0, react-dom@>=16.11.0, react-dom@>=16.8.0, react-dom@>=16.9.0:
   version "18.2.0"
   resolved "https://registry.npmmirror.com/react-dom/-/react-dom-18.2.0.tgz"
   integrity sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==
@@ -9026,11 +9036,31 @@ react-dom@*, "react-dom@^16.8 || ^17.0 || ^18.0", "react-dom@^16.8.0 || ^17.0.0
     loose-envify "^1.1.0"
     scheduler "^0.23.0"
 
+react-draggable@^4.4.6, react-draggable@^4.5.0:
+  version "4.5.0"
+  resolved "https://registry.npmmirror.com/react-draggable/-/react-draggable-4.5.0.tgz"
+  integrity sha512-VC+HBLEZ0XJxnOxVAZsdRi8rD04Iz3SiiKOoYzamjylUcju/hP9np/aZdLHf/7WOD268WMoNJMvYfB5yAK45cw==
+  dependencies:
+    clsx "^2.1.1"
+    prop-types "^15.8.1"
+
 react-error-overlay@^6.0.11:
   version "6.0.11"
   resolved "https://registry.npmmirror.com/react-error-overlay/-/react-error-overlay-6.0.11.tgz"
   integrity sha512-/6UZ2qgEyH2aqzYZgQPxEnz33NJ2gNsnHA2o5+o4wW9bLM/JYQitNP9xPhsXwC08hMMovfGe/8retsdDsczPRg==
 
+react-grid-layout@^2.2.2:
+  version "2.2.2"
+  resolved "https://registry.npmmirror.com/react-grid-layout/-/react-grid-layout-2.2.2.tgz"
+  integrity sha512-yNo9pxQWoxHWRAwHGSVT4DEGELYPyQ7+q9lFclb5jcqeFzva63/2F72CryS/jiTIr/SBIlTaDdyjqH+ODg8oBw==
+  dependencies:
+    clsx "^2.1.1"
+    fast-equals "^4.0.3"
+    prop-types "^15.8.1"
+    react-draggable "^4.4.6"
+    react-resizable "^3.0.5"
+    resize-observer-polyfill "^1.5.1"
+
 react-is@^16.12.0:
   version "16.13.1"
   resolved "https://registry.npmmirror.com/react-is/-/react-is-16.13.1.tgz"
@@ -9083,6 +9113,14 @@ react-refresh@^0.11.0, "react-refresh@>=0.10.0 <1.0.0":
   resolved "https://registry.npmmirror.com/react-refresh/-/react-refresh-0.11.0.tgz"
   integrity sha512-F27qZr8uUqwhWZboondsPx8tnC3Ct3SxZA3V5WyEvujRyyNv0VYPhoBg1gZ8/MV5tubQp76Trw8lTv9hzRBa+A==
 
+react-resizable@^3.0.5, react-resizable@^3.1.3:
+  version "3.1.3"
+  resolved "https://registry.npmmirror.com/react-resizable/-/react-resizable-3.1.3.tgz"
+  integrity sha512-liJBNayhX7qA4tBJiBD321FDhJxgGTJ07uzH5zSORXoE8h7PyEZ8mLqmosST7ppf6C4zUsbd2gzDMmBCfFp9Lw==
+  dependencies:
+    prop-types "15.x"
+    react-draggable "^4.5.0"
+
 react-router-dom@5.3:
   version "5.3.4"
   resolved "https://registry.npmmirror.com/react-router-dom/-/react-router-dom-5.3.4.tgz"
@@ -9174,7 +9212,7 @@ react-sortablejs@^6.1.4:
     classnames "2.3.1"
     tiny-invariant "1.2.0"
 
-react@*, "react@^16.8 || ^17.0 || ^18.0", "react@^16.8.0 || ^17.0.0 || ^18.0.0", react@^18.0.0, react@^18.2.0, "react@>= 16", "react@>= 16.8.0", react@>=15, react@>=16.0.0, react@>=16.11.0, react@>=16.8.0, react@>=16.9.0, react@>=17.0.0:
+react@*, "react@^16.8 || ^17.0 || ^18.0", "react@^16.8.0 || ^17.0.0 || ^18.0.0", react@^18.0.0, react@^18.2.0, "react@>= 16", "react@>= 16.3", "react@>= 16.3.0", "react@>= 16.8.0", react@>=15, react@>=16.0.0, react@>=16.11.0, react@>=16.8.0, react@>=16.9.0, react@>=17.0.0:
   version "18.2.0"
   resolved "https://registry.npmmirror.com/react/-/react-18.2.0.tgz"
   integrity sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==