import {compose} from '@avocado/core'; import {StateProperty, Trait} from '@avocado/entity'; import {Rectangle, Vector} from '@avocado/math'; import {Image} from '../image'; import {Sprite} from '../sprite'; const decorate = compose( StateProperty('currentImage', { track: true, }), ); class PicturedBase extends Trait { static defaultParams() { return { images: {}, }; } static defaultState() { return { currentImage: 'initial', }; } initialize() { this._images = this.params.get('images').toJS(); this.sprites = undefined; // Bounding box update. this.entity.emit('visibleBoundingBoxesUpdated'); } destroy() { if (this.sprites) { for (const key in this.sprites) { this.hideImage(key); const sprite = this.sprites[key]; sprite.destroy(); } } } hideImage(key) { if (!this.sprites) { return; } const sprite = this.sprites[key]; if (!sprite) { return; } this.entity.container.removeChild(sprite); } loadImagesIfPossible() { if (!this.entity.container) { return; } if (this.sprites) { return; } // Load all images. this.sprites = {}; for (const key in this._images) { const {uri} = this._images[key]; Image.load(uri).then((image) => { const sprite = this.sprites[key] = new Sprite(image); // Calculate any offset. sprite.position = this.viewPositionFor(key); // Set current image upfront. const isCurrentImage = key === this.entity.currentImage; if (isCurrentImage) { this.showImage(key); } }); } } offsetFor(key) { if (!this._images[key] || !this._images[key].offset) { return [0, 0]; } return this._images[key].offset; } showImage(key) { if (!this.sprites) { return; } const sprite = this.sprites[key]; if (!sprite) { return; } this.entity.container.addChild(sprite); } sizeFor(key) { if (!this._images[key] || !this._images[key].size) { return [0, 0]; } return this._images[key].size; } viewPositionFor(key) { const image = this._images[key]; if (!image) { return [0, 0]; } const size = this.sizeFor(key); const halfway = Vector.scale(size, -0.5); const offset = this.offsetFor(key); return Vector.add(halfway, offset); } hooks() { return { visibleBoundingBoxes: () => { const key = this.entity.currentImage; const image = this._images[key]; if (!image) { return [0, 0, 0, 0]; } const viewPosition = this.viewPositionFor(key); const position = Vector.add(this.entity.position, viewPosition); return Rectangle.compose(position, this.sizeFor(key)); } } } listeners() { return { currentImageChanged: (oldKey) => { // Bounding box update. this.entity.emit('visibleBoundingBoxesUpdated'); // Only client/graphics. if (!this.sprites) { return; } // Swap the image. this.hideImage(oldKey); this.showImage(this.entity.currentImage); }, traitAdded: (type) => { if (-1 === [ 'visible', 'pictured', ].indexOf(type)) { return; } this.loadImagesIfPossible(); }, }; } } export class Pictured extends decorate(PicturedBase) {}