HowTo: Marzipano Free 360 Image Viewer with React Native and WebView

Phat Ca wrote a React Native application that uses WebView to run the Marzipano open source viewer.

GitHub Code by Phat Ca


I often have challenges running React Native apps due to my lack of knowledge of React Native builds.

To run the code on my particular system, I did the following:

  • deleted Gemfile.lock, vendor
  • use rbenv to run ruby 3.2.2, which is what I have installed on my system, but it is not the latest version
  • set signing in Xcode (needed to run on physical devices)
  • set desired simulator in Xcode

in Xcode

image

In Gemfile

ruby ">= 3.2.2"

in ios,

bundle install
pod install

then, in the root of the project:

npm run start

then i to run on iOS.

To use the application, you need login credentials. You can either ask @jcasman to create an account for you. Or, you can use the credentials in the readme on github.

once logged in, you can select an image.

I’m going to try this 11K image from a THETA X. In the past, I had problems with 11K images on the iOS simulator, but not on the physical device.

Drag and drop the image from the mac finder to the iOS simulator.

It will then appear in photos.

It seems to be selectable in the app.

The image server does a lot of processing. It will take a minute.

Eventually, the upload succeeds.

At this point, I got an error from asyncstorage. I decided to reboot the app.

There’s auto-capitalization on the first letter. I need to type 2 Ccs and then then delete the first uppercase C.

The 11K thumbnail is appearing in the simular.

However, it’s not appearing in the 360 image viewer, which I believe to be a limitation of the iOS simulator.

trying smaller file

Using a 5.5K image.

Display and navigation are fine.

11K with physical device

ViewerScreen

The viewer screen has the html embedded into the screen as a constant.

The webview is able to get the image url variable with injectedJavaScriptBeforeContentLoaded

WebView

    const injectedJavaScriptBeforeContentLoaded = `
        window.imageUrl = "${imageUrl}";
        true;
    `;

    return (
        <WebView
            ref={webviewRef}
            originWhitelist={['*']}
            source={{ html }}
            style={{ flex: 1 }}
            injectedJavaScriptBeforeContentLoaded={injectedJavaScriptBeforeContentLoaded}
            onMessage={(event) => console.log("WebView log:", event.nativeEvent.data)}
        />
    );

Marzipano Viewer Setup

To use Marzipano, you first create a Viewer with the document element of the place for the viewer to appear.

                    try {
                        const viewer = new Marzipano.Viewer(document.getElementById('viewer'));
                        const source = Marzipano.ImageUrlSource.fromString(window.imageUrl);
                        const geometry = new Marzipano.EquirectGeometry([{ width: 4000 }]);
                        const limiter = Marzipano.RectilinearView.limit.traditional(4096, 90 * Math.PI / 180);
                        const view = new Marzipano.RectilinearView(null, limiter);

                        const scene = viewer.createScene({
                            source: source,
                            geometry: geometry,
                            view: view,
                            pinFirstLevel: true
                        });

The <div id="viewer"></div> is in the body of the HTML code that is included ViewerScreen.

<body>
        <h1 id="displayText">Click the button to view the 360 image</h1>
        <button id="loadButton">Load 360 Image</button>
        <div id="viewer"></div>
        <script>
...

getting console messages

To make WebView easier to work with, you need to get messages from the HTML into the console. ReactNativeWebView comes with postMessage to handle this.

window.ReactNativeWebView.postMessage("Script loaded");

summary

Marizipano works great in React Native and the performance is excellent with no graphics glitching or stuttering motion. Although nice to run when it is set up, the configuration for first-time users is challenging due to the need to communicate between the React Native app and the JavaScript code running inside of the WebView.

All the challenges can be overcome. Using a code example that works is useful when setting up the WebView.

Thanks for @Phat_Ca for providing this example.

other build errors

problem: Error: EMFILE: too many open files, watch

solution

npm install
brew update
brew install watchman

problem: unable to install on physical device iPad

solution

uninstall apps already on iPad.