shaogen1995 1 éve
commit
f05666e55a

+ 23 - 0
.gitignore

@@ -0,0 +1,23 @@
+.DS_Store
+node_modules
+/dist
+
+
+# local env files
+.env.local
+.env.*.local
+
+# Log files
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+pnpm-debug.log*
+
+# Editor directories and files
+.idea
+.vscode
+*.suo
+*.ntvs*
+*.njsproj
+*.sln
+*.sw?

+ 19 - 0
README.md

@@ -0,0 +1,19 @@
+# webm
+
+## Project setup
+```
+npm install
+```
+
+### Compiles and hot-reloads for development
+```
+npm run serve
+```
+
+### Compiles and minifies for production
+```
+npm run build
+```
+
+### Customize configuration
+See [Configuration Reference](https://cli.vuejs.org/config/).

+ 5 - 0
babel.config.js

@@ -0,0 +1,5 @@
+module.exports = {
+  presets: [
+    '@vue/cli-plugin-babel/preset'
+  ]
+}

A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 26412 - 0
package-lock.json


+ 29 - 0
package.json

@@ -0,0 +1,29 @@
+{
+  "name": "webm",
+  "version": "0.1.0",
+  "private": true,
+  "scripts": {
+    "serve": "vue-cli-service serve",
+    "build": "vue-cli-service build"
+  },
+  "dependencies": {
+    "axios": "^0.27.2",
+    "core-js": "^3.6.5",
+    "three": "^0.150.1",
+    "vue": "^2.6.11",
+    "vue-router": "^3.2.0"
+  },
+  "devDependencies": {
+    "@vue/cli-plugin-babel": "~4.5.13",
+    "@vue/cli-plugin-router": "~4.5.13",
+    "@vue/cli-service": "~4.5.13",
+    "less": "^3.0.4",
+    "less-loader": "^5.0.0",
+    "vue-template-compiler": "^2.6.11"
+  },
+  "browserslist": [
+    "> 1%",
+    "last 2 versions",
+    "not dead"
+  ]
+}

BIN
public/favicon.ico


+ 17 - 0
public/index.html

@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<html lang="">
+
+<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" />
+  <link rel="icon" href="<%= BASE_URL %>favicon.ico" />
+  <title>demo</title>
+</head>
+
+<body>
+  <div id="app"></div>
+  <!-- built files will be auto injected -->
+</body>
+
+</html>

+ 326 - 0
public/three/PanoramaControls.js

@@ -0,0 +1,326 @@
+
+function PanoramaControls(camera, domElement) {
+
+  
+    // fyz 相机放大缩小
+    this.activationThreshold = 1.1;
+    this.scrollZoomSpeed = 0.08;
+    this.scrollZoomSta = true;
+    this.zoomMin = 0.7;
+    this.zoomMax = 1.5;
+    this.baseFov = 70;
+ 
+    this.camera = camera;
+    this.camera.fov = this.baseFov;
+     
+    this.domElement = domElement; 
+    this.camera.controls = this; 
+    this.enabled = true;
+
+    this.target = new THREE.Vector3(0, 0, 0);
+    this.lookVector = new THREE.Vector3;
+    this.aimFrom = this.camera.position;
+
+
+    this.lat = 0;
+    this.latMin = -60//-40;
+    this.latMax = 60//40;
+    this.lon = 0;
+    this.phi = 0;
+    this.theta = 0;
+    this.lookSpeed = 0.05;
+    this.rotationAcc = new THREE.Vector2;
+    this.rotationSpeed = new THREE.Vector2;
+    this.rotationHistory = [];
+    this.rotationDifference = new THREE.Vector2;
+
+
+    this.pointerDragOn = !1;
+    this.pointer = new THREE.Vector3(0, 0, -1);
+    this.pointerDragStart = new THREE.Vector3(0, 0, -1); 
+    this._wheel = 0;
+    this.zoomLevel = 1; 
+    this.translationWorldDelta = new THREE.Vector3
+    this.bindEvents()
+}
+
+PanoramaControls.prototype.bindEvents = function() {
+
+    window.addEventListener("mousemove", this.onMouseMove.bind(this));
+    this.domElement.addEventListener("mousedown", this.onMouseDown.bind(this));
+    window.addEventListener("mouseup", this.onMouseUp.bind(this));
+    this.domElement.addEventListener("mouseover", (event) => this.pointerDragOn && 0 === event.which && this.onMouseUp(event));
+
+    this.domElement.addEventListener("touchstart", this.onTouchStart.bind(this));
+    this.domElement.addEventListener("touchmove", this.onTouchMove.bind(this));
+    this.domElement.addEventListener("touchend", this.onTouchEnd.bind(this));
+
+    this.domElement.addEventListener("wheel", this.onMouseWheel.bind(this));   // fyz wheel事件代替mousewheel事件
+    this.domElement.addEventListener("DOMMouseScroll", this.onMouseWheel.bind(this));
+    this.domElement.addEventListener("contextmenu", (event) => event.preventDefault());
+
+    //document.addEventListener("keydown", this.onKeyDown.bind(this));
+    //document.addEventListener("keyup", this.onKeyUp.bind(this));
+
+}
+PanoramaControls.prototype.lookAt = function(point) {
+ 
+
+    var directionNegative = this.camera.position.clone().sub(point),
+        theta = Math.atan(directionNegative.z / directionNegative.x);
+
+    theta += directionNegative.x < 0 ? Math.PI : 0;
+    theta += directionNegative.x > 0 && directionNegative.z < 0 ? 2 * Math.PI : 0;
+    this.lon = THREE.Math.radToDeg(theta) + 180;
+
+    let projectorR = Math.sqrt(directionNegative.x * directionNegative.x + directionNegative.z * directionNegative.z),
+        phi = Math.atan(directionNegative.y / projectorR);
+
+    this.lat = -THREE.Math.radToDeg(phi)
+}
+PanoramaControls.prototype.startRotationFrom = function(screenX, screenY) {
+
+    this.updatePointer(screenX, screenY);
+    this.pointerDragOn = true;
+    this.pointerDragStart.copy(this.pointer);
+    //TODO
+    //this.pointerDragStartIntersect = this.player.getMouseIntersect(this.pointer.clone(), [this.scene.skybox]).point;
+    this.rotationHistory = [];
+    this.rotationSpeed.set(0, 0);
+}
+PanoramaControls.prototype.onTouchStart = function(event) {
+
+    if (this.enabled) {
+
+        event.preventDefault();
+        event.stopPropagation();
+        this.startRotationFrom(event.changedTouches[0].clientX, event.changedTouches[0].clientY);
+    }
+
+}
+PanoramaControls.prototype.onMouseDown = function(event) {
+
+    if (this.enabled) {
+
+        event.preventDefault();
+        event.stopPropagation()
+
+        switch (event.button) {
+
+            case 0:
+                this.startRotationFrom(event.clientX, event.clientY);
+                break;
+            case 2:
+
+        }
+    }
+}
+
+PanoramaControls.prototype.updateRotation = function( ) {
+
+
+    if (this.enabled && this.pointerDragOn) {
+
+        var pointerDragStart3D = new THREE.Vector3(this.pointerDragStart.x, this.pointerDragStart.y, 1).unproject(this.camera);
+        var pointer3D = new THREE.Vector3(this.pointer.x, this.pointer.y, 1).unproject(this.camera);
+
+            //两交互点分别到原点的长度
+        var pointerDragStart3DLength = Math.sqrt(pointerDragStart3D.x * pointerDragStart3D.x + pointerDragStart3D.z * pointerDragStart3D.z);
+        var pointer3DLength = Math.sqrt(pointer3D.x * pointer3D.x + pointer3D.z * pointer3D.z);
+
+            //通过Math.atan2计算在XY面上与X轴的夹角弧度。
+            //注:因为 z = -1,所以两者到原点的长度近似为x分量(数值的大小也不需要绝对对应)
+        var anglePointerDragStart3DToX = Math.atan2(pointerDragStart3D.y, pointerDragStart3DLength);       //近似为 anglePointerDragStart3DToX = Math.atan2( pointerDragStart3D.y, pointerDragStart3D.x ) 
+        var anglePointer3DToX = Math.atan2(pointer3D.y, pointer3DLength);                                 //近似为 anglePointer3DToX = Math.atan2( pointer3D.y, pointer3D.x )
+
+        //算出两者角度差,作为竖直方向角度差值(rotationDifference.y)
+        this.rotationDifference.y = THREE.Math.radToDeg(anglePointerDragStart3DToX - anglePointer3DToX);
+
+
+        //y分量清零,原向量等价于在XZ轴上的投影向量
+        pointerDragStart3D.y = 0;
+        pointer3D.y = 0;
+
+        //归一化(/length),求两者夹角作为
+        //判断方向,最后记为水平方向角度差值(rotationDifference.x)
+        var anglePointerDragStart3DToPointer3D = Math.acos(pointerDragStart3D.dot(pointer3D) / pointerDragStart3D.length() / pointer3D.length());
+
+        if (!isNaN(anglePointerDragStart3DToPointer3D)) {
+            this.rotationDifference.x = THREE.Math.radToDeg(anglePointerDragStart3DToPointer3D);
+            if (this.pointerDragStart.x < this.pointer.x) {
+                this.rotationDifference.x *= -1;
+            }
+        }
+
+        //更新pointerDragStart记录当前帧坐标,用于下一帧求帧差值
+        this.pointerDragStart.copy(this.pointer);
+    }
+}
+PanoramaControls.prototype.onMouseMove = function(event) {
+     
+    this.updatePointer(event.clientX, event.clientY);
+
+}
+PanoramaControls.prototype.onTouchMove = function(event) {
+
+    this.updatePointer(event.changedTouches[0].clientX, event.changedTouches[0].clientY)
+
+}
+
+PanoramaControls.prototype.updatePointer = function(screenX, screenY) {
+    this.pointer.x = screenX / this.domElement.clientWidth * 2 - 1;   // 屏幕坐标换算相对于canvas的父级
+    this.pointer.y = 2 * -(screenY / this.domElement.clientHeight) + 1;
+
+}
+PanoramaControls.prototype.endRotation = function() {
+
+
+    this.pointerDragOn = false;
+    try{
+        var rotationHistoryAverage = averageVectors(this.rotationHistory);
+    }catch(e){
+        console.error(e)
+    } 
+    
+    this.rotationSpeed.set(rotationHistoryAverage.x * 30, rotationHistoryAverage.y * 30);
+
+}
+PanoramaControls.prototype.onTouchEnd = function(event) {
+
+    if (this.enabled) {
+
+        event.preventDefault();
+        event.stopPropagation();
+        this.endRotation()
+    }
+}
+PanoramaControls.prototype.onMouseUp = function(event) {
+
+
+    if (this.enabled) {
+
+        event.preventDefault();
+        event.stopPropagation();
+        this.endRotation()
+    }
+}
+PanoramaControls.prototype.update = function(deltaTime) {
+
+    if (this.enabled) {
+
+        this.updateRotation();
+
+        for (this.rotationHistory.push(this.rotationDifference.clone()); this.rotationHistory.length > 5;) {
+
+            this.rotationHistory.shift();
+        }
+
+        this.lon += this.rotationDifference.x;
+        this.lat += this.rotationDifference.y;
+        this.rotationDifference.set(0, 0);
+        this.rotationSpeed.x = this.rotationSpeed.x * (1 - 0.05) + this.rotationAcc.x * 4.5;
+        this.rotationSpeed.y = this.rotationSpeed.y * (1 - 0.05) + this.rotationAcc.y * 4.5;
+
+        this.lon += this.rotationSpeed.x * deltaTime;
+        this.lat += this.rotationSpeed.y * deltaTime;
+
+      
+
+        this.lat = Math.max(this.latMin, Math.min(this.latMax, this.lat));
+        this.phi = THREE.Math.degToRad(90 - this.lat);
+        this.theta = THREE.Math.degToRad(this.lon);
+
+        this.lookVector.x = Math.sin(this.phi) * Math.cos(this.theta);
+        this.lookVector.y = Math.cos(this.phi);
+        this.lookVector.z = Math.sin(this.phi) * Math.sin(this.theta);
+
+
+        this.camera.position.add(this.translationWorldDelta) 
+        this.translationWorldDelta.multiplyScalar(0.9); 
+    
+        this.target.copy(this.lookVector).add(this.aimFrom);
+        this.camera.lookAt(this.target)
+        
+        
+        
+    }
+}
+PanoramaControls.prototype.onMouseWheel = function(event) {
+
+    /* if (this.enabled) {
+
+        // let z = void 0 !== event['wheelDelta'] ? event['wheelDelta'] : 0 !== event.detail && -event.detail;
+        // this.flyDirection(new THREE.Vector3(0, 0, -z).normalize());
+        this._wheel = Math.floor(event['wheelDeltaY'] / 120);
+        this._wheel = Math.abs(this._wheel) > 0.1 ? Math.sign(this._wheel) : 0;
+        if (this._wheel !== 0 && this.scrollZoomSta) {
+            this._wheel > 0 ? this._wheel = 1 + this.scrollZoomSpeed : this._wheel = 1 - this.scrollZoomSpeed;
+            let curZoomLevel = this._wheel * this.zoomLevel;
+            this.zoomTo(curZoomLevel);
+        }
+
+    } */
+    
+    let delta
+    if (event.wheelDelta !== undefined) { // WebKit / Opera / Explorer 9
+        delta = event.wheelDelta;
+    } else if (event.detail !== undefined) { // Firefox
+        delta = -event.detail;
+    }
+        
+    
+    
+    if(delta != void 0){//滚轮缩放 
+        if(delta == 0)return //mac
+        let direction = new THREE.Vector3(0,0,-1).applyQuaternion(this.camera.quaternion)
+        let moveSpeed = 0.1
+        if(delta < 0) moveSpeed *=-1
+        this.translationWorldDelta.add(direction.multiplyScalar(moveSpeed))
+    }
+    
+}
+PanoramaControls.prototype.zoomTo = function(curZoomLevel) {
+    curZoomLevel < this.zoomMin && (curZoomLevel = this.zoomMin);
+    curZoomLevel > this.zoomMax && (curZoomLevel = this.zoomMax);
+    this.zoomLevel = curZoomLevel;
+    this.camera.fov = this.baseFov*(1/this.zoomLevel);
+    this.camera.updateProjectionMatrix();
+} 
+
+/*   
+reset() {
+
+    this.stop()
+}
+
+stop() {
+
+    this.rotationAcc.set(0, 0);
+    this.rotationSpeed.set(0, 0);
+}
+*/
+
+
+
+
+//-------------copyFromPlayer
+PanoramaControls.prototype.handleControlScroll = function(e) {
+     e > 0 ? e = 1 + this.scrollZoomSpeed : e < 0 && (e = 1 - this.scrollZoomSpeed);
+    0 !== e && this.zoomBy(e)
+}
+PanoramaControls.prototype.zoomBy = function(e) {
+     this.zoomTo(this.zoomLevel * e);
+}
+
+
+
+function averageVectors(e, t) {
+    var i = new THREE.Vector3();
+    if (0 === e.length) return i;
+    for (var r = 0, o = 0; o < e.length; o++) {
+        var a = t ? e[o][t] : e[o];
+        i.add(a), r++;
+    }
+    return i.divideScalar(r);
+} 
+ 

BIN
public/three/assets/0.jpg


BIN
public/three/assets/1.jpg


BIN
public/three/assets/10.jpg


BIN
public/three/assets/11.jpg


BIN
public/three/assets/12.jpg


BIN
public/three/assets/13.jpg


BIN
public/three/assets/14.jpg


BIN
public/three/assets/2.jpg


BIN
public/three/assets/3.jpg


BIN
public/three/assets/4.jpg


BIN
public/three/assets/5.jpg


BIN
public/three/assets/6.jpg


BIN
public/three/assets/7.jpg


BIN
public/three/assets/8.jpg


BIN
public/three/assets/9.jpg


BIN
public/three/background.jpg


+ 23 - 0
public/three/click.js

@@ -0,0 +1,23 @@
+// 点击
+viewer.addEventListener('clickObject', e => {
+  window.top.clickObject(e.imgName);
+  // 暂停动画
+  viewer.setAutoMove(false)
+})
+
+// 继续动画 - 给 父页面调用
+window.stareMove = (val) => {
+  viewer.setAutoMove(val)
+}
+
+// 鼠标移入
+viewer.addEventListener('hoverObject', e => {
+  // console.log('鼠标移入',e);
+  window.top.hoverObject(e);
+})
+
+// 鼠标移出
+viewer.addEventListener('mouseoutObject', e => {
+  // console.log('鼠标移出',e);
+  window.top.mouseoutObject(e);
+}) 

+ 100 - 0
public/three/index.html

@@ -0,0 +1,100 @@
+<!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;
+    }
+
+    /*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>
+
+    const cardNames = ['1.jpg', '2.jpg', '3.jpg', '4.jpg', '5.jpg', '6.jpg', '7.jpg', '8.jpg', '9.jpg', '10.jpg', '11.jpg', '12.jpg', '13.jpg', '14.jpg']
+
+
+    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米处时最高可以多少才能在视线内
+
+      }
+
+
+    };
+
+    /* var textarea = document.createElement('textarea');
+    textarea.id = "consoleLog";
+
+    document.getElementsByTagName("body")[0].appendChild(textarea);
+    var list = ["log", "error", "warn", "debug", "info", "time", "timeEnd"]
+    var exchange = function (o) {
+    console["old" + o] = console[o];
+    console[o] = function (str) {
+      console["old" + o](str);
+      var t = document.getElementById("consoleLog").innerHTML;
+      document.getElementById("consoleLog").innerHTML = str + "\n\n" + t;
+    }
+    }
+
+    for (var i = 0; i < list.length; i++) {
+    exchange(list[i])
+    }
+
+    */
+  </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>

