jeromeetienne / AR.js

Efficient Augmented Reality for the Web - 60fps on mobile!
MIT License
15.79k stars 2.22k forks source link

Can Chrome support aframe-ar.js? #564

Closed GoCoGit closed 5 years ago

michael-small commented 5 years ago

Could you please specify in what context, such as intended OS for development?

As per the README

Browser Support

Demo tested on the following browser setups:

  • Desktop Chrome with webcam and 2 tabs (one for Hero, one for result) (works!)
  • Android native 4.4.2 (doesn't work, doesn't ask for permission to use camera. I see white background and text)
  • Android native 5.0 (doesn't work, doesn't ask for permission, I see white background and text)
  • Chrome on Android 4.4.2 (works!)
  • Chrome on Android 5.0 (doesn't work, asks for permission, I see black background, text and a chart)
  • Safari and Chrome on iOS < 11 (doesn't work, doesn't ask for permission, I see white background and text)
  • Microsoft Edge on Windows 10 (Chrome on Google Pixel phone to view hologram)

Credits: @HelloDeadline, @sorianog

Additionally, do you intend to use aframe-ar.js with any framework? Frameworks like Angular2+ and React have had issues that are being actively discussed and worked on in this thread. My coworkers and I have been working on documenting an Angular solution based on the very useful linked React thread. Most of our issues with Angular have been in Chrome, so if you're asking from a bad experience with Angular or another framework then I can give you a more detailed write up.

nicolocarpignoli commented 5 years ago

@VinceMar please detail your issue. Also, @michael-small have answered you. If you have more doubts, open a more detailed issue, thanks

michael-small commented 5 years ago

@michael-small Hi, can you share with us the angular doc to run ar.js app on the thread linked by you?

To be honest I misspoke a bit because technically my team's Angular doc isn't quite ready. However, I will give a quick and dirty overview of our Angular advice. When our doc is fully fleshed out we will link it here and in the implementations thread. We are figuring this out and optimizing as we go, so this is no be-all-end-all solution and would gladly take input.

Apologies for the length and formatting, I was a bit rushed.

Solution # 1 (simpler)

Before anything with AFrame syntax can be used in Angular, CUSTOM_ELEMENTS_SCHEMA must be set in the NgModule directive and the two AR.js related script tags must be set in index.html. See this comment by @jsebrech on how to do that from the start on an Angular project with AR.js to the first run of it. Note: the scripts are outdated so make sure to use more current ones.

The example app.component.html from the link above should work out of the box on any supported browser with a Hiro marker. That said, our team uses barcode markers that show a-image's. My team's big issues with Angular and AR.js was with barcodes working in Chrome. Barcodes showed up using example code like below in all browsers except Chrome. More on that below.

This should be all that is necessary for Angular and AR.js to get along, in Chrome, on supported OS's, using barcode markers.

Our component is app.component with the selector app-root, so the HTML of index.html and app.component.html should look like this:

index.html

<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <title>ArSimulation</title>
  <base href="/">

  <meta name="viewport" content="width=device-width,initial-scale=1,user-scalable=no,minimum-scale=1,maximum-scale=1"/> <!--https://github.com/jeromeetienne/AR.js/issues/541#issue-457792891-->
  <link rel="icon" type="image/x-icon" href="favicon.ico">
  <link rel="manifest" href="manifest.webmanifest">
  <meta name="theme-color" content="#1976d2">
  <link href="https://fonts.googleapis.com/css?family=Roboto:300,400,500" rel="stylesheet">
  <link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet"> 

  <!-- AR.js related Script Imports -->
  <script src="https://aframe.io/releases/0.9.1/aframe.min.js"></script>
  <script src="https://cdn.rawgit.com/jeromeetienne/AR.js/1.7.2/aframe/build/aframe-ar.js"></script>

</head>

<body class="mdc-typography">

  <!-- Component selector on app.component -->
  <app-root></app-root>

  <noscript>Please enable JavaScript to continue using this application.</noscript>
</body>
</html>

app.component.html

<a-scene embedded arjs='sourceType: webcam; debugUIEnabled: false; detectionMode: mono_and_matrix; matrixCodeType: 3x3;'>
    <a-marker type='barcode' value='2'>
                <!-- Replace with your own image from the assets folder or linked from an HTTPS source  -->
        <a-image src="assets/image.png" position="0 0 0" rotation="-90 0 0" scale="1 1 1"></a-image>
    </a-marker>
    <a-entity camera></a-entity>
</a-scene> 

With this configuration, barcode images seemingly should work by the syntax conventions of AR.js, but is a little more complicated. As @fega mentions but modified from React to Angular, for AR.js to work in an Angular component there needs to be a delay in the component being instantiated (in Chrome, other browsers like Firefox or Edge didn't need this solution).

This can be achieved by just a few lines in the component's typescript file and html file.

app.component.ts

export class AppComponent implements OnInit {

  //tied to the *ngIf="showContent" in the component's html
  public showContent: boolean = false;

  public ngOnInit() {
    //This delays the content of the component from showing until aframe and aframe-ar.js load
    //NOTE: This arbitrary 1 second wait may be too long, or worse, too short. See end for more info.

    setTimeout(()=>this.showContent=true, 1000); 

    //Thanks to Valikhan Akhmedov, Oct 4 '16 at 12:22 @ https://stackoverflow.com/a/39852249/8273171
    //This move was inspired by kawalt's react-aframe-ar-test's index.js @ https://github.com/kalwalt/react-aframe-ar-test/blob/master/src/index.js
    //which was inspired by fega @ https://github.com/jeromeetienne/AR.js/issues/493#issuecomment-490303718
  }

app.component.html

<!-- Notice the *ngIf in the a-scene. The component will not show up until showContent is set true in ngOnInit -->

<a-scene embedded arjs='sourceType: webcam; debugUIEnabled: false; detectionMode: 
mono_and_matrix; matrixCodeType: 3x3;' *ngIf="showContent">
    <a-marker type='barcode' value='2'>
        <a-image src="assets/image.png" position="0 0 0" rotation="-90 0 0" scale="1 1 1"></a-image>
    </a-marker>
    <a-entity camera></a-entity>
</a-scene> 

And for our team implementing AR.js with Angular, that was all we needed.

Solution # 2 (refined Solution # 1)

However, there are a few caveats. As I mentioned about the showContent 1 second delay, delays with a static time aren't reliable as that delay is either too much or possibly not enough due to potential network or async issues.

Our solution is to dynamically load aframe and aframe-ar.js using DynamicScriptLoaderService.ts by Zain Zafar in his article "Angular: Load External JavaScript File Dynamically". First of all, remove the two script tags related to AR.js from index.html. Then add DynamicScriptLoaderService.ts to your project by making it a provider in app.module.ts's @NgModule and import the service in app.component.ts. Our ScriptStore is:

export const ScriptStore: Scripts[] = [
  { name: 'aframe', src: "https://aframe.io/releases/0.9.1/aframe.min.js" },
  { name: 'arjs', src: "https://cdn.rawgit.com/jeromeetienne/AR.js/1.7.2/aframe/build/aframe-ar.js" }
];

Back in your app.component.ts, add the following method loadScripts() and call it on ngOnInit():

app.component.ts

  ngOnInit() {
    this.loadScripts();
  }

  private loadScripts() {
    this.dynamicScriptLoader.load('aframe','arjs').then(data => {
      this.showContent=true;
    }).catch(error => console.log(error));
  }

We're almost there. The last needed bit is to add an event listener in ngOnInit.

Once everything is said and done, your app.component.ts should look like this:

app.component.ts

import { Component, OnInit } from '@angular/core';
import { DynamicScriptLoaderService } from '.../path_to_service/DynamicScriptLoaderService'  

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.scss']
})

export class AppComponent implements OnInit {

  public showContent: boolean = false;

  constructor(private dynamicScriptLoader: DynamicScriptLoaderService) {}

  ngOnInit() {
    this.loadScripts();

    window.addEventListener('load', (event) => {  //Thanks to this article: https://www.kirupa.com/html5/running_your_code_at_the_right_time.htm
    });
  }

  private loadScripts() {
    this.dynamicScriptLoader.load('aframe','arjs').then(data => {
      this.showContent=true;
    }).catch(error => console.log(error));
  }
}

Now AR.js and Aframe will be loaded in dynamically when it is ready.

This should be all that is necessary for Angular and AR.js to get along, in Chrome, on supported OS's, using barcode markers. As I said earlier, my team intends to refine and condense this info into a proper documentation document at a later date when we have more figured out.

PS: If you want to set markers's properties using our setup, check out this issue by @HeinPauwelyn. Our team uses the typescript solution inside of the window.addEventListener.