pmndrs / drei

🥉 useful helpers for react-three-fiber
https://docs.pmnd.rs/drei
MIT License
8.18k stars 668 forks source link

<Text> does not work in Native/Expo #1396

Open SebastianKG opened 1 year ago

SebastianKG commented 1 year ago

Problem description:

Drei's component works on Web but not on Native platforms (I have tested on Expo Go on iOS). The error in Expo Go on iOS is

 ERROR  ReferenceError: Property 'document' doesn't exist                                                

This error is located at:                                                                                
    in Unknown                                                                                           
    in FiberProvider                                                                                     
    in CanvasWrapper (created by App)                                                                    
    in RCTView (created by View)                                                                         
    in View (created by App)                                                                             
    in App (created by withDevTools(App))                                                                
    in withDevTools(App)                                                                                 
    in RCTView (created by View)                                                                         
    in View (created by AppContainer)                                                                    
    in RCTView (created by View)
    in View (created by AppContainer)
    in AppContainer
    in main(RootComponent), js engine: hermes

Relevant code:

I have a complete Expo-wrapped application in this repository: https://github.com/SebastianKG/drei-expo-text-repro. To repro, clone the repository, cd to its root, and run npx expo start. Press 'w' to open the web version, it should show a cube and text reading "Hello World" with no issue. If you download the Expo Go application on your smartphone and follow the instructions to install the sample app on your phone (possibly by scanning the QR code in your terminal from your phone, after installing the Expo Go app), you should see the error I've described instead of the working app.

This is the content of that app's App.js, for context:

import { StatusBar } from "expo-status-bar";
import { View } from "react-native";
import { Canvas } from "@react-three/fiber";
import { Text } from "@react-three/drei";

export default function App() {
  return (
    <View style={{ width: "100%", height: "100%" }}>
      <Canvas>
        <ambientLight />
        <pointLight position={[10, 10, 10]} />
        <mesh position-y={0.75} rotation-x={-Math.PI / 6}>
          <boxGeometry />
          <meshStandardMaterial color="blue" />
        </mesh>
        <Text position-y={-0.75} rotation-x={-Math.PI / 6}>
          Hello World
        </Text>
      </Canvas>
      <StatusBar style="auto" />
    </View>
  );
}

Suggested solution:

Factor out use of document in the Text component.

lojjic commented 1 year ago

The offending "document" is a document.createElement('canvas') used for creating a CanvasTexture.

I may need an assist here, does anyone know how to get that to work in Native/Expo? I see several other usages of document.createElement('canvas') in the drei core modules, are those all broken or is there some magic to make them work?

drcmda commented 1 year ago

The offending "document" is a document.createElement('canvas') used for creating a CanvasTexture.

I may need an assist here, does anyone know how to get that to work in Native/Expo? I see several other usages of document.createElement('canvas') in the drei core modules, are those all broken or is there some magic to make them work?

i think these wouldn't work. there's also offscreen: https://github.com/pmndrs/react-three-offscreen im guessing troika text wouldn't work there either. is there any way around the necessity for canvas?

jmatsushita commented 1 year ago

Hey @drcmda how about this suggestion ?

Maybe we could pass the expo-gl Canvas instance through to webgl-sdf-generator?

drcmda commented 1 year ago

sounds great if possible!

CodyJasonBennett commented 1 year ago

expo-gl doesn't create a canvas but a native surface. The only useful part for troika would be its underlying WebGL context. There's https://github.com/expo/expo-2d-context in lieu of Canvas2D also.

You can maybe hack it in like:

<Canvas
  onCreated={(state) => {
    globalThis.document = {
      createElement(type) {
        if (type === 'canvas') {
          return {
            width: 300,
            height: 150,
            style: {},
            addEventListener: () => {},
            removeEventListener: () => {},
            clientWidth: 300,
            clientHeight: 150,
            getContext: () => new Expo2DContext(state.gl.getContext()),
          }
        }
      },
    }
  }}
/>
yunus-faro commented 11 months ago

i experience the same problem. is there any solution yet ?

amashianov commented 4 months ago
onCreated={(state) => {
    globalThis.document = {
      createElement(type) {
        if (type === 'canvas') {
          return {
            width: 300,
            height: 150,
            style: {},
            addEventListener: () => {},
            removeEventListener: () => {},
            clientWidth: 300,
            clientHeight: 150,
            getContext: () => new Expo2DContext(state.gl.getContext()),
          }
        }
      },
    }
  }}

Thanks for the suggestion, just tried that with

    "three": "^0.160.1",
    "expo-gl": "~13.6.0",
    "expo": "^50.0.0",
    "expo-2d-context": "^0.0.4",
    "@react-three/drei": "^9.96.4",
    "@react-three/fiber": "^8.15.15",

Result:

image