[claude] Add floating commit banner sprites (#116) #195
119
app.js
119
app.js
@@ -258,6 +258,31 @@ function animate() {
|
||||
if (photoMode) {
|
||||
orbitControls.update();
|
||||
}
|
||||
|
||||
// Animate floating commit banners
|
||||
const FADE_DUR = 1.5;
|
||||
commitBanners.forEach(banner => {
|
||||
const ud = banner.userData;
|
||||
if (ud.spawnTime === null) {
|
||||
if (elapsed < ud.startDelay) return;
|
||||
ud.spawnTime = elapsed;
|
||||
}
|
||||
const age = elapsed - ud.spawnTime;
|
||||
let opacity;
|
||||
if (age < FADE_DUR) {
|
||||
opacity = age / FADE_DUR;
|
||||
} else if (age < ud.lifetime - FADE_DUR) {
|
||||
opacity = 1;
|
||||
} else if (age < ud.lifetime) {
|
||||
opacity = (ud.lifetime - age) / FADE_DUR;
|
||||
} else {
|
||||
ud.spawnTime = elapsed + 3;
|
||||
opacity = 0;
|
||||
}
|
||||
banner.material.opacity = opacity * 0.85;
|
||||
banner.position.y = ud.baseY + Math.sin(elapsed * ud.floatSpeed + ud.floatPhase) * 0.4;
|
||||
});
|
||||
|
||||
composer.render();
|
||||
}
|
||||
|
||||
@@ -404,3 +429,97 @@ document.addEventListener('keydown', (e) => {
|
||||
window.addEventListener('beforeunload', () => {
|
||||
wsClient.disconnect();
|
||||
});
|
||||
|
||||
// === COMMIT BANNERS ===
|
||||
const commitBanners = [];
|
||||
|
||||
/**
|
||||
* Creates a canvas texture for a commit banner.
|
||||
* @param {string} hash - Short commit hash
|
||||
* @param {string} message - Commit subject line
|
||||
* @returns {THREE.CanvasTexture}
|
||||
*/
|
||||
function createCommitTexture(hash, message) {
|
||||
const canvas = document.createElement('canvas');
|
||||
canvas.width = 512;
|
||||
canvas.height = 64;
|
||||
const ctx = canvas.getContext('2d');
|
||||
|
||||
ctx.fillStyle = 'rgba(0, 0, 16, 0.75)';
|
||||
ctx.fillRect(0, 0, 512, 64);
|
||||
|
||||
ctx.strokeStyle = '#4488ff';
|
||||
ctx.lineWidth = 1;
|
||||
ctx.strokeRect(0.5, 0.5, 511, 63);
|
||||
|
||||
ctx.font = 'bold 11px "Courier New", monospace';
|
||||
ctx.fillStyle = '#4488ff';
|
||||
ctx.fillText(hash, 10, 20);
|
||||
|
||||
ctx.font = '12px "Courier New", monospace';
|
||||
ctx.fillStyle = '#ccd6f6';
|
||||
const displayMsg = message.length > 54 ? message.slice(0, 54) + '\u2026' : message;
|
||||
ctx.fillText(displayMsg, 10, 46);
|
||||
|
||||
return new THREE.CanvasTexture(canvas);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches recent commits and spawns floating banner sprites.
|
||||
*/
|
||||
async function initCommitBanners() {
|
||||
let commits;
|
||||
try {
|
||||
const res = await fetch(
|
||||
'http://143.198.27.163:3000/api/v1/repos/Timmy_Foundation/the-nexus/commits?limit=5',
|
||||
{ headers: { 'Authorization': 'token dc0517a965226b7a0c5ffdd961b1ba26521ac592' } }
|
||||
);
|
||||
if (!res.ok) throw new Error('fetch failed');
|
||||
const data = await res.json();
|
||||
commits = data.map(/** @type {(c: any) => {hash: string, message: string}} */ c => ({
|
||||
hash: c.sha.slice(0, 7),
|
||||
message: c.commit.message.split('\n')[0],
|
||||
}));
|
||||
} catch {
|
||||
commits = [
|
||||
{ hash: 'a1b2c3d', message: 'feat: depth of field effect on distant objects' },
|
||||
{ hash: 'e4f5g6h', message: 'feat: photo mode with orbit controls' },
|
||||
{ hash: 'i7j8k9l', message: 'feat: sovereignty easter egg animation' },
|
||||
{ hash: 'm0n1o2p', message: 'feat: overview mode bird\'s-eye view' },
|
||||
{ hash: 'q3r4s5t', message: 'feat: star field and constellation lines' },
|
||||
];
|
||||
}
|
||||
|
||||
const spreadX = [-7, -3.5, 0, 3.5, 7];
|
||||
const spreadY = [1.0, -1.5, 2.2, -0.8, 1.6];
|
||||
const spreadZ = [-1.5, -2.5, -1.0, -2.0, -1.8];
|
||||
|
||||
commits.forEach((commit, i) => {
|
||||
const texture = createCommitTexture(commit.hash, commit.message);
|
||||
const material = new THREE.SpriteMaterial({
|
||||
map: texture,
|
||||
transparent: true,
|
||||
opacity: 0,
|
||||
depthWrite: false,
|
||||
});
|
||||
const sprite = new THREE.Sprite(material);
|
||||
sprite.scale.set(12, 1.5, 1);
|
||||
sprite.position.set(
|
||||
spreadX[i % spreadX.length],
|
||||
spreadY[i % spreadY.length],
|
||||
spreadZ[i % spreadZ.length]
|
||||
);
|
||||
sprite.userData = {
|
||||
baseY: spreadY[i % spreadY.length],
|
||||
floatPhase: (i / commits.length) * Math.PI * 2,
|
||||
floatSpeed: 0.25 + i * 0.07,
|
||||
startDelay: i * 2.5,
|
||||
lifetime: 12 + i * 1.5,
|
||||
spawnTime: /** @type {number|null} */ (null),
|
||||
};
|
||||
scene.add(sprite);
|
||||
commitBanners.push(sprite);
|
||||
});
|
||||
}
|
||||
|
||||
initCommitBanners();
|
||||
|
||||
Reference in New Issue
Block a user