170 lines
4.2 KiB
JavaScript
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;
|
|
}
|
|
|
|
}
|