将 Google 的 <model-viewer> 与 React/Reagent 集成

Bor*_*urt 4 web-component clojurescript reactjs reagent re-frame

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

我正在努力解决如何将其正确集成到 Reagent(以及 React)结构中。

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

我试图天真地在组件内使用它,并试图消除重新渲染的可能性。使用 ref 直接改变它。

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

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

我想知道是否有人有关于如何最好地将其包装在 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>
Run Code Online (Sandbox Code Playgroud)

感谢您的任何提示。


关于Clojurians Slack 的其他讨论(链接需要访问 Slack)

关于Clojureverse的附加讨论

Gan*_*zal 9

  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>
Run Code Online (Sandbox Code Playgroud)
  1. 将其用作反应组件中的元素
<!DOCTYPE html>
<html lang="en">
  <head>
....
    <script
      type="module"
      src="https://unpkg.com/@google/model-viewer/dist/model-viewer.min.js"
    ></script>
...

  </head>

  ...
</html>
Run Code Online (Sandbox Code Playgroud)
  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>
  );
}
Run Code Online (Sandbox Code Playgroud)

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

  • 谢谢,这是一个非常好的例子。正如问题中提到的,我之前已经走过参考路线。希望还有其他方法可以解决它。但到目前为止似乎还不是这样。在进行一些测试后,我会将其标记为已解决。 (2认同)
  • 更新:这是我最终的方向。这并不理想,因为更新方面存在大量的谨慎,并且 &lt;model-viewer&gt; 没有在属性 API 之外公开我需要的所有内容。 (2认同)