Skip to content

Commit

Permalink
Add Teapot example that uses the new ES module bundle with importmap (#…
Browse files Browse the repository at this point in the history
…5645)

* Configure webpack dev server to serve /dist/aframe-master.module.min.js

* Add Teapot example (use importmap and import from three/addons)

* Create the aframe-v{nextVersion}.module.min.js / aframe.module.min.js bundle in prerelease script

* Update super-three version in examples and docs in prerelease script

* Add the new ES module bundle and importmap to the faq

* Use super-three from node_modules when running on master with webpack-dev-server

* Rename example to importmap

* Reword FAQ

---------

Co-authored-by: Diego Marcos <[email protected]>
  • Loading branch information
vincentfretin and dmarcos authored Jan 28, 2025
1 parent 763263b commit 72cc193
Show file tree
Hide file tree
Showing 6 changed files with 160 additions and 4 deletions.
39 changes: 39 additions & 0 deletions docs/introduction/faq.md
Original file line number Diff line number Diff line change
Expand Up @@ -378,6 +378,45 @@ await import('aframe');
window.AFRAME.ready();
```

Since version 1.7.0, A-Frame ships an ES module bundle without the three dependency.
Developers can import from `three` and `three/addons` and avoid the
"Multiple instances of Three.js being imported." warning. Add the three dependency in the importmap like the example below.
Make sure the three and A-Frame versions are compatible. See browser console (or package.json) to see what THREE version A-Frame ships with by default.

```HTML
<head>
<script type="importmap">
{
"imports": {
"aframe": "https://aframe.io/releases/1.7.0/aframe.module.min.js",
"three": "https://cdn.jsdelivr.net/npm/[email protected]/build/three.module.js",
"three/addons/": "https://cdn.jsdelivr.net/npm/[email protected]/examples/jsm/",
"aframe-extras/controls": "https://cdn.jsdelivr.net/gh/c-frame/[email protected]/dist/aframe-extras.controls.min.js"
}
}
</script>
<script type="module">
import AFRAME from "aframe";
// AFRAME and THREE variables are available globally, the imported aframe-master.module.min.js bundle basically does:
// import * as THREE from "three"
// window.THREE = THREE
import { TeapotGeometry } from "three/addons/geometries/TeapotGeometry.js"; // This uses the same three instance.
AFRAME.registerComponent("teapot", {
...
}
</script>
</head>
```
The [importmap example](https://aframe.io/aframe/examples/boilerplate/importmap/index.html) uses the above code.
## "Multiple instances of Three.js being imported." warning
See `Can I load A-Frame as an ES module?` above.
As a library author of aframe components, be sure to configure your bundler configuration to produce a build with the three dependency declared as external if you're using any `import ... from three` in your code or any addons you import like `import ... from three/addons/...js`.
You can look at the webpack configuration in the [aframe-extras repository](https://github.com/c-frame/aframe-extras) as an example.
## What order does A-Frame render objects in?
[sortTransparentObjects]: ../components/renderer.md#sorttransparentobjects
Expand Down
83 changes: 83 additions & 0 deletions examples/boilerplate/importmap/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Importmap • A-Frame</title>
<meta name="description" content="Importmap • A-Frame" />
<script type="importmap">
{
"imports": {
"aframe": "../../../dist/aframe-master.module.min.js",
"three": "../../../super-three-package/build/three.module.js",
"three/addons/": "../../../super-three-package/examples/jsm/",
"aframe-extras/controls": "https://cdn.jsdelivr.net/gh/c-frame/[email protected]/dist/aframe-extras.controls.min.js"
}
}
</script>
<script type="module">
// We use an importmap to load three as a module to avoid the warning "Multiple instances of Three.js being imported."
import AFRAME from "aframe";
// AFRAME and THREE variables are available globally, the imported aframe-master.module.min.js bundle basically does:
// import * as THREE from "three"
// window.THREE = THREE
import "aframe-extras/controls"; // This uses the THREE global variable in the bundle.
import { Mesh, MeshStandardMaterial } from "three"; // This uses the same three instance.
import { TeapotGeometry } from "three/addons/geometries/TeapotGeometry.js"; // This uses the same three instance.

AFRAME.registerComponent("teapot", {
init() {
this.geometry = new TeapotGeometry();
const mesh = new Mesh();
mesh.geometry = this.geometry;
// Default material if not defined on the entity.
if (!this.el.getAttribute("material")) {
mesh.material = new MeshStandardMaterial({
color: Math.random() * 0xffffff,
metalness: 0,
roughness: 0.5,
side: THREE.DoubleSide,
});
}
this.el.setObject3D("mesh", mesh);
},
remove() {
this.geometry.dispose();
},
});
</script>
</head>
<body>
<a-scene background="color: #ECECEC">
<a-cylinder
position="0 0.25 -2"
radius="0.5"
height="0.5"
color="#FFC65D"
>
<a-entity
teapot
position="0 0.48 0"
scale="0.005 0.005 0.005"
material="color: #713f12; roughness: 0.5; side: double"
></a-entity>
</a-cylinder>

<a-plane
position="0 0 -2"
rotation="-90 0 0"
width="4"
height="4"
color="#7BC8A4"
></a-plane>

<a-entity
position="0 0 1"
id="rig"
movement-controls="controls: gamepad,keyboard,nipple"
nipple-controls="mode: static"
>
<a-entity camera position="0 1.6 0" look-controls></a-entity>
</a-entity>
</a-scene>
</body>
</html>
1 change: 1 addition & 0 deletions examples/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,7 @@ <h2>Examples</h2>
<li><a href="boilerplate/3d-model/">3D Model (glTF)</a></li>
<li><a href="mixed-reality/anchor/">Anchor (Mixed Reality)</a></li>
<li><a href="mixed-reality/real-world-meshing/">Real World Meshing (Mixed Reality)</a></li>
<li><a href="boilerplate/importmap/">Importmap (import teapot geometry from three/addons)</a></li>
</ul>

<h2>Examples from Documentation</h2>
Expand Down
5 changes: 4 additions & 1 deletion scripts/preghpages.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ const path = require('path');
const shell = require('shelljs');
const replaceInFileSync = require('replace-in-file').replaceInFileSync;

const pkg = require('../package.json');
const threeVersion = pkg.dependencies.three.split('@')[1];

const rootDir = path.join(__dirname, '..');

shell.cd(rootDir);
Expand All @@ -15,7 +18,6 @@ shell.cp('-r', [
'.nojekyll',
'dist',
'examples',
'*.html',
'*.md'
], 'gh-pages');

Expand All @@ -28,3 +30,4 @@ function htmlReplace (before, after) {
}

htmlReplace('dist/aframe-master.js', 'dist/aframe-master.min.js');
htmlReplace(/\.\.\/\.\.\/\.\.\/super-three-package/g, `https://cdn.jsdelivr.net/npm/super-three@${threeVersion}`);
20 changes: 20 additions & 0 deletions scripts/release.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,16 +12,20 @@ if (!prevVersion || !nextVersion) {
process.exit(1);
}

let distModule;
let distMin;
let distMax;
if (process.env.FOR_RELEASE) {
distModule = `${pkg.scripts['dist:module']} --output-filename aframe.module.min.js`;
distMin = `${pkg.scripts['dist:min']} --output-filename aframe.min.js`;
distMax = `${pkg.scripts['dist:max']} --output-filename aframe.js`;
} else {
distModule = `${pkg.scripts['dist:module']} --output-filename aframe-v${nextVersion}.module.min.js`;
distMin = `${pkg.scripts['dist:min']} --output-filename aframe-v${nextVersion}.min.js`;
distMax = `${pkg.scripts['dist:max']} --output-filename aframe-v${nextVersion}.js`;
}

execSync(distModule, {stdio: 'inherit'});
execSync(distMin, {stdio: 'inherit'});
execSync(distMax, {stdio: 'inherit'});

Expand All @@ -32,10 +36,26 @@ glob.sync(`dist/aframe*v${prevVersion}*`).forEach(fs.unlinkSync);
var versionRegex = new RegExp(`${prevVersion.replace(/\./g, '\\.')}`, 'g');
glob.sync('docs/**/*.md').forEach(updateDoc);
glob.sync('README.md').forEach(updateDoc);

// Replace super-three version in examples, docs and README
var threeVersion = pkg.dependencies.three.split('@')[1];
var threeVersionRegex = new RegExp('super-three@.*?/', 'g');
glob.sync('examples/**/*.html').forEach(updateThreeVersion);
glob.sync('docs/**/*.md').forEach(updateThreeVersion);
glob.sync('README.md').forEach(updateThreeVersion);

function updateDoc (docFilename) {
var contents = fs.readFileSync(docFilename, 'utf-8');
if (versionRegex.exec(contents)) {
contents = contents.replace(versionRegex, nextVersion);
fs.writeFileSync(docFilename, contents);
}
}

function updateThreeVersion (docFilename) {
var contents = fs.readFileSync(docFilename, 'utf-8');
if (threeVersionRegex.exec(contents)) {
contents = contents.replaceAll(threeVersionRegex, `super-three@${threeVersion}/`);
fs.writeFileSync(docFilename, contents);
}
}
16 changes: 13 additions & 3 deletions webpack.config.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,18 @@ module.exports = {
port: process.env.PORT || 9000,
hot: false,
liveReload: true,
static: {
directory: 'examples'
}
static: [
{
directory: 'examples'
},
{
directory: 'dist',
publicPath: '/dist'
},
{
directory: 'node_modules/three/',
publicPath: '/super-three-package'
}
]
}
};

0 comments on commit 72cc193

Please sign in to comment.