Imported three.js module OrbitControls "not defined" - javascript

Javascript and HTML newbie here:
I'm trying to display a 3D model (.gltf file) in a website with controls (pan, zoom, etc.), using the javascript three.js OrbitControls module. In spite of using <script type="module"> tags in the HTML to load OrbitControls, when I try to load the page, I get Uncaught ReferenceError: OrbitControls is not defined from firefox. I can successfully render the image if I do not include the statement defining the controls, but do include the import tag.
Brief code:
--HTML:
<head>
<!-- Imports for three.js and GLTFLoader.js, both seem to be working -->
<script type="module">
import { OrbitControls } from "https://unpkg.com/three/examples/jsm/controls/OrbitControls.js";
</script>
<script src="script.js" defer="defer"></script>
</head>
--Javascript (from script.js):
"use strict";
function main() {
//Create render and camera objects; both work when not using controls
const controls = new OrbitControls( camera, renderer.domElement );
// create scene, GLTFLoader, run animate function
}
const obj_returned = main();
I've tried a few different styles of the script command:
<script type="module"> import { OrbitControls } from "https://unpkg.com/three/examples/jsm/controls/OrbitControls.js"; </script>
This gives the Uncaught ReferenceError: OrbitControls is not defined error,
<script type="module" src="https://unpkg.com/three/examples/jsm/controls/OrbitControls.js">
produces the same error, and
<script src="https://unpkg.com/three/examples/jsm/controls/OrbitControls.js"></script>
This actually gives the same error plus Uncaught SyntaxError: import declarations may only appear at top level of a module, I think due to the missing type="module" flag.
All local files are in the same directory.
I've looked at several other examples of this sort of thing to see what I'm missing (like here, here, here, the three.js documentation, the OrbitControls source code, and several others).
I'm very new to JS and HTML, so 1) it's likely something very obvious and 2) if it's not trivial / a typo, I would appreciate a little explanation as to why this doesn't work so I can avoid similar mistakes in the future. Thanks!

Related

Issues with importing THREE.js modules

I'm trying to import the custom geometry "OutlinesGeometry.js" from https://plnkr.co/edit/uEYo6L3pgbIaYXXzVzXd?preview
I'm trying to import following way:
<script type="module" src="./three/build/three.module.js"></script>
<script type="module" src="./three/examples/jsm/controls/OrbitControls.js"></script>
<script type="module" src="./three/examples/jsm/libs/dat.gui.min.js"></script>
<script type="module" src="./OutlinesGeometry.js"></script>
This gives me following errors:
First I did not import three, OrbitControls and dat.gui as modules, as everything worked fine (see imports below). But as soon I'm importing the OutlinesGeometry I received following error: Uncaught TypeError: class constructors must be invoked with 'new'. Because of that I'm importing the modules.
<script src="./three/build/three.js"></script>
<script src="./three/examples/js/controls/OrbitControls.js"></script>
<script src="./three/examples/js/libs/dat.gui.min.js"></script>
<script src="./OutlinesGeometry.js"></script>
How should the imports be done correctly?
The runtime error happens because you are deriving from a ES6 class via ES5 syntax.
In newer releases BufferGeometry is a ES6 class. When creating a custom geometry generator, the following code is invalid:
THREE.BufferGeometry.call( this );
You can only solve this issue by migrating OutlinesGeometry to an ES6 class as well.
BTW: When working with modules, you just have a single <script type="module"></script> tag and use inside of the tag ES6 import syntax. Meaning:
<script type="module">
import * as THREE from './three/build/three.module.js';
import OrbitControls from './three/examples/jsm/controls/OrbitControls.js';
I suggest you study the official three.js example for more details.

Why Stats.min.js can only be imported by `script src=` but not `import`?

I have Javascript codes as below. When the importation is inside <script src="..."></script>, it works. However, if I moved the importation to import ...;, it doesn't work. The error message is: "Uncaught ReferenceError: Stats is not defined".
Why is that? Since I am working on a Jekyll site, I prefer to do the import ...; way to make sure other elements of the site work. Any idea how to do the import ...; way without error?
It works
<div id="stats"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/stats.js/r16/Stats.min.js"></script>
<script type="module">
const stats = new Stats()
stats.setMode(0)
document.getElementById('stats').appendChild(stats.domElement)
</script>
It doesn't work
<div id="stats"></div>
<script type="module">
import 'https://cdnjs.cloudflare.com/ajax/libs/stats.js/r16/Stats.min.js';
const stats = new Stats()
stats.setMode(0)
document.getElementById('stats').appendChild(stats.domElement)
</script>
import allows you to import JavaScript modules which conform to the ES6 module format.
Stats.min.js does not. The code is obfuscated but appears to create a global and support the old CommonJS module format.

Cannot use console.log() in a module

