import {Proton} from './proton'; import {Vector} from '@avocado/math'; export class TextNode { constructor(text) { this.lastAlpha = undefined; this.lastColor = undefined; this.lastPosition = undefined; this.lastAngle = undefined; this.lastScale = undefined; this.parent = null; const wrapper = window.document.createElement('div'); this.wrapper = wrapper; wrapper.style.position = 'absolute'; const div = window.document.createElement('div'); wrapper.appendChild(div); div.className = 'particle'; const span = window.document.createElement('span'); span.className = 'text'; span.textContent = text; this.span = span; div.appendChild(span); this.div = div; } copyFrom(other) { this.span.textContent = other.span.textContent; } update(particle, stage) { const {div, span} = this; if (particle.useAlpha) { const alpha = Math.floor(256 * particle.alpha) / 256; if (this.lastAlpha !== alpha) { div.style.opacity = alpha; this.lastAlpha = alpha; } } if (particle.useColor) { const color = particle.color; color.r = Math.floor(255 * color.r) / 255; color.g = Math.floor(255 * color.g) / 255; color.b = Math.floor(255 * color.b) / 255; if ( !this.lastColor || this.lastColor.r !== color.r || this.lastColor.g !== color.g || this.lastColor.b !== color.b ) { div.style.color = `rgb( ${color.r * 255}, ${color.g * 255}, ${color.b * 255} )`; this.lastColor = { r: color.r, g: color.g, b: color.b, }; } } let transformsChanged; const camera = stage.camera; const position = [particle.p.x, particle.p.y]; if (camera) { const realOffset = camera.realOffset; position[0] -= realOffset[0]; position[1] += realOffset[1]; } position[0] = Math.round(position[0] * 4) / 4; position[1] = Math.round(position[1] * 4) / 4; if ( !this.lastPosition || this.lastPosition[0] !== position[0] || this.lastPosition[1] !== position[1] ) { transformsChanged = true; this.lastPosition = position; } const angle = Math.floor(1440 * (particle.rotation.x / (Math.PI * 2))) / 1440; if (this.lastAngle !== angle) { const normalAngle = (360 + angle * 360) % 360; transformsChanged = true; this.lastAngle = angle; } let scale = particle.scale; scale += particle.p.z > 0 ? particle.p.z / 70 : 0; scale = Math.floor(100 * scale) / 100; if (this.lastScale !== scale) { transformsChanged = true; const spanTranslate = 50 * (1 / particle.scale); span.style.transform = `translate(-${spanTranslate}%, -${spanTranslate}%)`; this.lastScale = scale; } if (transformsChanged) { const transforms = []; const normalAngle = (360 + this.lastAngle * 360) % 360; transforms.push(`translate(${this.lastPosition[0]}px, ${-this.lastPosition[1]}px)`); transforms.push(`rotate(${normalAngle}deg)`); transforms.push(`scale(${this.lastScale})`); div.style.transform = transforms.join(' '); } } } export class TextNodeRenderer extends Proton.BaseRender { static get freeList() { if (!this._freeList) { this._freeList = []; } return this._freeList; } constructor(selector, stage) { super(); this._body = new TextNode(''); this.freeList = []; this.name = 'NodeRenderer'; this.queued = []; this.stage = stage; const promise = stage.findUiElement(selector); promise.then((element) => { this.parent = element; }); } onParticleCreated(particle) { const freeList = this.constructor.freeList; if (freeList.length > 0) { particle.target = freeList.pop(); // ... if (particle.body) { particle.target.copyFrom(particle.body); } } else { if (!particle.body) { particle.body = this._body.clone(); } particle.target = particle.body; particle.target.wrapper.style.opacity = 0; } if (!this.parent) { this.queued.push(particle.target); } else { for (let i = 0; i < this.queued.length; ++i) { this.parent.appendChild(this.queued[i].wrapper); this.queued[i].parent = this.parent; this.queued[i].wrapper.style.opacity = 1; } this.queued = []; if (!particle.target.parent) { this.parent.appendChild(particle.target.wrapper); particle.target.parent = this.parent; } particle.target.wrapper.style.opacity = 1; } } onParticleUpdate(particle) { if (!particle.target) { return; } particle.target.update(particle, this.stage); } onParticleDead(particle) { particle.target.wrapper.style.opacity = 0; const freeList = this.constructor.freeList; freeList.push(particle.target); particle.target = undefined; } }