avocado-old/packages/graphics/proton/text-node-renderer.js
2019-04-25 02:17:24 -05:00

170 lines
4.2 KiB
JavaScript

'undefined' !== typeof window ? require('web-animations-js') : '';
import {Proton} from './proton';
import {Vector} from '@avocado/math';
export class TextNode {
constructor(text) {
this.text = text;
}
applyCssRule(rule) {
for (const attribute in rule) {
const value = rule[attribute];
this.div.style[attribute] = value;
}
}
copyFrom(other) {
this.span.className = other.spanClassName();
this.span.textContent = other.text;
this.span.style.fontSize = `${other.sizeInPx()}px`;
}
createDom() {
const div = window.document.createElement('div');
div.className = 'particle';
const span = window.document.createElement('span');
this.span = span;
div.appendChild(span);
this.div = div;
}
sizeInPx() {
return 12;
}
spanClassName() {
return 'text';
}
particleStateToCssRule(particle) {
const color = particle.color;
// Position.
const transforms = [];
const position = [particle.p.x, particle.p.y];
const scale = particle.scale;
// Offset for font size.
const {style, textContent} = particle.target.span;
const fontSize = this.sizeInPx();
const length = textContent.length;
const offset = (fontSize / 2) * (particle.scale / 2);
position[0] -= offset * length;
position[1] += offset;
transforms.push(`translate(${position[0]}px, ${-position[1]}px)`);
// Angle.
const angle = 360 * (particle.rotation.x / (Math.PI * 2));
transforms.push(`rotate(${angle}deg)`);
// Scale.
transforms.push(`scale(${scale})`);
const rules = {
transform: transforms.join(' '),
};
// Alpha?
if (particle.useAlpha) {
rules.opacity = particle.alpha || 1;
}
// Color?
if (particle.useColor) {
rules.color = `rgb(${color.r * 255}, ${color.g * 255}, ${color.b * 255})`;
}
return rules;
}
}
export class TextNodeRenderer extends Proton.BaseRender {
static get freeList() {
if (!this._freeList) {
this._freeList = [];
}
return this._freeList;
}
static pushToFreeList(target) {
const freeList = this.freeList;
freeList.push(target);
}
static pullFromFreeList() {
const freeList = this.freeList;
return freeList.pop();
}
constructor(selector, stage) {
super();
this._body = new TextNode('');
this.name = 'NodeRenderer';
this.queued = [];
const promise = stage.findUiElement(selector);
promise.then((element) => {
if (this.parent) {
let freeNode;
while (freeNode = this.constructor.pullFromFreeList()) {
this.parent.removeChild(freeNode.div);
}
}
this.parent = element;
// Start with a warm cache.
if (this.parent && 0 === this.constructor.freeList.length) {
for (let i = 0; i < 500; ++i) {
const target = new TextNode();
target.createDom();
this.parent.appendChild(target.div);
target.div.style.opacity = 0;
this.constructor.pushToFreeList(target);
}
}
});
}
onParticleCreated(particle) {
// Clone global body if none was passed.
if (!particle.body) {
particle.body = this._body.clone();
}
// Pull from free list if we can.
const target = this.constructor.pullFromFreeList();
if (target) {
particle.target = target;
particle.target.div.style.opacity = 1;
}
else {
particle.target = particle.body;
particle.target.createDom();
if (this.parent) {
this.parent.appendChild(particle.target.div);
}
}
particle.target.copyFrom(particle.body);
// Queue if we don't have a parent yet.
if (!this.parent) {
this.queued.push(particle.target);
}
else {
for (let i = 0; i < this.queued.length; ++i) {
this.parent.appendChild(this.queued[i].div);
}
this.queued = [];
}
const rule = particle.target.particleStateToCssRule(particle);
particle.target.applyCssRule(rule);
}
onParticleUpdate(particle) {
const rule = particle.target.particleStateToCssRule(particle);
particle.target.applyCssRule(rule);
}
onParticleDead(particle) {
particle.target.div.style.opacity = 0;
this.constructor.pushToFreeList(particle.target);
particle.target = undefined;
}
}