question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Using drei HTML is not showing anything

See original GitHub issue

I have tried to add html when using react-xr. I am using HTML from drei but cannot get anything to show up.

import React from "react";
import { VRCanvas, DefaultXRControllers } from "@react-three/xr";
import { Html } from "drei";

function App() {
  const style = {
    heigh: "100px",
    width: "100px",
    backgroundColor: "red",
  };
  return (
    <div className="App">
      <VRCanvas>
        <ambientLight intensity={0.5} />
        <pointLight position={[5, 5, 5]} />
        <mesh position={[1, 1, 1]}>
          <Html
            prepend
            center
            fullscreen
            scaleFactor={10}
            zIndexRange={[100, 0]}
          >
            <h1 style={style}>hello</h1>
            <p>world</p>
          </Html>
        </mesh>
        <DefaultXRControllers />
      </VRCanvas>
    </div>
  );
}

export default App;

Issue Analytics

  • State:closed
  • Created 3 years ago
  • Comments:5 (2 by maintainers)

github_iconTop GitHub Comments

9reactions
Enijarcommented, Nov 30, 2021

You can also render textures dynamically by rendering HTML to a canvas using a library like html2canvas, then using that canvas as a texture on a plane.

Here’s an example of how to do that:

import React from "react";
import html2canvas from "html2canvas";
import { renderToString } from "react-dom/server";
import * as THREE from "three";
import { useTexture } from "@react-three/drei";
import { useThree } from "@react-three/fiber";

// Prevents html2canvas warnings
// @todo maybe remove this if it causes performance issues?
HTMLCanvasElement.prototype.getContext = (function (origFn) {
  return function (type, attribs) {
    attribs = attribs || {};
    attribs.preserveDrawingBuffer = true;
    return origFn.call(this, type, attribs);
  };
})(HTMLCanvasElement.prototype.getContext);

let container = document.querySelector("#htmlContainer");
if (!container) {
  const node = document.createElement("div");
  node.setAttribute("id", "htmlContainer");
  node.style.position = "fixed";
  node.style.opacity = "0";
  node.style.pointerEvents = "none";
  document.body.appendChild(node);
  container = node;
}

export default function Html({
  children,
  width,
  height,
  color = "transparent",
}) {
  const { camera, size: viewSize, gl } = useThree();

  const sceneSize = React.useMemo(() => {
    const cam = camera as THREE.PerspectiveCamera;
    const fov = (cam.fov * Math.PI) / 180; // convert vertical fov to radians
    const height = 2 * Math.tan(fov / 2) * 5; // visible height
    const width = height * (viewSize.width / viewSize.height);
    return { width, height };
  }, [camera, viewSize]);

  const lastUrl = React.useRef(null);

  const [image, setImage] = React.useState(
    "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII="
  );
  const [textureSize, setTextureSize] = React.useState({ width, height });

  const node = React.useMemo(() => {
    const node = document.createElement("div");
    node.innerHTML = renderToString(children);
    return node;
  }, [children]);

  React.useEffect(() => {
    container.appendChild(node);
    html2canvas(node, { backgroundColor: color }).then((canvas) => {
      setTextureSize({ width: canvas.width, height: canvas.height });
      if (container.contains(node)) {
        container.removeChild(node);
      }
      canvas.toBlob((blob) => {
        if (blob === null) return;
        if (lastUrl.current !== null) {
          URL.revokeObjectURL(lastUrl.current);
        }
        const url = URL.createObjectURL(blob);
        lastUrl.current = url;
        setImage(url);
      });
    });
    return () => {
      if (!container) return;
      if (container.contains(node)) {
        container.removeChild(node);
      }
    };
  }, [node, viewSize, sceneSize, color]);

  const texture = useTexture(image);

  const size = React.useMemo(() => {
    const imageAspectW = texture.image.height / texture.image.width;
    const imageAspectH = texture.image.width / texture.image.height;

    const cam = camera as THREE.PerspectiveCamera;
    const fov = (cam.fov * Math.PI) / 180; // convert vertical fov to radians

    let h = 2 * Math.tan(fov / 2) * 5; // visible height
    let w = h * imageAspectH;

    if (width !== undefined) {
      w = width;
    }
    if (height !== undefined) {
      h = height;
    }

    if (height === undefined) {
      h = width * imageAspectW;
    }
    if (width === undefined) {
      w = h * imageAspectH;
    }
    return {
      width: w,
      height: h,
    };
  }, [texture, width, height, camera]);

  React.useMemo(() => {
    texture.matrixAutoUpdate = false;
    const aspect = size.width / size.height;
    const imageAspect = texture.image.width / texture.image.height;
    texture.anisotropy = gl.capabilities.getMaxAnisotropy();
    texture.minFilter = THREE.LinearFilter;
    if (aspect < imageAspect) {
      texture.matrix.setUvTransform(0, 0, 1, imageAspect / aspect, 0, 0.5, 0.5);
    } else {
      texture.matrix.setUvTransform(0, 0, aspect / imageAspect, 1, 0, 0.5, 0.5);
    }
  }, [texture, size, textureSize]);

  return (
    <mesh>
      <planeBufferGeometry args={[size.width, size.height]} />
      <meshBasicMaterial map={texture} side={THREE.DoubleSide} transparent />
    </mesh>
  );
}

Then you can use it like the Drei Html component:

function App() {
  return (
    <Html width={2} height={2}>
      <div
        style={{
          fontFamily: "Arial, sans-serif",
          fontSize: "100px",
          margin: "1px",
        }}
      >
        <ol style={{ marginLeft: "1em" }}>
          <li>
            <strong>bold</strong>
          </li>
          <li>
            <em>italics</em>
          </li>
          <li>
            <span style={{ color: "red" }}>red</span>
          </li>
          <li>
            <span style={{ color: "green" }}>green</span>
          </li>
          <li>
            <span style={{ color: "blue" }}>blue</span>
          </li>
        </ol>

        <img
          src="/assets/test.png"
          alt=""
          crossOrigin="anonymous"
          style={{ border: "0.1em solid red" }}
        />
      </div>
    </Html>
  );
}

It should look something like this: Example

6reactions
sniokcommented, Dec 14, 2020

Hi, drei Html component will not work in VR/AR. It works by adding DOM node over canvas and not in a threejs scene so when you’re in a VR session you only see what is being rendered in 3D scene.

If you want to render text in VR I recommend using drei’s Text component that works in VR/AR

Read more comments on GitHub >

github_iconTop Results From Across the Web

React three fiber Drei HTML Transform mode not working
My parent mesh is imported from GLTF file with "z-up" orientation. So I rotated it -90 degrees. Child component's orientation is dependent to ......
Read more >
Position property in drei's Html not working : r/threejs - Reddit
I'm trying to implement an onPointerOver function in my model, where when you select it, a text should be displayed, as this example...
Read more >
@react-three/drei - npm
Start using @react-three/drei in your project by running `npm i ... The native route of the library does not export Html or Loader...
Read more >
HTML in WebGL With React-Three-Fiber And Drei - YouTube
Our stream overlay gets some more elbow grease in the form of additional features. We work on shifting the chat overlay into our...
Read more >
@react-three/drei examples - CodeSandbox
Learn how to use @react-three/drei by viewing and forking @react-three/drei example apps on CodeSandbox.
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found