+ 479 - 0
public/three/index.js

@@ -0,0 +1,479 @@
+
+
+let camera, scene, renderer;
+
+const shadowHasAlpha = false  //阴影是否考虑光透过透明材质  
+
+const planeGeo = new THREE.PlaneBufferGeometry(1, 1)
+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")
+
+};
+
+
+
+
+
+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("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()
+}
+
+/* 
+    同一时间,视线范围不能同时出现两张卡。也就是分布要根据视角范围变化,但是如果浏览器变宽导致的出现两张卡不算。
+
+ */
+
+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)
+    })
+
+}
+
+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 count = this.cardGroup.children.filter(card => {
+        return frustum.containsPoint(card.position)
+    }).length
+
+
+    let density = count / (this.renderer.domElement.width * this.renderer.domElement.height) * 1000
+
+    return density
+}
+
+
+Viewer.prototype.addCard = function (around) {
+
+    let cardIndex = Math.floor(cardNames.length * Math.random())
+    common.loadTexture("assets/" + cardNames[cardIndex], (map) => {
+
+        console.log('------',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 },
+                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
+        }
+    })
+    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);
+
+}
+
+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")
+    }
+}
+
+
+
+
+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 };
+    }
+    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)  
+
+    copy()
+
+    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);
+
+            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()
+}
+
+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(time - this.clickTime < 300){
+            if(this.intersects.length){
+                console.log('doubleClick');
+                transitions.cancelById(0)
+                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() })
+        }
+    }
+
+    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) {
+
+
+        }
+    }
+    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
+    }
+    this.intersects = intersects;
+}
+
+
+
+Viewer.prototype.setAutoMove = function (state) {//设置相机飞行状态
+    this.autoMove = !!state
+
+}
+
+Object.assign(Viewer.prototype, THREE.EventDispatcher.prototype);
+
+//============
+
+var startTime = new Date().getTime();
+var viewer = new Viewer(0, $("#player")[0])
+
+
+
+
+
+
+
+
+
+
+

