avocado-old/packages/graphics/traits/pictured.trait.js
2019-04-19 17:09:48 -05:00

177 lines
3.8 KiB
JavaScript

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,
}),
);
export class Pictured extends decorate(Trait) {
static defaultParams() {
return {
images: {},
};
}
static defaultState() {
return {
currentImage: 'initial',
};
}
initialize() {
this._images = this.params.get('images').toJS();
this.sprites = undefined;
}
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.
const imagePromises = [];
this.sprites = {};
for (const key in this._images) {
const {uri} = this._images[key];
const imagePromise = Image.load(uri).then((image) => {
const sprite = this.sprites[key] = new Sprite(image);
// Calculate any offset.
sprite.position = this.offsetFor(key);
// Set current image upfront.
const isCurrentImage = key === this.entity.currentImage;
if (isCurrentImage) {
this.showImage(key);
}
});
imagePromises.push(imagePromise);
}
Promise.all(imagePromises).then((images) => {
// Bounding box update.
this.entity.updateVisibleBoundingBox();
this.setSpriteScale();
});
}
offsetFor(key) {
if (!this._images[key] || !this._images[key].offset) {
return [0, 0];
}
return this._images[key].offset;
}
setSpriteScale() {
if (!this.sprites) {
return;
}
const rawScale = this.entity.rawVisibleScale;
for (const key in this.sprites) {
const sprite = this.sprites[key];
sprite.scale = rawScale;
}
}
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;
}
hooks() {
return {
visibleBoundingBoxes: () => {
const key = this.entity.currentImage;
const image = this._images[key];
if (!image) {
return [0, 0, 0, 0];
}
const viewPosition = this.offsetFor(key);
const position = Vector.add(this.entity.position, viewPosition);
const size = this.sizeFor(key);
const rectangle = Rectangle.compose(position, size);
const expanded = Rectangle.expand(
rectangle,
Vector.sub(Vector.mul(size, this.entity.rawVisibleScale), size),
);
return expanded;
},
}
}
listeners() {
return {
currentImageChanged: (oldKey) => {
// Bounding box update.
this.entity.updateVisibleBoundingBox();
// Only client/graphics.
if (!this.sprites) {
return;
}
// Swap the image.
this.hideImage(oldKey);
this.showImage(this.entity.currentImage);
},
visibleScaleChanged: () => {
this.setSpriteScale();
},
traitAdded: (type) => {
if (-1 === [
'visible',
'pictured',
].indexOf(type)) {
return;
}
this.loadImagesIfPossible();
},
};
}
}