I'm trying to understand how to use modules in javascript.
But it seems that if I import a module, then I cannot log anything to console.
Here's my code:
index.html:
<!DOCTYPE html>
<html>
<head>
</head>
<body>
<script src="js/scene.js" type="module"></script>
<script src="js/main.js" type="module"></script>
</body>
</html>
scene.js:
class Scene {
constructor() {
console.log("Scene created");
}
}
export default Scene;
main.js:
import { Scene } from './js/scene.js';
var scene = new Scene();
console.log("Hello World");
The Expected Result:
Scene created
Hello World
The Result I Get:
Nothing (No Result)
What is wrong with my code and how can I properly use a module?
I see 3 mistakes
in index.html the first script tag is useless.
import is relative to script file, not html.
Scene.js export a default value not a variable named Scene.
solutions:
import Scene from './scene.js';
or
remove default in Scene.js, and import {Scene} from './scene.js';
In your scenario, I believe that line 1 of main.js is throwing an error. In your index.html file, you're suggesting that you have a folder named js with both javascript modules in it; however, in the main.js file, you're suggesting that scene.js is at path js/js/scene.js.
You probably meant import { Scene } from './scene.js';.
You should open your browser's console to view any errors.
For example, if you are using Google Chrome, becoming familiar with the Chrome Devtools will be invaluable in allowing you to resolve bugs like this one by yourself in the future.
The Chrome Devtools console will allow you to view errors in your website, which will give you an immediate answer on what is wrong. In addition, setting breakpoints will allow you to step-through each line of code and trace the flow of executed lines of code and the value of variables at each point in time.
Learn more here: https://developers.google.com/web/tools/chrome-devtools/javascript

Uncaught SyntaxError: Cannot use import statement outside a module