A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 4 - 0
public/three/jquery-2.1.1.min.js


A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 2 - 0
public/three/three.min.js


+ 636 - 0
public/three/utils.js

@@ -0,0 +1,636 @@
+var lerp = {
+	vector: function(e, t, f) {//xzw change, add f
+		var i = e.clone();
+		return t = t.clone(),
+		function(n) {
+			e.set(i.x * (1 - n) + t.x * n, i.y * (1 - n) + t.y * n, i.z * (1 - n) + t.z * n)
+			f && f(e,n);
+		}
+	},
+    quaternion: function(e, t, f) {//xzw change, add f
+        var i = e.clone();
+        return function(n) {
+            e.copy(i).slerp(t, n);
+			f && f(e,n);
+        }
+    },
+    property: function(e, t, i, n) {
+        var r = e[t];
+        return function(o) {
+            e[t] = r * (1 - o) + i * o,
+            n && n(e[t])
+        }
+    },
+    uniform: function(e, t, i) {
+        var n = e.material.uniforms[t].value;
+        return function(r) {
+            try{
+                e.material.uniforms[t] && (e.material.uniforms[t].value = n * (1 - r) + i * r)
+            }catch(e){
+                console.log(1)
+            }
+            
+        }
+    },
+    matrix4: function(e, t) {
+        var i = e.clone();
+        return function(n) {
+            for (var r = e.elements, o = i.elements, a = t.elements, s = 0; s < 16; s++)
+                r[s] = o[s] * (1 - n) + a[s] * n
+        }
+    },
+    allUniforms: function(e, t, i) {
+        var n = e.map(function(e) {
+            return this.uniform(e, t, i)
+        }
+        .bind(this));
+        return function(e) {
+            n.forEach(function(t) {
+                t(e)
+            })
+        }
+    }
+};
+
+
+//////
+ 
+var easing = {};
+//渐变曲线函数,反应加速度的变化
+easing.linearTween = function(e, t, i, n) {
+    return i * e / n + t
+}
+,
+easing.easeInQuad = function(e, t, i, n) {
+    return e /= n,
+    i * e * e + t
+}
+,
+easing.easeOutQuad = function(e, t, i, n) {
+    return e /= n,
+    -i * e * (e - 2) + t
+}
+,
+easing.easeInOutQuad = function(e, t, i, n) {
+    return e /= n / 2,
+    e < 1 ? i / 2 * e * e + t : (e--,
+    -i / 2 * (e * (e - 2) - 1) + t)
+}
+,
+easing.easeInCubic = function(e, t, i, n) {
+    return e /= n,
+    i * e * e * e + t
+}
+,
+easing.easeOutCubic = function(e, t, i, n) {
+    return e /= n,
+    e--,
+    i * (e * e * e + 1) + t
+}
+,
+easing.easeInOutCubic = function(e, t, i, n) {
+    return e /= n / 2,
+    e < 1 ? i / 2 * e * e * e + t : (e -= 2,
+    i / 2 * (e * e * e + 2) + t)
+}
+,
+easing.easeInQuart = function(e, t, i, n) {
+    return e /= n,
+    i * e * e * e * e + t
+}
+,
+easing.easeOutQuart = function(e, t, i, n) {
+    return e /= n,
+    e--,
+    -i * (e * e * e * e - 1) + t
+}
+,
+easing.easeInOutQuart = function(e, t, i, n) {
+    return e /= n / 2,
+    e < 1 ? i / 2 * e * e * e * e + t : (e -= 2,
+    -i / 2 * (e * e * e * e - 2) + t)
+}
+,
+easing.easeInQuint = function(e, t, i, n) {
+    return e /= n,
+    i * e * e * e * e * e + t
+}
+,
+easing.easeOutQuint = function(e, t, i, n) {
+    return e /= n,
+    e--,
+    i * (e * e * e * e * e + 1) + t
+}
+,
+easing.easeInOutQuint = function(e, t, i, n) {
+    return e /= n / 2,
+    e < 1 ? i / 2 * e * e * e * e * e + t : (e -= 2,
+    i / 2 * (e * e * e * e * e + 2) + t)
+}
+,
+easing.easeInSine = function(e, t, i, n) {
+    return -i * Math.cos(e / n * (Math.PI / 2)) + i + t
+}
+,
+easing.easeOutSine = function(e, t, i, n) {
+    return i * Math.sin(e / n * (Math.PI / 2)) + t
+}
+,
+easing.easeInOutSine = function(e, t, i, n) {
+    return -i / 2 * (Math.cos(Math.PI * e / n) - 1) + t
+}
+,
+easing.easeInExpo = function(e, t, i, n) {
+    return i * Math.pow(2, 10 * (e / n - 1)) + t
+}
+,
+easing.easeOutExpo = function(e, t, i, n) {
+    return i * (-Math.pow(2, -10 * e / n) + 1) + t
+}
+,
+easing.easeInOutExpo = function(e, t, i, n) {
+    return e /= n / 2,
+    e < 1 ? i / 2 * Math.pow(2, 10 * (e - 1)) + t : (e--,
+    i / 2 * (-Math.pow(2, -10 * e) + 2) + t)
+}
+,
+easing.easeInCirc = function(e, t, i, n) {
+    return e /= n,
+    -i * (Math.sqrt(1 - e * e) - 1) + t
+}
+,
+easing.easeOutCirc = function(e, t, i, n) {
+    return e /= n,
+    e--,
+    i * Math.sqrt(1 - e * e) + t
+}
+,
+easing.easeInOutCirc = function(e, t, i, n) {
+    return e /= n / 2,
+    e < 1 ? -i / 2 * (Math.sqrt(1 - e * e) - 1) + t : (e -= 2,
+    i / 2 * (Math.sqrt(1 - e * e) + 1) + t)
+}
+,
+easing.easeInElastic = function(e, t, i, n) {
+    var r = 1.70158
+      , o = 0
+      , a = i;
+    return 0 === e ? t : 1 === (e /= n) ? t + i : (o || (o = .3 * n),
+    a < Math.abs(i) ? (a = i,
+    r = o / 4) : r = o / (2 * Math.PI) * Math.asin(i / a),
+    -(a * Math.pow(2, 10 * (e -= 1)) * Math.sin((e * n - r) * (2 * Math.PI) / o)) + t)
+}
+,
+easing.easeOutElastic = function(e, t, i, n) {
+    var r = 1.70158
+      , o = 0
+      , a = i;
+    return 0 === e ? t : 1 === (e /= n) ? t + i : (o || (o = .3 * n),
+    a < Math.abs(i) ? (a = i,
+    r = o / 4) : r = o / (2 * Math.PI) * Math.asin(i / a),
+    a * Math.pow(2, -10 * e) * Math.sin((e * n - r) * (2 * Math.PI) / o) + i + t)
+}
+,
+easing.easeInOutElastic = function(e, t, i, n) {
+    var r = 1.70158
+      , o = 0
+      , a = i;
+    return 0 === e ? t : 2 === (e /= n / 2) ? t + i : (o || (o = n * (.3 * 1.5)),
+    a < Math.abs(i) ? (a = i,
+    r = o / 4) : r = o / (2 * Math.PI) * Math.asin(i / a),
+    e < 1 ? -.5 * (a * Math.pow(2, 10 * (e -= 1)) * Math.sin((e * n - r) * (2 * Math.PI) / o)) + t : a * Math.pow(2, -10 * (e -= 1)) * Math.sin((e * n - r) * (2 * Math.PI) / o) * .5 + i + t)
+}
+,
+easing.easeInBack = function(e, t, i, n, r) {
+    return void 0 === r && (r = 1.70158),
+    i * (e /= n) * e * ((r + 1) * e - r) + t
+}
+,
+easing.easeOutBack = function(e, t, i, n, r) {
+    return void 0 === r && (r = 1.70158),
+    i * ((e = e / n - 1) * e * ((r + 1) * e + r) + 1) + t
+}
+,
+easing.easeInOutBack = function(e, t, i, n, r) {
+    return void 0 === r && (r = 1.70158),
+    (e /= n / 2) < 1 ? i / 2 * (e * e * (((r *= 1.525) + 1) * e - r)) + t : i / 2 * ((e -= 2) * e * (((r *= 1.525) + 1) * e + r) + 2) + t
+}
+,
+easing.easeOutBounce = function(e, t, i, n) {
+    return (e /= n) < 1 / 2.75 ? i * (7.5625 * e * e) + t : e < 2 / 2.75 ? i * (7.5625 * (e -= 1.5 / 2.75) * e + .75) + t : e < 2.5 / 2.75 ? i * (7.5625 * (e -= 2.25 / 2.75) * e + .9375) + t : i * (7.5625 * (e -= 2.625 / 2.75) * e + .984375) + t
+}
+,
+easing.easeInBounce = function(e, t, i, r) {
+    return i - easing.easeOutBounce(r - e, 0, i, r) + t
+}
+,
+easing.easeInOutBounce = function(e, t, i, r) {
+    return e < r / 2 ? .5 * easing.easeInBounce(2 * e, 0, i, r) + t : .5 * easing.easeOutBounce(x, 2 * e - r, 0, i, r) + .5 * i + t
+}
+
+ 
+ 
+ 
+ 
+/* 
+    渐变
+    
+
+ */
+
+var transitions = {
+    globalDone: null,
+    funcs: [],
+    counter: 0,
+    uniqueID: 0,
+    start: function(e, t, i, r, o, a, s, cancelFun) {
+        r = r || 0 
+        let info = {
+            func: e,
+            current: -r * Math.abs(t),                      //当前时间
+            duration: (1 - Math.max(r, 0)) * Math.abs(t),   //总时长
+            done: i,
+            easing: o || easing.linearTween,                //渐变曲线
+            cycling: t < 0,
+            running: !0,
+            debug: r < 0,
+            name: a || "T" + this.counter,
+            id: void 0 === s ? this.counter : s,
+            paused: !1,
+			cancelFun : cancelFun,   //取消时执行的函数
+        }  
+        this.funcs.push(info),
+        e(0, 16),
+        this.counter += 1 
+        return info
+    },
+    trigger: function(e) {
+        var t = void 0 === e.delayRatio ? 0 : e.delayRatio
+            , i = e.func || function() {}
+            , r = void 0 === e.duration ? 0 : e.duration;
+        void 0 !== e.cycling && e.cycling && (r = -Math.abs(r));
+        var o = e.done || null
+            , a = e.easing || easing.linearTween
+            , s = e.name || "R" + this.counter
+            , l = void 0 === e.id ? this.counter : e.id;
+        return this.start(i, r, o, t, a, s, l)
+    },
+    setTimeout: function(e, t, i) {
+        var n = void 0 === i ? this.counter : i;
+        return this.trigger({
+            done: e,
+            duration: void 0 === t ? 0 : t,
+            name: "O" + this.counter,
+            id: n
+        })
+    },
+    pause: function() {
+        this.paused = !0
+    },
+    resume: function() {
+        this.paused = !1
+    },
+    update: function(e) {
+        this.funcs.forEach(function(t) {
+            if (!(t.paused || (t.current += 1e3 * e, t.current < 0))){ 
+                if (t.current >= t.duration && !t.cycling) {
+                    var i = t.easing(1, 0, 1, 1);
+                    t.func(i, 1e3 * e),
+                    t.done && t.done(),
+                    t.running = !1 
+                } else {
+                    var n = t.easing(t.current % t.duration / t.duration, 0, 1, 1)
+                        , r = t.func(n, 1e3 * e) || !1;
+                    r && (t.done && t.done(),
+                    t.running = !1)
+                }
+            }
+        });
+        var t = this.funcs.length;
+        this.funcs = this.funcs.filter(function(e) {
+            return e.running
+        }); 
+        var i = this.funcs.length;
+        if (t > 0 && 0 === i && this.globalDone) {
+            var n = this.globalDone;
+            this.globalDone = null,
+            n()
+        }
+    },
+    adjustSpeed: function(e, t) {
+        for (var i = this.getById(e), n = 0; n < i.length; n++) {
+            var r = i[n];
+            r.duration /= t,
+            r.current /= t
+        }
+    },
+    getById: function(e) {
+        return this.funcs.filter(function(t) {
+            return e === t.id
+        })
+    },
+    get: function(e) {
+        for (var t = 0; t < this.funcs.length; t += 1)
+            if (this.funcs[t].func === e)
+                return this.funcs[t];
+        return null
+    },
+    isRunning: function(e) {
+        var t = this.get(e);
+        return null !== t && t.running
+    },
+    countActive: function() {
+        for (var e = 0, t = 0; t < this.funcs.length; t += 1)
+            e += this.funcs[t].running;
+        return e
+    },
+    listActive: function() {
+        for (var e = [], t = 0; t < this.funcs.length; t += 1)
+            this.funcs[t].running && e.push(this.funcs[t].name);
+        return e
+    },
+    done: function(e) {
+        this.globalDone = e
+    },
+    cancelById: function(e, dealCancelFun) { //xzw add dealDone
+        var t = void 0 === e ? 0 : e;
+		 
+        this.funcs = this.funcs.filter(function(e) {
+			var is = e.id == t;
+			
+			if(is && dealCancelFun){
+				e.cancelFun && e.cancelFun()
+			} 
+            return !is
+        })
+    },
+    cancel: function(e) {
+        this.funcs = this.funcs.filter(function(t) {
+            return t.func !== e
+        }) 
+        
+    },
+    getUniqueId: function() {
+        return this.uniqueID -= 1,
+        this.uniqueID
+    }
+};
+
+ 
+let convertTool = {
+    
+	getPos2d : function(point, camera, dom){//获取一个三维坐标对应屏幕中的二维坐标
+	  
+        
+        
+        if(!camera)return
+		var pos = point.clone().project(camera)	//比之前hotspot的计算方式写得简单  project用于3转2(求法同shader); unproject用于2转3 :new r.Vector3(e.x, e.y, -1).unproject(this.camera);
+		
+		var x,y;
+		x = (pos.x + 1) / 2 * dom.clientWidth;
+		y = (1 - (pos.y + 1) / 2) * dom.clientHeight; 
+  
+		var inSight = x <= dom.clientWidth &&  x >= 0    //是否在屏幕中   
+					&& y <= dom.clientHeight &&  y >= 0 
+	 
+	
+		return {
+			pos: new THREE.Vector2(x,y),  // 屏幕像素坐标
+			vector:  pos,   //(范围 -1 ~ 1)
+			trueSide : pos.z<1, //trueSide为false时,即使在屏幕范围内可见,也是反方向的另一个不可以被渲染的点   参见Tag.update
+			inSight : inSight	//在屏幕范围内可见
+		};
+	},
+
+	ifShelter: function (pos3d,  pos2d,  camera, colliders, margin=0  ) {
+        //检测某点在视线中是否被mesh遮挡
+        if (!pos2d) pos2d = convertTool.getPos2d(pos3d )
+        camera = camera || player.camera
+        var ori = new THREE.Vector3(pos2d.x, pos2d.y, -1).unproject(camera) //找到视线原点
+        var dir = pos3d.clone().sub(ori).normalize()
+        var ray = new THREE.Raycaster(ori, dir); //由外向里 因为模型从内侧是可见的所以从外侧
+        var o = ray.intersectObjects(colliders); 
+	 
+		var len = pos3d.distanceTo(ori);
+		if (o && o.length) {
+			for(var i=0;i<o.length;i++){
+				if(o[i].distance < len-margin){  return true;  }//有遮挡
+			} 
+		} 
+	},
+    
+    updateVisible : function(object, reason, ifShow, level=0, type){//当所有加入的条件都不为false时才显示. reason='force'一般是强制、临时的
+        if(!object.unvisibleReasons) object.unvisibleReasons = []; //如果length>0代表不可见
+        if(!object.visibleReasons) object.visibleReasons = []; //在同级时,优先可见
+        
+        
+        var update = function(){
+            
+            //先按从高到低的level排列
+            object.unvisibleReasons = object.unvisibleReasons.sort((a,b)=>b.level-a.level)
+            object.visibleReasons = object.visibleReasons.sort((a,b)=>b.level-a.level)
+            var maxVisiLevel = object.visibleReasons[0] ? object.visibleReasons[0].level : -1
+            var maxunVisiLevel = object.unvisibleReasons[0] ? object.unvisibleReasons[0].level : -1
+            
+            var shouldVisi = maxVisiLevel >= maxunVisiLevel
+            var visiBefore = object.visible
+            
+            
+            if(visiBefore != shouldVisi){
+                object.visible = shouldVisi
+                object.dispatchEvent({
+                    type: 'isVisible',
+                    visible: shouldVisi,
+                    reason,
+                }) 
+            }
+            
+            
+        }    
+        
+        
+        
+        if(ifShow){ 
+
+            var index = object.unvisibleReasons.findIndex(e=>e.reason == reason) 
+            if(index > -1){
+                type = 'cancel'
+                object.unvisibleReasons.splice(index, 1); 
+            }
+            
+            if(type == 'add' ){
+                if(!object.visibleReasons.some(e=>e.reason == reason)){
+                    object.visibleReasons.push({reason,level})
+                }
+            } 
+        }else{ 
+            var index = object.visibleReasons.findIndex(e=>e.reason == reason) 
+            if(index > -1){
+                type = 'cancel'
+                object.visibleReasons.splice(index, 1); 
+            }
+            
+            if(type != 'cancel' ){
+                if(!object.unvisibleReasons.some(e=>e.reason == reason)){
+                    object.unvisibleReasons.push({reason,level})
+                }
+            }
+        }
+          
+        update() 
+        
+    }, 
+     
+
+    toPrecision: function (e, t) {//xzw change 保留小数
+		var f = function (e, t) {
+			var i = Math.pow(10, t);
+			return Math.round(e * i) / i
+		}
+		if (e instanceof Array) {
+			for (var s = 0; s < e.length; s++) {
+				e[s] = f(e[s], t);
+			}
+			return e;
+		} else if (e instanceof Object) {
+			for (var s in e) {
+				e[s] = f(e[s], t);
+			}
+			return e;
+		} else return f(e, t)
+	},
+
+    intervalTool:{  //延时update,防止卡顿
+        list:[],
+        
+        isWaiting:function(name, func, delayTime){
+            if(!this.list.includes(name)){  //如果没有该项, 则开始判断
+                var needWait = func(); //触发了改变,则等待一段时间后再自动判断
+                if(needWait){
+                    this.list.push(name);
+                    setTimeout(()=>{
+                        var a = this.list.indexOf(name);
+                        this.list.splice(a,1);
+                        this.isWaiting(name, func, delayTime) //循环
+                    },delayTime)
+                } 
+            }
+        }, 
+    }
+    ,
+
+}
+ 
+
+
+let math = {
+    closeTo : function(a,b, precision=1e-6){ 
+        let f = (a,b)=>{
+            return Math.abs(a-b) < precision;
+        }; 
+          
+        if(typeof (a) == 'number'){
+            return f(a, b);
+        }else {
+            let judge = (name)=>{
+                if(a[name] == void 0)return true //有值就判断,没值就不判断
+                else return f(a[name],b[name])
+            };
+            return judge('x') && judge('y') && judge('z') && judge('w')  
+        } 
+        
+    }, 
+    linearClamp(value, xArr , yArr){ //xArr需要按顺序从小到大,yArr对应xArr中的值
+	        
+	        let len = xArr.length; 
+	        if(value <= xArr[0]) return yArr[0]
+	        if(value >= xArr[len - 1]) return yArr[len - 1]
+	        let i = 0; 
+	        
+	        while(++i < len ){
+	            if(value < xArr[i]){
+	                let x1 = xArr[i-1], x2 = xArr[i], y1 = yArr[i-1], y2 = yArr[i]; 
+	                value = y1 + ( y2 - y1) * (value - x1)  / (x2 - x1);  
+	                break
+	            }
+	        }
+	        return value
+	        
+	         
+	    } 
+
+
+}
+var cameraLight = {
+    clampVFOV: function(currentFov, maxHFov, width, height) {//限制currentFov, 使之造成的横向fov不大于指定值maxHFov
+        var r = cameraLight.getHFOVFromVFOV(currentFov, width, height);
+        return r > maxHFov ? cameraLight.getVFOVFromHFOV(maxHFov, width, height) : currentFov
+    },
+    getHFOVForCamera: function(camera,  getRad) {
+        return cameraLight.getHFOVByScreenPrecent(camera.fov, camera.aspect, getRad)
+    }, 
+    //add
+    getHFOVByScreenPrecent: function(fov, percent, getRad) { //当fov为占比百分百时,percent代表在屏幕上从中心到边缘的占比
+        let rad = 2 * Math.atan(percent * Math.tan(THREE.Math.degToRad(fov * 2)));
+        if(getRad)return rad 
+        else return rad * MathLight.DEGREES_PER_RADIAN;
+    }
+};
+
+
+let texLoader = new THREE.TextureLoader; 
+let texs = new Map
+let common = {
+    urlHasValue(key, isGetValue) { 
+        let querys = window.location.search.substr(1).split('?')
+        if (isGetValue) {
+            for (let i = 0; i < querys.length; i++) {
+                let keypair = querys[i].split('=')
+                if (keypair.length === 2 && keypair[0] === key) {
+                    return keypair[1]
+                }
+            }
+            return ''
+        } else {
+            //return window.location.search.match("&" + key + "|\\?" + key) != null  有bug
+            for (let i = 0; i < querys.length; i++) {
+                let keypair = querys[i].split('=')
+                if(keypair[0] == key){
+                    return true
+                }
+            }
+            return false
+        }
+    }, 
+
+
+    dataURLtoBlob(dataurl) {//将base64转换blob
+        var arr = dataurl.split(','), mime = arr[0].match(/:(.*?);/)[1],
+            bstr = atob(arr[1]), n = bstr.length, u8arr = new Uint8Array(n);
+        while (n--) {
+            u8arr[n] = bstr.charCodeAt(n);
+        }
+        return new Blob([u8arr], { type: mime });
+    },
+     
+    loadTexture(src,done){
+        let o = texs.get(src)
+        if(o){
+            if(o.tex.image) done && done(o.tex)//加载完毕
+            else{
+                o.callbacks.push(done)//等待加载
+            }
+            return 
+        }
+        
+        
+        
+        let callbacks = []
+        let tex = texLoader.load(src,(tex)=>{
+            callbacks.forEach(done=>done(tex)) 
+        }) 
+        done && callbacks.push(done)
+        texs.set(src,{tex,callbacks})
+        return tex
+    }
+}

