Interactive NFT by @andriibakulin
<code> const ITEMS_COUNT = 30;let gEntities = []; let gSizeArea; let gDistance, gDistanceSqr;
// MagicLogic
class Entity { constructor(seq01, xOffset, yOffset, treeAngle) { this.seq01 = seq01; this.angel = this.seq01 * Math.PI * 2; this.xOffset = xOffset; this.yOffset = yOffset; this.treeAngle = treeAngle;
this.color = this.seq01 * 255;
this.alpha = 255;
this.power = new SmoothValue();
// Dynamic vars
this.drawAtX = this.drawAtY = null;
this.colorV = null;
}
next(dt)
{
this.color += dt * 128;
this.drawAtX = gSizeArea * this.xOffset;
this.drawAtY = gSizeArea * this.yOffset;
this.colorV = normalizeColorComp(this.color);
let dstOffset = 0;
if (mouseIsPressed)
{
const dstLengthSqr = (mouseX-this.drawAtX)**2 + (mouseY-this.drawAtY)**2;
if (dstLengthSqr < gDistanceSqr)
dstOffset = Math.sqrt(dstLengthSqr) / gDistance;
this.alpha = Math.max(this.alpha - dt*1000, 255 * this.power.v * 2, 32);
this.alpha = Math.min(this.alpha, 255);
}
else
{
if (this.alpha < 255)
{
this.alpha = this.alpha + dt * 255;
this.alpha = Math.min(this.alpha, 255);
}
}
if (dstOffset > 0)
this.power.update(1 - dstOffset, 2 * dt);
else
this.power.update(0, 2 * dt);
}
render()
{
resetMatrix();
translate(this.drawAtX, this.drawAtY);
if (this.power.v > 0)
this.drawBranch(0, 0, gSizeArea*0.125*this.power.v, this.angel + 360*this.seq01, 4);
strokeWeight(8);
stroke(this.colorV, 255, 255, this.alpha);
point(0,0);
}
drawBranch(x, y, len, dir, depth, level=0)
{
if (len < 10)
return;
level++;
const x2 = x + sin(dir) * len * (this.power.v+0.33);
const y2 = y + cos(dir) * len * (this.power.v+0.33);
strokeWeight(2);
stroke(this.colorV, 255, 255, this.alpha * (1 - level/(depth+1)));
line(x,y, x2,y2);
if (level < depth)
{
this.drawBranch(x2, y2, len * 0.9, dir + this.treeAngle, depth, level);
this.drawBranch(x2, y2, len * 0.9, dir - this.treeAngle, depth, level);
}
}
}
// Lifecycle
function setup() { updateConsts(); createCanvas(gSizeArea, gSizeArea); background(0);
pixelDensity(1);
frameRate(60);
colorMode(HSB, 255);
angleMode(DEGREES);
let count = ITEMS_COUNT;
for (let index=0; index<count; index++)
{
for (let level=0; level<10; level++)
{
let seq01 = index / count;
let radius = 0.10 + level*0.08;
let offsetX = 0.50 + Math.sin(seq01 * Math.PI * 2) * radius;
let offsetY = 0.50 + Math.cos(seq01 * Math.PI * 2) * radius;
let treeAngle = 25*(level+1);
if (offsetX < 0.0 || offsetY > 1) continue;
if (offsetY < 0.0 || offsetY > 1) continue;
gEntities.push(new Entity(seq01, offsetX, offsetY, treeAngle));
}
}
}
function windowResized() { updateConsts(); resizeCanvas(gSizeArea, gSizeArea); }
function draw() { const dt = deltaTime/1000;
background(0);
for (const idx in gEntities)
{
const ent = gEntities[idx];
ent.next(dt);
ent.render();
}
}
// Helpers
function updateConsts() { gSizeArea = Math.min(window.innerWidth, window.innerHeight); gDistance = gSizeArea / 3; gDistanceSqr = gDistance ** 2; }
function normalizeColorComp(v) { v %= 256; return v >= 0 ? v : v+256; }
function clamp(value, min, max) { if (value < min) return min; if (value > max) return max; return value; }
// Helpers : Smooth
function smoothValue(v0, v1, delta) { if (v0 === null) return v1;
const dv = v1 - v0;
return Math.abs(dv) <= delta ? v1 : (dv < 0 ? v0 - delta : v0 + delta);
}
class SmoothValue { constructor(v=null) { this.v = v; }
update(v, delta)
{
this.v = smoothValue(this.v, v, delta);
}
} </code>