🐐
This commit is contained in:
@@ -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`;
|
||||
|
||||
// Fisher–Yates 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; });
|
||||
});
|
||||
})();
|
||||
|
||||
Reference in New Issue
Block a user