optimize: fastest possible precalc web animation?
This commit is contained in:
parent
950c598c5a
commit
177b719ef4
|
@ -7,23 +7,27 @@ import {Vector} from '@avocado/math';
|
|||
export class TextNode {
|
||||
|
||||
constructor(text) {
|
||||
this.parent = null;
|
||||
const wrapper = window.document.createElement('div');
|
||||
this.wrapper = wrapper;
|
||||
wrapper.style.position = 'absolute';
|
||||
this.text = text;
|
||||
}
|
||||
|
||||
createDom() {
|
||||
const div = window.document.createElement('div');
|
||||
wrapper.appendChild(div);
|
||||
div.className = 'particle';
|
||||
const span = window.document.createElement('span');
|
||||
span.className = 'text';
|
||||
span.textContent = text;
|
||||
span.className = this.spanClassName();
|
||||
span.textContent = this.text;
|
||||
span.style.fontSize = `${this.sizeInPx()}px`;
|
||||
this.span = span;
|
||||
div.appendChild(span);
|
||||
this.div = div;
|
||||
}
|
||||
|
||||
copyFrom(other) {
|
||||
this.span.textContent = other.span.textContent;
|
||||
sizeInPx() {
|
||||
return 12;
|
||||
}
|
||||
|
||||
spanClassName() {
|
||||
return 'text';
|
||||
}
|
||||
|
||||
particleStateToCssRules(particle) {
|
||||
|
@ -34,7 +38,7 @@ export class TextNode {
|
|||
const scale = particle.scale;
|
||||
// Offset for font size.
|
||||
const {style, textContent} = particle.target.span;
|
||||
const fontSize = parseFloat(style.fontSize);
|
||||
const fontSize = this.sizeInPx();
|
||||
const length = textContent.length;
|
||||
const offset = (fontSize / 2) * (particle.scale / 2);
|
||||
position[0] -= offset * length;
|
||||
|
@ -63,10 +67,6 @@ export class TextNode {
|
|||
|
||||
export class TextNodeRenderer extends Proton.BaseRender {
|
||||
|
||||
static clearFreeList() {
|
||||
this._freeList = [];
|
||||
}
|
||||
|
||||
static get freeList() {
|
||||
if (!this._freeList) {
|
||||
this._freeList = [];
|
||||
|
@ -74,6 +74,16 @@ export class TextNodeRenderer extends Proton.BaseRender {
|
|||
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('');
|
||||
|
@ -82,48 +92,61 @@ export class TextNodeRenderer extends Proton.BaseRender {
|
|||
const promise = stage.findUiElement(selector);
|
||||
promise.then((element) => {
|
||||
if (this.parent) {
|
||||
const freeList = this.constructor.freeList;
|
||||
while (freeList.length > 0) {
|
||||
const node = freeList.pop();
|
||||
this.parent.removeChild(node);
|
||||
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 < 200; ++i) {
|
||||
const target = new TextNode();
|
||||
target.createDom();
|
||||
this.parent.appendChild(target.div);
|
||||
target.div.style.opacity = 0;
|
||||
this.constructor.pushToFreeList(target);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
onParticleCreated(particle) {
|
||||
const freeList = this.constructor.freeList;
|
||||
// Clone global body if none was passed.
|
||||
if (!particle.body) {
|
||||
particle.body = this._body.clone();
|
||||
}
|
||||
if (freeList.length > 0) {
|
||||
particle.target = freeList.pop();
|
||||
particle.target.copyFrom(particle.body);
|
||||
// Pull from free list if we can.
|
||||
const target = this.constructor.pullFromFreeList();
|
||||
if (target) {
|
||||
particle.target = target;
|
||||
particle.target.span.className = particle.body.spanClassName();
|
||||
particle.target.span.textContent = particle.body.text;
|
||||
particle.target.span.style.fontSize = `${particle.body.sizeInPx()}px`;
|
||||
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.wrapper.style.opacity = 0;
|
||||
// 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].wrapper);
|
||||
this.queued[i].parent = this.parent;
|
||||
this.queued[i].wrapper.style.opacity = 1;
|
||||
this.parent.appendChild(this.queued[i].div);
|
||||
}
|
||||
this.queued = [];
|
||||
if (!particle.target.parent) {
|
||||
this.parent.appendChild(particle.target.wrapper);
|
||||
particle.target.parent = this.parent;
|
||||
}
|
||||
particle.target.wrapper.style.opacity = 1;
|
||||
}
|
||||
// Simulate the particle in discrete steps to build keyframes for
|
||||
// animation.
|
||||
const life = particle.life;
|
||||
particle.life = life * 1.05;
|
||||
const stepsPerSec = 30;
|
||||
particle.life = life * 1.01;
|
||||
const stepsPerSec = 10;
|
||||
let stepCount = life * stepsPerSec;
|
||||
const keyframes = [];
|
||||
const damping = 1 - .006;
|
||||
|
@ -136,12 +159,6 @@ export class TextNodeRenderer extends Proton.BaseRender {
|
|||
Proton.integrator.integrate(particle, time, damping);
|
||||
keyframes.push(keyframe);
|
||||
}
|
||||
// Immediately apply the first keyframe.
|
||||
const firstKeyframe = keyframes[0];
|
||||
for (const attribute in firstKeyframe) {
|
||||
const value = firstKeyframe[attribute];
|
||||
particle.target.div.style[attribute] = value;
|
||||
}
|
||||
particle.life = Infinity;
|
||||
// Start animation.
|
||||
const animation = particle.target.div.animate(keyframes, {
|
||||
|
@ -149,9 +166,8 @@ export class TextNodeRenderer extends Proton.BaseRender {
|
|||
});
|
||||
animation.addEventListener('finish', () => {
|
||||
particle.dead = true;
|
||||
particle.target.wrapper.style.opacity = 0;
|
||||
const freeList = this.constructor.freeList;
|
||||
freeList.push(particle.target);
|
||||
particle.target.div.style.opacity = 0;
|
||||
this.constructor.pushToFreeList(particle.target);
|
||||
particle.target = undefined;
|
||||
});
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user