https://www.youtube.com/watch?v=1TeMXIWRrqE
<!DOCTYPE html>
<html>
<head>
<title>Three.js</title>
<style type="text/css">
html, body {margin: 0; padding: 0; overflow: hidden}
</style>
</head>
<body>
<div id="webgl"></div>
<script src="three.js"></script>
<script src="GLTFLoader.js"></script>
<script>
let scene,camera,renderer;
function init(){
scene = new THREE.Scene();
scene.background = new THREE.Color(0xdddddd);
camera = new THREE.PerspectiveCamera(40,window.innerWidth/window.innerHeight,1,5000);
hlight = new THREE.AmbientLight(0x404040,100);
scene.add(hlight);
renderer = new THREE.WebGLRenderer({antialias:true});
renderer.setSize(window.innerWidth,window.innerHeight);
document.getElementById('webgl').appendChild(renderer.domElement);
let loader = new THREE.GLTFLoader(); //THE ERROR WITH THIS, I TRIED WITHOUT THREE. ONLY GLTLOADER() BUT DID NOT WORK
loader.load('scene.gltf', function(gltf){
scene.add(gltf.scene);
renderer.render(scene,camera);
});
}
init();
</script>
</body>
</html>
https://sketchfab.com/3d-models/1972-datsun-240k-gt-non-commercial-use-only-b2303a552b444e5b8637fdf5169b41cb
//https://www.youtube.com/watch?v=1TeMXIWRrqE
This is tutorial for GLTFLOADER , i imported same 3d file as well and written same code, but when i open the file on local server, i get 2 errors in console:
1) "GLTFLoader.js:9 Uncaught SyntaxError: Cannot use import statement outside a module"
2) "(index):29 Uncaught TypeError: THREE.GLTFLoader is not a constructor."
Although in youtube video it opens with him immediatly!! :(
I would be greatful if anybody can hep me out with this. THANK YOU SO MUCH
GLTFLoader.js:9 Uncaught SyntaxError
(index):29 Uncaught TypeError
Here's a newer tutorial. The bottom of this article explains what changed.
The short version is three.js changed to prefer using es6 modules so instead of
<script src="three.js"></script>
<script src="GLTFLoader.js"></script>
<script>
let scene,camera,renderer;
function init(){
...
THREE.GLTFLoader.load(...
you'd do this
<script type="module">
import * as THREE from './build/three.module.js';
import {GLTFLoader} from './examples/jsm/loaders/GLTFLoader.js';
let scene,camera,renderer;
function init(){
...
GLTFLoader.load(...
But to use it like that requires you copy the three.js files to the same folder structure.
someFolder
|
├-build
| |
| +-three.module.js
|
+-examples
|
+-jsm
|
+-controls
| |
| +-OrbitControls.js
| +-TrackballControls.js
| +-...
|
+-loaders
| |
| +-GLTFLoader.js
| +-...
|
...
If you want to use the old method <script> tag style then you need to make sure you use the files from the js folder, not the jsm folder
Note: You must use this folder structure because the files in the examples/jsm folder like GLTFLoader.js refer to various other files like three.module.js via relative but hard coded paths. For example in GLTFLoader.js there is a line which is effectively
import {stuff} from "../../../build/three.module.js";
A couple of advantages to the new style are
the various modules can pull in the other parts they need.
In the past to use one extra part you might need to add 1 to 10 other <script> tags. Now you just need 1 import and that part can pull in the other 1 to 10 parts on it's own
If you build your page with a web builder it can strip out the parts you are not using.
Rumor is there are plans to get rid of the <script> method completely at some point in the future.
Just to try to make it clear both why ES6 modules are better and why you need to keep the same structure.
In pre r105 to use the EffectComposer you'd do this
<script src="threejs/examples/js/postprocessing/EffectComposer.js"></script>
You run and you'd get an error
THREE.EffectComposer relies on THREE.CopyShader
So you'd dig around to find it, it's not in the same folder as EffectsComposer.js. When you finally find it you add it
<script src="threejs/examples/js/postprocessing/EffectComposer.js"></script>
<script src="threejs/examples/js/shaders/CopyShader.js"></script>
Run again and get another error THREE.EffectComposer relies on THREE.ShaderPass so again digging around you add that
<script src="threejs/examples/js/postprocessing/EffectComposer.js"></script>
<script src="threejs/examples/js/shaders/CopyShader.js"></script>
<script src="threejs/examples/js/postprocessing/ShaderPass.js"></script>
That sucked.
Now as of r105 using es6 modules you can just do
import {EffectComposer} from './threejs/examples/jsm/postprocessing/EffectComposer.js';
Which is arguably much nicer. You won't get the other errors because the ES6 module version EffectComposer.js can reference the files it needs, its dependencies, itself. At the top of EffectComposer.js are the references to its dependencies.
import {
Clock,
LinearFilter,
Mesh,
OrthographicCamera,
PlaneBufferGeometry,
RGBAFormat,
Vector2,
WebGLRenderTarget
} from "../../../build/three.module.js";
import { CopyShader } from "../shaders/CopyShader.js";
import { ShaderPass } from "../postprocessing/ShaderPass.js";
import { MaskPass } from "../postprocessing/MaskPass.js";
import { ClearMaskPass } from "../postprocessing/MaskPass.js";
But, as you can see above, EffectsComposer.js expects that three.module.js is in a folder called build 3 subfolders down from itself. It expects CopyShader.js is in a folder called shaders one folder down from itself. Etc...
In other words, it needs the same folder structure.
I faced a similar issue, this is how I solved it.
In your HTML, the order matters when adding other scripts as it needs certain things from Three.js.
It should be
<script src="../build/three.min.js"></script>
<script src="three.js-master/examples/js/controls/OrbitControls.js"></script>
<script src="three.js-master/examples/js/loaders/GLTFLoader.js"></script>
I would suggest trying to import from your local javascript folder instead of using from jsm. I hope this will solve your issue.

Browsers support module imports, how do I use it?

I'm trying to figure out why my script tags don't work in my html file, and I came across an article that says you can import modules into browsers via a script tag with a module type. Directly from this site: https://www.sitepoint.com/understanding-es6-modules/
<script type="module" src="./main.js"></script>
// or
<script type="module">
import { something } from './somewhere.js';
// ...
</script>
So then I tried it and the imported module is recognized but getting this error Can't find variable World is it because my example.js file doesn't have it in its scope? I don't understand the order in which they are "processed?" don't know the terminology sorry.
My attempt below
//HTML
<body>
<script type="module">
import { World, Ball, Paddle, Brick, Hud} from "./brickbreakclasses.js"
</script>
<script src="example.js"></script>
</body>
example.js file
// commented this part out because it was imported in html file
// import { World, Ball, Paddle, Brick, Hud } from "./brickbreakclasses";
var canvas = new World(document.getElementById("canvas") );
var ball = new Ball(canvas)
var brick = new Brick(canvas)
var hud = new Hud(canvas)
var paddle = new Paddle(canvas)
I hope this makes sense but I really want to understand why I can't just require or import something in JS file and add it to html via script. I've been using webpack to bundle everything and putting that into my html.However I'm now making a npm package and I hear people don't like pre-bundled packages so I'm trying to find a way for a user to use my modules in html without having to install webpack or something like it.
Variables declared inside a <script type="module"> are local to that module. Make example.js a module, import the classes from there, and you can use them:
HTML
<body>
<script type="module" src="example.js"></script>
</body>
example.js
import { World, Ball, Paddle, Brick, Hud } from "./brickbreakclasses.js";
var canvas = new World(document.getElementById("canvas") );
var ball = new Ball(canvas);
var brick = new Brick(canvas);
var hud = new Hud(canvas);
var paddle = new Paddle(canvas);

Categories