refactor: Stage
This commit is contained in:
parent
700ed92b68
commit
e5d1caa385
|
@ -8,7 +8,7 @@ import {
|
||||||
useState,
|
useState,
|
||||||
} from '@latus/react';
|
} from '@latus/react';
|
||||||
|
|
||||||
import {Stage} from '@avocado/graphics/persea';
|
import {Stage} from '@avocado/graphics';
|
||||||
|
|
||||||
const EntityStage = ({entity}) => {
|
const EntityStage = ({entity}) => {
|
||||||
const [size, setSize] = useState([0, 0]);
|
const [size, setSize] = useState([0, 0]);
|
||||||
|
|
|
@ -11,7 +11,7 @@ import {
|
||||||
} from '@latus/react';
|
} from '@latus/react';
|
||||||
import Slider from 'rc-slider';
|
import Slider from 'rc-slider';
|
||||||
|
|
||||||
import Renderer from '../../../renderer';
|
import Renderer from '../../renderer';
|
||||||
|
|
||||||
const marks = {
|
const marks = {
|
||||||
1: '1x',
|
1: '1x',
|
|
@ -2,13 +2,13 @@ import {gatherWithLatus} from '@latus/core';
|
||||||
|
|
||||||
export {default as Canvas} from './canvas';
|
export {default as Canvas} from './canvas';
|
||||||
export {default as Color} from './color';
|
export {default as Color} from './color';
|
||||||
|
export {default as Stage} from './components/stage';
|
||||||
export {default as Container} from './container';
|
export {default as Container} from './container';
|
||||||
export {Image} from './resources/image';
|
export {Image} from './resources/image';
|
||||||
export {default as Primitives} from './primitives';
|
export {default as Primitives} from './primitives';
|
||||||
export {default as Renderable} from './renderable';
|
export {default as Renderable} from './renderable';
|
||||||
export {default as Renderer} from './renderer';
|
export {default as Renderer} from './renderer';
|
||||||
export {default as Sprite} from './sprite';
|
export {default as Sprite} from './sprite';
|
||||||
// export {default as Stage} from './stage';
|
|
||||||
export {default as Text} from './text';
|
export {default as Text} from './text';
|
||||||
|
|
||||||
if ('client' === process.env.SIDE) {
|
if ('client' === process.env.SIDE) {
|
||||||
|
|
|
@ -1,7 +1,5 @@
|
||||||
import ImageController from './controllers/image';
|
import ImageController from './controllers/image';
|
||||||
|
|
||||||
export {default as Stage} from './components/stage';
|
|
||||||
|
|
||||||
export {
|
export {
|
||||||
ImageController,
|
ImageController,
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,244 +0,0 @@
|
||||||
import {Property} from '@avocado/core';
|
|
||||||
import {InputNormalizer} from '@avocado/input';
|
|
||||||
import {Vector} from '@avocado/math';
|
|
||||||
import {compose} from '@latus/core';
|
|
||||||
|
|
||||||
import Container from './container';
|
|
||||||
import Renderer from './renderer';
|
|
||||||
|
|
||||||
const ASPECT = 16 / 9;
|
|
||||||
|
|
||||||
const decorate = compose(
|
|
||||||
Property('camera', {
|
|
||||||
track: true,
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
|
|
||||||
export default class Stage extends decorate(Container) {
|
|
||||||
|
|
||||||
constructor(visibleSize, visibleScale) {
|
|
||||||
super();
|
|
||||||
const size = Vector.mul(visibleSize, visibleScale);
|
|
||||||
// Container element.
|
|
||||||
this.element = window.document.createElement('div');
|
|
||||||
this.element.className = 'avocado-stage';
|
|
||||||
this.element.style.height = '100%';
|
|
||||||
this.element.style.lineHeight = '0';
|
|
||||||
this.element.style.position = 'relative';
|
|
||||||
this.element.style.width = '100%';
|
|
||||||
// DOM parent.
|
|
||||||
this.parent = undefined;
|
|
||||||
// Set scale.
|
|
||||||
this.scale = visibleScale;
|
|
||||||
// Canvas renderer.
|
|
||||||
this.renderer = new Renderer(size);
|
|
||||||
this.renderer.element.style.width = '100%';
|
|
||||||
this.renderer.element.style.height = '100%';
|
|
||||||
// "real" dimensions.
|
|
||||||
this.size = size;
|
|
||||||
// Precalc for position/dimension transformation.
|
|
||||||
this._transformRatio = 1;
|
|
||||||
// UI DOM node.
|
|
||||||
this.ui = this.createUiLayer();
|
|
||||||
this._queuedFindSelectors = {};
|
|
||||||
// Event handlers.
|
|
||||||
this.onWindowResize = this.onWindowResize.bind(this);
|
|
||||||
window.addEventListener('resize', this.onWindowResize);
|
|
||||||
// Normalize input.
|
|
||||||
this.inputNormalizer = new InputNormalizer();
|
|
||||||
this.inputNormalizer.listen(this.element);
|
|
||||||
this.inputNormalizer.on('keyDown', this.onKeyDown, this);
|
|
||||||
this.inputNormalizer.on('keyUp', this.onKeyUp, this);
|
|
||||||
this.inputNormalizer.on('pointerDown', this.onPointerDown, this);
|
|
||||||
this.inputNormalizer.on('pointerMove', this.onPointerMove, this);
|
|
||||||
this.inputNormalizer.on('pointerUp', this.onPointerUp, this);
|
|
||||||
this.inputNormalizer.on('wheel', this.onWheel, this);
|
|
||||||
// Put the renderer and UI in the container element, and mark it
|
|
||||||
// focusable.
|
|
||||||
this.element.appendChild(this.renderer.element);
|
|
||||||
this.element.appendChild(this.ui);
|
|
||||||
this.element.tabIndex = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
addToDom(parent) {
|
|
||||||
// Had a parent? Remove.
|
|
||||||
this.removeFromDom();
|
|
||||||
this.parent = parent;
|
|
||||||
// Add to new parent (if any) and focus.
|
|
||||||
if (parent) {
|
|
||||||
parent.appendChild(this.element);
|
|
||||||
}
|
|
||||||
// Recalculate size and ratio.
|
|
||||||
this.onWindowResize();
|
|
||||||
}
|
|
||||||
|
|
||||||
createUiLayer() {
|
|
||||||
const node = window.document.createElement('div');
|
|
||||||
node.className = 'ui';
|
|
||||||
node.style.overflow = 'hidden';
|
|
||||||
node.style.position = 'absolute';
|
|
||||||
node.style.width = `${this.size[0] / this.scale[0]}px`;
|
|
||||||
node.style.height = `${this.size[1] / this.scale[1]}px`;
|
|
||||||
node.style.left = 0;
|
|
||||||
node.style.top = 0;
|
|
||||||
node.style.transformOrigin = '0 0 0';
|
|
||||||
return node;
|
|
||||||
}
|
|
||||||
|
|
||||||
destroy() {
|
|
||||||
window.removeEventListener('resize', this.onWindowResize);
|
|
||||||
this.renderer.destroy();
|
|
||||||
super.destroy();
|
|
||||||
if (this.parent) {
|
|
||||||
this.parent.removeChild(this.renderer.element);
|
|
||||||
}
|
|
||||||
this.inputNormalizer.off('keyDown', this.onKeyDown);
|
|
||||||
this.inputNormalizer.off('keyUp', this.onKeyUp);
|
|
||||||
this.inputNormalizer.off('pointerDown', this.onPointerDown);
|
|
||||||
this.inputNormalizer.off('pointerMove', this.onPointerMove);
|
|
||||||
this.inputNormalizer.off('pointerUp', this.onPointerUp);
|
|
||||||
this.inputNormalizer.off('wheel', this.onWheel);
|
|
||||||
this.inputNormalizer.destroy();
|
|
||||||
}
|
|
||||||
|
|
||||||
get displaySize() {
|
|
||||||
return [
|
|
||||||
this.element.style.width,
|
|
||||||
this.element.style.height,
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
findUiElement(selector) {
|
|
||||||
const node = this.ui.querySelector(selector);
|
|
||||||
if (node) {
|
|
||||||
return Promise.resolve(node);
|
|
||||||
}
|
|
||||||
const queued = this._queuedFindSelectors[selector];
|
|
||||||
if (queued) {
|
|
||||||
return queued.promise;
|
|
||||||
}
|
|
||||||
let resolve;
|
|
||||||
const promise = new Promise((resolve_) => {
|
|
||||||
resolve = resolve_;
|
|
||||||
});
|
|
||||||
this._queuedFindSelectors[selector] = {resolve, promise};
|
|
||||||
return promise;
|
|
||||||
}
|
|
||||||
|
|
||||||
flushUiElements() {
|
|
||||||
const selectors = Object.keys(this._queuedFindSelectors);
|
|
||||||
for (let i = 0; i < selectors.length; i++) {
|
|
||||||
const selector = selectors[i];
|
|
||||||
const {resolve} = this._queuedFindSelectors[selector];
|
|
||||||
resolve(this.ui.querySelector(selector));
|
|
||||||
}
|
|
||||||
this._queuedFindSelectors = {};
|
|
||||||
}
|
|
||||||
|
|
||||||
focus() {
|
|
||||||
this.element.focus();
|
|
||||||
}
|
|
||||||
|
|
||||||
onKeyDown(event) {
|
|
||||||
this.emit('keyDown', event);
|
|
||||||
}
|
|
||||||
|
|
||||||
onKeyUp(event) {
|
|
||||||
this.emit('keyUp', event);
|
|
||||||
}
|
|
||||||
|
|
||||||
onPointerDown(event) {
|
|
||||||
// eslint-disable-next-line no-param-reassign
|
|
||||||
event.position = this.transformCanvasPosition(event.position);
|
|
||||||
this.emit('pointerDown', event);
|
|
||||||
}
|
|
||||||
|
|
||||||
onPointerMove(event) {
|
|
||||||
// eslint-disable-next-line no-param-reassign
|
|
||||||
event.position = this.transformCanvasPosition(event.position);
|
|
||||||
this.emit('pointerMove', event);
|
|
||||||
}
|
|
||||||
|
|
||||||
onPointerUp(event) {
|
|
||||||
// eslint-disable-next-line no-param-reassign
|
|
||||||
event.position = this.transformCanvasPosition(event.position);
|
|
||||||
this.emit('pointerUp', event);
|
|
||||||
}
|
|
||||||
|
|
||||||
onWheel(event) {
|
|
||||||
this.emit('wheel', event);
|
|
||||||
}
|
|
||||||
|
|
||||||
onWindowResize() {
|
|
||||||
if (!this.parent) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// Find the biggest axe, width or height.
|
|
||||||
const ratio = this.parent.clientWidth / this.parent.clientHeight;
|
|
||||||
const biggest = ratio > ASPECT ? 'height' : 'width';
|
|
||||||
// Key parent client size by axe.
|
|
||||||
const parentClient = {
|
|
||||||
width: this.parent.clientWidth,
|
|
||||||
height: this.parent.clientHeight,
|
|
||||||
};
|
|
||||||
['height', 'width'].forEach((axe) => {
|
|
||||||
// Biggest axe? Inherit parent axe size.
|
|
||||||
if (axe === biggest) {
|
|
||||||
this.element.style[axe] = `${parentClient[biggest]}px`;
|
|
||||||
}
|
|
||||||
// Derive height from width.
|
|
||||||
else if ('width' === biggest) {
|
|
||||||
this.element.style.height = `${parentClient[biggest] * (1 / ASPECT)}px`;
|
|
||||||
}
|
|
||||||
// Derive width from height.
|
|
||||||
else {
|
|
||||||
this.element.style.width = `${parentClient[biggest] * ASPECT}px`;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
// Precalc the transformation ratio and apply it to the UI layer.
|
|
||||||
this._transformRatio = this.size[0] / this.element.clientWidth;
|
|
||||||
const scaleFactor = 1 / this._transformRatio;
|
|
||||||
const scaleX = scaleFactor * this.scale[0];
|
|
||||||
const scaleY = scaleFactor * this.scale[1];
|
|
||||||
this.ui.style.transform = `scaleX(${scaleX}) scaleY(${scaleY})`;
|
|
||||||
this.emit('displaySizeChanged', this.displaySize);
|
|
||||||
}
|
|
||||||
|
|
||||||
removeFromDom() {
|
|
||||||
if (this.parent) {
|
|
||||||
this.parent.removeChild(this.element);
|
|
||||||
this.parent = undefined;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
renderTick(elapsed) {
|
|
||||||
super.renderTick(elapsed);
|
|
||||||
this.renderer.render(this);
|
|
||||||
if (this.camera) {
|
|
||||||
const inverseOffset = Vector.mul(
|
|
||||||
this.camera.realOffset,
|
|
||||||
Vector.scale(this.scale, -1),
|
|
||||||
);
|
|
||||||
this.position = inverseOffset;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
resolveUiRendered() {
|
|
||||||
this._uiResolve();
|
|
||||||
}
|
|
||||||
|
|
||||||
transformCanvasPosition(position) {
|
|
||||||
const rect = this.renderer.element.getBoundingClientRect();
|
|
||||||
const topLeft = [rect.x, rect.y];
|
|
||||||
const offset = Vector.sub(position, topLeft);
|
|
||||||
return Vector.div(
|
|
||||||
Vector.scale(offset, this._transformRatio),
|
|
||||||
this.scale,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
get transformRatio() {
|
|
||||||
return this._transformRatio;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -4,7 +4,7 @@ import {join} from 'path';
|
||||||
|
|
||||||
import {EntityListView} from '@avocado/entity';
|
import {EntityListView} from '@avocado/entity';
|
||||||
import {EntityController} from '@avocado/entity/persea';
|
import {EntityController} from '@avocado/entity/persea';
|
||||||
import {Stage} from '@avocado/graphics/persea';
|
import {Stage} from '@avocado/graphics';
|
||||||
import {Number} from '@avocado/persea';
|
import {Number} from '@avocado/persea';
|
||||||
import {useJsonPatcher} from '@avocado/resource/persea';
|
import {useJsonPatcher} from '@avocado/resource/persea';
|
||||||
import {
|
import {
|
||||||
|
|
|
@ -2,8 +2,7 @@ import './animation-visualization.scss';
|
||||||
|
|
||||||
import {join} from 'path';
|
import {join} from 'path';
|
||||||
|
|
||||||
import {Container} from '@avocado/graphics';
|
import {Container, Stage} from '@avocado/graphics';
|
||||||
import {Stage} from '@avocado/graphics/persea';
|
|
||||||
import {Vector} from '@avocado/math';
|
import {Vector} from '@avocado/math';
|
||||||
import {vectorPropType} from '@avocado/math/persea';
|
import {vectorPropType} from '@avocado/math/persea';
|
||||||
import {useProject} from '@avocado/persea';
|
import {useProject} from '@avocado/persea';
|
||||||
|
|
|
@ -2,8 +2,8 @@ import {
|
||||||
Color,
|
Color,
|
||||||
Container,
|
Container,
|
||||||
Primitives,
|
Primitives,
|
||||||
|
Stage,
|
||||||
} from '@avocado/graphics';
|
} from '@avocado/graphics';
|
||||||
import {Stage} from '@avocado/graphics/persea';
|
|
||||||
import {
|
import {
|
||||||
Vector,
|
Vector,
|
||||||
} from '@avocado/math';
|
} from '@avocado/math';
|
||||||
|
|
Loading…
Reference in New Issue
Block a user