aframe-react-assets
v2.0.5
Published
A React component for smart managing your AFrame VR assets. You can declare your assets at your React component.
Downloads
19
Maintainers
Readme
Introduce
A React component for smart managing your AFrame VR assets. You can declare your assets at your React component.
If you need an example of Aframe with React, I've made a real demo for Aframe, include implementation of this package. Just take a look at https://github.com/luatnd/aframe-react-demo
The problem why you need to use this package
TL;DR
Aframe assets manager system
require you too put all your assets inside only one<a-assets>
tag in your app AND a must be the direct child of<a-scene>
. But when you break your very big layout into so many nested React components, you need to find a way to put your assets at your components to ensure component oriented spirit of React while stay out of being conflict withAframe assets manager system
. Usingaframe-react-assets
is a good solution for you.
You can skip the detail and jump to How section if you aren't interested in the detail of problem.
Another benefit is you can track the progress of assets loading if you want, see onLoadingByAmount()
event handle:
Detail of the problem
This is a good HTML section for Aframe,
we'll take about migrating the Sky
section and its relate assets in to React component:
<a-scene>
<!-- Aframe Asset management system. -->
<a-assets>
<!-- <Sky/> React component's assets: -->
<img id="sky" src="assets/img/sky.jpg" alt="Sky Component's asset #sky"/>
<video id="videoMilkyWay" src="assets/img/videoMilkyWay.mp4"/>
<!-- Other Component Assets -->
<a-asset-item id="horse-obj" src="horse.obj"></a-asset-item>
<a-asset-item id="horse-mtl" src="horse.mtl"></a-asset-item>
<a-mixin id="giant" scale="5 5 5"></a-mixin>
<audio id="neigh" src="neigh.mp3"></audio>
<img id="advertisement" src="ad.png">
<video id="kentucky-derby" src="derby.mp4"></video>
</a-assets>
<a-entity class="camera" camera=""></a-entity>
<Entity>
<!-- This will be <Sky/> Component -->
<Entity className="theSky">
<a-sky className="sky" src="#sky" rotation="0 0 0"/>
<!-- ... more content ... -->
<!-- ... more content ... -->
<!-- ... more content ... -->
</Entity>
<!--End Sky component-->
<Light/>
<FloorAndWall/>
<Entity className="advertise">
<a-plane src="#advertisement"></a-plane>
<a-sound src="#neigh"></a-sound>
<a-entity geometry="primitive: plane" material="src: #kentucky-derby"></a-entity>
<a-entity mixin="giant" obj-model="obj: #horse-obj; mtl: #horse-mtl"></a-entity>
</Entity>
</Entity>
</a-scene>
So if you create Aframe with React, you need to divide your Aframe HTML into some small component.
<Entity className="theSky">
<a-sky className="sky" src="#sky" rotation="0 0 0"/>
<!-- ... more content ... -->
<!-- ... more content ... -->
<!-- ... more content ... -->
</Entity>
into this:
<Sky/>
So what about the Sky assets ?
<img id="sky" src="assets/img/sky.jpg" alt="Sky Component's asset #sky"/>
<video id="videoMilkyWay" src="assets/img/videoMilkyWay.mp4"/>
AFrame recommend you to put your assets "inside" the (You might read the TL;DR section again)
You can not do like this:
Sky.jsx:
<Entity className="theSky">
{/* Aframe do not recommend you put your assets here: */}
<img id="sky" src="assets/img/sky.jpg" alt="Sky Component's asset #sky"/>
<video id="videoMilkyWay" src="assets/img/videoMilkyWay.mp4"/>
<a-sky className="sky" src="#sky" rotation="0 0 0"/>
<!-- ... more content ... -->
<!-- ... more content ... -->
<!-- ... more content ... -->
</Entity>
Because:
- AFrame assets manager system rules, Read the TL;DR
- If your react component was re-render, browser will re-make a new request to load assets again. This is redundant. Imagine your component contain 10 assets, and component will be re-render every second. How bad that will be ?
- Use Aframe asset manager system is a most efficient way to use your assets
How
How to use this plugin
0.. Install plugin
yarn add aframe-react-assets
1.. Declare your static Assets
array at your component:
import imgSky from "assets/img/sky.jpg";
import videoMilkyWay from "assets/img/videoMilkyWay.mp4";
export default class Sky extends React.Component {
static Assets = [
<img id="sky" src={imgSky} alt="sky"/>,
<video id="videoMilkyWay" src={videoMilkyWay}/>
];
render() {
return (
<Entity {...this.props}>
<a-sky className="sky" src="#sky" rotation="0 0 0"/>
</Entity>
);
}
}
2.. Create an rootAssets.js
like this:
rootAssets.js
export default {
// [ComponentName:string]: Your declared Assets array
Sky: require('../Sky/Sky').default.Assets,
FloorAndWall: require('../FloorAndWall/FloorAndWall').default.Assets,
Workspace: require('../Workspace/Workspace').default.Assets,
BackWall: require('../Decorator/BackWall').default.Assets,
FrontSea: require('../Decorator/FrontSea').default.Assets,
Center: require('../Decorator/Center').default.Assets,
Light: require('../Light/Light').default.Assets,
};
3.. Use Assets
:
MyScene.jsx:
import {Entity, Scene} from 'aframe-react';
import Assets from 'aframe-react-assets';
import rootAssets from 'path/to/rootAssets.js';
export default class MyScene extends React.Component {
render () {
return <Scene>
{/* Use assets here */}
<Assets
assets={rootAssets}
timeout={4e4}
interval={200}
debug={true}
onLoad={this.updateAssetsLoadingStatus}
onLoadingBySize={this.updateAssetsCurrentInfo}
onLoadingByAmount={this.updateAssetsLoadingInfo}
/>
<Entity camera="userHeight: 2; fov: 80;"/>
<Entity>
<Sky/>
<Light/>
<FloorAndWall/>
<Workspace/>
<Entity className="decorator">
<BackWall/>
<FrontSea/>
<Center/>
</Entity>
</Entity>
</Scene>
}
}
Assets Props
assets: object
- See
rootAssets.js
above
timeout: number
- Stop loading pending/waiting assets and consider the loading was all successful when this value was reached, in milliseconds.
- @default 30000
interval: number
- The interval duration in milliseconds that this component will do update via
event handle
on*() bellow - Example: onLoadingByAmount() will be triggered each 200ms (default)
- @default 200
debug: boolean
- Turn on console.log this component activities
onLoad(status: boolean
): void
- When
<a-assets/>
was start loading its assets:onLoad(true)
was triggered. - When all assets was loaded or exceed
timeout
props:onLoad(false)
was triggered.
onLoadingBySize({assetCurrentLoadedBytes: number, assetTotalBytes: number}
): void
- onLoadingBySize was triggered each
interval
milliseconds. Seeinterval
props. - You can calculate current progress by percent:
const currentPercent = assetCurrentLoadedBytes / assetTotalBytes * 100;
- NOTE: TODO: This feature has not completed yet;
- NOTE: Choose and use only one onLoading*() handle, because they're using same interval manager
onLoadingByAmount({assetLoaded: number, assetTotal: number, assetCurrentItem: object}
):void
- onLoadingByAmount was triggered each
interval
milliseconds. Seeinterval
props. - Update loading info every
interval
millisecondsassetLoaded
: Number of successfully loaded assets,assetTotal
: Total amount of all your assets,assetCurrentItem
: The current loaded assets, value is the html element
- NOTE: Choose and use only one onLoading*() handle, because they're using same interval manager
Contribution
You're very welcome. This package is an just quick initial idea, for a production app, this plugin is lacking:
- Track how many bytes assets was loaded (there was a draft version inside this package)
- Code splitting support (It's might buggy when this component re-render)