aframe-react-assets
Version:
A React component for smart managing your AFrame VR assets. You can declare your assets at your React component.
224 lines (187 loc) • 6.59 kB
JSX
/**
* Assets component for managing AFrame assets
* See more detail here:
* https://www.npmjs.com/package/aframe-react-assets
*/
import React from 'react';
import PropTypes from 'prop-types';
const defaultTimeout = 30000;
const defaultInterval = 200;
export default class Assets extends React.PureComponent {
static propTypes = {
assets: PropTypes.object,
timeout: PropTypes.number,
interval: PropTypes.number,
debug: PropTypes.bool,
onLoad: PropTypes.func,
onLoadingBySize: PropTypes.func,
onLoadingByAmount: PropTypes.func,
};
// Internal state that does not cause re-render.
iState = {
assetsInstance: null,
current: 0,
total: 0,
assetCurrentItem: null,
idleTimestamp: 0,
};
componentDidMount() {
ConsoleLogger.log('Assets Component mounted', 'Assets');
//console.log('iState.assetsInstance.fileLoader: ', this.iState.assetsInstance.fileLoader);
//if (this.iState.assetsInstance.fileLoader) {
// const mng = this.iState.assetsInstance.fileLoader.manager;
//
// mng.onError = function (a, b) {
// console.log("mng onError a, b: ", a, b);
// }
// mng.onLoad = function (a, b) {
// console.log("mng onLoad a, b: ", a, b);
// }
// mng.onProgress = function (a, b) {
// console.log("mng onProgress a, b: ", a, b);
// }
// mng.onStart = function (a, b) {
// console.log("mng onStart a, b: ", a, b);
// }
//}
this.iState.assetsInstance.addEventListener('loaded', () => {
// Force too complete
this.props.onLoadingByAmount({
assetLoaded: this.iState.total,
assetTotal: this.iState.total,
assetCurrentItem: this.iState.assetCurrentItem,
});
setTimeout(this.props.onLoad(false), 1000);
ConsoleLogger.log('All assets were loaded', 'Assets');
//console.info('And THREE.Cache', THREE.Cache);
});
}
componentWillUnmount() {
// Make sure to remove the DOM listener when the component is unmounted.
this.iState.assetsInstance.removeEventListener('loaded');
}
static getCurrUnixMili() {
return (new Date()).getTime();
}
countLoadedAssetItem = (e) => {
//console.log('countLoadedAssetItem this.iState.current: ', this.iState.current, e, e.target);
this.iState.current++;
this.iState.assetCurrentItem = e.target;
if (this.props.debug && e.target) {
console.info('[Assets] loaded: ', e.target);
}
let currentUnix = Assets.getCurrUnixMili();
const {interval = defaultInterval} = this.props;
if (currentUnix - interval > this.iState.idleTimestamp) {
this.iState.idleTimestamp = currentUnix;
if (this.props.debug) {
ConsoleLogger.log('Attempt to updateAssetsLoadingInfo', 'Assets');
}
this.props.onLoadingByAmount({
assetLoaded: this.iState.current,
assetTotal: this.iState.total,
assetCurrentItem: this.iState.assetCurrentItem,
})
}
}
/**
* NOTE: TODO: This feature has not completed yet;
*/
updateProgress = (e) => {
//console.log('xhr: ', e);
let currentUnix = Assets.getCurrUnixMili();
const {interval = defaultInterval} = this.props;
if (currentUnix - interval > this.iState.idleTimestamp) {
this.iState.idleTimestamp = currentUnix;
this.props.onLoadingBySize({
assetCurrentLoadedBytes: e.detail.loadedBytes,
assetCurrentTotalBytes: e.detail.totalBytes ? e.detail.totalBytes : e.detail.loadedBytes
})
}
}
/**
* Try to Attach "loaded" event listener foreach asset items.
* "loaded" event name was different from each item
*
* @param item React element, eg: <img src=""/>
* @returns {*}
*/
getBindingProps = (item) => {
let eventName;
switch (item.type) {
case 'a-asset-item':
eventName = 'loaded'; // aframe / threejs event
return {
// NOTE: This case is an react component, not a pure HTML so that we need to pass eventListener to `ref`
ref: ele => {
ele.addEventListener(eventName, this.countLoadedAssetItem);
//ele.addEventListener('progress', this.updateProgress);
},
};
case 'img':
eventName = 'onLoad'; // js event
return {
[eventName]: this.countLoadedAssetItem,
//ref: ele => this.assetItemInstances[getId(item)] = ele,
};
case 'audio':
case 'video':
eventName = 'onLoadeddata'; // js event
//eventName = 'loadeddata'; // aframe event
return {
[eventName]: this.countLoadedAssetItem,
//ref: ele => this.assetItemInstances[getId(item)] = ele,
};
default:
console.warn('Un-recognize asset type: ', item.type);
return {}
}
}
// TODO: Support asset management with lazy load
getAssetsList = () => {
const {assets = []} = this.props;
const assetItemComponents = Object.keys(assets).map((key) => {
const componentAssets = this.props.assets[key];
this.iState.total += componentAssets.length;
return <a-entity key={key} className={key}>
{componentAssets.map(item => item.hasOwnProperty('type')
? React.cloneElement(item, {
key: item.props.id ? item.props.id : ConsoleLogger.getUnix(),
//ref: ele => this.assetItems.push(ele),
...this.getBindingProps(item), // Bind event listener for this elements
}
)
: null // Some user mis-type comment: [ {/*Asset was commented*/} ] ==> [ {} ] , so this is not valid assets
)}
</a-entity>
});
if (this.props.debug) {
console.log('Component list to add assets: ', assetItemComponents);
}
return assetItemComponents;
}
render() {
const {timeout = defaultTimeout} = this.props;
return (
<a-assets {...{timeout}} ref={ele => this.iState.assetsInstance = ele}>
{this.getAssetsList()}
</a-assets>
);
}
}
class ConsoleLogger {
static getLocaleTimeStr() {
const d = new Date();
const h = d.getHours();
const m = d.getMinutes();
const s = d.getSeconds();
const ms = d.getMilliseconds();
return `${h}:${m}:${s}-${ms}`;
}
static getUnix() {
return Math.floor(new Date().getTime() / 1e3);
}
static log(msg, componentName = "") {
console.log(`[${componentName}] ${msg} at ${this.getLocaleTimeStr()}`);
}
}