import {compose, EventEmitter, Property} from '@avocado/core'; import {Rectangle, Vector} from '@avocado/math'; const decorate = compose( EventEmitter, Vector.Mixin('areaSize', 'areaWidth', 'areaHeight', { default: [0, 0], track: true, }), Property('drag', { default: 0.2, }), Vector.Mixin('position', 'x', 'y', { default: [0, 0], }), Vector.Mixin('realOffset', 'realOffsetX', 'realOffsetY', { default: [0, 0], track: true, }), Vector.Mixin('realPosition', 'realX', 'realY', { default: [0, 0], track: true, }), Vector.Mixin('viewSize', 'viewWidth', 'viewHeight', { default: [0, 0], track: true, }), ); export class Camera extends decorate(class {}) { constructor() { super(); this.viewOrigin = [0, 0]; this.on('areaSizeChanged', this.onAreaSizeChanged, this); this.on('realPositionChanged', this.onRealPositionChanged, this); this.on('viewSizeChanged', this.onViewSizeChanged, this); } destroy() { this.off('areaSizeChanged', this.onAreaSizeChanged); this.off('realPositionChanged', this.onRealPositionChanged, this); this.off('viewSizeChanged', this.onViewSizeChanged); } onAreaSizeChanged() { this.position = this.position; } onRealPositionChanged() { this.realOffset = Vector.sub(this.realPosition, this.viewOrigin); } onViewSizeChanged() { this.position = this.position; this.viewOrigin = Vector.scale(this.viewSize, 0.5); this.realOffset = Vector.sub(this.realPosition, this.viewOrigin); } get position() { return super.position; } set position(position) { if (0 === Vector.area(this.areaSize)) { return super.position = position; } if (0 === Vector.area(this.viewSize)) { return super.position = position; } const viewOrigin = this.viewOrigin; super.position = Vector.clamp( position, viewOrigin, Vector.sub(this.areaSize, viewOrigin) ); } get rectangle() { return Rectangle.compose( Vector.sub(this.position, this.viewOrigin), this.viewSize, ); } tick(elapsed) { if (Vector.equals(this.position, this.realPosition)) { return; } if (0 === this.drag) { this.realPosition = this.position; } if (Vector.equalsClose(this.position, this.realPosition, 0.5)) { this.realPosition = this.position; return; } // Sanity check. const k = elapsed / this.drag; if (k >= 1) { this.realPosition = this.position; return; } const diff = Vector.sub(this.position, this.realPosition); const magnitude = Vector.scale(diff, k); this.realPosition = Vector.add(this.realPosition, magnitude); } }