Skip to content

Commit 0ca55a8

Browse files
authored
Merge pull request #14 from readme-SVG/claude/fix-animation-gif-frames-8wUEw
Improve blob animations and GIF export frame synchronization
2 parents a1279e5 + 99a1cc1 commit 0ca55a8

2 files changed

Lines changed: 35 additions & 13 deletions

File tree

assets/css/styles.css

Lines changed: 19 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -262,19 +262,27 @@ h1 span {
262262
pointer-events: none;
263263
}
264264

265-
@keyframes floatBlob {
265+
@keyframes waveBlob1 {
266266
0% { transform: translate(0px, 0px) scale(1); }
267-
25% { transform: translate(30px, -25px) scale(1.05); }
268-
50% { transform: translate(-20px, 35px) scale(0.96); }
269-
75% { transform: translate(25px, 20px) scale(1.03); }
267+
25% { transform: translate(40px, -30px) scale(1.06); }
268+
50% { transform: translate(-25px, 40px) scale(0.95); }
269+
75% { transform: translate(30px, 25px) scale(1.04); }
270270
100% { transform: translate(0px, 0px) scale(1); }
271271
}
272272

273-
@keyframes floatBlobCenter {
273+
@keyframes waveBlob2 {
274+
0% { transform: translate(0px, 0px) scale(1); }
275+
25% { transform: translate(-35px, 30px) scale(0.94); }
276+
50% { transform: translate(45px, -20px) scale(1.07); }
277+
75% { transform: translate(-20px, -35px) scale(0.97); }
278+
100% { transform: translate(0px, 0px) scale(1); }
279+
}
280+
281+
@keyframes waveBlob3 {
274282
0% { transform: translate(-50%, -50%) scale(1); }
275-
25% { transform: translate(calc(-50% + 30px), calc(-50% - 25px)) scale(1.05); }
276-
50% { transform: translate(calc(-50% - 20px), calc(-50% + 35px)) scale(0.96); }
277-
75% { transform: translate(calc(-50% + 25px), calc(-50% + 20px)) scale(1.03); }
283+
25% { transform: translate(calc(-50% + 20px), calc(-50% + 35px)) scale(1.05); }
284+
50% { transform: translate(calc(-50% - 35px), calc(-50% - 20px)) scale(0.95); }
285+
75% { transform: translate(calc(-50% + 30px), calc(-50% - 30px)) scale(1.03); }
278286
100% { transform: translate(-50%, -50%) scale(1); }
279287
}
280288

@@ -288,7 +296,7 @@ h1 span {
288296
filter: blur(120px);
289297
opacity: 0.25;
290298
transition: background 0.5s;
291-
animation: floatBlob 12s ease-in-out infinite;
299+
animation: waveBlob1 4s ease-in-out infinite;
292300
}
293301

294302
.card-bg-blob2 {
@@ -301,7 +309,7 @@ h1 span {
301309
filter: blur(140px);
302310
opacity: 0.15;
303311
transition: background 0.5s;
304-
animation: floatBlob 9s ease-in-out infinite 3s;
312+
animation: waveBlob2 4s ease-in-out infinite;
305313
}
306314

307315
.card-bg-blob3 {
@@ -315,7 +323,7 @@ h1 span {
315323
filter: blur(100px);
316324
opacity: 0.1;
317325
transition: background 0.5s;
318-
animation: floatBlobCenter 15s ease-in-out infinite 6s;
326+
animation: waveBlob3 4s ease-in-out infinite;
319327
}
320328

321329
.card-bg-grain {

assets/js/export.js

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,12 @@ export async function downloadGif({ button, captureElement, repoDisplayElement }
44
const originalMarkup = button.innerHTML;
55
button.disabled = true;
66

7-
const FRAME_COUNT = 15;
7+
const FRAME_COUNT = 40;
8+
const ANIMATION_DURATION = 4000; // ms, must match CSS animation duration
9+
10+
// Pause all animations so we can scrub them deterministically
11+
const animations = captureElement.getAnimations({ subtree: true });
12+
animations.forEach(anim => anim.pause());
813

914
try {
1015
const response = await fetch('https://cdnjs.cloudflare.com/ajax/libs/gif.js/0.2.0/gif.worker.js');
@@ -21,14 +26,21 @@ export async function downloadGif({ button, captureElement, repoDisplayElement }
2126

2227
for (let i = 0; i < FRAME_COUNT; i++) {
2328
button.textContent = `Frame ${i + 1}/${FRAME_COUNT}…`;
29+
30+
// Scrub every animation to the exact timestamp for this frame
31+
const frameTime = (i / FRAME_COUNT) * ANIMATION_DURATION;
32+
animations.forEach(anim => { anim.currentTime = frameTime; });
33+
2434
const canvas = await window.html2canvas(captureElement, {
2535
scale: 1,
2636
backgroundColor: '#0d1117'
2737
});
2838
gif.addFrame(canvas, { delay: 100 });
29-
await new Promise((r) => setTimeout(r, 100));
3039
}
3140

41+
// Restore live preview before rendering
42+
animations.forEach(anim => anim.play());
43+
3244
button.textContent = 'Assembling GIF…';
3345
gif.render();
3446

@@ -47,6 +59,8 @@ export async function downloadGif({ button, captureElement, repoDisplayElement }
4759
button.disabled = false;
4860
});
4961
} catch (error) {
62+
// Restore live preview even on failure
63+
animations.forEach(anim => anim.play());
5064
console.error(error);
5165
button.textContent = 'Error';
5266
setTimeout(() => {

0 commit comments

Comments
 (0)