Bladeren bron

animial基本框架

robert 4 dagen geleden
bovenliggende
commit
73dd7ce6ec
9 gewijzigde bestanden met toevoegingen van 1505 en 0 verwijderingen
  1. 1 0
      doc/animal.html
  2. 339 0
      doc/animal_basic.html
  3. 177 0
      doc/demo.css
  4. 187 0
      doc/index.html
  5. 81 0
      doc/list_callbacks.html
  6. 79 0
      doc/list_targets.html
  7. 282 0
      doc/search.html
  8. 168 0
      doc/test_css_selector.html
  9. 191 0
      doc/test_on_update.html

+ 1 - 0
doc/animal.html

@@ -44,6 +44,7 @@
     <div class="progress-bar"></div>
 
     <h1>Animal.js Demo</h1>
+    <p><a href="animal_basic.html">View CSS Selector Demo</a></p>
 
     <section>
         <h2>1. Basic Transform & Opacity (WAAPI)</h2>

+ 339 - 0
doc/animal_basic.html

@@ -0,0 +1,339 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+    <meta charset="UTF-8">
+    <meta name="viewport" content="width=device-width, initial-scale=1.0">
+    <title>CSS Selector - Anime.js</title>
+    <style>
+        :root {
+            --bg-color: #111;
+            --text-color: #9b9b9b;
+            --highlight-color: #ff4b4b;
+            --secondary-color: #2a2a2e;
+            --accent-color: #ff9f43;
+            --code-bg: #000;
+            --orange: #FF8F42; /* Anime.js orange */
+            --border-color: #222;
+        }
+        body {
+            background-color: var(--bg-color);
+            color: var(--text-color);
+            font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Open Sans", "Helvetica Neue", sans-serif;
+            margin: 0;
+            padding: 40px;
+            display: flex;
+            justify-content: center;
+        }
+        .container {
+            max-width: 900px;
+            width: 100%;
+        }
+        
+        /* Header Section */
+        h1 {
+            color: var(--orange);
+            font-size: 42px;
+            margin-bottom: 15px;
+            font-weight: 600;
+            letter-spacing: -1px;
+        }
+        .description {
+            font-size: 18px;
+            line-height: 1.5;
+            margin-bottom: 40px;
+            color: #ccc;
+        }
+        
+        h2 {
+            font-size: 24px;
+            color: #fff;
+            margin-bottom: 10px;
+            font-weight: 500;
+        }
+        p {
+            margin-top: 0;
+            margin-bottom: 20px;
+            line-height: 1.6;
+        }
+        
+        code.inline {
+            background: rgba(255,255,255,0.1);
+            color: #e6e6e6;
+            padding: 2px 6px;
+            border-radius: 3px;
+            font-family: 'Roboto Mono', monospace;
+            font-size: 0.9em;
+        }
+
+        /* Code & Demo Box */
+        .box-container {
+            margin-top: 50px;
+            background: #171717; /* slightly lighter than bg */
+            border-radius: 6px;
+            overflow: hidden;
+            box-shadow: 0 10px 40px rgba(0,0,0,0.2);
+        }
+
+        .box-header {
+            display: flex;
+            justify-content: space-between;
+            align-items: center;
+            padding: 0 20px;
+            height: 50px;
+            background: rgba(255,255,255,0.02);
+            border-bottom: 1px solid var(--border-color);
+        }
+        .box-title {
+            color: var(--orange);
+            font-weight: 600;
+            font-size: 14px;
+        }
+        
+        .tabs {
+            display: flex;
+            gap: 20px;
+            height: 100%;
+        }
+        .tab {
+            display: flex;
+            align-items: center;
+            height: 100%;
+            cursor: pointer;
+            color: #666;
+            font-weight: 600;
+            font-size: 13px;
+            border-bottom: 2px solid transparent;
+            transition: color 0.2s;
+        }
+        .tab:hover {
+            color: #999;
+        }
+        .tab.active {
+            color: var(--orange);
+            border-bottom-color: var(--orange);
+        }
+
+        .code-view, .html-view {
+            background: var(--code-bg);
+            padding: 20px;
+            font-family: 'Roboto Mono', 'Monaco', monospace;
+            font-size: 13px;
+            line-height: 1.6;
+            color: #abb2bf;
+            overflow-x: auto;
+            display: none;
+        }
+        .code-view.active, .html-view.active {
+            display: block;
+        }
+
+        /* Syntax Highlighting Mock */
+        .kwd { color: #c678dd; } /* keyword */
+        .str { color: #98c379; } /* string */
+        .fun { color: #61afef; } /* function */
+        .num { color: #d19a66; } /* number */
+        .tag { color: #e06c75; } /* tag */
+        .attr { color: #d19a66; } /* attribute */
+        .val { color: #98c379; } /* value */
+        .punc { color: #abb2bf; } /* punctuation */
+        
+        /* Visual Demo */
+        .demo-visual {
+            padding: 30px;
+            background: #1a1a1a;
+            border-top: 1px solid var(--border-color);
+        }
+        
+        .row {
+            position: relative;
+            padding: 20px 0;
+            border-bottom: 1px solid #2a2a2a;
+            display: flex;
+            align-items: center;
+        }
+        .row:last-child {
+            border-bottom: none;
+        }
+        
+        .square {
+            width: 28px;
+            height: 28px;
+            background-color: var(--highlight-color);
+            border-radius: 2px;
+        }
+        #css-selector-id {
+            background-color: var(--accent-color);
+        }
+
+        .action-bar {
+            padding: 15px 30px;
+            display: flex;
+            justify-content: flex-end;
+            border-top: 1px solid var(--border-color);
+        }
+        
+        .play-btn {
+            background: transparent;
+            border: 1px solid #444;
+            color: #fff;
+            padding: 6px 14px;
+            border-radius: 100px;
+            font-size: 12px;
+            font-weight: 600;
+            cursor: pointer;
+            transition: all 0.2s;
+        }
+        .play-btn:hover {
+            border-color: var(--orange);
+            color: var(--orange);
+        }
+
+    </style>
+</head>
+<body>
+
+    <div class="container">
+        <!-- Breadcrumb / Header -->
+        <div style="font-size: 10px; font-weight: bold; letter-spacing: 1px; color: #ff4b4b; margin-bottom: 20px;">ANIMATION > TARGETS</div>
+        
+        <h1>CSS Selector</h1>
+        <p class="description">Targets one or multiple DOM Elements using a CSS selector.</p>
+
+        <h2>Accepts</h2>
+        <p>Any <code class="inline">String</code> accepted by <code class="inline">document.querySelectorAll()</code></p>
+
+        <div class="box-container">
+            <div class="box-header">
+                <div class="box-title">CSS Selector code example</div>
+                <div class="tabs">
+                    <div class="tab active" onclick="switchTab('js')">JavaScript</div>
+                    <div class="tab" onclick="switchTab('html')">HTML</div>
+                </div>
+            </div>
+
+            <!-- JS Code View -->
+            <div id="js-code" class="code-view active">
+<span class="kwd">import</span> <span class="punc">{</span> animate <span class="punc">}</span> <span class="kwd">from</span> <span class="str">'animejs'</span><span class="punc">;</span>
+
+<span class="fun">animate</span><span class="punc">(</span><span class="str">'.square'</span><span class="punc">,</span> <span class="punc">{</span> x<span class="punc">:</span> <span class="str">'17rem'</span> <span class="punc">}</span><span class="punc">)</span><span class="punc">;</span>
+<span class="fun">animate</span><span class="punc">(</span><span class="str">'#css-selector-id'</span><span class="punc">,</span> <span class="punc">{</span> rotate<span class="punc">:</span> <span class="str">'1turn'</span> <span class="punc">}</span><span class="punc">)</span><span class="punc">;</span>
+<span class="fun">animate</span><span class="punc">(</span><span class="str">'.row:nth-child(3) .square'</span><span class="punc">,</span> <span class="punc">{</span> scale<span class="punc">:</span> <span class="punc">[</span><span class="num">1</span><span class="punc">,</span> <span class="num">.5</span><span class="punc">,</span> <span class="num">1</span><span class="punc">]</span> <span class="punc">}</span><span class="punc">)</span><span class="punc">;</span>
+            </div>
+
+            <!-- HTML Code View -->
+            <div id="html-code" class="html-view">
+<span class="tag">&lt;div</span> <span class="attr">class</span>=<span class="val">"medium row"</span><span class="tag">&gt;</span>
+  <span class="tag">&lt;div</span> <span class="attr">class</span>=<span class="val">"square"</span><span class="tag">&gt;&lt;/div&gt;</span>
+<span class="tag">&lt;/div&gt;</span>
+<span class="tag">&lt;div</span> <span class="attr">class</span>=<span class="val">"medium row"</span><span class="tag">&gt;</span>
+  <span class="tag">&lt;div</span> <span class="attr">id</span>=<span class="val">"css-selector-id"</span> <span class="attr">class</span>=<span class="val">"square"</span><span class="tag">&gt;&lt;/div&gt;</span>
+<span class="tag">&lt;/div&gt;</span>
+<span class="tag">&lt;div</span> <span class="attr">class</span>=<span class="val">"medium row"</span><span class="tag">&gt;</span>
+  <span class="tag">&lt;div</span> <span class="attr">class</span>=<span class="val">"square"</span><span class="tag">&gt;&lt;/div&gt;</span>
+<span class="tag">&lt;/div&gt;</span>
+            </div>
+
+            <!-- Live Demo -->
+            <div class="demo-visual">
+                <!-- Row 1 -->
+                <div class="medium row">
+                  <div class="square"></div>
+                </div>
+                <!-- Row 2 -->
+                <div class="medium row">
+                  <div id="css-selector-id" class="square"></div>
+                </div>
+                <!-- Row 3 -->
+                <div class="medium row">
+                  <div class="square"></div>
+                </div>
+            </div>
+            
+            <div class="action-bar">
+                <button class="play-btn" onclick="runDemo()">REPLAY</button>
+            </div>
+        </div>
+
+    </div>
+
+    <script src="../animal.js"></script>
+    <script>
+        function switchTab(tab) {
+            document.querySelectorAll('.tab').forEach(t => t.classList.remove('active'));
+            document.querySelectorAll('.code-view, .html-view').forEach(v => v.classList.remove('active'));
+            
+            if (tab === 'js') {
+                document.querySelector('.tabs .tab:nth-child(1)').classList.add('active');
+                document.getElementById('js-code').classList.add('active');
+            } else {
+                document.querySelector('.tabs .tab:nth-child(2)').classList.add('active');
+                document.getElementById('html-code').classList.add('active');
+            }
+        }
+
+        function runDemo() {
+            // Reset styles first to allow replay
+            document.querySelectorAll('.square').forEach(el => {
+                el.style.transform = 'none';
+            });
+            
+            // To faithfully replicate the demo where animations appear independent
+            // but the code selector is broad ('.square'), we need to be careful.
+            // If we run animal.animate('.square', ...) it targets ALL squares.
+            // In the visual demo, likely all squares move RIGHT.
+            // Then the second one ALSO rotates.
+            // Then the third one ALSO scales.
+            // Let's implement exactly what the code says.
+            
+            // 1. Move all squares
+            animal.animate('.square', { 
+                x: '17rem', 
+                duration: 1000,
+                easing: 'ease-out'
+            });
+
+            // 2. Rotate the ID one
+            animal.animate('#css-selector-id', { 
+                rotate: '1turn', 
+                duration: 1000,
+                easing: 'ease-out'
+            });
+
+            // 3. Scale the 3rd row square
+            // Note: The HTML structure in demo-visual matches the selectors:
+            // .medium.row -> .square
+            // We use :nth-child(3) on the ROW, then find .square inside it.
+            // But wait, in .demo-visual, the rows are children of .demo-visual.
+            // row 1 is child 1
+            // row 2 is child 2
+            // row 3 is child 3
+            // So .medium.row:nth-child(3) should work relative to .demo-visual container if we scope it 
+            // or if we use the global document selector properly.
+            
+            // However, document.querySelectorAll('.row:nth-child(3)') might pick up other rows on the page if any.
+            // The class is "medium row".
+            // Let's use the specific selector from the code example.
+            
+            // We need to be careful about ":nth-child(3)". 
+            // The .medium.row elements are inside .demo-visual.
+            // If .demo-visual has other children (like text nodes or comments), index might differ.
+            // But browsers usually count element children for querySelector.
+            
+            // Let's tweak the selector to ensure it hits OUR 3rd row.
+            // The code example says: '.row:nth-child(3) .square'
+            // In our HTML: <div class="medium row"> is the class.
+            // So '.row' works if it has that class.
+            
+            animal.animate('.medium.row:nth-child(3) .square', { 
+                scale: [1, .5, 1], 
+                duration: 1000,
+                easing: 'ease-in-out'
+            });
+        }
+        
+        // Auto run
+        setTimeout(runDemo, 500);
+
+    </script>
+</body>
+</html>

+ 177 - 0
doc/demo.css

@@ -0,0 +1,177 @@
+:root {
+    --bg-color: #111;
+    --text-color: #9b9b9b;
+    --highlight-color: #ff4b4b;
+    --secondary-color: #2a2a2e;
+    --accent-color: #ff9f43;
+    --code-bg: #000;
+    --orange: #FF8F42;
+    --border-color: #222;
+}
+
+body {
+    background-color: var(--bg-color);
+    color: var(--text-color);
+    font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Open Sans", "Helvetica Neue", sans-serif;
+    margin: 0;
+    padding: 40px;
+    display: flex;
+    justify-content: center;
+    overflow-y: auto;
+}
+
+.container {
+    max-width: 900px;
+    width: 100%;
+}
+
+h1 {
+    color: var(--orange);
+    font-size: 42px;
+    margin-bottom: 15px;
+    font-weight: 600;
+    letter-spacing: -1px;
+}
+
+.description {
+    font-size: 18px;
+    line-height: 1.5;
+    margin-bottom: 40px;
+    color: #ccc;
+}
+
+h2 {
+    font-size: 24px;
+    color: #fff;
+    margin-bottom: 10px;
+    font-weight: 500;
+}
+
+p {
+    margin-top: 0;
+    margin-bottom: 20px;
+    line-height: 1.6;
+}
+
+code.inline {
+    background: rgba(255,255,255,0.1);
+    color: #e6e6e6;
+    padding: 2px 6px;
+    border-radius: 3px;
+    font-family: 'Roboto Mono', monospace;
+    font-size: 0.9em;
+}
+
+/* Code & Demo Box */
+.box-container {
+    margin-top: 50px;
+    background: #171717;
+    border-radius: 6px;
+    overflow: hidden;
+    box-shadow: 0 10px 40px rgba(0,0,0,0.2);
+}
+
+.box-header {
+    display: flex;
+    justify-content: space-between;
+    align-items: center;
+    padding: 0 20px;
+    height: 50px;
+    background: rgba(255,255,255,0.02);
+    border-bottom: 1px solid var(--border-color);
+}
+
+.box-title {
+    color: var(--orange);
+    font-weight: 600;
+    font-size: 14px;
+}
+
+.tabs {
+    display: flex;
+    gap: 20px;
+    height: 100%;
+}
+
+.tab {
+    display: flex;
+    align-items: center;
+    height: 100%;
+    cursor: pointer;
+    color: #666;
+    font-weight: 600;
+    font-size: 13px;
+    border-bottom: 2px solid transparent;
+    transition: color 0.2s;
+}
+
+.tab:hover {
+    color: #999;
+}
+
+.tab.active {
+    color: var(--orange);
+    border-bottom-color: var(--orange);
+}
+
+.code-view, .html-view {
+    background: var(--code-bg);
+    padding: 20px;
+    font-family: 'Roboto Mono', 'Monaco', monospace;
+    font-size: 13px;
+    line-height: 1.6;
+    color: #abb2bf;
+    overflow-x: auto;
+    display: none;
+}
+
+.code-view.active, .html-view.active {
+    display: block;
+}
+
+/* Syntax Highlighting */
+.kwd { color: #c678dd; }
+.str { color: #98c379; }
+.fun { color: #61afef; }
+.num { color: #d19a66; }
+.tag { color: #e06c75; }
+.attr { color: #d19a66; }
+.val { color: #98c379; }
+.punc { color: #abb2bf; }
+.com { color: #5c6370; font-style: italic; }
+
+.demo-visual {
+    padding: 30px;
+    background: #1a1a1a;
+    border-top: 1px solid var(--border-color);
+    min-height: 150px;
+    display: flex;
+    flex-direction: column;
+    justify-content: center;
+    gap: 20px;
+}
+
+.action-bar {
+    padding: 15px 30px;
+    display: flex;
+    justify-content: flex-end;
+    border-top: 1px solid var(--border-color);
+}
+
+.play-btn {
+    background: transparent;
+    border: 1px solid #444;
+    color: #fff;
+    padding: 6px 14px;
+    border-radius: 100px;
+    font-size: 12px;
+    font-weight: 600;
+    cursor: pointer;
+    transition: all 0.2s;
+}
+
+.play-btn:hover {
+    border-color: var(--orange);
+    color: var(--orange);
+}
+

+ 187 - 0
doc/index.html

@@ -0,0 +1,187 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+    <meta charset="UTF-8">
+    <meta name="viewport" content="width=device-width, initial-scale=1.0">
+    <title>Animal.js Documentation</title>
+    <style>
+        :root {
+            --sidebar-bg: #111;
+            --main-bg: #111;
+            --border-color: #222;
+            --text-color: #999;
+            --active-color: #ff4b4b;
+            --hover-color: #eee;
+            --middle-col-bg: #161616;
+        }
+        
+        body {
+            margin: 0;
+            padding: 0;
+            height: 100vh;
+            display: flex;
+            background: var(--main-bg);
+            color: var(--text-color);
+            font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
+            overflow: hidden;
+        }
+
+        /* 1. LEFT SIDEBAR - NAVIGATION */
+        .sidebar-left {
+            width: 240px;
+            background: var(--sidebar-bg);
+            border-right: 1px solid var(--border-color);
+            display: flex;
+            flex-direction: column;
+            overflow-y: auto;
+            flex-shrink: 0;
+        }
+
+        .logo-area {
+            padding: 20px;
+            font-size: 18px;
+            font-weight: bold;
+            color: #fff;
+            border-bottom: 1px solid var(--border-color);
+            display: flex;
+            align-items: center;
+            gap: 10px;
+        }
+        .logo-circle {
+            width: 12px;
+            height: 12px;
+            background: var(--active-color);
+            border-radius: 50%;
+        }
+
+        .nav-group {
+            padding: 20px 0;
+        }
+        
+        .nav-header {
+            padding: 0 20px 10px 20px;
+            font-size: 12px;
+            text-transform: uppercase;
+            letter-spacing: 1px;
+            color: #666;
+            font-weight: 600;
+        }
+
+        .nav-item {
+            display: block;
+            padding: 8px 20px;
+            color: var(--text-color);
+            text-decoration: none;
+            font-size: 14px;
+            cursor: pointer;
+            transition: color 0.2s;
+            border-left: 2px solid transparent;
+        }
+
+        .nav-item:hover {
+            color: var(--hover-color);
+        }
+
+        .nav-item.active {
+            color: var(--active-color);
+            border-left-color: var(--active-color);
+            background: rgba(255, 75, 75, 0.05);
+        }
+        
+        .nav-sub {
+            padding-left: 35px;
+            font-size: 13px;
+        }
+
+        /* 2. MIDDLE SIDEBAR - SEARCH & CARDS */
+        .sidebar-middle {
+            width: 320px;
+            background: var(--middle-col-bg);
+            border-right: 1px solid var(--border-color);
+            display: flex;
+            flex-direction: column;
+            overflow: hidden;
+            flex-shrink: 0;
+        }
+        
+        #search-frame {
+            width: 100%;
+            height: 100%;
+            border: none;
+            background: transparent;
+        }
+
+        /* 3. RIGHT CONTENT - EXAMPLES & DEMO */
+        .content-right {
+            flex: 1;
+            display: flex;
+            flex-direction: column;
+            background: #000;
+            overflow: hidden;
+        }
+        
+        #content-frame {
+            flex: 1;
+            border: none;
+            width: 100%;
+            height: 100%;
+        }
+
+    </style>
+</head>
+<body>
+
+    <!-- 1. Left Sidebar -->
+    <div class="sidebar-left">
+        <div class="logo-area">
+            <div class="logo-circle"></div>
+            Animal.js
+        </div>
+        
+        <div class="nav-group">
+            <div class="nav-item" onclick="loadContent('test_on_update.html')" style="padding-left: 20px; font-weight: 500;">Search</div>
+        </div>
+        
+        <div class="nav-group">
+            <div class="nav-header">Getting Started</div>
+            <a class="nav-item">Installation</a>
+            <a class="nav-item">Module imports</a>
+        </div>
+
+        <div class="nav-group">
+            <div class="nav-header">Animation</div>
+            <div class="nav-item">Targets</div>
+            <a class="nav-item nav-sub" onclick="loadContent('test_css_selector.html', this)">CSS Selector</a>
+            <a class="nav-item nav-sub" onclick="loadContent('test_css_selector.html', this)">DOM Elements</a>
+            <a class="nav-item nav-sub" onclick="loadContent('test_css_selector.html', this)">JavaScript Objects</a>
+            
+            <div class="nav-item" style="margin-top: 10px;">Callbacks</div>
+            <a class="nav-item nav-sub active" onclick="loadContent('test_on_update.html', this)">onUpdate</a>
+            <a class="nav-item nav-sub">onBegin</a>
+            <a class="nav-item nav-sub">onComplete</a>
+        </div>
+    </div>
+
+    <!-- 2. Middle Sidebar (Static Search/Hub) -->
+    <div class="sidebar-middle">
+        <iframe id="search-frame" src="search.html"></iframe>
+    </div>
+
+    <!-- 3. Right Content (Examples & Demo) -->
+    <div class="content-right">
+        <iframe id="content-frame" src="test_on_update.html"></iframe>
+    </div>
+
+    <script>
+        // Update Right Content directly from Nav
+        // AND Middle column stays fixed as 'search.html' per user request
+        function loadContent(url, el) {
+            document.getElementById('content-frame').src = url;
+            
+            // Update active state
+            document.querySelectorAll('.nav-item').forEach(item => item.classList.remove('active'));
+            if (el) el.classList.add('active');
+        }
+    </script>
+</body>
+</html>

+ 81 - 0
doc/list_callbacks.html

@@ -0,0 +1,81 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+    <meta charset="UTF-8">
+    <meta name="viewport" content="width=device-width, initial-scale=1.0">
+    <title>Callbacks - List</title>
+    <link rel="stylesheet" href="demo.css">
+    <style>
+        body { padding: 20px; display: block; }
+        .search-header { margin-bottom: 30px; }
+        .search-title { font-size: 14px; font-weight: 600; color: #888; margin-bottom: 15px; }
+        .search-input-container { position: relative; width: 100%; }
+        .search-input { width: 100%; background: #1a1a1a; border: 1px solid #333; color: #fff; padding: 12px 40px; border-radius: 6px; font-size: 14px; box-sizing: border-box; outline: none; }
+        .search-input:focus { border-color: var(--orange); }
+        .search-icon { position: absolute; left: 15px; top: 50%; transform: translateY(-50%); width: 14px; height: 14px; border: 2px solid #666; border-radius: 50%; }
+        .search-icon:after { content: ''; position: absolute; top: 10px; left: 10px; width: 4px; height: 2px; background: #666; transform: rotate(45deg); }
+        .grid-section { margin-bottom: 30px; }
+        .section-title { font-size: 14px; color: #888; margin-bottom: 15px; font-weight: 500; }
+        .card-grid { display: grid; grid-template-columns: 1fr; gap: 10px; }
+        .card { background: #1a1a1a; border-radius: 8px; padding: 15px; display: flex; align-items: center; cursor: pointer; transition: background 0.2s; border: 1px solid transparent; }
+        .card:hover { background: #222; border-color: #333; }
+        .card.active { border-color: var(--orange); background: #222; }
+        .card-icon { width: 30px; height: 30px; background: #333; border-radius: 4px; margin-right: 15px; display: flex; align-items: center; justify-content: center; font-weight: bold; color: #888; font-size: 12px; }
+        .card-text { color: #ccc; font-size: 14px; font-weight: 500; }
+        .card-tag { margin-left: auto; font-size: 10px; padding: 2px 6px; background: #333; border-radius: 4px; color: #aaa; }
+    </style>
+</head>
+<body>
+    <div class="search-header">
+        <div class="search-title">Callbacks</div>
+        <div class="search-input-container">
+            <div class="search-icon"></div>
+            <input type="text" class="search-input" placeholder="Filter callbacks...">
+        </div>
+    </div>
+
+    <div class="grid-section">
+        <div class="section-title">Standard Callbacks</div>
+        <div class="card-grid">
+            <!-- onUpdate -->
+            <div class="card" onclick="selectItem('test_on_update.html', this)">
+                <div class="card-icon">fn</div>
+                <div class="card-text">onUpdate</div>
+                <div class="card-tag">JS</div>
+            </div>
+            
+            <!-- onBegin -->
+            <div class="card">
+                <div class="card-icon">fn</div>
+                <div class="card-text">onBegin</div>
+                <div class="card-tag">JS</div>
+            </div>
+
+            <!-- onComplete -->
+            <div class="card">
+                <div class="card-icon">fn</div>
+                <div class="card-text">onComplete</div>
+            </div>
+            
+            <!-- onLoop -->
+            <div class="card">
+                <div class="card-icon">fn</div>
+                <div class="card-text">onLoop</div>
+                <div class="card-tag">JS</div>
+            </div>
+        </div>
+    </div>
+
+    <script>
+        function selectItem(url, el) {
+            document.querySelectorAll('.card').forEach(c => c.classList.remove('active'));
+            el.classList.add('active');
+            
+            if (window.parent && window.parent.loadContent) {
+                window.parent.loadContent(url);
+            }
+        }
+    </script>
+</body>
+</html>
+

+ 79 - 0
doc/list_targets.html

@@ -0,0 +1,79 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+    <meta charset="UTF-8">
+    <meta name="viewport" content="width=device-width, initial-scale=1.0">
+    <title>Targets - List</title>
+    <link rel="stylesheet" href="demo.css">
+    <style>
+        body { padding: 20px; display: block; }
+        .search-header { margin-bottom: 30px; }
+        .search-title { font-size: 14px; font-weight: 600; color: #888; margin-bottom: 15px; }
+        .search-input-container { position: relative; width: 100%; }
+        .search-input { width: 100%; background: #1a1a1a; border: 1px solid #333; color: #fff; padding: 12px 40px; border-radius: 6px; font-size: 14px; box-sizing: border-box; outline: none; }
+        .search-input:focus { border-color: var(--orange); }
+        .search-icon { position: absolute; left: 15px; top: 50%; transform: translateY(-50%); width: 14px; height: 14px; border: 2px solid #666; border-radius: 50%; }
+        .search-icon:after { content: ''; position: absolute; top: 10px; left: 10px; width: 4px; height: 2px; background: #666; transform: rotate(45deg); }
+        .grid-section { margin-bottom: 30px; }
+        .section-title { font-size: 14px; color: #888; margin-bottom: 15px; font-weight: 500; }
+        .card-grid { display: grid; grid-template-columns: 1fr; gap: 10px; } /* Single column for sidebar list usually better? Or grid? Screenshot showed grid. sticking to grid but maybe 1 col if narrow. */
+        .card { background: #1a1a1a; border-radius: 8px; padding: 15px; display: flex; align-items: center; cursor: pointer; transition: background 0.2s; border: 1px solid transparent; }
+        .card:hover { background: #222; border-color: #333; }
+        .card.active { border-color: var(--orange); background: #222; }
+        .card-icon { width: 30px; height: 30px; background: #333; border-radius: 4px; margin-right: 15px; display: flex; align-items: center; justify-content: center; font-weight: bold; color: #888; font-size: 12px; }
+        .card-text { color: #ccc; font-size: 14px; font-weight: 500; }
+    </style>
+</head>
+<body>
+    <div class="search-header">
+        <div class="search-title">Targets</div>
+        <div class="search-input-container">
+            <div class="search-icon"></div>
+            <input type="text" class="search-input" placeholder="Filter targets...">
+        </div>
+    </div>
+
+    <div class="grid-section">
+        <div class="section-title">Selectors & Objects</div>
+        <div class="card-grid">
+            <!-- CSS Selector -->
+            <div class="card" onclick="selectItem('test_css_selector.html', this)">
+                <div class="card-icon">#</div>
+                <div class="card-text">CSS Selector</div>
+            </div>
+            
+            <!-- DOM Elements -->
+            <div class="card" onclick="selectItem('test_css_selector.html', this)"> <!-- Reuse same test for demo -->
+                <div class="card-icon">&lt;&gt;</div>
+                <div class="card-text">DOM Elements</div>
+            </div>
+
+            <!-- JS Objects -->
+            <div class="card" onclick="selectItem('test_css_selector.html', this)">
+                <div class="card-icon">{}</div>
+                <div class="card-text">JavaScript Objects</div>
+            </div>
+            
+            <!-- Arrays -->
+            <div class="card">
+                <div class="card-icon">[]</div>
+                <div class="card-text">Array of targets</div>
+            </div>
+        </div>
+    </div>
+
+    <script>
+        function selectItem(url, el) {
+            // Update UI
+            document.querySelectorAll('.card').forEach(c => c.classList.remove('active'));
+            el.classList.add('active');
+            
+            // Call parent to load content
+            if (window.parent && window.parent.loadContent) {
+                window.parent.loadContent(url);
+            }
+        }
+    </script>
+</body>
+</html>
+

+ 282 - 0
doc/search.html

@@ -0,0 +1,282 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+    <meta charset="UTF-8">
+    <meta name="viewport" content="width=device-width, initial-scale=1.0">
+    <title>Search - Animal.js</title>
+    <link rel="stylesheet" href="demo.css">
+    <style>
+        body {
+            padding: 20px;
+            display: block; /* Override flex center */
+            background-color: transparent; /* Allow iframe bg to show if needed */
+        }
+        
+        .search-header {
+            margin-bottom: 30px;
+        }
+        
+        .search-title {
+            font-size: 14px;
+            font-weight: 600;
+            color: #888;
+            margin-bottom: 15px;
+        }
+        
+        .search-input-container {
+            position: relative;
+            width: 100%;
+        }
+        
+        .search-input {
+            width: 100%;
+            background: #1a1a1a;
+            border: 1px solid #333;
+            color: #fff;
+            padding: 12px 40px;
+            border-radius: 6px;
+            font-size: 14px;
+            box-sizing: border-box;
+            outline: none;
+        }
+        
+        .search-input:focus {
+            border-color: var(--orange);
+        }
+        
+        .search-icon {
+            position: absolute;
+            left: 15px;
+            top: 50%;
+            transform: translateY(-50%);
+            width: 14px;
+            height: 14px;
+            border: 2px solid #666;
+            border-radius: 50%;
+        }
+        .search-icon:after {
+            content: '';
+            position: absolute;
+            top: 10px;
+            left: 10px;
+            width: 4px;
+            height: 2px;
+            background: #666;
+            transform: rotate(45deg);
+        }
+
+        .grid-section {
+            margin-bottom: 30px;
+        }
+        
+        .section-title {
+            font-size: 14px;
+            color: #888;
+            margin-bottom: 15px;
+            font-weight: 500;
+        }
+        
+        .card-grid {
+            display: grid;
+            grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
+            gap: 15px;
+        }
+        
+        .card {
+            background: #1a1a1a; 
+            border-radius: 8px;
+            padding: 20px;
+            display: flex;
+            flex-direction: column;
+            cursor: pointer;
+            transition: background 0.2s, border-color 0.2s;
+            position: relative;
+            min-height: 80px;
+            justify-content: space-between;
+            border: 1px solid transparent;
+        }
+        
+        .card:hover {
+            background: #222;
+            border-color: #333;
+        }
+        
+        .card-placeholder-line {
+            height: 8px;
+            width: 40px;
+            background: #333;
+            border-radius: 2px;
+        }
+        
+        .card-large-icon {
+            font-size: 40px;
+            color: #444;
+            display: flex;
+            justify-content: center;
+            align-items: center;
+            height: 60px;
+            background: #222;
+            border-radius: 4px;
+            margin-bottom: 10px;
+            font-family: monospace;
+            font-weight: bold;
+            transition: transform 0.1s;
+        }
+        
+        .card-footer {
+            margin-top: 10px;
+            background: #222;
+            padding: 5px 10px;
+            border-radius: 4px;
+            font-size: 10px;
+            color: #666;
+            text-align: center;
+            text-transform: uppercase;
+            letter-spacing: 1px;
+        }
+        
+        .dot {
+            width: 8px;
+            height: 8px;
+            background: #444;
+            border-radius: 2px;
+        }
+
+    </style>
+</head>
+<body>
+
+    <!-- Search Section -->
+    <div class="search-header">
+        <div class="search-title">Search</div>
+        <div class="search-input-container">
+            <div class="search-icon"></div>
+            <input type="text" class="search-input" placeholder="Search documentation...">
+        </div>
+    </div>
+
+    <!-- Documentation Section -->
+    <div class="grid-section">
+        <div class="section-title">Documentation</div>
+        <div class="card-grid">
+            <div class="card" style="height: 100px;">
+                <div style="display: grid; grid-template-columns: repeat(10, 1fr); gap: 4px; opacity: 0.1;">
+                    <div class="dot"></div><div class="dot"></div><div class="dot"></div><div class="dot"></div><div class="dot"></div>
+                    <div class="dot"></div><div class="dot"></div><div class="dot"></div><div class="dot"></div><div class="dot"></div>
+                    <div class="dot"></div><div class="dot"></div><div class="dot"></div><div class="dot"></div><div class="dot"></div>
+                    <div class="dot"></div><div class="dot"></div><div class="dot"></div><div class="dot"></div><div class="dot"></div>
+                </div>
+            </div>
+        </div>
+    </div>
+
+    <!-- Vanilla JS Section (Interactive) -->
+    <div class="grid-section">
+        <div class="section-title">Using with vanilla JS</div>
+        <div class="card-grid">
+            <div class="card" onclick="playRotation(this, 'js-icon', 'js-count', 'test_css_selector.html')">
+                <div id="js-icon" class="card-large-icon">JS</div>
+                <div id="js-count" class="card-footer">Rotations: 0</div>
+            </div>
+        </div>
+    </div>
+
+    <!-- React Section (Interactive) -->
+    <div class="grid-section">
+        <div class="section-title">Using with React</div>
+        <div class="card-grid">
+            <div class="card" onclick="playRotation(this, 'react-icon', 'react-count', 'test_on_update.html')">
+                <div id="react-icon" class="card-large-icon" style="font-size: 30px;">⚛</div>
+                <div id="react-count" class="card-footer">Rotations: 0</div>
+            </div>
+        </div>
+    </div>
+    
+    <!-- Timer Section (Interactive) -->
+    <div class="grid-section">
+        <div class="section-title">Timer</div>
+        <div class="card-grid">
+             <div class="card" style="flex-direction: row; align-items: center; justify-content: space-between; padding: 15px; cursor: default;">
+                 <div style="font-size: 10px; color: #666;">current time</div>
+                 <div style="font-size: 10px; color: #666;">callback fired</div>
+             </div>
+             <div class="card" id="timer-card" onclick="toggleTimer()" style="flex-direction: row; align-items: center; justify-content: space-between; background: #1a1a1a;">
+                  <div id="timer-time" style="font-size: 24px; color: #666; font-family: monospace;">0</div>
+                  <div id="timer-cb" style="font-size: 24px; color: #666; font-family: monospace;">0</div>
+             </div>
+        </div>
+    </div>
+    
+    <script src="../animal.js"></script>
+    <script>
+        // --- Rotation Logic ---
+        let jsRotations = 0;
+        let reactRotations = 0;
+
+        function playRotation(card, iconId, countId, targetUrl) {
+            // 1. Update Parent Content
+            if (window.parent && window.parent.loadContent) {
+                window.parent.loadContent(targetUrl);
+            }
+            
+            // 2. Animate Icon
+            const icon = document.getElementById(iconId);
+            const count = document.getElementById(countId);
+            
+            // Increment logic
+            let current = (iconId === 'js-icon') ? ++jsRotations : ++reactRotations;
+            
+            // Use animal.js to rotate
+            // Rotate +360 from current visual state
+            // Simplified: just set rotation to current * 360
+            animal.animate(icon, {
+                rotate: current * 360, // 1turn, 2turn...
+                duration: 600,
+                easing: 'ease-out'
+            });
+            
+            count.textContent = 'Rotations: ' + current;
+        }
+        
+        // --- Timer Logic ---
+        let timerRunning = false;
+        let timerId = null;
+        let startTime = 0;
+        let callbacks = 0;
+        
+        function toggleTimer() {
+            if (timerRunning) {
+                // Stop
+                cancelAnimationFrame(timerId);
+                timerRunning = false;
+                document.getElementById('timer-card').style.borderColor = 'transparent';
+            } else {
+                // Start
+                timerRunning = true;
+                startTime = Date.now();
+                callbacks = 0;
+                document.getElementById('timer-card').style.borderColor = 'var(--orange)';
+                tick();
+            }
+        }
+        
+        function tick() {
+            if (!timerRunning) return;
+            
+            const now = Date.now();
+            const elapsed = now - startTime;
+            
+            document.getElementById('timer-time').textContent = elapsed;
+            document.getElementById('timer-cb').textContent = ++callbacks;
+            
+            timerId = requestAnimationFrame(tick);
+        }
+        
+        // Start timer by default to look alive? Or wait for click?
+        // Screenshot implies it's running (numbers > 0).
+        toggleTimer();
+        
+    </script>
+
+</body>
+</html>

+ 168 - 0
doc/test_css_selector.html

@@ -0,0 +1,168 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+    <meta charset="UTF-8">
+    <meta name="viewport" content="width=device-width, initial-scale=1.0">
+    <title>CSS Selector - Animal.js</title>
+    <link rel="stylesheet" href="demo.css">
+    <style>
+        .row {
+            position: relative;
+            padding: 20px 0;
+            border-bottom: 1px solid #2a2a2a;
+            display: flex;
+            align-items: center;
+        }
+        .row:last-child { border-bottom: none; }
+        
+        .square {
+            width: 28px;
+            height: 28px;
+            background-color: var(--highlight-color);
+            border-radius: 2px;
+        }
+        #css-selector-id {
+            background-color: var(--accent-color);
+        }
+    </style>
+</head>
+<body>
+
+    <div class="container">
+        <!-- Breadcrumb / Header -->
+        <div style="font-size: 10px; font-weight: bold; letter-spacing: 1px; color: #ff4b4b; margin-bottom: 20px;">ANIMATION > TARGETS</div>
+        
+        <h1>CSS Selector</h1>
+        <p class="description">Targets one or multiple DOM Elements using a CSS selector.</p>
+
+        <h2>Accepts</h2>
+        <p>Any <code class="inline">String</code> accepted by <code class="inline">document.querySelectorAll()</code></p>
+
+        <div class="box-container">
+            <div class="box-header">
+                <div class="box-title">CSS Selector code example</div>
+                <div class="tabs">
+                    <div class="tab active" onclick="switchTab('js')">JavaScript</div>
+                    <div class="tab" onclick="switchTab('html')">HTML</div>
+                </div>
+            </div>
+
+            <!-- JS Code View -->
+            <div id="js-code" class="code-view active">
+<span class="kwd">import</span> <span class="punc">{</span> animate <span class="punc">}</span> <span class="kwd">from</span> <span class="str">'animejs'</span><span class="punc">;</span>
+
+<span class="fun">animate</span><span class="punc">(</span><span class="str">'.square'</span><span class="punc">,</span> <span class="punc">{</span> x<span class="punc">:</span> <span class="str">'17rem'</span> <span class="punc">}</span><span class="punc">)</span><span class="punc">;</span>
+<span class="fun">animate</span><span class="punc">(</span><span class="str">'#css-selector-id'</span><span class="punc">,</span> <span class="punc">{</span> rotate<span class="punc">:</span> <span class="str">'1turn'</span> <span class="punc">}</span><span class="punc">)</span><span class="punc">;</span>
+<span class="fun">animate</span><span class="punc">(</span><span class="str">'.row:nth-child(3) .square'</span><span class="punc">,</span> <span class="punc">{</span> scale<span class="punc">:</span> <span class="punc">[</span><span class="num">1</span><span class="punc">,</span> <span class="num">.5</span><span class="punc">,</span> <span class="num">1</span><span class="punc">]</span> <span class="punc">}</span><span class="punc">)</span><span class="punc">;</span>
+            </div>
+
+            <!-- HTML Code View -->
+            <div id="html-code" class="html-view">
+<span class="tag">&lt;div</span> <span class="attr">class</span>=<span class="val">"medium row"</span><span class="tag">&gt;</span>
+  <span class="tag">&lt;div</span> <span class="attr">class</span>=<span class="val">"square"</span><span class="tag">&gt;&lt;/div&gt;</span>
+<span class="tag">&lt;/div&gt;</span>
+<span class="tag">&lt;div</span> <span class="attr">class</span>=<span class="val">"medium row"</span><span class="tag">&gt;</span>
+  <span class="tag">&lt;div</span> <span class="attr">id</span>=<span class="val">"css-selector-id"</span> <span class="attr">class</span>=<span class="val">"square"</span><span class="tag">&gt;&lt;/div&gt;</span>
+<span class="tag">&lt;/div&gt;</span>
+<span class="tag">&lt;div</span> <span class="attr">class</span>=<span class="val">"medium row"</span><span class="tag">&gt;</span>
+  <span class="tag">&lt;div</span> <span class="attr">class</span>=<span class="val">"square"</span><span class="tag">&gt;&lt;/div&gt;</span>
+<span class="tag">&lt;/div&gt;</span>
+            </div>
+
+            <!-- Live Demo -->
+            <div class="demo-visual">
+                <!-- Row 1 -->
+                <div class="medium row">
+                  <div class="square"></div>
+                </div>
+                <!-- Row 2 -->
+                <div class="medium row">
+                  <div id="css-selector-id" class="square"></div>
+                </div>
+                <!-- Row 3 -->
+                <div class="medium row">
+                  <div class="square"></div>
+                </div>
+            </div>
+            
+            <div class="action-bar">
+                <button class="play-btn" onclick="runDemo()">REPLAY</button>
+            </div>
+        </div>
+
+    </div>
+
+    <script src="../animal.js"></script>
+    <script>
+        function switchTab(tab) {
+            document.querySelectorAll('.tab').forEach(t => t.classList.remove('active'));
+            document.querySelectorAll('.code-view, .html-view').forEach(v => v.classList.remove('active'));
+            
+            if (tab === 'js') {
+                document.querySelector('.tabs .tab:nth-child(1)').classList.add('active');
+                document.getElementById('js-code').classList.add('active');
+            } else {
+                document.querySelector('.tabs .tab:nth-child(2)').classList.add('active');
+                document.getElementById('html-code').classList.add('active');
+            }
+        }
+
+        function runDemo() {
+            // Reset
+            document.querySelectorAll('.square').forEach(el => {
+                el.style.transform = 'none';
+            });
+            
+            // 1. Move all squares
+            animal.animate('.square', { 
+                x: '17rem', 
+                duration: 1000,
+                easing: 'ease-out'
+            });
+
+            // 2. Rotate the ID one
+            animal.animate('#css-selector-id', { 
+                rotate: '1turn', 
+                duration: 1000,
+                easing: 'ease-out'
+            });
+
+            // 3. Scale the 3rd row square
+            // Note: The HTML structure in demo-visual matches the selectors:
+            // .medium.row -> .square
+            // We use :nth-child(3) on the ROW, then find .square inside it.
+            // But wait, in .demo-visual, the rows are children of .demo-visual.
+            // row 1 is child 1
+            // row 2 is child 2
+            // row 3 is child 3
+            // So .medium.row:nth-child(3) should work relative to .demo-visual container if we scope it 
+            // or if we use the global document selector properly.
+            
+            // However, document.querySelectorAll('.row:nth-child(3)') might pick up other rows on the page if any.
+            // The class is "medium row".
+            // Let's use the specific selector from the code example.
+            
+            // We need to be careful about ":nth-child(3)". 
+            // The .medium.row elements are inside .demo-visual.
+            // If .demo-visual has other children (like text nodes or comments), index might differ.
+            // But browsers usually count element children for querySelector.
+            
+            // Let's tweak the selector to ensure it hits OUR 3rd row.
+            // The code example says: '.row:nth-child(3) .square'
+            // In our HTML: <div class="medium row"> is the class.
+            // So '.row' works if it has that class.
+            
+            animal.animate('.medium.row:nth-child(3) .square', { 
+                scale: [1, .5, 1], 
+                duration: 1000,
+                easing: 'ease-in-out'
+            });
+        }
+        
+        // Auto run
+        setTimeout(runDemo, 500);
+
+    </script>
+</body>
+</html>
+

+ 191 - 0
doc/test_on_update.html

@@ -0,0 +1,191 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+    <meta charset="UTF-8">
+    <meta name="viewport" content="width=device-width, initial-scale=1.0">
+    <title>onUpdate - Animal.js</title>
+    <link rel="stylesheet" href="demo.css">
+    <style>
+        .circle {
+            width: 50px;
+            height: 50px;
+            background-color: var(--highlight-color);
+            border-radius: 50%;
+        }
+        .value {
+            font-size: 20px;
+            font-weight: bold;
+            color: #fff;
+            margin-bottom: 20px;
+            font-family: monospace;
+        }
+        .demo-content {
+            display: flex;
+            flex-direction: column;
+            align-items: center;
+            justify-content: center;
+            width: 100%;
+        }
+    </style>
+</head>
+<body>
+
+    <div class="container">
+        <!-- Breadcrumb / Header -->
+        <div style="font-size: 10px; font-weight: bold; letter-spacing: 1px; color: #ff4b4b; margin-bottom: 20px;">ANIMATION > CALLBACKS</div>
+        
+        <h1>onUpdate</h1>
+        <p class="description">Executes a function on every frames of a running animation at the specified <code class="inline">frameRate</code>.</p>
+
+        <h2>Accepts</h2>
+        <p>A <code class="inline">Function</code> whose first argument is the animation itself</p>
+        
+        <h2>Default</h2>
+        <p><code class="inline">noop</code></p>
+        
+        <!-- Info Box (Blue) -->
+        <div style="background: rgba(33, 150, 243, 0.1); border-left: 4px solid #2196F3; padding: 15px; border-radius: 4px; margin-bottom: 30px; font-size: 14px;">
+            <div style="font-weight: bold; color: #2196F3; margin-bottom: 5px; font-size: 10px; letter-spacing: 1px;">INFO</div>
+            <div style="color: #90caf9; margin-bottom: 10px;">To change the default value globally, update the <code class="inline" style="color: #fff">engine.defaults</code> object.</div>
+            <div style="background: rgba(0,0,0,0.3); padding: 10px; border-radius: 4px; font-family: monospace; font-size: 12px; color: #a9b7c6;">
+                <span class="kwd">import</span> { engine } <span class="kwd">from</span> <span class="str">'animejs'</span>;<br>
+                engine.defaults.onUpdate = self => console.log(self.id);
+            </div>
+        </div>
+
+        <div class="box-container">
+            <div class="box-header">
+                <div class="box-title">onUpdate code example</div>
+                <div class="tabs">
+                    <div class="tab active" onclick="switchTab('js')">JavaScript</div>
+                    <div class="tab" onclick="switchTab('html')">HTML</div>
+                </div>
+            </div>
+
+            <!-- JS Code View -->
+            <div id="js-code" class="code-view active">
+<span class="kwd">import</span> <span class="punc">{</span> animate<span class="punc">,</span> utils <span class="punc">}</span> <span class="kwd">from</span> <span class="str">'animejs'</span><span class="punc">;</span>
+
+<span class="kwd">const</span> <span class="punc">[</span> $value <span class="punc">]</span> <span class="punc">=</span> utils.<span class="fun">$</span><span class="punc">(</span><span class="str">'.value'</span><span class="punc">);</span>
+
+<span class="kwd">let</span> updates <span class="punc">=</span> <span class="num">0</span><span class="punc">;</span>
+
+<span class="kwd">const</span> animation <span class="punc">=</span> <span class="fun">animate</span><span class="punc">(</span><span class="str">'.circle'</span><span class="punc">,</span> <span class="punc">{</span>
+  x<span class="punc">:</span> <span class="str">'16rem'</span><span class="punc">,</span>
+  loopDelay<span class="punc">:</span> <span class="num">1500</span><span class="punc">,</span>
+  loop<span class="punc">:</span> <span class="kwd">true</span><span class="punc">,</span>
+  alternate<span class="punc">:</span> <span class="kwd">true</span><span class="punc">,</span>
+  onUpdate<span class="punc">:</span> self <span class="punc">=></span> $value.textContent <span class="punc">=</span> ++updates
+<span class="punc">}</span><span class="punc">);</span>
+            </div>
+
+            <!-- HTML Code View -->
+            <div id="html-code" class="html-view">
+<span class="tag">&lt;div</span> <span class="attr">class</span>=<span class="val">"value"</span><span class="tag">&gt;</span>0<span class="tag">&lt;/div&gt;</span>
+<span class="tag">&lt;div</span> <span class="attr">class</span>=<span class="val">"circle"</span><span class="tag">&gt;&lt;/div&gt;</span>
+            </div>
+
+            <!-- Live Demo -->
+            <div class="demo-visual">
+                <div class="demo-content">
+                    <div class="value">0</div>
+                    <div class="circle"></div>
+                </div>
+            </div>
+            
+            <div class="action-bar">
+                <button class="play-btn" onclick="runDemo()">REPLAY</button>
+            </div>
+        </div>
+
+    </div>
+
+    <script src="../animal.js"></script>
+    <script>
+        // Mock 'utils' for the demo to match code example
+        const utils = {
+            $: (selector) => document.querySelectorAll(selector)
+        };
+
+        function switchTab(tab) {
+            document.querySelectorAll('.tab').forEach(t => t.classList.remove('active'));
+            document.querySelectorAll('.code-view, .html-view').forEach(v => v.classList.remove('active'));
+            
+            if (tab === 'js') {
+                document.querySelector('.tabs .tab:nth-child(1)').classList.add('active');
+                document.getElementById('js-code').classList.add('active');
+            } else {
+                document.querySelector('.tabs .tab:nth-child(2)').classList.add('active');
+                document.getElementById('html-code').classList.add('active');
+            }
+        }
+
+        let currentAnimation = null;
+
+        function runDemo() {
+            // Reset
+            const $circle = document.querySelector('.circle');
+            const $value = document.querySelector('.value');
+            $circle.style.transform = 'none';
+            $value.textContent = '0';
+            
+            // NOTE: animal.js support for 'onUpdate'
+            // The library I read earlier supports 'update' in params, which receives { target, progress, value }.
+            // The screenshot code uses 'onUpdate: self => ...'.
+            // To make this work with existing animal.js, I should use 'update'.
+            // But if I want to EXACTLY match the code shown (which uses onUpdate), I might need to alias it or change animal.js.
+            // For this test page, I will pass 'update' property to animal.js but use the logic shown.
+            // Wait, animal.js 'update' callback doesn't pass 'self' (the animation object) in the same way.
+            // It passes { target, progress, value }.
+            // The screenshot code uses `++updates`. It doesn't use `self` properties really, just the callback firing.
+            // So I can map `update` to `onUpdate` functionality.
+            
+            // However, animal.js 'animate' options keys are filtered.
+            // 'update' is allowed. 'onUpdate' is NOT in the allowed list in animal.js.
+            // "if (['duration', ..., 'update', ...].includes(key)) return;" -> this is for excluding from css props.
+            // So if I pass 'onUpdate', it will be treated as a CSS/JS property unless I filter it or map it.
+            // Looking at animal.js source again:
+            // "if (['duration', ..., 'update', ...].includes(key)) return;"
+            // If I pass 'onUpdate', it will fall through to be treated as a property and tried to animate!
+            // So I MUST use 'update' key for animal.js.
+            
+            // To make the demo code valid for animal.js, I would have to change the display code or wrapper.
+            // But the user wants "Replicate the test".
+            // So I will use `update` in the actual execution code.
+            
+            let updates = 0;
+            
+            // 1. Get elements
+            const [ valueEl ] = utils.$('.value');
+            
+            // 2. Animate
+            animal.animate('.circle', {
+                x: '16rem',
+                duration: 1000, // Explicit duration as default might differ
+                loop: true,
+                direction: 'alternate', // animal.js uses 'direction: alternate', anime.js uses 'alternate: true' (helper) or direction
+                // In screenshot: loop: true, alternate: true
+                // animal.js: loop is number of iterations. loop: true might be treated as 1 or infinite?
+                // Let's check animal.js: "loop = 1" default. "iterations: loop".
+                // If I pass true, loop might be cast to 1? WAAPI iterations accepts number or Infinity.
+                // Boolean true might be problematic for WAAPI iterations? "iterations" usually expects a number.
+                // Anime.js 'loop: true' means infinite.
+                
+                // Let's adjust for animal.js quirks to make the visual work:
+                loop: Infinity, 
+                direction: 'alternate',
+                
+                // The callback
+                update: () => {
+                    valueEl.textContent = ++updates;
+                }
+            });
+        }
+        
+        // Auto run
+        setTimeout(runDemo, 500);
+
+    </script>
+</body>
+</html>
+