+ 13 - 0
src/App.vue

@@ -0,0 +1,13 @@
+<template>
+  <div id="app">
+    <Router-view />
+  </div>
+</template>
+
+<style lang="less">
+#app {
+  width: 100vw;
+  height: 100vh;
+  overflow: hidden;
+}
+</style>

+ 168 - 0
src/assets/base.css

@@ -0,0 +1,168 @@
+html,
+body,
+div,
+span,
+applet,
+object,
+iframe,
+h1,
+h2,
+h3,
+h4,
+h5,
+h6,
+p,
+blockquote,
+pre,
+a,
+abbr,
+acronym,
+address,
+big,
+cite,
+code,
+del,
+dfn,
+em,
+img,
+ins,
+kbd,
+q,
+s,
+samp,
+small,
+strike,
+strong,
+sub,
+sup,
+tt,
+var,
+b,
+u,
+i,
+center,
+dl,
+dt,
+dd,
+ol,
+ul,
+li,
+fieldset,
+form,
+label,
+legend,
+table,
+caption,
+tbody,
+tfoot,
+thead,
+tr,
+th,
+td,
+article,
+aside,
+canvas,
+details,
+embed,
+figure,
+figcaption,
+footer,
+header,
+menu,
+nav,
+output,
+ruby,
+section,
+summary,
+time,
+mark,
+audio,
+video,
+input {
+  box-sizing: border-box;
+  margin: 0;
+  padding: 0;
+  border: 0;
+  font-size: 100%;
+  font-weight: normal;
+  vertical-align: baseline;
+}
+
+/* HTML5 display-role reset for older browsers */
+article,
+aside,
+details,
+figcaption,
+figure,
+footer,
+header,
+menu,
+nav,
+section {
+  display: block;
+}
+
+body {
+  line-height: 1;
+}
+
+blockquote,
+q {
+  quotes: none;
+}
+
+blockquote:before,
+blockquote:after,
+q:before,
+q:after {
+  content: none;
+}
+
+table {
+  border-collapse: collapse;
+  border-spacing: 0;
+}
+
+/* custom */
+a {
+  color: #7e8c8d;
+  text-decoration: none;
+  -webkit-backface-visibility: hidden;
+}
+
+li {
+  list-style: none;
+}
+
+::-webkit-scrollbar {
+  width: 5px;
+  height: 5px;
+}
+
+::-webkit-scrollbar-track-piece {
+  background-color: rgba(0, 0, 0, 0.2);
+  -webkit-border-radius: 6px;
+}
+
+::-webkit-scrollbar-thumb:vertical {
+  height: 5px;
+  background-color: rgba(125, 125, 125, 0.7);
+  -webkit-border-radius: 6px;
+}
+
+::-webkit-scrollbar-thumb:horizontal {
+  width: 5px;
+  background-color: rgba(125, 125, 125, 0.7);
+  -webkit-border-radius: 6px;
+}
+
+html,
+body {
+  width: 100%;
+}
+
+body {
+  user-select: none;
+  -webkit-text-size-adjust: none;
+  -webkit-tap-highlight-color: rgba(0, 0, 0, 0);
+}

