This commit is contained in:
2026-08-08 16:55:26 +02:00
parent ee5729e4b7
commit ba513bba61
30 changed files with 380 additions and 217 deletions
+64 -3
View File
@@ -3,7 +3,8 @@
const canvas = document.getElementById('bgCanvas');
const ctx = canvas.getContext('2d');
const COLORS = ['#e63946', '#f4722b', '#ffbe0b', '#ff006e', '#e07b39', '#ffd166'];
const COLORS = ['#ffd84d', '#8cbcff', '#ff9fca', '#a9e76c', '#b99cff', '#ff9d57'];
const INK = '#171717';
const COUNT = 1000; // number of sprinkles
const MIN_LEN = 12; // px
const MAX_LEN = 18; // px
@@ -63,10 +64,14 @@
ctx.beginPath();
ctx.moveTo(0, -s.len / 2);
ctx.lineTo(0, s.len / 2);
ctx.strokeStyle = s.color;
ctx.lineWidth = s.width;
ctx.lineCap = 'round';
ctx.globalAlpha = s.alpha;
// Ink outline under the colored fill (sticker look): same path, two strokes
ctx.strokeStyle = INK;
ctx.lineWidth = s.width + 3;
ctx.stroke();
ctx.strokeStyle = s.color;
ctx.lineWidth = s.width;
ctx.stroke();
ctx.restore();
@@ -223,3 +228,59 @@
});
})();
// ── Pepsi gallery: shuffled on load, swipe cycles through on click ─
(function () {
const COUNT = 15;
const img = document.querySelector('.pepsi-placeholder img');
if (!img) return;
const box = img.parentElement;
const reduceMotion = window.matchMedia('(prefers-reduced-motion: reduce)');
const src = (n) => `Pepsi/Pepsi-${n}.jpg`;
// FisherYates shuffle of [1..COUNT]; clicks cycle through this order
const order = Array.from({ length: COUNT }, (_, i) => i + 1);
for (let i = order.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
[order[i], order[j]] = [order[j], order[i]];
}
let pos = 0;
img.src = src(order[pos]);
let busy = false;
box.addEventListener('click', () => {
if (busy) return;
pos = (pos + 1) % order.length;
const next = order[pos];
if (reduceMotion.matches) {
img.src = src(next);
return;
}
busy = true;
const incoming = new Image();
incoming.src = src(next);
incoming.alt = img.alt;
incoming.draggable = false;
incoming.className = 'pepsi-incoming';
// Decode before animating so the swipe never reveals a half-loaded frame
incoming.decode().then(() => {
box.appendChild(incoming);
const opts = { duration: 300, easing: 'cubic-bezier(0.25, 0.46, 0.45, 0.94)' };
img.animate(
[{ transform: 'translateX(0)' }, { transform: 'translateX(-100%)' }],
opts
);
incoming.animate(
[{ transform: 'translateX(100%)' }, { transform: 'translateX(0)' }],
opts
).finished.then(() => {
img.src = incoming.src; // already decoded, swaps instantly
incoming.remove();
busy = false;
});
}).catch(() => { busy = false; });
});
})();