1<!DOCTYPE html> 2<html lang="en"> 3 4<head> 5 <meta charset="utf-8"> 6 <meta name="viewport" content="width=device-width, initial-scale=1"> 7 <meta property="og:image" content="https://yeolj00.github.io/research/vector-prism/assets/og_image.png"> 8 <title>Vector Prism: Animating Vector Graphics by Stratifying Semantic Structure</title> 9 <link rel="icon" href="assets/favicon_davian.png" type="image/png"> 10 <link rel="stylesheet" href="afterglow-bootstrap.css"> 11 <style> 12 /* Hero video grid */ 13 .teaser-grid { 14 display: grid; 15 grid-template-columns: repeat(4, 1fr); 16 gap: 15px; 17 margin: 30px 0; 18 } 19 20 .teaser-item { 21 text-align: center; 22 } 23 24 /* Styled iframe container for animations */ 25 .animation-frame { 26 position: relative; 27 width: 100%; 28 aspect-ratio: 1 / 1; 29 border-radius: var(--border-radius-lg); 30 overflow: hidden; 31 box-shadow: var(--box-sha
31dow); 32 background: white; 33 display: flex; 34 align-items: center; 35 justify-content: center; 36 } 37 38 /* SVG will scale to fit container */ 39 .animation-frame svg { 40 width: 100%; 41 height: 100%; 42 display: block; 43 } 44 45 /* Loading state */ 46 .animation-frame.loading::after { 47 content: "Loading..."; 48 color: var(--gray-400); 49 font-size: 0.85rem; 50 } 51 52 /* Hover effect for teaser animations */ 53 .teaser-item .animation-frame { 54 transition: transform 0.3s ease, box-shadow 0.3s ease; 55 } 56 57 .teaser-item .animation-frame:hover { 58 transform: translateY(-5px); 59 box-shadow: var(--box-shadow-lg); 60 } 61 62 .teaser-item figcaption { 63 font-size: 0.85rem; 64 margin-top: 12px; 65 color: var(--gray-600); 66 font-style: italic; 67 line-height: 1.4; 68 } 69 70 /* Interactive scaling demo */ 71 .scalable-demo-container { 72 max-width: 300px; 73 margin: 0 auto 3rem; 74 cursor: pointer; 75 transition: all 0.5s cubic-bezier(0.34, 1.56, 0.64, 1); 76 } 77 78 .scalable-demo-container:hover { 79 max-width: 600px; 80 transform: translateY(-10px); 81 } 82 83 .scalable-demo-container .animation-frame { 84 overflow: visible; 85 border: 2px solid rgba(255, 106, 138, 0.3); 86 transition: all 0.5s cubic-bezier(0.34, 1.56, 0.64, 1); 87 } 88 89 .scalable-demo-container:hover .animation-frame { 90 border-color: rgba(255, 106, 138, 0.8); 91 box-shadow: 0 20px 60px rgba(255, 106, 138, 0.3); 92 } 93 94 .scale-hint { 95 font-style: italic; 96 color: #666; 97 margin-top: 1rem; 98 font-size: 0.9rem; 99 transition: all 0.3s ease; 100 } 101 102 .scalable-demo-container:hover + .scale-hint { 103 color: #ff6a8a; 104 font-weight: 600; 105 transform: scale(1.05); 106 } 107 108 /* Comparison section */ 109 .comparison-section { 110 background: var(--gray-100); 111 padding: 3rem 0; 112 margin: 3rem 0; 113 } 114 115 .comparison-grid { 116 display: grid; 117 grid-template-columns: repeat(5, 1fr); 118 gap: 12px; 119 margin: 20px 0; 120 } 121 122 .comparison-item { 123 text-align: center; 124 } 125 126 /* Comparison iframe styling */ 127 .comparison-item .animation-frame { 128 border-radius: var(--border-radius); 129 box-shadow: none; 130 border: 1px solid var(--gray-300); 131 } 132 133 .comparison-item.ours .animation-frame { 134 border: 2px solid var(--primary); 135 box-shadow: 0 0 0 3px rgba(255, 106, 138, 0.2); 136 } 137 138 /* Video styling for baselines */ 139 .comparison-item video { 140 width: 100%; 141 aspect-ratio: 1 / 1; 142 object-fit: cover; 143 border-radius: var(--border-radius); 144 border: 1px solid var(--gray-300); 145 background: #000; 146 } 147 148 .comparison-item .method-label { 149 font-size: 0.85rem; 150 font-weight: 600; 151 margin-top: 8px; 152 color: var(--gray-700); 153 } 154 155 .comparison-item.ours .method-label { 156 color: var(--primary); 157 } 158 159 .refresh-btn { 160 display: inline-block; 161 margin-left: 8px; 162 cursor: pointer; 163 font-size: 1rem; 164 opacity: 0.7; 165 transition: all 0.2s ease; 166 user-select: none; 167 } 168 169 .refresh-btn:hover { 170 opacity: 1; 171 transform: rotate(180deg); 172 } 173 174 .refresh-btn:active { 175 transform: rotate(180deg) scale(0.9); 176 } 177 178 .instruction-text { 179 font-weight: 500; 180 text-align: center; 181 padding: 1rem 2rem; 182 background: linear-gradient(135deg, rgba(255, 167, 166, 0.15) 0%, rgba(157, 79, 255, 0.15) 100%); 183 border-radius: var(--border-radius-lg); 184 margin-bottom: 1.5rem; 185 font-size: 1.1rem; 186 color: var(--gray-800); 187 } 188 189 /* Stats section */ 190 .stats-grid { 191 display: flex; 192 justify-content: center; 193 gap: 2rem; 194 margin: 2rem 0; 195 flex-wrap: wrap; 196 } 197 198 .stat-card { 199 flex: 0 0 calc(33.333% - 2rem); 200 max-width: 350px; 201 text-align: center; 202 padding: 2rem; 203 background: white; 204 border-radius: var(--border-radius-lg);
205 box-shadow: var(--box-shadow-sm); 206 border-top: 4px solid; 207 border-image: var(--gradient-afterglow) 1; 208 } 209 210 .stat-card .number { 211 font-size: 3rem; 212 font-weight: 700; 213 background: var(--gradient-afterglow); 214 -webkit-background-clip: text; 215 -webkit-text-fill-color: transparent; 216 background-clip: text; 217 } 218 219 .stat-card .label { 220 color: var(--gray-600); 221 font-size: 1rem; 222 } 223 224 /* No video placeholder */ 225 .no-video { 226 width: 100%; 227 aspect-ratio: 1; 228 background: var(--gray-200); 229 border-radius: var(--border-radius); 230 display: flex; 231 align-items: center; 232 justify-content: center; 233 color: var(--gray-500); 234 font-size: 0.8rem; 235 } 236 237 /* No video placeholder */ 238 .no-video { 239 width: 100%; 240 aspect-ratio: 1; 241 background: var(--gray-200); 242 border-radius: var(--border-radius); 243 display: flex; 244 align-items: center; 245 justify-content: center; 246 color: var(--gray-500); 247 font-size: 0.8rem; 248 } 249 250 /* CVPR Badge */ 251 .conference-badge { 252 display: inline-block; 253 font-size: 1.3rem; 254 font-weight: 600; 255 letter-spacing: 0.5px; 256 margin: 1rem 0 1.5rem; 257 background: linear-gradient(135deg, #FF6A8A 0%, #9D4FFF 100%); 258 -webkit-background-clip: text; 259 -webkit-text-fill-color: transparent; 260 background-clip: text; 261 } 262 263 /* Responsive */ 264 @media (max-width: 992px) { 265 .teaser-grid { 266 grid-template-columns: repeat(2, 1fr); 267 } 268 .comparison-grid { 269 grid-template-columns: repeat(3, 1fr); 270 } 271 .stats-grid { 272 gap: 1rem; 273 } 274 .stat-card { 275 flex: 0 0 calc(50% - 1rem); 276 } 277 } 278 279 @media (max-width: 768px) { 280 h1.text-gradient { 281 font-size: 2.4rem !important; 282 } 283 h4.text-muted { 284 font-size: 1.1rem !important; 285 } 286 .comparison-grid { 287 grid-template-columns: repeat(2, 1fr); 288 } 289 .instruction-text { 290 font-size: 0.95rem; 291 padding: 0.75rem 1rem; 292 } 293 .stat-card { 294 flex: 0 0 100%; 295 max-width: 100%; 296 } 297 .stat-card .number { 298 font-size: 2.2rem; 299 } 300 pre code { 301 font-size: 0.75rem; 302 } 303 .scalable-demo-container:hover { 304 max-width: 100%; 305 transform: none; 306 } 307 } 308 309 @media (max-width: 576px) { 310 .teaser-grid { 311 grid-template-columns: repeat(2, 1fr); 312 gap: 10px; 313 } 314 .comparison-grid { 315 grid-template-columns: repeat(2, 1fr); 316 } 317 .pipeline-visual { 318 flex-direction: column; 319 } 320 .pipeline-arrow { 321 transform: rotate(90deg); 322 } 323 h1.text-gradient { 324 font-size: 2rem !important; 325 } 326 .container { 327 padding-left: 12px; 328 padding-right: 12px; 329 } 330 .col-1 { 331 display: none; 332 } 333 .col-10 { 334 flex: 0 0 100%; 335 max-width: 100%; 336 } 337 .btn-lg { 338 padding: 0.6rem 1.2rem; 339 font-size: 1rem; 340 } 341 img[src="assets/exp_user_study.png"] { 342 max-width: 100% !important; 343 } 344.row.justify-content-center .col-3 { 345 flex: 0 0 50%; 346 max-width: 50%; 347 } 348 } 349 </style> 350</head> 351 352<body> 353 <div class="container" style="margin-top: 40px; margin-bottom: 60px;"> 354 <div class="row"> 355 <div class="col-1"></div> 356 <div class="col-10"> 357 358 <!-- Title and Authors --> 359 <div class="text-center"> 360 <h1 class="text-gradient" 361 style="margin-top: 20px; margin-bottom: 20px; line-height: 1.2em; font-weight: 600; font-size: 3.5rem;"> 362 Vector Prism 363 </h1> 364 <h4 class="text-muted mb-4" style="font-weight: 400; font-size: 1.5rem;"> 365 Animating Vector Graphics by Stratifying Semantic Structure 366 </h4> 367 368 <!-- Authors - FIXED LAYOUT --> 369 <div class="row justify-content-center mt-4 mb-4"> 370 <div class="col-3 col-md-3 mb-3"> 371 <a href="https://yeolj00.github.io/" style="text-decoration: none;"> 372 <h5 class="text-dark mb-1">Jooyeol Yun</h5> 373 </a> 374 <p class="text-primary mb-0" style="font-size: 0.85rem;">[email protected]</p> 375 </div> 376 <div class="col-3 col-md-3 mb-3"> 377 <h5 class="text-dark mb-1">Jaegul Choo</h5> 378 <p class="text-primary mb-0" style="font-size: 0.85rem;">
378[email protected]</p> 379 </div> 380 </div> 381 <div class="row justify-content-center mt-4 mb-2"> 382 <img src="assets/logo_kaist.svg" class="figure-img img-fluid" style="margin: 0px 50px" width="20%"> 383 </div> 384 385 <!-- Conference Badge --> 386 <div class="mt-3 mb-2"> 387 <span class="conference-badge">CVPR 2026 · Highlight</span> 388 </div> 389 </div> 390 391 <!-- Teaser Videos --> 392 <div class="teaser-grid"> 393 <div class="teaser-item"> 394 <div class="animation-frame loading" data-src="assets/19-1-cool.html" id="teaser-emoji"></div> 395 <figcaption>"Make the emoji look left and right"<span class="refresh-btn" onclick="reloadAnimation('teaser-emoji')" title="Reload animation">ð</span></figcaption> 396 </div> 397 <div class="teaser-item"> 398 <div class="animation-frame loading" data-src="assets/6-1-rainbow.html" id="teaser-rainbow"></div> 399 <figcaption>"Elements smoothly pop up in a lively manner"<span class="refresh-btn" onclick="reloadAnimation('teaser-rainbow')" title="Reload animation">ð</span></figcaption> 400 </div> 401 <div class="teaser-item"> 402 <div class="animation-frame loading" data-src="assets/18-2-compass.html" id="teaser-compass"></div> 403 <figcaption>"Compass needle quickly spins around once"<span class="refresh-btn" onclick="reloadAnimation('teaser-compass')" title="Reload animation">ð</span></figcaption> 404 </div> 405 <div class="teaser-item"> 406 <div class="animation-frame loading" data-src="assets/8-2-calculator.html" id="teaser-calculator"></div> 407 <figcaption>"Buttons bounce in one by one"<span class="refresh-btn" onclick="reloadAnimation('teaser-calculator')" title="Reload animation">ð</span></figcaption> 408 </div> 409 </div> 410 411 <div class="teaser-grid"> 412 <div class="teaser-item"> 413 <div class="animation-frame loading" data-src="assets/3-2-architecture.html" id="teaser-architecture"></div> 414 <figcaption>"Turn the scene to night"<span class="refresh-btn" onclick="reloadAnimation('teaser-architecture')" title="Reload animation">ð</span></figcaption> 415 </div> 416 <div class="teaser-item"> 417 <div class="animation-frame loading" data-src="assets/42-1-python.html" id="teaser-python"></div> 418 <figcaption>"Python logo bounces in and meets in the middle"<span class="refresh-btn" onclick="reloadAnimation('teaser-python')" title="Reload animation">ð</span></figcaption> 419 </div> 420 <div class="teaser-item"> 421 <div class="animation-frame loading" data-src="assets/43-2-pytorch.html" id="teaser-pytorch"></div> 422 <figcaption>"PyTorch logo bounces and changes colors"<span class="refresh-btn" onclick="reloadAnimation('teaser-pytorch')" title="Reload animation">ð</span></figcaption> 423 </div> 424 <div class="teaser-item"> 425 <div class="animation-frame loading" data-src="assets/24-1-doughnut.html" id="teaser-doughnut"></div> 426 <figcaption>"Doughnut bounces up, sprinkles fall in"<span class="refresh-btn" onclick="reloadAnimation('teaser-doughnut')" title="Reload animation">ð</span></figcaption> 427 </div> 428 </div> 429 430 <!-- Action Buttons --> 431 <div class="text-center mt-5"> 432 <a href="https://arxiv.org/abs/2512.14336" class="btn btn-gradient btn-lg mr-2 mb-2">ð Paper</a> 433 <a href="https://github.com/YeolJ00/vector-prism" class="btn btn-outline-primary btn-lg mr-2 mb-2">ð» Code</a> 434 </div> 435 436 <hr class="my-5"> 437 438 <!-- TL;DR --> 439 <div class="text-center"> 440 <h2 class="text-gradient mb-5" style="font-weight: 600;">Why Animate Vector Graphics?</h2> 441 <div class="scalable-demo-container"> 442 <div class="animation-frame loading" data-src="assets/54-1-rocket.html" id="teaser-rocket"></div> 443 </div> 444 <p class="scale-hint"> 445 "The rocket exhaust to glow softly as if traveling through space."<br>
446 <span style="font-size: 0.85rem; color: #999;">ð¡ Hover to see infinite scalability</span> 447 </p> 448 449 </div> 450 <div style="max-width: 950px; margin: 0 auto;"> 451 <p class="lead" style="font-size: 1rem; margin-bottom: 2.5rem; line-height: 1.8; color: #333;"> 452 Vector graphics (SVGs) power the modern web. They are <strong>infinitely scalable</strong> with 453 perfect clarity at any resolution, <strong>54Ã smaller</strong> than video files, 454 <strong>editable</strong> with CSS and code, and <strong>portable</strong> across any device. 455 Yet animating them meaningfully is incredibly difficult, even for LLMs. This is because SVGs 456 are made of thousands of <strong>low-level elements</strong> (paths, groups, shapes) that lack 457 semantic structure. Naively animating these elements leads to <strong>chaotic, incoherent results</strong>. 458 </p> 459 460 <div style="background: linear-gradient(135deg, rgba(255, 167, 166, 0.1) 0%, rgba(157, 79, 255, 0.1) 100%); 461 padding: 1rem 2.5rem; border-radius: 16px; margin: 2.5rem 0;"> 462 <p class="lead" style="font-size: 1.1rem; margin: 0; line-height: 1.8; color: #1a1a1a;"> 463 Vector Prism 464 <strong>re-organizing vector graphics to have a semantically meaningful structure</strong> 465 before animating them, enabling high-quality, user-controllable animations 466 that were previously impossible. 467 </p> 468 </div> 469 </div> 470 471 <!-- How Do We Do It Section --> 472 <div class="text-center"> 473 <h2 class="text-gradient mb-5" style="font-weight: 600;">How Do We Do It?</h2> 474 </div> 475 <p style="font-size: 1rem; line-height: 1.8; color: #333; margin-bottom: 2.5rem;"> 476 When you say "make the buttons bounce," there is no "buttons" in the code, but just scattered <code><path></code> elements with no semantic structure. 477 We need to identify which elements correspond to "buttons" first. 478 </p> 479 480 <!-- First Image - Properly Centered --> 481 <div class="text-center mt-4 mb-4"> 482 <img src="assets/method_main.png" alt="Semantic Structure Visualization" 483 style="max-width: 100%; height: auto; border-radius: var(--border-radius-lg);
483 box-shadow: var(--box-shadow);"> 484 </div> 485 <p class="method-intro" style="margin-bottom: 3rem;"> 486 We render each SVG element multiple ways (highlighted, isolated, zoomed), 487 collect (possibly incorrect) predictions from a vision-language model, then use Dawid-Skene model to aggregate these noisy predictions into reliable semantic labels. 488 This turns weak, contradictory signals into robust semantic decisions. 489 </p> 490 491 <hr class="my-5"> 492 493 <!-- Stats --> 494 <h2 class="text-gradient text-center mb-4" style="font-weight: 600; margin-bottom: 1rem;">Results at a glance</h2> 495 <div class="stats-grid"> 496 <div class="stat-card"> 497 <div class="number">76.1</div> 498 <div class="label">GPT-T2V Score<br><small class="text-muted">vs. Sora 2's 69.1</small></div> 499 </div> 500 <div class="stat-card"> 501 <div class="number">54Ã</div> 502 <div class="label">Smaller Files<br><small class="text-muted">vs. video formats</small></div> 503 </div> 504 </div> 505 506 <!-- Second Image - Properly Centered Below Stats --> 507 <div class="text-center mt-5 mb-4"> 508 <img src="assets/exp_user_study.png" alt="User Study Results" 509 style="max-width: 60%; height: auto; border-radius: var(--border-radius-lg);
509 box-shadow: var(--box-shadow);"> 510 </div> 511 <p class="method-intro" style="margin-bottom: 3rem;"> 512 Vector Prism achieves state-of-the-art performance across both instruction-following metrics and file efficiency, demonstrating that proper semantic structure unlocks superior animation quality. 513 </p> 514 515 </div> 516 <div class="col-1"></div> 517 </div> 518 </div> 519 520 <!-- Comparisons Section (Full Width Background) --> 521 <div class="comparison-section"> 522 <div class="container"> 523 <div class="row"> 524 <div class="col-1"></div> 525 <div class="col-10"> 526 527 <h2 class="text-gradient text-center mb-5" style="font-weight: 600;">Side-by-Side Comparisons</h2> 528 529 <!-- Comparison 1 --> 530 <div class="instruction-text"> 531 "I want an opening animation, starting from the bottom and moving up." 532 </div> 533 <div class="comparison-grid"> 534 <div class="comparison-item"> 535 <video controls loop muted playsinline> 536 <source src="assets/videos/57-2-aniclip/video.mp4" type="video/mp4"> 537 </video> 538 <div class="method-label">AniClipart</div> 539 </div> 540 <div class="comparison-item"> 541 <video controls loop muted playsinline> 542 <source src="assets/videos/57-2-gpt/video.mp4" type="video/mp4"> 543 </video> 544 <div class="method-label">GPT-5</div> 545 </div> 546 <div class="comparison-item"> 547 <video controls loop muted playsinline> 548 <source src="assets/videos/57-2-wan/video.mp4" type="video/mp4"> 549 </video> 550 <div class="method-label">Wan 2.2</div> 551 </div> 552 <div class="comparison-item"> 553 <video controls loop muted playsinline> 554 <source src="assets/videos/57-2-sora/video.mp4" type="video/mp4"> 555 </video> 556 <div class="method-label">Sora 2</div> 557 </div> 558 <div class="comparison-item ours"> 559 <div class="animation-frame loading" data-src="assets/57-2-windmill.html" id="anim-windmill"></div> 560 <div class="method-label">Ours â¨<span class="refresh-btn" onclick="reloadAnimation('anim-windmill')" title="Reload animation">ð</span></div> 561 </div> 562 </div> 563 564 <!-- Comparison 2 --> 565 <div class="instruction-text mt-5"> 566 "I want the lightning bolt to glow softly and raindrops to fade in and out." 567 </div> 568 <div class="comparison-grid"> 569 <div class="comparison-item"> 570 <video controls loop muted playsinline> 571 <source src="assets/videos/16-2-aniclip/video.mp4" type="video/mp4"> 572 </video> 573 <div class="method-label">AniClipart</div> 574 </div> 575 <div class="comparison-item"> 576 <video controls loop muted playsinline> 577 <source src="assets/videos/16-2-gpt/video.mp4" type="video/mp4"> 578 </video> 579 <div class="method-label">GPT-5</div> 580 </div> 581 <div class="comparison-item"> 582 <video controls loop muted playsinline> 583 <source src="assets/videos/16-2-wan/video.mp4" type="video/mp4"> 584 </video> 585 <div class="method-label">Wan 2.2</div> 586 </div> 587 <div class="comparison-item"> 588 <video controls loop muted playsinline> 589 <source src="assets/videos/16-2-sora/video.mp4" type="video/mp4"> 590 </video> 591 <div class="method-label">Sora 2</div> 592 </div> 593 <div class="comparison-item ours"> 594 <div class="animation-frame loading" data-src="assets/16-2-lightning.html" id="anim-lightning"></div> 595 <div class="method-label">Ours â¨<span class="refresh-btn" onclick="reloadAnimation('anim-lightning')" title="Reload animation">ð</span></div> 596 </div> 597 </div> 598 599 <!-- Comparison 4 --> 600 <div class="instruction-text mt-5"> 601 "I want the hexagon to appear first, then the X sign enters by spinning in." 602 </div> 603 <div class="comparison-grid"> 604 <div class="comparison-item"> 605 <div class="no-video">No output</div> 606 <div class="method-label">AniClipart</div> 607 </div> 608 <div class="comparison-item"> 609 <video controls loop muted playsinline> 610 <source src="assets/videos/12-1-gpt/video.mp4" type="video/mp4"> 611 </video> 612 <div class="method-label">GPT-5</div> 613 </div> 614 <div class="comparison-item"> 615 <video controls loop muted playsinline> 616 <source src="assets/videos/12-1-wan/video.mp4" type="video/mp4"> 617 </video> 618 <div class="method-label">Wan 2.2</div> 619 </div> 620 <div class="comparison-item"> 621 <video controls loop muted playsinline> 622 <source src="assets/videos/12-1-sora/video.mp4" type="video/mp4"> 623 </video> 624 <div class="method-label">Sora 2</div> 625 </div> 626 <div class="comparison-item ours"> 627 <div class="animation-frame loading" data-src="assets/12-1-cancel.html" id="anim-cancel"></div> 628 <div class="method-label">Ours â¨<span class="refresh-btn" onclick="reloadAnimation('anim-cancel')" title="Reload animation">ð</span></div> 629 </div> 630 </div> 631 632 </div> 633 <div class="col-1"></div> 634 </div> 635 </div> 636 </div> 637 638 <!-- Back to container --> 639 <div class="container"> 640 <div class="row"> 641 <div class="col-1"></div> 642 <div class="col-10"> 643 644 <hr class="my-5"> 645 646 <!-- Citation --> 647 <h2 class="text-gradient mb-4">Citation</h2> 648 <pre class="border rounded p-3 bg-light" style="font-size: 0.85rem;"><code>@article{yun2026vectorprism, 649 title={Vector Prism: Animating Vector Graphics by Stratifying Semantic Structure}, 650 author={Yun, Jooyeol and Choo, Jaegul}, 651 journal={arXiv preprint arXiv:2512.14336}, 652 year={2026} 653}</code></pre> 654 655 <hr class="my-5"> 656 657 </div> 658 <div class="col-1"></div> 659 </div> 660 </div> 661 662 <!-- Footer --> 663 <div class="footer"> 664 <div class="container text-center"> 665 <p>© 2026 KAIST AI. All rights reserved.</p> 666 <p class="text-muted" style="font-size: 0.9rem;"> 667 Made with the Firefox Afterglow color palette ð¦â¨ 668 </p> 669 </div> 670 </div> 671</body> 672 <!-- JavaScript to load SVG animations --> 673
673<script> 674 function reloadAnimation(elementId) { 675 const frame = document.getElementById(elementId); 676 if (!frame) return; 677 678 const src = frame.getAttribute('data-src'); 679 if (!src) return; 680 681 // Clear the frame 682 frame.innerHTML = ''; 683 frame.classList.add('loading'); 684 685 // Reload the animation 686 fetch(src) 687 .then(response => { 688 if (!response.ok) throw new Error('Failed to load: ' + src); 689 return response.text(); 690 }) 691 .then(html => { 692 // Parse the HTML 693 const parser = new DOMParser(); 694 const doc = parser.parseFromString(html, 'text/html'); 695 696 // Extract SVG 697 const svg = doc.querySelector('svg'); 698 699 // Extract all style tags 700 const styles = doc.querySelectorAll('style'); 701 702 if (svg) { 703 // Create a unique ID for scoping 704 const uniqueId = elementId + '-reload-' + Date.now(); 705 706 // Add unique class to SVG for scoping 707 svg.classList.add(uniqueId); 708 709 // Process styles to scope them 710 styles.forEach(style => { 711 let css = style.textContent; 712 713 // Create a new scoped style element 714 const scopedStyle = document.createElement('style'); 715 scopedStyle.textContent = css; 716 frame.appendChild(scopedStyle); 717 }); 718 719 // Add SVG to frame 720 frame.appendChild(svg); 721 722 // Remove loading state 723 frame.classList.remove('loading'); 724 } 725 }) 726 .catch(error => { 727 console.error(error); 728 frame.innerHTML = '<span style="color: #999; font-size: 0.8rem;">Failed to load</span>'; 729 frame.classList.remove('loading'); 730 }); 731 } 732 733 document.addEventListener('DOMContentLoaded', function() { 734 // Find all animation frames with data-src 735 const frames = document.querySelectorAll('.animation-frame[data-src]'); 736 737 frames.forEach((frame, index) => { 738 const src = frame.getAttribute('data-src'); 739 740 fetch(src) 741 .then(response => { 742 if (!response.ok) throw new Error('Failed to load: ' + src); 743 return response.text(); 744 }) 745 .then(html => { 746 // Parse the HTML 747 const parser = new DOMParser(); 748 const doc = parser.parseFromString(html, 'text/html'); 749 750 // Extract SVG 751 const svg = doc.querySelector('svg'); 752 753 // Extract all style tags 754 const styles = doc.querySelectorAll('style'); 755 756 if (svg) { 757 // Create a unique ID for scoping 758 const uniqueId = 'anim-' + index; 759 760 // Add unique class to SVG for scoping 761 svg.classList.add(uniqueId); 762 763 // Process styles to scope them 764 styles.forEach(style => { 765 // Optionally scope CSS - prepend unique class 766 // This helps avoid conflicts between animations 767 let css = style.textContent; 768 769 // Create a new scoped style element 770 const scopedStyle = document.createElement('style'); 771 scopedStyle.textContent = css; 772 frame.appendChild(scopedStyle); 773 }); 774 775 // Add SVG to frame 776 frame.appendChild(svg); 777 778 // Remove loading state 779 frame.classList.remove('loading'); 780 } 781 }) 782 .catch(error => { 783 console.error(error); 784 frame.innerHTML = '<span style="color: #999; font-size: 0.8rem;">Failed to load</span>'; 785 frame.classList.remove('loading'); 786 }); 787 }); 788 }); 789 </script>
789 790 791</html>
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.