UNPKG

infamous

Version:

A CSS3D/WebGL UI library.

199 lines (167 loc) 6.79 kB
import Class from 'lowclass' import { PerspectiveCamera as ThreePerspectiveCamera } from 'three' import Node from './Node' import Motor from './Motor' import { props } from './props' // TODO: update this to have a CSS3D-perspective-like API like with the Scene's // default camera. export default Class('PerspectiveCamera').extends( Node, ({ Super, Public, Private }) => ({ static: { defaultElementName: 'i-perspective-camera', // TODO remove attributeChangedCallback, replace with updated based on these props props: { ...Node.props, fov: { ...props.number, default: 75 }, aspect: { ...props.number, default() { return Private(this)._getDefaultAspect() }, deserialize(val) { val == null ? this.constructor.props.aspect.default.call(this) : props.number.deserialize(val) }, }, near: { ...props.number, default: 0.1 }, far: { ...props.number, default: 1000 }, zoom: { ...props.number, default: 1 }, active: { ...props.boolean, default: false }, }, }, updated(oldProps, oldState, modifiedProps) { Super(this).updated(oldProps, oldState, modifiedProps) if (!this.isConnected) return if (modifiedProps.active) { this._setSceneCamera( this.active ? undefined : 'unset' ) } if (modifiedProps.aspect) { if (!this.aspect) // default aspect value based on the scene size. privateThis._startAutoAspect() else privateThis._stopAutoAspect() } // TODO handle the other props here, remove attributeChangedCallback }, makeThreeObject3d() { return new ThreePerspectiveCamera(75, 16/9, 1, 1000) }, connectedCallback() { Super(this).connectedCallback() const privateThis = Private(this) privateThis._lastKnownScene = this.scene }, // TODO replace with unmountedCallback #150 deinit() { Super(this).deinit() // TODO we want to call this in the upcoming // unmountedCallback, but for now it's harmless but // will run unnecessary logic. #150 Private(this)._setSceneCamera( 'unset' ) Private(this)._lastKnownScene = null }, // TODO, unmountedCallback functionality. issue #150 unmountedCallback() {}, attributeChangedCallback( attr, oldVal, newVal ) { Super(this).attributeChangedCallback( attr, oldVal, newVal ) if ( typeof newVal == 'string' ) { Private(this)._attributeAddedOrChanged( attr, newVal ) } else { Private(this)._attributeRemoved( attr ) } }, private: { _lastKnownScene: null, // TODO CAMERA-DEFAULTS, get defaults from somewhere common. _attributeRemoved(attr, newVal) { const publicThis = Public(this) if ( attr == 'fov' ) { publicThis.threeObject3d.fov = 75 publicThis.threeObject3d.updateProjectionMatrix() } else if ( attr == 'aspect' ) { this._startAutoAspect() publicThis.threeObject3d.aspect = this._getDefaultAspect() publicThis.threeObject3d.updateProjectionMatrix() } else if ( attr == 'near' ) { publicThis.threeObject3d.near = 0.1 publicThis.threeObject3d.updateProjectionMatrix() } else if ( attr == 'far' ) { publicThis.threeObject3d.far = 1000 publicThis.threeObject3d.updateProjectionMatrix() } else if ( attr == 'zoom' ) { publicThis.threeObject3d.zoom = 1 publicThis.threeObject3d.updateProjectionMatrix() } else if ( attr == 'active' ) { this._setSceneCamera( 'unset' ) } }, _attributeAddedOrChanged(attr, newVal) { const publicThis = Public(this) if ( attr == 'fov' ) { publicThis.threeObject3d.fov = parseFloat(newVal) publicThis.threeObject3d.updateProjectionMatrix() } else if ( attr == 'aspect' ) { this._stopAutoAspect() publicThis.threeObject3d.aspect = parseFloat(newVal) publicThis.threeObject3d.updateProjectionMatrix() } else if ( attr == 'near' ) { publicThis.threeObject3d.near = parseFloat(newVal) publicThis.threeObject3d.updateProjectionMatrix() } else if ( attr == 'far' ) { publicThis.threeObject3d.far = parseFloat(newVal) publicThis.threeObject3d.updateProjectionMatrix() } else if ( attr == 'zoom' ) { publicThis.threeObject3d.zoom = parseFloat(newVal) publicThis.threeObject3d.updateProjectionMatrix() } else if ( attr == 'active' ) { this._setSceneCamera() } }, _startAutoAspect() { if (!this._startedAutoAspect) { this._startedAutoAspect = true Public(this).scene.on('sizechange', this._updateAspectOnSceneResize, this) } }, _stopAutoAspect() { if (this._startedAutoAspect) { this._startedAutoAspect = false Public(this).scene.off('sizechange', this._updateAspectOnSceneResize) } }, _updateAspectOnSceneResize({x, y}) { Public(this).threeObject3d.aspect = x / y }, _getDefaultAspect() { let result = 0 const publicThis = Public(this) if ( publicThis.scene ) { result = publicThis.scene.calculatedSize.x / publicThis.scene.calculatedSize.y } // in case of a 0 or NaN (0 / 0 == NaN) if (!result) result = 16 / 9 return result }, _setSceneCamera( unset ) { const publicThis = Public(this) if ( unset ) { // TODO: unset might be triggered before the scene was mounted, so // there might not be a last known scene. We won't need this check // when we add unmountedCallback. #150 if ( this._lastKnownScene ) this._lastKnownScene._removeCamera( publicThis ) } else { if (!publicThis.scene || !publicThis.isConnected) return publicThis.scene._addCamera( publicThis ) } }, }, }))