index.md 8.9 KB

@dage/krpano 将常用的标签使用 React 进行了组件化封装(MVVM 真香),并在实例上补充了 vtourskin 的功能,支持类型推断,基本满足开发需求。

开发环境

krpano 1.19-pr13

示例

import React, { useState, useMemo } from "react";
import {
  Autorotate,
  Krpano,
  Scene,
  View,
  HotSpot,
  Events,
  SceneProps,
  ROTATE_DIRECTION,
  ZOOM_ACTION,
} from "@dage/krpano";
import { MouseHoldView } from "./components";
import "./index.less";

interface ISceneProps extends Partial<SceneProps> {
  thumbUrl?: string;
}

const URL = "https://houseoss.4dkankan.com/project/leifeng-transfer";

const Demo = () => {
  const [currentMenu, setCurrentMenu] = useState(0);
  const [currentScene, setCurrentScene] = useState("center1");
  const [autorotateEnable, setAutorotateEnable] = useState(false);

  const CENTER_SCENE_LIST = useMemo<ISceneProps[]>(
    () => [
      {
        name: "center1",
        thumbUrl: URL + "/panos/center1.tiles/thumb.jpg",
        previewUrl: URL + "/panos/center1.tiles/preview.jpg",
        imageTagAttributes: {
          type: "cube",
          tileSize: 512,
          multires: true,
        },
        images: [
          {
            tiledImageWidth: 2624,
            tiledImageHeight: 2624,
            url: URL + "/panos/center1.tiles/%s/l3/%v/l3_%s_%v_%h.jpg",
          },
          {
            tiledImageWidth: 1280,
            tiledImageHeight: 1280,
            url: URL + "/panos/center1.tiles/%s/l2/%v/l2_%s_%v_%h.jpg",
          },
          {
            tiledImageWidth: 640,
            tiledImageHeight: 640,
            url: URL + "/panos/center1.tiles/%s/l1/%v/l1_%s_%v_%h.jpg",
          },
        ],
        children: (
          <>
            <View
              hlookat={0}
              vlookat={0}
              fovType="MFOV"
              fov={120}
              maxPixelZoom={2}
              fovMin={70}
              fovMax={140}
              limitView="auto"
            />

            <HotSpot
              name="hotspot1"
              url="/krpano/images/guide.png"
              atv={5}
              scale={0.5}
              edge="top"
              distorted={true}
              onClick={() => setCurrentScene("center2")}
            />
          </>
        ),
      },
      {
        name: "center2",
        thumbUrl: URL + "/panos/center2.tiles/thumb.jpg",
        previewUrl: URL + "/panos/center2.tiles/preview.jpg",
        imageTagAttributes: {
          type: "cube",
          tileSize: 512,
          multires: true,
        },
        images: [
          {
            tiledImageWidth: 2624,
            tiledImageHeight: 2624,
            url: URL + "/panos/center2.tiles/%s/l3/%v/l3_%s_%v_%h.jpg",
          },
          {
            tiledImageWidth: 1280,
            tiledImageHeight: 1280,
            url: URL + "/panos/center2.tiles/%s/l2/%v/l2_%s_%v_%h.jpg",
          },
          {
            tiledImageWidth: 640,
            tiledImageHeight: 640,
            url: URL + "/panos/center2.tiles/%s/l1/%v/l1_%s_%v_%h.jpg",
          },
        ],
        children: (
          <View
            hlookat={0}
            vlookat={0}
            fovType="MFOV"
            fov={120}
            maxPixelZoom={2}
            fovMin={70}
            fovMax={140}
            limitView="auto"
          />
        ),
      },
    ],
    []
  );
  // 支持从 xml 中获取场景
  const PARTY_SCENE_LIST = useMemo<ISceneProps[]>(
    () => [
      {
        name: "scene_party",
        thumbUrl: URL + "/panos/party.tiles/thumb.jpg",
      },
    ],
    []
  );
  const MENUS = [
    {
      title: "长沙国防教育馆",
      scenes: CENTER_SCENE_LIST,
    },
    {
      title: "游客服务中心",
      scenes: PARTY_SCENE_LIST,
    },
  ];

  const handleSceneClick = (name: string) => {
    setCurrentScene(name);
  };

  const handleMenuClick = (idx: number) => {
    setCurrentMenu(idx);
  };

  const handleView = (direction: ROTATE_DIRECTION) => {
    window.ReactKrpanoActionProxy?.rotateView(direction);
  };

  const handleZoom = (action: ZOOM_ACTION) => {
    window.ReactKrpanoActionProxy?.zoomView(action);
  };

  const handleAutoRotateOneRound = () => {
    const count = window.ReactKrpanoActionProxy?.get("scene").count;
    const curIndex = window.ReactKrpanoActionProxy?.get(
      "scene[get(xml.scene)].index"
    );
    const nextScene = window.ReactKrpanoActionProxy?.get("scene").getItem(
      curIndex + 1 >= count ? 0 : curIndex + 1
    );
    setCurrentScene(nextScene.name);
  };

  return (
    <div className="demo">
      <Krpano
        className="krpano"
        xml="./party.xml"
        webvrUrl="./krpano/1.21/plugins/webvr.xml"
        currentScene={currentScene}
        littlePlanetIntro={true}
        passQueryParameters={true}
      >
        <Autorotate enabled={autorotateEnable} />
        <Events onAutoRotateOneRound={handleAutoRotateOneRound} />

        {[...CENTER_SCENE_LIST, ...PARTY_SCENE_LIST].map(
          (sc) => sc.imageTagAttributes && <Scene key={sc.name} {...sc} />
        )}
      </Krpano>

      <div className="scene-panel">
        <div className="scene-panel__menu">
          {MENUS.map((sc, idx) => (
            <div
              key={sc.title}
              className={`scene-panel__menu__item ${
                idx === currentMenu && "active"
              }`}
              onClick={handleMenuClick.bind(undefined, idx)}
            >
              {sc.title}
            </div>
          ))}
        </div>

        <div className="scene-panel__list">
          {MENUS[currentMenu].scenes.map((sc) => (
            <div
              key={sc.name}
              className={`scene-panel__list__item ${
                sc.name === currentScene ? "active" : ""
              }`}
              onClick={handleSceneClick.bind(undefined, sc.name)}
            >
              <img
                src={sc.thumbUrl || sc.images![0].url.replace("%s", "f")}
                alt={sc.name}
              />
            </div>
          ))}
        </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
          className={`${autorotateEnable && "active-btn"}`}
          onClick={() => setAutorotateEnable(!autorotateEnable)}
        >
          360°
        </button>
        <button
          onClick={() => {
            window.ReactKrpanoActionProxy?.call("webvr.enterVR();");
          }}
        >
          vr
        </button>
      </div>
    </div>
  );
};

export default Demo;

API

Krpano

window.ReactKrpanoActionProxy Methods

call

执行 Javascript 函数

Name Description Type Default
action 动作 string (required)
nexttick 是否在下一个渲染帧后执行 boolean false

setTag

动态添加标签

Name Description Type Default
tag 标签 string (required)
name 名称 string (required)
attrs 属性 Record<string, any> (required)

removeScene

删除场景

Name Description Type Default
name 场景名称 string (required)

loadScene

加载场景

Name Description Type Default
name 场景名称 string (required)

rotateView

旋转视图

Name Description Type Default
direction 方位 ROTATE_DIRECTION (required)
degrees 旋转度数 number 10

zoomView

缩放视图

Name Description Type Default
action 动作 ZOOM_ACTION (required)
num 缩放大小 number 10