将 Google 的 <model-viewer> 与 React/Reagent 整合

Integrating Google's <model-viewer> with React/Reagent

Google 的 <model-viewer> 提供了我需要的所有关键功能,而无需通过 react-three-fiber 或直接在 three.js.[=19 中编写自定义解决方案=]

我正在努力研究如何将它正确地集成到 Reagent(以及 React)结构中。

为了使其易于与 vanilla JS 一起使用,它被构建为一个 Web 组件,并且主要通过其 html 元素上的属性进行控制。通常这不会是什么大问题,但是由于 3D 的开销和加载大型模型重新渲染这很昂贵并且在许多情况下会破坏功能。

我试图天真地在组件内部使用它并试图消除重新渲染的可能性。使用 ref 直接对其进行变异。

我还尝试将其设置为来自 Reagent/React 受控应用程序的手动创建的 html 元素,并通过其 dom 元素在各种事件中引用它。

这两个选项都引入了很多 hack,在单页应用程序中并不理想。

我想知道是否有人有关于如何最好地将其包装在 React/Reagent shell 中的任何提示,同时仍然可以访问核心元素以使用其底层 JS api.

答案不必在 ClojureScript.


这是他们页面上的用法示例:

<model-viewer 
  alt="Neil Armstrong's Spacesuit from the Smithsonian Digitization Programs Office and National Air and Space Museum" 
  src="shared-assets/models/NeilArmstrong.glb" 
  ar ar-modes="webxr scene-viewer quick-look" environment-image="shared-assets/environments/moon_1k.hdr" 
  poster="shared-assets/models/NeilArmstrong.webp" 
  seamless-poster 
  shadow-intensity="1" 
  camera-controls>
</model-viewer>

感谢任何提示。


关于 Clojurians Slack 的额外讨论(link 需要访问 Slack)

关于 Clojureverse

的补充讨论
  1. 将其作为 module 类型脚本包含在您的 index.html 中。
<!DOCTYPE html>
<html lang="en">
  <head>
....
    <script
      type="module"
      src="https://unpkg.com/@google/model-viewer/dist/model-viewer.min.js"
    ></script>
...

  </head>

  ...
</html>
  1. 将它用作你的 React 组件中的一个元素
    import "./styles.css";
    import React, { useState } from "react";
    
    export default function App() {
      const modelRef = React.useRef();
    
      return (
        <model-viewer
          // className="model-viewer"
          src="./M08.glb"
          alt="A rock"
          exposure="0.008"
          camera-controls
          ar
          ar-modes="webxr"
          ref={(ref) => {
            modelRef.current = ref;
          }}
        >
        </model-viewer>
      );
    }
  1. 与之互动

这是一个交互,其中一个 onclick 事件使用模型提供的函数将屏幕坐标转换为模型坐标。它存储这些注释并将它们呈现为模型的子项。这在文档中可用

import "./styles.css";
import React, { useState } from "react";

export default function App() {
  const modelRef = React.useRef();
  const [annots, setAnnots] = useState([]);

  const handleClick = (event) => {
    const { clientX, clientY } = event;

    if (modelRef.current) {
      let hit = modelRef.current.positionAndNormalFromPoint(clientX, clientY);
      if (hit) {
        setAnnots((annots) => {
          return [...annots, hit];
        });
      }
    }
  };

  const getDataPosition = (annot) => {
    return `${annot.position.x} ${annot.position.y} ${annot.position.z}`;
  };

  const getDataNormal = (annot) => {
    return `${annot.normal.x} ${annot.normal.y} ${annot.normal.z}`;
  };

  return (
    <model-viewer
      // className="model-viewer"
      src="./M08.glb"
      alt="A rock"
      exposure="0.008"
      camera-controls
      ar
      ar-modes="webxr"
      onClick={handleClick}
      ref={(ref) => {
        modelRef.current = ref;
      }}
    >
      {annots.map((annot, idx) => (
        <button
          key={`hotspot-${idx}`}
          className="view-button"
          slot={`hotspot-${idx}`}
          data-position={getDataPosition(annot)}
          data-normal={getDataNormal(annot)}
        ></button>
      ))}
    </model-viewer>
  );
}

代码沙盒:https://codesandbox.io/s/lingering-tree-d41cr?file=/src/App.js:0-1287