Browse Source

feat[krpano]: 操控视图

chenlei 1 year ago
parent
commit
5ef89daed8

+ 34 - 0
packages/krpano-cli/template/src/components/MouseHoldView.tsx

@@ -0,0 +1,34 @@
+import React, { useRef, FC } from "react";
+
+export interface MouseHoldViewProps {
+  children?: React.ReactNode;
+  onHold: () => void;
+}
+
+export const MouseHoldView: FC<MouseHoldViewProps> = ({ children, onHold }) => {
+  const isMouseHeld = useRef(false);
+  const requestIdRef = useRef<number | null>(null);
+
+  const handleMouseDown = () => {
+    isMouseHeld.current = true;
+    requestAnimationFrame(checkMouseHold);
+  };
+
+  const handleMouseUp = () => {
+    isMouseHeld.current = false;
+    requestIdRef.current && cancelAnimationFrame(requestIdRef.current);
+  };
+
+  const checkMouseHold = () => {
+    if (isMouseHeld.current) {
+      onHold();
+      requestIdRef.current = requestAnimationFrame(checkMouseHold);
+    }
+  };
+
+  return (
+    <div onMouseDown={handleMouseDown} onMouseUp={handleMouseUp}>
+      {children}
+    </div>
+  );
+};

+ 1 - 0
packages/krpano-cli/template/src/components/index.tsx

@@ -0,0 +1 @@
+export * from "./MouseHoldView";

+ 9 - 1
packages/krpano-cli/template/src/pages/Home/index.scss

@@ -6,9 +6,17 @@
 
 $primaryColor: coral;
 
+.toolbar {
+  display: flex;
+  gap: 10px;
+  position: absolute;
+  bottom: 20px;
+  right: 20px;
+}
+
 .scene-panel {
   position: absolute;
-  bottom: 80px;
+  bottom: 20px;
   left: 50%;
   padding: 10px;
   user-select: none;

+ 39 - 1
packages/krpano-cli/template/src/pages/Home/index.tsx

@@ -1,7 +1,8 @@
 import { useMemo, useState } from "react";
 import classnames from "classnames";
-import { Krpano, Scene } from "@dage/krpano";
+import { Krpano, ROTATE_DIRECTION, Scene, ZOOM_ACTION } from "@dage/krpano";
 import { ALL_SCENES, MENUS } from "./constants";
+import { MouseHoldView } from "@/components";
 import "./index.scss";
 
 export default function HomePage() {
@@ -17,6 +18,14 @@ export default function HomePage() {
     setCurrentMenu(idx);
   };
 
+  const handleView = (direction: ROTATE_DIRECTION) => {
+    window.ReactKrpanoActionProxy?.rotateView(direction);
+  };
+
+  const handleZoom = (action: ZOOM_ACTION) => {
+    window.ReactKrpanoActionProxy?.zoomView(action);
+  };
+
   return (
     <div className="home-page">
       <Krpano
@@ -62,6 +71,35 @@ export default function HomePage() {
           ))}
         </div>
       </div>
+
+      <div className="toolbar">
+        <MouseHoldView
+          onHold={handleView.bind(undefined, ROTATE_DIRECTION.LEFT)}
+        >
+          <button>←</button>
+        </MouseHoldView>
+        <MouseHoldView
+          onHold={handleView.bind(undefined, ROTATE_DIRECTION.RIGHT)}
+        >
+          <button>→</button>
+        </MouseHoldView>
+        <MouseHoldView onHold={handleView.bind(undefined, ROTATE_DIRECTION.UP)}>
+          <button>↑</button>
+        </MouseHoldView>
+        <MouseHoldView
+          onHold={handleView.bind(undefined, ROTATE_DIRECTION.DOWN)}
+        >
+          <button>↓</button>
+        </MouseHoldView>
+        <MouseHoldView onHold={handleZoom.bind(undefined, ZOOM_ACTION.IN)}>
+          <button>+</button>
+        </MouseHoldView>
+        <MouseHoldView onHold={handleZoom.bind(undefined, ZOOM_ACTION.OUT)}>
+          <button>-</button>
+        </MouseHoldView>
+        <button>360°</button>
+        <button>vr</button>
+      </div>
     </div>
   );
 }

+ 57 - 1
packages/krpano/src/KrpanoActionProxy.ts

@@ -1,6 +1,17 @@
-import { NativeKrpanoRendererObject } from "./types";
+import { ViewProps } from "./components";
+import {
+  NativeKrpanoRendererObject,
+  ROTATE_DIRECTION,
+  ZOOM_ACTION,
+} from "./types";
 import { buildKrpanoAction, buildKrpanoTagSetterActions } from "./utils";
 
+declare global {
+  interface Window {
+    ReactKrpanoActionProxy?: KrpanoActionProxy;
+  }
+}
+
 export type HandlerFunc = (renderer: KrpanoActionProxy) => void;
 
 interface EventHandler {
@@ -64,9 +75,54 @@ export class KrpanoActionProxy {
     }
   }
 
+  /**
+   * 加载场景
+   * @param name 场景 name
+   */
   loadScene(name: string): void {
     this.call(
       buildKrpanoAction("loadscene", name, "null", "MERGE", "BLEND(0.5)")
     );
   }
+
+  /**
+   * 旋转视图
+   * @param direction 方位
+   * @param degrees 旋转度数,默认为 10
+   */
+  rotateView(direction: ROTATE_DIRECTION, degrees = 10) {
+    let str = "";
+    const view: ViewProps = this.get("view");
+
+    switch (direction) {
+      case ROTATE_DIRECTION.LEFT:
+        str = `view.hlookat, ${(view.hlookat || 0) - degrees}`;
+        break;
+      case ROTATE_DIRECTION.RIGHT:
+        str = `view.hlookat, ${(view.hlookat || 0) + degrees}`;
+        break;
+      case ROTATE_DIRECTION.UP:
+        str = `view.vlookat, ${(view.vlookat || 0) - degrees}`;
+        break;
+      case ROTATE_DIRECTION.DOWN:
+        str = `view.vlookat, ${(view.vlookat || 0) + degrees}`;
+        break;
+    }
+
+    this.call(buildKrpanoAction("tween", str, 0.5));
+  }
+
+  /**
+   * 缩放视图
+   * @param action 动作
+   * @param num 缩放大小
+   */
+  zoomView(action: ZOOM_ACTION, num = 10) {
+    const view: ViewProps = this.get("view");
+    const targetFov = action === ZOOM_ACTION.IN ? -num : num;
+
+    this.call(
+      buildKrpanoAction("tween", "view.fov", (view.fov || 0) + targetFov, 1)
+    );
+  }
 }

+ 18 - 0
packages/krpano/src/types.ts

@@ -50,6 +50,24 @@ export interface XMLMeta {
   children?: XMLMeta[];
 }
 
+/**
+ * 旋转方位
+ */
+export enum ROTATE_DIRECTION {
+  UP = "up",
+  DOWN = "down",
+  LEFT = "left",
+  RIGHT = "right",
+}
+
+/**
+ * 缩放动作
+ */
+export enum ZOOM_ACTION {
+  IN = "in",
+  OUT = "out",
+}
+
 declare global {
   interface Window {
     embedpano?: (config: IKrpanoConfig) => void;

+ 1 - 0
packages/krpano/src/utils.ts

@@ -5,6 +5,7 @@ type FuncName =
   | "set"
   | "loadxml"
   | "loadscene"
+  | "tween"
   | "addhotspot"
   | "removehotspot"
   | "nexttick";