Merge branch 'main' of git.broken.graphics:alina/alinamarquardt.com
# Conflicts: # public_html/functions.jsmain
commit
86580c51a9
|
@ -8,13 +8,6 @@ document.querySelectorAll('.paused-animation').forEach(el => {
|
||||||
// track players
|
// track players
|
||||||
const players = document.querySelectorAll('.track-player');
|
const players = document.querySelectorAll('.track-player');
|
||||||
|
|
||||||
function updateAllWaveformWidths() {
|
|
||||||
players.forEach(player => {
|
|
||||||
const container = player.querySelector('.track-container');
|
|
||||||
if (container) update_player_waveform_widths(container);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
document.addEventListener('DOMContentLoaded', updateAllWaveformWidths);
|
document.addEventListener('DOMContentLoaded', updateAllWaveformWidths);
|
||||||
window.addEventListener('resize', updateAllWaveformWidths);
|
window.addEventListener('resize', updateAllWaveformWidths);
|
||||||
|
|
||||||
|
@ -22,7 +15,7 @@ players.forEach(player => {
|
||||||
const trackName = player.getAttribute('data-track');
|
const trackName = player.getAttribute('data-track');
|
||||||
|
|
||||||
const button = document.createElement('div');
|
const button = document.createElement('div');
|
||||||
button.className = 'blurgreen play-button play'; // start as play
|
button.className = 'blurgreen play-button play';
|
||||||
player.appendChild(button);
|
player.appendChild(button);
|
||||||
|
|
||||||
const wrapper = document.createElement('div');
|
const wrapper = document.createElement('div');
|
||||||
|
@ -38,7 +31,6 @@ players.forEach(player => {
|
||||||
const playercontainer = player.querySelector('.track-container');
|
const playercontainer = player.querySelector('.track-container');
|
||||||
const played = player.querySelector('.track-played');
|
const played = player.querySelector('.track-played');
|
||||||
const unplayed = player.querySelector('.track-unplayed');
|
const unplayed = player.querySelector('.track-unplayed');
|
||||||
played.style.backgroundImage = `url('tracks/${trackName}-played.webp')`;
|
|
||||||
unplayed.style.backgroundImage = `url('tracks/${trackName}.webp')`;
|
unplayed.style.backgroundImage = `url('tracks/${trackName}.webp')`;
|
||||||
|
|
||||||
const audio = document.createElement('audio');
|
const audio = document.createElement('audio');
|
||||||
|
@ -46,7 +38,7 @@ players.forEach(player => {
|
||||||
player.appendChild(audio);
|
player.appendChild(audio);
|
||||||
|
|
||||||
button.addEventListener('click', () => {
|
button.addEventListener('click', () => {
|
||||||
// Pause other players immediately
|
// pause other players immediately
|
||||||
players.forEach(p => {
|
players.forEach(p => {
|
||||||
const otherAudio = p.querySelector('audio');
|
const otherAudio = p.querySelector('audio');
|
||||||
const otherBtn = p.querySelector('.play-button');
|
const otherBtn = p.querySelector('.play-button');
|
||||||
|
@ -59,8 +51,9 @@ players.forEach(player => {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Then lazy-load and play/pause current audio as before...
|
// player needs mp3 url (and played waveform image to lazyload)
|
||||||
if (!audio.src) {
|
if (!audio.src) {
|
||||||
|
played.style.backgroundImage = `url('tracks/${trackName}-played.webp')`;
|
||||||
audio.src = 'tracks/' + trackName + '.mp3';
|
audio.src = 'tracks/' + trackName + '.mp3';
|
||||||
audio.currentTime = 0;
|
audio.currentTime = 0;
|
||||||
audio.load();
|
audio.load();
|
||||||
|
@ -93,15 +86,14 @@ players.forEach(player => {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
audio.addEventListener('ended', () => {
|
audio.addEventListener('ended', () => {
|
||||||
button.classList.add('play');
|
button.classList.add('play');
|
||||||
button.classList.remove('pause');
|
button.classList.remove('pause');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// skip to playback position on waveform interaction
|
||||||
playercontainer.addEventListener('click', e => {
|
playercontainer.addEventListener('click', e => {
|
||||||
const rect = playercontainer.getBoundingClientRect(); // Use container rect!
|
const rect = playercontainer.getBoundingClientRect();
|
||||||
const clickX = e.clientX - rect.left;
|
const clickX = e.clientX - rect.left;
|
||||||
const width = rect.width;
|
const width = rect.width;
|
||||||
|
|
||||||
|
@ -112,18 +104,18 @@ players.forEach(player => {
|
||||||
|
|
||||||
audio.addEventListener('timeupdate', () => {
|
audio.addEventListener('timeupdate', () => {
|
||||||
const portion = audio.duration ? audio.currentTime / audio.duration : 0;
|
const portion = audio.duration ? audio.currentTime / audio.duration : 0;
|
||||||
update_play_position(played, unplayed, portion);
|
updatePlayPosition(played, unplayed, portion);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
function update_play_position(played_element, unplayed_element, portion) {
|
function updatePlayPosition(played_element, unplayed_element, portion) {
|
||||||
const playedPercent = portion * 100;
|
const playedPercent = portion * 100;
|
||||||
const unplayedPercent = 100 - playedPercent;
|
const unplayedPercent = 100 - playedPercent;
|
||||||
played_element.style.width = playedPercent + "%";
|
played_element.style.width = playedPercent + "%";
|
||||||
unplayed_element.style.width = unplayedPercent + "%";
|
unplayed_element.style.width = unplayedPercent + "%";
|
||||||
}
|
}
|
||||||
|
|
||||||
function update_player_waveform_widths(player_container_element) {
|
function updatePlayerWaveformWidths(player_container_element) {
|
||||||
const trackWidth = player_container_element.offsetWidth;
|
const trackWidth = player_container_element.offsetWidth;
|
||||||
const played_element = player_container_element.querySelector('.track-played');
|
const played_element = player_container_element.querySelector('.track-played');
|
||||||
const unplayed_element = player_container_element.querySelector('.track-unplayed');
|
const unplayed_element = player_container_element.querySelector('.track-unplayed');
|
||||||
|
@ -131,51 +123,49 @@ function update_player_waveform_widths(player_container_element) {
|
||||||
unplayed_element.style.backgroundSize = trackWidth + "px 80px";
|
unplayed_element.style.backgroundSize = trackWidth + "px 80px";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function updateAllWaveformWidths() { // required for proper display of played/unplayed waveforms
|
||||||
|
players.forEach(player => {
|
||||||
|
const container = player.querySelector('.track-container');
|
||||||
|
if (container) updatePlayerWaveformWidths(container);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// icosahedron
|
// icosahedron
|
||||||
// Wait for everything to load
|
|
||||||
window.onload = function() {
|
window.onload = function() {
|
||||||
// Make sure THREE is available
|
// Make sure three.js is available
|
||||||
if (typeof THREE === 'undefined') {
|
if (typeof THREE === 'undefined') {
|
||||||
console.error('THREE.js is not loaded from CDN');
|
console.error('THREE.js is not loaded from CDN');
|
||||||
|
|
||||||
// Provide visual feedfront in the container
|
|
||||||
const container = document.getElementById('icosahedron-container');
|
|
||||||
container.innerHTML = '<div style="color: red; padding: 10px;">THREE.js not loaded</div>';
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize the scene
|
// init scene
|
||||||
const container = document.getElementById('icosahedron-container');
|
const container = document.getElementById('icosahedron-container');
|
||||||
const containerWidth = container.clientWidth;
|
const containerWidth = container.clientWidth;
|
||||||
const containerHeight = container.clientHeight;
|
const containerHeight = container.clientHeight;
|
||||||
|
|
||||||
// Setup renderer
|
// setup renderer
|
||||||
const renderer = new THREE.WebGLRenderer({
|
const renderer = new THREE.WebGLRenderer({
|
||||||
antialias: true,
|
antialias: true,
|
||||||
alpha: true
|
alpha: true
|
||||||
});
|
});
|
||||||
renderer.setSize(containerWidth, containerHeight);
|
renderer.setSize(containerWidth, containerHeight);
|
||||||
renderer.setPixelRatio(window.devicePixelRatio);
|
renderer.setPixelRatio(window.devicePixelRatio);
|
||||||
renderer.setClearColor(0x000000, 0); // Transparent frontground
|
renderer.setClearColor(0x000000, 0); // transparent
|
||||||
container.appendChild(renderer.domElement);
|
container.appendChild(renderer.domElement);
|
||||||
|
|
||||||
// Setup scene
|
// setup scene and camera
|
||||||
const scene = new THREE.Scene();
|
const scene = new THREE.Scene();
|
||||||
|
|
||||||
// Setup camera
|
|
||||||
const camera = new THREE.PerspectiveCamera(26, containerWidth / containerHeight, 0.1, 100);
|
const camera = new THREE.PerspectiveCamera(26, containerWidth / containerHeight, 0.1, 100);
|
||||||
camera.position.z = 4;
|
camera.position.z = 4;
|
||||||
|
|
||||||
// Create icosahedron with manually defined vertices
|
// icosahedron with manually defined vertices
|
||||||
function createIcosahedron(radius) {
|
function createIcosahedron(radius) {
|
||||||
// Golden ratio for icosahedron vertices
|
// golden ratio
|
||||||
const t = (1 + Math.sqrt(5)) / 2;
|
const t = (1 + Math.sqrt(5)) / 2;
|
||||||
|
// normalize radius
|
||||||
// Normalize radius
|
|
||||||
const normRadius = radius / Math.sqrt(1 + t * t);
|
const normRadius = radius / Math.sqrt(1 + t * t);
|
||||||
|
|
||||||
// Create vertices
|
|
||||||
const vertices = [
|
const vertices = [
|
||||||
[-1, t, 0],
|
[-1, t, 0],
|
||||||
[1, t, 0],
|
[1, t, 0],
|
||||||
|
@ -191,7 +181,7 @@ window.onload = function() {
|
||||||
[-t, 0, 1]
|
[-t, 0, 1]
|
||||||
].map(v => new THREE.Vector3(v[0] * normRadius, v[1] * normRadius, v[2] * normRadius));
|
].map(v => new THREE.Vector3(v[0] * normRadius, v[1] * normRadius, v[2] * normRadius));
|
||||||
|
|
||||||
// Define edges (pairs of vertex indices)
|
// edges (pairs of vertex indices)
|
||||||
const edges = [
|
const edges = [
|
||||||
[0, 11],
|
[0, 11],
|
||||||
[0, 5],
|
[0, 5],
|
||||||
|
@ -221,8 +211,7 @@ window.onload = function() {
|
||||||
[6, 10],
|
[6, 10],
|
||||||
[7, 8],
|
[7, 8],
|
||||||
[7, 10],
|
[7, 10],
|
||||||
[8, 9], /*[8, 10],*/
|
[8, 9],
|
||||||
/*[9, 11],*/
|
|
||||||
[10, 11]
|
[10, 11]
|
||||||
];
|
];
|
||||||
|
|
||||||
|
@ -232,7 +221,7 @@ window.onload = function() {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Convert vertex indices to actual vertices and create a line
|
// convert vertex indices to actual vertices and create a line
|
||||||
function createLine(icosa, edgeIndex, material) {
|
function createLine(icosa, edgeIndex, material) {
|
||||||
const startIndex = icosa.edges[edgeIndex][0];
|
const startIndex = icosa.edges[edgeIndex][0];
|
||||||
const endIndex = icosa.edges[edgeIndex][1];
|
const endIndex = icosa.edges[edgeIndex][1];
|
||||||
|
@ -248,49 +237,44 @@ window.onload = function() {
|
||||||
return new THREE.Line(geometry, material);
|
return new THREE.Line(geometry, material);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Function to create the dual line rendering (thin back lines, thick front lines)
|
// create thin back lines, thick front lines
|
||||||
function createDualLineRendering(radius) {
|
function createDualLineRendering(radius) {
|
||||||
const group = new THREE.Group();
|
const group = new THREE.Group();
|
||||||
const icosa = createIcosahedron(radius);
|
const icosa = createIcosahedron(radius);
|
||||||
|
|
||||||
// Create materials
|
|
||||||
const backMaterial = new THREE.MeshBasicMaterial({
|
const backMaterial = new THREE.MeshBasicMaterial({
|
||||||
color: 0x63a8b8, // green for back lines
|
color: 0x63a8b8,
|
||||||
depthTest: false, // Don't test depth for back lines
|
depthTest: false, // don't test depth for back lines
|
||||||
transparent: true,
|
transparent: true,
|
||||||
opacity: 0.4,
|
opacity: 0.4,
|
||||||
side: THREE.DoubleSide
|
side: THREE.DoubleSide
|
||||||
});
|
});
|
||||||
|
|
||||||
const frontMaterial = new THREE.MeshBasicMaterial({
|
const frontMaterial = new THREE.MeshBasicMaterial({
|
||||||
color: 0xffffff, // white for front lines
|
color: 0xffffff,
|
||||||
|
depthTest: true, // depth test for front lines
|
||||||
transparent: true,
|
transparent: true,
|
||||||
depthTest: true, // Use depth test for front lines
|
|
||||||
side: THREE.DoubleSide
|
side: THREE.DoubleSide
|
||||||
});
|
});
|
||||||
|
|
||||||
// For each edge, create:
|
// cylinders for each edge
|
||||||
// 1. A thin cylindrical tube with back material that ignores depth
|
const backRadius = 0.025;
|
||||||
// 2. A cylindrical tube with front material that uses depth testing
|
const frontRadius = 0.035;
|
||||||
const backRadius = 0.025; // thin for back lines
|
|
||||||
const frontRadius = 0.035; // thick for front lines
|
|
||||||
|
|
||||||
icosa.edges.forEach((edge, index) => {
|
icosa.edges.forEach((edge, index) => {
|
||||||
const start = icosa.vertices[edge[0]];
|
const start = icosa.vertices[edge[0]];
|
||||||
const end = icosa.vertices[edge[1]];
|
const end = icosa.vertices[edge[1]];
|
||||||
|
|
||||||
// Create back line (thick)
|
|
||||||
const backLine = createCylinderBetweenPoints(start, end, backRadius, 1, backMaterial);
|
const backLine = createCylinderBetweenPoints(start, end, backRadius, 1, backMaterial);
|
||||||
backLine.renderOrder = 1; // Render after front lines
|
backLine.renderOrder = 1;
|
||||||
group.add(backLine);
|
group.add(backLine);
|
||||||
|
|
||||||
// Create front line (thin)
|
|
||||||
const frontLine = createCylinderBetweenPoints(start, end, frontRadius, 0.95, frontMaterial);
|
const frontLine = createCylinderBetweenPoints(start, end, frontRadius, 0.95, frontMaterial);
|
||||||
frontLine.renderOrder = 3; // Render before back lines
|
frontLine.renderOrder = 3;
|
||||||
group.add(frontLine);
|
group.add(frontLine);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Create faces for depth testing (invisible)
|
// invisible face material for depth testing
|
||||||
const faceMaterial = new THREE.MeshBasicMaterial({
|
const faceMaterial = new THREE.MeshBasicMaterial({
|
||||||
color: 0xffffff,
|
color: 0xffffff,
|
||||||
transparent: true,
|
transparent: true,
|
||||||
|
@ -299,7 +283,7 @@ window.onload = function() {
|
||||||
side: THREE.DoubleSide
|
side: THREE.DoubleSide
|
||||||
});
|
});
|
||||||
|
|
||||||
// Create faces for internal icosahedron
|
// internal icosahedra material
|
||||||
const internalMaterial = new THREE.MeshBasicMaterial({
|
const internalMaterial = new THREE.MeshBasicMaterial({
|
||||||
color: 0x63a8b8,
|
color: 0x63a8b8,
|
||||||
depthTest: false,
|
depthTest: false,
|
||||||
|
@ -308,7 +292,7 @@ window.onload = function() {
|
||||||
side: THREE.DoubleSide
|
side: THREE.DoubleSide
|
||||||
});
|
});
|
||||||
|
|
||||||
// Define faces of icosahedron (each is a triangle of vertex indices)
|
// triangles of vertex indices
|
||||||
const faces = [
|
const faces = [
|
||||||
[0, 11, 5],
|
[0, 11, 5],
|
||||||
[0, 5, 1],
|
[0, 5, 1],
|
||||||
|
@ -332,7 +316,7 @@ window.onload = function() {
|
||||||
[9, 8, 1]
|
[9, 8, 1]
|
||||||
];
|
];
|
||||||
|
|
||||||
// Create invisible faces for depth testing
|
// create depth testing and internal icosahedra
|
||||||
faces.forEach(face => {
|
faces.forEach(face => {
|
||||||
const geometry = new THREE.BufferGeometry();
|
const geometry = new THREE.BufferGeometry();
|
||||||
const positions = new Float32Array([
|
const positions = new Float32Array([
|
||||||
|
@ -345,50 +329,46 @@ window.onload = function() {
|
||||||
|
|
||||||
const faceMesh = new THREE.Mesh(geometry, faceMaterial);
|
const faceMesh = new THREE.Mesh(geometry, faceMaterial);
|
||||||
faceMesh.scale.set(0.975, 0.975, 0.975);
|
faceMesh.scale.set(0.975, 0.975, 0.975);
|
||||||
faceMesh.renderOrder = 0; // Render first for depth buffer
|
faceMesh.renderOrder = 0;
|
||||||
group.add(faceMesh);
|
group.add(faceMesh);
|
||||||
|
|
||||||
|
// stack internal icosahedra for volumetric effect
|
||||||
const internalMesh1 = new THREE.Mesh(geometry, internalMaterial);
|
const internalMesh1 = new THREE.Mesh(geometry, internalMaterial);
|
||||||
const internalMesh2 = new THREE.Mesh(geometry, internalMaterial);
|
const internalMesh2 = new THREE.Mesh(geometry, internalMaterial);
|
||||||
const internalMesh3 = new THREE.Mesh(geometry, internalMaterial);
|
const internalMesh3 = new THREE.Mesh(geometry, internalMaterial);
|
||||||
const internalMesh4 = new THREE.Mesh(geometry, internalMaterial);
|
const internalMesh4 = new THREE.Mesh(geometry, internalMaterial);
|
||||||
const internalMesh5 = new THREE.Mesh(geometry, internalMaterial);
|
const internalMesh5 = new THREE.Mesh(geometry, internalMaterial);
|
||||||
internalMesh1.scale.set(0.85, 0.85, 0.85);
|
internalMesh1.scale.set(0.85, 0.85, 0.85);
|
||||||
internalMesh1.renderOrder = 1; // Render between front and back
|
internalMesh1.renderOrder = 1;
|
||||||
group.add(internalMesh1);
|
group.add(internalMesh1);
|
||||||
internalMesh2.scale.set(0.65, 0.65, 0.65);
|
internalMesh2.scale.set(0.65, 0.65, 0.65);
|
||||||
internalMesh2.renderOrder = 1; // Render between front and back
|
internalMesh2.renderOrder = 1;
|
||||||
group.add(internalMesh2);
|
group.add(internalMesh2);
|
||||||
internalMesh3.scale.set(0.5, 0.5, 0.5);
|
internalMesh3.scale.set(0.5, 0.5, 0.5);
|
||||||
internalMesh3.renderOrder = 1; // Render between front and back
|
internalMesh3.renderOrder = 1;
|
||||||
group.add(internalMesh3);
|
group.add(internalMesh3);
|
||||||
internalMesh4.scale.set(0.4, 0.4, 0.4);
|
internalMesh4.scale.set(0.4, 0.4, 0.4);
|
||||||
internalMesh4.renderOrder = 1; // Render between front and back
|
internalMesh4.renderOrder = 1;
|
||||||
group.add(internalMesh4);
|
group.add(internalMesh4);
|
||||||
internalMesh5.scale.set(0.3, 0.3, 0.3);
|
internalMesh5.scale.set(0.3, 0.3, 0.3);
|
||||||
internalMesh5.renderOrder = 1; // Render between front and back
|
internalMesh5.renderOrder = 1;
|
||||||
group.add(internalMesh5);
|
group.add(internalMesh5);
|
||||||
});
|
});
|
||||||
|
|
||||||
return group;
|
return group;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Function to create a cylinder between two points
|
|
||||||
function createCylinderBetweenPoints(pointX, pointY, radius, lengthmultiplier, material) {
|
function createCylinderBetweenPoints(pointX, pointY, radius, lengthmultiplier, material) {
|
||||||
// Direction from pointX to pointY
|
|
||||||
const direction = new THREE.Vector3().subVectors(pointY, pointX);
|
const direction = new THREE.Vector3().subVectors(pointY, pointX);
|
||||||
const length = direction.length();
|
const length = direction.length();
|
||||||
|
|
||||||
// Create cylinder
|
|
||||||
const geometry = new THREE.CylinderGeometry(radius, radius, length * lengthmultiplier, 4, 1);
|
const geometry = new THREE.CylinderGeometry(radius, radius, length * lengthmultiplier, 4, 1);
|
||||||
|
|
||||||
// By default, cylinder is along Y-axis, so rotate it
|
// default cylinder is along y-axis, rotate it
|
||||||
geometry.rotateX(Math.PI / 2);
|
geometry.rotateX(Math.PI / 2);
|
||||||
|
|
||||||
// Create mesh
|
|
||||||
const cylinder = new THREE.Mesh(geometry, material);
|
const cylinder = new THREE.Mesh(geometry, material);
|
||||||
|
|
||||||
// Position and orient cylinder
|
// position and orient cylinder
|
||||||
const midpoint = new THREE.Vector3().addVectors(pointX, pointY).multiplyScalar(0.5);
|
const midpoint = new THREE.Vector3().addVectors(pointX, pointY).multiplyScalar(0.5);
|
||||||
cylinder.position.copy(midpoint);
|
cylinder.position.copy(midpoint);
|
||||||
cylinder.lookAt(pointY);
|
cylinder.lookAt(pointY);
|
||||||
|
@ -396,11 +376,11 @@ window.onload = function() {
|
||||||
return cylinder;
|
return cylinder;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create our icosahedron
|
// create icosahedron
|
||||||
const icosahedronGroup = createDualLineRendering(0.85);
|
const icosahedronGroup = createDualLineRendering(0.85);
|
||||||
scene.add(icosahedronGroup);
|
scene.add(icosahedronGroup);
|
||||||
|
|
||||||
// Animation state
|
// animation state
|
||||||
let animating = true;
|
let animating = true;
|
||||||
|
|
||||||
let lastFrameTime = 0;
|
let lastFrameTime = 0;
|
||||||
|
@ -408,7 +388,6 @@ window.onload = function() {
|
||||||
const animMultiplier = 30 / targetFPS;
|
const animMultiplier = 30 / targetFPS;
|
||||||
const frameDuration = 1000 / targetFPS;
|
const frameDuration = 1000 / targetFPS;
|
||||||
|
|
||||||
// Animation function
|
|
||||||
function animate(now) {
|
function animate(now) {
|
||||||
requestAnimationFrame(animate);
|
requestAnimationFrame(animate);
|
||||||
|
|
||||||
|
@ -417,7 +396,6 @@ window.onload = function() {
|
||||||
|
|
||||||
lastFrameTime = now;
|
lastFrameTime = now;
|
||||||
|
|
||||||
// Rotate if animation is enabled
|
|
||||||
if (animating) {
|
if (animating) {
|
||||||
icosahedronGroup.rotation.x -= 0.002 * animMultiplier;
|
icosahedronGroup.rotation.x -= 0.002 * animMultiplier;
|
||||||
icosahedronGroup.rotation.y += 0.004 * animMultiplier;
|
icosahedronGroup.rotation.y += 0.004 * animMultiplier;
|
||||||
|
@ -427,12 +405,9 @@ window.onload = function() {
|
||||||
renderer.render(scene, camera);
|
renderer.render(scene, camera);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Start animation
|
|
||||||
animate();
|
animate();
|
||||||
|
|
||||||
// Handle window resize
|
|
||||||
window.addEventListener('resize', function() {
|
window.addEventListener('resize', function() {
|
||||||
// Only update if container dimensions change
|
|
||||||
const newWidth = container.clientWidth;
|
const newWidth = container.clientWidth;
|
||||||
const newHeight = container.clientHeight;
|
const newHeight = container.clientHeight;
|
||||||
|
|
||||||
|
@ -443,5 +418,5 @@ window.onload = function() {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
console.log('Icosahedron wireframe created successfully');
|
// console.log('icosahedron wireframe created successfully');
|
||||||
};
|
};
|
||||||
|
|
|
@ -12,6 +12,8 @@
|
||||||
|
|
||||||
<link rel="stylesheet" href="style.css" fetchpriority="high" />
|
<link rel="stylesheet" href="style.css" fetchpriority="high" />
|
||||||
|
|
||||||
|
<link rel="me" href="https://tech.lgbt/@0" />
|
||||||
|
|
||||||
<link rel="preload" as="image" type="image/webp" href="img/bg.webp" fetchpriority="high" />
|
<link rel="preload" as="image" type="image/webp" href="img/bg.webp" fetchpriority="high" />
|
||||||
<link rel="preload" as="image" type="image/webp" href="img/bg_blur.webp" />
|
<link rel="preload" as="image" type="image/webp" href="img/bg_blur.webp" />
|
||||||
<link rel="preload" as="image" type="image/svg+xml" href="img/track-pause.svg" />
|
<link rel="preload" as="image" type="image/svg+xml" href="img/track-pause.svg" />
|
||||||
|
@ -141,7 +143,7 @@
|
||||||
<div class="paused-animation" style="aspect-ratio: 900 / 528;">
|
<div class="paused-animation" style="aspect-ratio: 900 / 528;">
|
||||||
<div class="play-btn"></div>
|
<div class="play-btn"></div>
|
||||||
<img class="poster-frame" src="img/experiments/lf-poster.webp" width="680" height="400" alt="LF 3d animation poster-frame" />
|
<img class="poster-frame" src="img/experiments/lf-poster.webp" width="680" height="400" alt="LF 3d animation poster-frame" />
|
||||||
<img class="anim" src="img/experiments/lf-anim.gif" width="680" height="400" alt="LF 3d animation" />
|
<img class="anim" src="img/experiments/lf-anim.gif" width="680" height="400" alt="LF 3d animation" loading="lazy" />
|
||||||
</div>
|
</div>
|
||||||
<p>a specific animation style I had in mind for my music persona lastfuture required objects with transparent flat faces, integer snapping, controlled image noise and visual artefacts. to solve this I wrote the tiny 3d engine <span class="nowrap">“<a href="https://git.broken.graphics/alina/3d-simple/" target="_blank">3d simple</a>”</span> that can create this animation style then render it out as a gif.</p>
|
<p>a specific animation style I had in mind for my music persona lastfuture required objects with transparent flat faces, integer snapping, controlled image noise and visual artefacts. to solve this I wrote the tiny 3d engine <span class="nowrap">“<a href="https://git.broken.graphics/alina/3d-simple/" target="_blank">3d simple</a>”</span> that can create this animation style then render it out as a gif.</p>
|
||||||
</article>
|
</article>
|
||||||
|
|
Loading…
Reference in New Issue