+ 9 - 0
src/main.js

@@ -0,0 +1,9 @@
+import Vue from 'vue'
+import App from './App.vue'
+import router from './router'
+Vue.config.productionTip = false
+import './assets/base.css'
+new Vue({
+  router,
+  render: h => h(App)
+}).$mount('#app')

+ 18 - 0
src/router/index.js

@@ -0,0 +1,18 @@
+import Vue from 'vue'
+import VueRouter from 'vue-router'
+
+Vue.use(VueRouter)
+
+const routes = [
+  {
+    path: '/',
+    name: 'Home',
+    component: () => import('../views/Home/index.vue')
+  }
+]
+
+const router = new VueRouter({
+  routes
+})
+
+export default router

+ 68 - 0
src/views/Home/index.vue

@@ -0,0 +1,68 @@
+<template>
+  <div class="Home">
+    <div class="btn" @click="btnFu">开始动画</div>
+    <iframe id="iframe" src="/three/index.html" frameborder="0"></iframe>
+  </div>
+</template>
+
+<script>
+export default {
+  components: {},
+  data() {
+    return {
+      iframeDom: null,
+    };
+  },
+  computed: {},
+  watch: {},
+  methods: {
+    btnFu() {
+      // 调用 iframe 的开始动画
+      this.iframeDom.contentWindow.stareMove(true);
+    },
+  },
+  created() {},
+  mounted() {
+    this.iframeDom = document.querySelector("#iframe");
+    // 点击图片
+    window.clickObject = (val) => {
+      console.log('000', val);
+    };
+    // 鼠标移入
+    window.hoverObject = (val) => {
+      // console.log(111, val);
+    };
+    // 鼠标移出
+    window.mouseoutObject = (val) => {
+      // console.log(222, val);
+    };
+  },
+  beforeCreate() {}, //生命周期 - 创建之前
+  beforeMount() {}, //生命周期 - 挂载之前
+  beforeUpdate() {}, //生命周期 - 更新之前
+  updated() {}, //生命周期 - 更新之后
+  beforeDestroy() {}, //生命周期 - 销毁之前
+  destroyed() {}, //生命周期 - 销毁完成
+  activated() {}, //如果页面有keep-alive缓存功能,这个函数会触发
+};
+</script>
+<style lang="less" scoped>
+.Home {
+  width: 100%;
+  height: 100%;
+  iframe {
+    width: 100%;
+    height: 100%;
+  }
+  .btn {
+    position: absolute;
+    top: 50px;
+    left: 50px;
+    cursor: pointer;
+    background-color: red;
+    display: flex;
+    justify-content: center;
+    align-items: center;
+  }
+}
+</style>

+ 3 - 0
vue.config.js

@@ -0,0 +1,3 @@
+module.exports = {
+  publicPath: './'
+}