wzz преди 1 седмица
родител
ревизия
4dfed2489a

BIN
src/assets/ai_bg.png


BIN
src/assets/header_deco1.png


BIN
src/assets/icon-setting.png


BIN
src/assets/icon-shield.png


BIN
src/assets/icon-upload.png


BIN
src/assets/icon-webcam.png


BIN
src/assets/logo.png


BIN
src/assets/panel_frame.png


+ 126 - 44
src/components/GlobeTransition.vue

@@ -46,7 +46,7 @@ export default {
   name: "GlobeTransition",
   props: {
     // 总时长:默认约 3.8s(含 0.5s 俯冲 + 同步渐黑)
-    durationMs: { type: Number, default: 3800 },
+    durationMs: { type: Number, default: 5000 },
     // 系统可传入 { lon, lat, name },不传则默认北京·通州
     target: {
       type: Object,
@@ -80,6 +80,7 @@ export default {
 
       // china outline on globe
       chinaLine: null,
+      chinaGlowLine: null,
 
       // stage states
       fading: false,
@@ -281,6 +282,17 @@ export default {
       if (this.chinaLine) {
         this.chinaLine.visible = false;
         group.add(this.chinaLine);
+
+        // 额外发光层:复制一层略微放大,形成更明显的“粗亮边界”
+        const glowLine = this.chinaLine.clone();
+        glowLine.material = this.chinaLine.material.clone();
+        glowLine.material.opacity = 0.0;
+        glowLine.material.blending = THREE.AdditiveBlending;
+        glowLine.material.depthWrite = false;
+        glowLine.scale.setScalar(1.006); // 避免 z-fighting
+        glowLine.visible = false;
+        group.add(glowLine);
+        this.chinaGlowLine = glowLine;
       }
 
       // Label (HTML)
@@ -306,9 +318,10 @@ export default {
       const t = (now - this.t0) / 1000; // seconds
       const totalBase = (this.durationMs || 3800) / 1000;
       // 为了避免俯冲后暴露网格线细节:强制将总时长收敛到短过渡(可按需提高上限)
-      const total = Math.min(totalBase, 4.5);
+      const HOLD_EXTRA = 0.5; // 中国地图停留额外增加 0.5s
+      const baseTotal = Math.min(totalBase, 4.5);
+      const total = baseTotal + HOLD_EXTRA;
       const p = Math.min(1, t / total);
-
       // --- Timeline (short dive + fade) ---
       // 目标节奏:
       // 0~2.5s :旋转展示 + 平滑转向目标(无停顿)
@@ -317,8 +330,9 @@ export default {
       const A = Math.min(2.5, Math.max(1.8, total - 1.3)); // 尽量保持 2.5s(总时长过短时自适应)
       const T1 = A * 0.45;                 // 旋转展示段结束
       const T2 = A;                        // 转向目标结束(到达正面)
-      const T3 = Math.min(T2 + 0.8, total - 0.5); // HUD 结束(约0.8s),确保给俯冲留 0.5s
-      const T4 = total;                    // 结束(俯冲尾段同步渐黑)
+      const T3Base = Math.min(T2 + 0.8, baseTotal - 0.5); // 原 HUD 段(约0.8s),给俯冲留 0.5s
+      const T3 = T3Base + HOLD_EXTRA;               // 延长停留后再俯冲
+      const T4 = total;                            // 结束(俯冲尾段同步渐黑)
 
 
 
@@ -346,15 +360,6 @@ export default {
           // 更快淡出网格线
           this.wire.material.opacity = base * Math.pow(1 - fadeK, 2.4);
         }
-        if (this.chinaLine && this.chinaLine.material) {
-          const base = this.chinaLine.material.__baseOpacity ?? this.chinaLine.material.opacity;
-          this.chinaLine.material.__baseOpacity = base;
-          this.chinaLine.material.opacity = base * (1 - fadeK);
-        }
-        if (this.label && this.label.el) {
-          const cur = Number(this.label.el.style.opacity || 1);
-          this.label.el.style.opacity = String(cur * (1 - fadeK));
-        }
       }
 
 
@@ -367,35 +372,114 @@ export default {
       }
 
       // ----- Lock phase: bring target to front -----
-      // 平滑把目标点“转到屏幕前方”(避免突然加速)
-      const lockK = easeInOutCubic(p2);
-      if (this.earthGroup) {
-        // 进入锁定段时记录起始四元数,只记录一次
-        if (t >= T1 && t < T2 && !this.lockInit) {
-          this.lockStartQuat = this.earthGroup.quaternion.clone();
-          this.lockInit = true;
-        }
-        if (t < T1) this.lockInit = false;
-
-        const targetDir = this.targetLocal.clone().normalize();
-        const front = new THREE.Vector3(0, 0, 1);
-        const targetQ = new THREE.Quaternion().setFromUnitVectors(targetDir, front);
+// 平滑把目标点“转到屏幕前方”(避免突然加速)
+// 并在“展示中国地图阶段”(T2~T3) 额外绕屏幕朝向(front)做一次旋转,
+// 用来把中国轮廓从“倒着/歪着”调整为更正面的观感。
+const lockK = easeInOutCubic(p2);
+const extraK = easeInOutCubic(p3);
+
+// 额外旋转角度(度):如果方向不对,把 90 改成 -90;如果还是倒着可试 180
+const EXTRA_ROT_DEG = 120;
+
+if (this.earthGroup) {
+  // 进入锁定段时记录起始四元数,只记录一次
+  if (t >= T1 && t < T2 && !this.lockInit) {
+    this.lockStartQuat = this.earthGroup.quaternion.clone();
+    this.lockInit = true;
+  }
+  if (t < T1) this.lockInit = false;
+
+  const targetDir = this.targetLocal.clone().normalize();
+  const front = new THREE.Vector3(0, 0, 1);
+
+  // 基础锁定:目标点居中到屏幕正前方
+  const baseTargetQ = new THREE.Quaternion().setFromUnitVectors(targetDir, front);
+
+  if (t < T2) {
+    // 锁定段:start -> baseTargetQ
+    const startQ = this.lockStartQuat ? this.lockStartQuat : this.earthGroup.quaternion.clone();
+    const q = startQ.clone();
+    q.slerp(baseTargetQ, lockK);
+    this.earthGroup.quaternion.copy(q);
+  } else {
+    // 展示中国阶段:在 baseTargetQ 基础上叠加一次额外旋转(不破坏“目标点居中”)
+    const extraAngle = THREE.MathUtils.degToRad(EXTRA_ROT_DEG) * extraK;
+    const qExtra = new THREE.Quaternion().setFromAxisAngle(front, extraAngle);
+
+    // qFinal = qExtra * baseTargetQ
+    const qFinal = baseTargetQ.clone().premultiply(qExtra);
+    this.earthGroup.quaternion.copy(qFinal);
+  }
+}
 
-        const startQ = this.lockStartQuat ? this.lockStartQuat : this.earthGroup.quaternion.clone();
-        const q = startQ.clone();
-        q.slerp(targetQ, lockK);
-        this.earthGroup.quaternion.copy(q);
+      // ----- China outline glow during lock/HUD/dive -----
+      // 仪式感曲线:先快速点亮 -> 稳定发光 -> 进入俯冲前轻微收束 -> 俯冲渐黑淡出
+      // 说明:
+      // - T1~T2:轮廓逐步显现(跟随锁定)
+      // - T2 附近:快速“点亮/爆闪”并回落到稳定强度
+      // - T3 前:轻微收束,避免俯冲近景时太“刺”
+      // - T3~T4:由 fadeK 控制统一淡出
+      const chinaVis = t >= T1 && t <= T4;
+      const fadeMult = 1 - fadeK;
+
+      // 分段关键点
+      const flashStart = T2 - 0.12;
+      const flashEnd = T2 + 0.18;
+      const settleEnd = T2 + 0.48;
+      const preFadeStart = Math.max(T2 + 0.6, T3 - 0.22);
+
+      // 1) 基础显现(随锁定)
+      const appearK = easeOutCubic(clamp01((t - T1) / Math.max(0.0001, (flashStart - T1))));
+      // 2) 快速点亮(爆闪)
+      const flashK = easeOutExpo(clamp01((t - flashStart) / Math.max(0.0001, (flashEnd - flashStart))));
+      // 3) 回落到稳定(避免一直过曝)
+      const settleK = easeOutCubic(clamp01((t - flashEnd) / Math.max(0.0001, (settleEnd - flashEnd))));
+      // 4) 俯冲前轻微收束
+      const preFadeK = easeInOutCubic(clamp01((t - preFadeStart) / Math.max(0.0001, (T3 - preFadeStart))));
+
+      // 组合强度(>1 允许短暂过曝,然后回落)
+      let intensity = 0;
+      if (t < T1) {
+        intensity = 0;
+      } else {
+        // 先从 0 -> 0.7
+        intensity = 0.7 * appearK;
+        // 点亮爆闪:额外 +0.75(短促)
+        intensity += 0.75 * flashK;
+        // 回落:从 1.45 -> 1.0
+        const over = 1.45;
+        const stable = 1.0;
+        const target = over + (stable - over) * settleK;
+        intensity = Math.min(intensity, target);
+        // 稳定呼吸(弱)
+        const breathe = 0.94 + 0.06 * Math.sin(now * 0.0026);
+        intensity *= breathe;
+        // 俯冲前收束到 0.88
+        intensity *= (1 - 0.12 * preFadeK);
       }
 
-      // ----- China outline glow during lock/HUD/dive ----- during lock/HUD/dive -----
-      const chinaVis = t >= T1 && t <= T4;
-      if (this.chinaLine) {
+      // 短暂“电流脉冲”附加:定位成功的仪式感(衰减振荡,保证不抖)
+      const dt = t - T2;
+      const shock = dt > 0 ? Math.max(0, Math.sin(dt * Math.PI * 3.0)) * Math.exp(-dt / 0.55) : 0;
+      intensity += 0.18 * shock;
+
+      // 统一淡出(俯冲渐黑)
+      intensity *= fadeMult;
+      // 防止数值越界
+      intensity = Math.max(0, Math.min(1.6, intensity));
+
+      // 主线/发光层输出
+      const mainMax = 0.98;
+      const glowMax = 0.62;
+
+      if (this.chinaLine && this.chinaLine.material) {
         this.chinaLine.visible = chinaVis;
-        const mat = this.chinaLine.material;
-        if (mat) {
-          const pulse = 0.55 + 0.45 * Math.sin(now * 0.004);
-          mat.opacity = chinaVis ? 0.20 + 0.35 * pulse : 0.0;
-        }
+        this.chinaLine.material.opacity = chinaVis ? Math.min(1, mainMax * intensity) : 0.0;
+      }
+      if (this.chinaGlowLine && this.chinaGlowLine.material) {
+        this.chinaGlowLine.visible = chinaVis;
+        // 发光层更柔,允许稍微高一点制造“粗亮边缘”
+        this.chinaGlowLine.material.opacity = chinaVis ? Math.min(1, glowMax * Math.pow(intensity, 0.85)) : 0.0;
       }
 
       // ----- Marker intensity -----
@@ -417,11 +501,9 @@ export default {
       if (this.wire && this.wire.material) {
         this.wire.material.opacity = 0.12 * (1 - fadeK);
       }
-      if (this.chinaLine && this.chinaLine.material) {
-        // china outline opacity already animated; multiply by (1-fadeK)
-        this.chinaLine.material.opacity *= (1 - fadeK);
+      if (this.label && this.label.el) {
+        this.label.el.style.opacity = String(1 - fadeK);
       }
-        this.label.el.style.opacity = String((Number(this.label.el.style.opacity) || 1) * (1 - fadeK));
       
 // ----- China HUD (2D) -----
       // 进入 HUD 阶段:出现 -> 淡出
@@ -636,7 +718,7 @@ export default {
       const mat = new THREE.LineBasicMaterial({
         color: 0x66f2ff,
         transparent: true,
-        opacity: 0.0, // 你时间轴里会动态调
+        opacity: 0.95, // 你时间轴里会动态调
         blending: THREE.AdditiveBlending,
         depthWrite: false
       });

Файловите разлики са ограничени, защото са твърде много
+ 1071 - 128
src/views/Home.vue


+ 29 - 22
src/views/Login.vue

@@ -7,7 +7,7 @@
       <div class="door right" @animationend="handleDoorEnd"></div>
     </div>
     <div class="header-deco"></div>
-    <div class="top-title">智慧交通信号控制管理平台</div>
+    <!-- <div class="top-title">智慧交通信号控制管理平台</div> -->
     <div class="content">
       <!-- 左侧酷炫区域 -->
       <div class="left">
@@ -15,10 +15,10 @@
           <img class="ellipse" :src="require('@/assets/ellipse-line.png')" alt="ellipse" />
           <!-- 沿椭圆的流光:不旋转椭圆图,只做覆盖层沿线跑动 -->
           <div class="ellipse-glow"></div>
-          <div class="icon icon-1"><img :src="require('@/assets/icon-shield.png')" /></div>
-          <div class="icon icon-2"><img :src="require('@/assets/icon-webcam.png')" /></div>
-          <div class="icon icon-3"><img :src="require('@/assets/icon-upload.png')" /></div>
-          <div class="icon icon-4"><img :src="require('@/assets/icon-setting.png')" /></div>
+          <div class="icon icon-1"><img :src="require('@/assets/icon-upload.png')" width="87px" height="87px" /></div>
+          <div class="icon icon-2"><img :src="require('@/assets/icon-webcam.png')" width="87px" height="87px" /></div>
+          <div class="icon icon-3"><img :src="require('@/assets/icon-shield.png')" width="87px" height="87px" /></div>
+          <div class="icon icon-4"><img :src="require('@/assets/icon-setting.png')" width="87px" height="87px" /></div>
         </div>
       </div>
 
@@ -103,7 +103,7 @@ export default {
           if (this.doorNavigated) return;
           this.doorNavigated = true;
           this.$router.push("/transition");
-        }, 1000); // 0.95s 动画 + 50ms 缓冲
+        }, 1000);
       });
     },
     handleDoorEnd() {
@@ -133,8 +133,8 @@ export default {
 .header-deco{
   position:absolute;
   top: 0; left: 0; right: 0;
-  height: 88px; /* 按你图6收敛高度 */
-  background: url("~@/assets/header_deco.png") center/cover no-repeat;
+  height: 86px;
+  background: url("~@/assets/header_deco1.png") center/cover no-repeat;
   pointer-events:none;
   opacity: 0.95;
   z-index: 2;
@@ -160,8 +160,8 @@ export default {
   display:grid;
   grid-template-columns: 1fr 1fr;
   gap: clamp(14px, 1.8vw, 28px);
-  padding: clamp(14px, 2.4vw, 40px);
-  padding-top: 100px; /* 给头部留空间 */
+  padding: clamp(14px, 2.4vw, 30px);
+  padding-top: 60px; /* 给头部留空间 */
   align-items:center;
   z-index: 1;
 }
@@ -272,8 +272,8 @@ export default {
   justify-content:center;
 }
 .panel{
-  width: min(680px, 46vw);
-  aspect-ratio: 835 / 714;
+  width: min(680px, 30vw);
+  aspect-ratio: 900 / 680;
   position: relative;
   background: url("~@/assets/panel_frame.png") center/100% 100% no-repeat;
   display:flex;
@@ -283,7 +283,7 @@ export default {
 
 /* 用绝对比例控制内容区域 */
 .panel-inner{
-  width: 77.34%;          /* 输入框主宽度比例 */
+  width: 88%;          /* 输入框主宽度比例 */
   display:flex;
   flex-direction:column;
   align-items:center;
@@ -380,12 +380,13 @@ export default {
   height: 54px;
   border: none;
   border-radius: 8px;
-  background: #06378E;
   color: rgba(235,255,255,0.95);
   font-size: var(--fs-base);
   font-weight: 600;
   cursor: pointer;
-  box-shadow: 0 0 18px rgba(0,140,255,0.22);
+  background: linear-gradient( 180deg, rgba(119,161,255,0) 0%, #77A1FF 100%);
+  border-radius: 8px;
+  border: 1px solid rgba(161,190,255,0.7);
 }
 .btn:hover{ filter: brightness(1.08); }
 
@@ -463,9 +464,13 @@ export default {
   inset: 0;
   z-index: 0;
   pointer-events: none;
-  opacity: 1; /* 始终存在,避免 opening 时“突然出现一层” */
+  opacity: 1; 
+  perspective: 1200px;
+}
+.door{
+  transform-origin: center;
+  backface-visibility: hidden;
 }
-
 /* 两扇门:都用全屏做 cover,再裁半屏(关键:避免中间断裂) */
 .split-door .door{
   position: absolute;
@@ -486,15 +491,17 @@ export default {
   clip-path: inset(0 0 0 50%);
 }
 
-/* opening:左右分开 + 轻微透视感 */
+/* 开门动作更慢更清晰 */
 .split-door.opening .door.left{
-  animation: door-left 0.95s cubic-bezier(.18,.86,.2,1) forwards;
+  animation: door-left 1.5s cubic-bezier(.18,.85,.22,1) forwards;
 }
-
 .split-door.opening .door.right{
-  animation: door-right 0.95s cubic-bezier(.18,.86,.2,1) forwards;
+  animation: door-right 1.5s cubic-bezier(.18,.85,.22,1) forwards;
+}
+.split-door.opening .door.left,
+.split-door.opening .door.right{
+  animation-delay: .12s;
 }
-
 /* 中间能量缝(升级:更亮、更柔) */
 .split-door::after{
   content:"";

+ 1 - 1
src/views/Transition.vue

@@ -1,7 +1,7 @@
 <template>
   <div class="page">
     <GlobeTransition
-      :durationMs="4500"
+      :durationMs="5000"
       :target="{ lon: 116.657, lat: 39.909, name: '北京·通州' }"
       @done="goHome"
     />