@mumulinya167/ccm-pet-assets 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (178) hide show
  1. package/README.md +4 -0
  2. package/assets/calico/calico-collapsing.apng +0 -0
  3. package/assets/calico/calico-dozing.apng +0 -0
  4. package/assets/calico/calico-error.apng +0 -0
  5. package/assets/calico/calico-happy.apng +0 -0
  6. package/assets/calico/calico-idle-follow.svg +324 -0
  7. package/assets/calico/calico-idle.apng +0 -0
  8. package/assets/calico/calico-mini-alert.apng +0 -0
  9. package/assets/calico/calico-mini-crabwalk.apng +0 -0
  10. package/assets/calico/calico-mini-enter.apng +0 -0
  11. package/assets/calico/calico-mini-happy.apng +0 -0
  12. package/assets/calico/calico-mini-idle.apng +0 -0
  13. package/assets/calico/calico-mini-peek.apng +0 -0
  14. package/assets/calico/calico-mini-sleep.apng +0 -0
  15. package/assets/calico/calico-notification.apng +0 -0
  16. package/assets/calico/calico-react-drag.apng +0 -0
  17. package/assets/calico/calico-react-left.apng +0 -0
  18. package/assets/calico/calico-react-poke.apng +0 -0
  19. package/assets/calico/calico-sleeping.apng +0 -0
  20. package/assets/calico/calico-thinking.apng +0 -0
  21. package/assets/calico/calico-waking.apng +0 -0
  22. package/assets/calico/calico-working-building.apng +0 -0
  23. package/assets/calico/calico-working-carrying.apng +0 -0
  24. package/assets/calico/calico-working-conducting.apng +0 -0
  25. package/assets/calico/calico-working-juggling.apng +0 -0
  26. package/assets/calico/calico-working-sweeping.apng +0 -0
  27. package/assets/calico/calico-working-typing.apng +0 -0
  28. package/assets/calico/calico-yawning.apng +0 -0
  29. package/assets/calico.svg +324 -0
  30. package/assets/clawd/LICENSE +22 -0
  31. package/assets/clawd/clawd-about-hero.svg +202 -0
  32. package/assets/clawd/clawd-collapse-sleep.svg +247 -0
  33. package/assets/clawd/clawd-error.svg +94 -0
  34. package/assets/clawd/clawd-happy.svg +161 -0
  35. package/assets/clawd/clawd-headphones-groove.svg +194 -0
  36. package/assets/clawd/clawd-idle-bubble.svg +180 -0
  37. package/assets/clawd/clawd-idle-collapse.svg +101 -0
  38. package/assets/clawd/clawd-idle-doze.svg +72 -0
  39. package/assets/clawd/clawd-idle-follow.svg +64 -0
  40. package/assets/clawd/clawd-idle-living.svg +196 -0
  41. package/assets/clawd/clawd-idle-look.svg +115 -0
  42. package/assets/clawd/clawd-idle-reading.svg +252 -0
  43. package/assets/clawd/clawd-idle-yawn.svg +158 -0
  44. package/assets/clawd/clawd-mini-alert.svg +129 -0
  45. package/assets/clawd/clawd-mini-crabwalk.svg +76 -0
  46. package/assets/clawd/clawd-mini-enter-sleep.svg +64 -0
  47. package/assets/clawd/clawd-mini-enter.svg +114 -0
  48. package/assets/clawd/clawd-mini-happy.svg +124 -0
  49. package/assets/clawd/clawd-mini-idle.svg +83 -0
  50. package/assets/clawd/clawd-mini-peek.svg +82 -0
  51. package/assets/clawd/clawd-mini-sleep.svg +112 -0
  52. package/assets/clawd/clawd-mini-typing.svg +153 -0
  53. package/assets/clawd/clawd-notification.svg +438 -0
  54. package/assets/clawd/clawd-react-annoyed.svg +167 -0
  55. package/assets/clawd/clawd-react-double-jump.svg +229 -0
  56. package/assets/clawd/clawd-react-double.svg +108 -0
  57. package/assets/clawd/clawd-react-drag.svg +102 -0
  58. package/assets/clawd/clawd-react-left.svg +102 -0
  59. package/assets/clawd/clawd-react-right.svg +102 -0
  60. package/assets/clawd/clawd-sleeping.svg +118 -0
  61. package/assets/clawd/clawd-static-base.svg +21 -0
  62. package/assets/clawd/clawd-wake.svg +277 -0
  63. package/assets/clawd/clawd-working-building.svg +329 -0
  64. package/assets/clawd/clawd-working-carrying.svg +178 -0
  65. package/assets/clawd/clawd-working-debugger.svg +245 -0
  66. package/assets/clawd/clawd-working-juggling.svg +183 -0
  67. package/assets/clawd/clawd-working-sweeping.svg +248 -0
  68. package/assets/clawd/clawd-working-thinking.svg +501 -0
  69. package/assets/clawd/clawd-working-typing-boss.svg +307 -0
  70. package/assets/clawd/clawd-working-typing.svg +273 -0
  71. package/assets/clawd/clawd-working-ultrathink.svg +166 -0
  72. package/assets/clawd/clawd-working-wizard.svg +356 -0
  73. package/assets/clawd.svg +64 -0
  74. package/assets/cloudling/cloudling/cloudling-attention.svg +380 -0
  75. package/assets/cloudling/cloudling/cloudling-building.svg +667 -0
  76. package/assets/cloudling/cloudling/cloudling-carrying.svg +604 -0
  77. package/assets/cloudling/cloudling/cloudling-conducting.svg +616 -0
  78. package/assets/cloudling/cloudling/cloudling-dozing-to-sleeping.svg +578 -0
  79. package/assets/cloudling/cloudling/cloudling-dozing.svg +358 -0
  80. package/assets/cloudling/cloudling/cloudling-error.svg +428 -0
  81. package/assets/cloudling/cloudling/cloudling-idle-reading.svg +658 -0
  82. package/assets/cloudling/cloudling/cloudling-idle-to-dozing.svg +568 -0
  83. package/assets/cloudling/cloudling/cloudling-idle-to-sleeping.svg +811 -0
  84. package/assets/cloudling/cloudling/cloudling-idle.svg +255 -0
  85. package/assets/cloudling/cloudling/cloudling-juggling.svg +648 -0
  86. package/assets/cloudling/cloudling/cloudling-mini-alert.svg +390 -0
  87. package/assets/cloudling/cloudling/cloudling-mini-crabwalk.svg +335 -0
  88. package/assets/cloudling/cloudling/cloudling-mini-enter-roll-in.svg +226 -0
  89. package/assets/cloudling/cloudling/cloudling-mini-enter-sleep.svg +232 -0
  90. package/assets/cloudling/cloudling/cloudling-mini-happy.svg +371 -0
  91. package/assets/cloudling/cloudling/cloudling-mini-idle.svg +279 -0
  92. package/assets/cloudling/cloudling/cloudling-mini-peek.svg +328 -0
  93. package/assets/cloudling/cloudling/cloudling-mini-sleep.svg +260 -0
  94. package/assets/cloudling/cloudling/cloudling-mini-typing.svg +692 -0
  95. package/assets/cloudling/cloudling/cloudling-notification.svg +494 -0
  96. package/assets/cloudling/cloudling/cloudling-react-drag.svg +83 -0
  97. package/assets/cloudling/cloudling/cloudling-sleeping-to-idle.svg +555 -0
  98. package/assets/cloudling/cloudling/cloudling-sleeping.svg +500 -0
  99. package/assets/cloudling/cloudling/cloudling-sweeping.svg +292 -0
  100. package/assets/cloudling/cloudling/cloudling-thinking.svg +720 -0
  101. package/assets/cloudling/cloudling/cloudling-typing.svg +354 -0
  102. package/assets/cloudling/cloudling-attention.svg +380 -0
  103. package/assets/cloudling/cloudling-building.svg +667 -0
  104. package/assets/cloudling/cloudling-carrying.svg +604 -0
  105. package/assets/cloudling/cloudling-conducting.svg +616 -0
  106. package/assets/cloudling/cloudling-dozing-to-sleeping.svg +578 -0
  107. package/assets/cloudling/cloudling-dozing.svg +358 -0
  108. package/assets/cloudling/cloudling-error.svg +428 -0
  109. package/assets/cloudling/cloudling-idle-reading.svg +658 -0
  110. package/assets/cloudling/cloudling-idle-to-dozing.svg +568 -0
  111. package/assets/cloudling/cloudling-idle-to-sleeping.svg +811 -0
  112. package/assets/cloudling/cloudling-idle.svg +255 -0
  113. package/assets/cloudling/cloudling-juggling.svg +648 -0
  114. package/assets/cloudling/cloudling-mini-alert.svg +390 -0
  115. package/assets/cloudling/cloudling-mini-crabwalk.svg +335 -0
  116. package/assets/cloudling/cloudling-mini-enter-roll-in.svg +226 -0
  117. package/assets/cloudling/cloudling-mini-enter-sleep.svg +232 -0
  118. package/assets/cloudling/cloudling-mini-happy.svg +371 -0
  119. package/assets/cloudling/cloudling-mini-idle.svg +279 -0
  120. package/assets/cloudling/cloudling-mini-peek.svg +328 -0
  121. package/assets/cloudling/cloudling-mini-sleep.svg +260 -0
  122. package/assets/cloudling/cloudling-mini-typing.svg +692 -0
  123. package/assets/cloudling/cloudling-notification.svg +494 -0
  124. package/assets/cloudling/cloudling-react-drag.svg +83 -0
  125. package/assets/cloudling/cloudling-sleeping-to-idle.svg +555 -0
  126. package/assets/cloudling/cloudling-sleeping.svg +500 -0
  127. package/assets/cloudling/cloudling-sweeping.svg +292 -0
  128. package/assets/cloudling/cloudling-thinking.svg +720 -0
  129. package/assets/cloudling/cloudling-typing.svg +354 -0
  130. package/assets/cloudling.svg +255 -0
  131. package/assets/ghost-attention.svg +19 -0
  132. package/assets/ghost-carrying.svg +19 -0
  133. package/assets/ghost-collapsing.svg +19 -0
  134. package/assets/ghost-dozing.svg +23 -0
  135. package/assets/ghost-error.svg +19 -0
  136. package/assets/ghost-happy.svg +23 -0
  137. package/assets/ghost-idle-action1.svg +19 -0
  138. package/assets/ghost-idle-action2.svg +19 -0
  139. package/assets/ghost-idle-action3.svg +19 -0
  140. package/assets/ghost-idle.svg +11 -0
  141. package/assets/ghost-juggling.svg +19 -0
  142. package/assets/ghost-notification.svg +19 -0
  143. package/assets/ghost-react-double.svg +23 -0
  144. package/assets/ghost-react-drag.svg +23 -0
  145. package/assets/ghost-react-left.svg +19 -0
  146. package/assets/ghost-react-right.svg +19 -0
  147. package/assets/ghost-sleeping.svg +19 -0
  148. package/assets/ghost-sweeping.svg +19 -0
  149. package/assets/ghost-thinking.svg +19 -0
  150. package/assets/ghost-waking.svg +19 -0
  151. package/assets/ghost-working.svg +19 -0
  152. package/assets/ghost-yawning.svg +23 -0
  153. package/assets/ghost.svg +11 -0
  154. package/assets/robot-attention.svg +19 -0
  155. package/assets/robot-carrying.svg +19 -0
  156. package/assets/robot-collapsing.svg +19 -0
  157. package/assets/robot-dozing.svg +23 -0
  158. package/assets/robot-error.svg +19 -0
  159. package/assets/robot-happy.svg +23 -0
  160. package/assets/robot-idle-action1.svg +19 -0
  161. package/assets/robot-idle-action2.svg +19 -0
  162. package/assets/robot-idle-action3.svg +19 -0
  163. package/assets/robot-idle.svg +11 -0
  164. package/assets/robot-juggling.svg +19 -0
  165. package/assets/robot-notification.svg +19 -0
  166. package/assets/robot-react-double.svg +23 -0
  167. package/assets/robot-react-drag.svg +23 -0
  168. package/assets/robot-react-left.svg +19 -0
  169. package/assets/robot-react-right.svg +19 -0
  170. package/assets/robot-sleeping.svg +19 -0
  171. package/assets/robot-sweeping.svg +19 -0
  172. package/assets/robot-thinking.svg +19 -0
  173. package/assets/robot-waking.svg +19 -0
  174. package/assets/robot-working.svg +19 -0
  175. package/assets/robot-yawning.svg +23 -0
  176. package/assets/robot.svg +11 -0
  177. package/manifest.json +889 -0
  178. package/package.json +18 -0
@@ -0,0 +1,255 @@
1
+ <svg id="cloudling" xmlns="http://www.w3.org/2000/svg" viewBox="-6 -6 36 36" preserveAspectRatio="xMidYMid meet">
2
+ <style><![CDATA[
3
+ .edge-rim-tight { stroke-opacity: .9; }
4
+ .edge-rim-soft { stroke-opacity: .75; }
5
+ .edge-contact { stroke-opacity: .9; }
6
+ @media (prefers-color-scheme: light) {
7
+ .edge-rim-tight { stroke-opacity: .95; }
8
+ .edge-rim-soft { stroke-opacity: .85; }
9
+ .edge-contact { stroke-opacity: .95; }
10
+ .cloud-drop-shadow { flood-opacity: .13; }
11
+ .food-cloud-shadow { flood-opacity: .28; flood-color: #5060FF; }
12
+ .food-cloud > path {
13
+ stroke: #5870E8 !important;
14
+ stroke-width: 1.2 !important;
15
+ stroke-opacity: .9 !important;
16
+ vector-effect: non-scaling-stroke !important;
17
+ }
18
+ }
19
+ ]]></style>
20
+ <defs>
21
+ <linearGradient id="body-grad" x1="12" y1="2" x2="12" y2="22" gradientUnits="userSpaceOnUse">
22
+ <stop offset="0" stop-color="#93c5fd"/>
23
+ <stop offset="0.5" stop-color="#60a5fa"/>
24
+ <stop offset="1" stop-color="#3b82f6"/>
25
+ </linearGradient>
26
+ <linearGradient id="eye-grad" x1="12" y1="3" x2="12" y2="21" gradientUnits="userSpaceOnUse">
27
+ <stop offset="0" stop-color="#B1A7FF"/>
28
+ <stop offset="0.5" stop-color="#7A9DFF"/>
29
+ <stop offset="1" stop-color="#3941FF"/>
30
+ </linearGradient>
31
+ <radialGradient id="hi-grad" cx="0.45" cy="0.25" r="0.55">
32
+ <stop offset="0" stop-color="#93c5fd" stop-opacity="0.9"/>
33
+ <stop offset="1" stop-color="#93c5fd" stop-opacity="0"/>
34
+ </radialGradient>
35
+ <filter id="drop" x="-40%" y="-40%" width="180%" height="180%">
36
+ <feDropShadow class="cloud-drop-shadow" id="drop-shadow" dx="0" dy="0.6" stdDeviation="0.8" flood-color="#3941FF" flood-opacity="0.35"/>
37
+ </filter>
38
+ <filter id="edge-blur" x="-40%" y="-40%" width="180%" height="180%">
39
+ <feGaussianBlur stdDeviation="0.16"/>
40
+ </filter>
41
+ <path id="cloud-shape" d="M9.064 3.344a4.578 4.578 0 012.285-.312c1 .115 1.891.54 2.673 1.275.01.01.024.017.037.021a.09.09 0 00.043 0 4.55 4.55 0 013.046.275l.047.022.116.057a4.581 4.581 0 012.188 2.399c.209.51.313 1.041.315 1.595a4.24 4.24 0 01-.134 1.223.123.123 0 00.03.115c.594.607.988 1.33 1.183 2.17.289 1.425-.007 2.71-.887 3.854l-.136.166a4.548 4.548 0 01-2.201 1.388.123.123 0 00-.081.076c-.191.551-.383 1.023-.74 1.494-.9 1.187-2.222 1.846-3.711 1.838-1.187-.006-2.239-.44-3.157-1.302a.107.107 0 00-.105-.024c-.388.125-.78.143-1.204.138a4.441 4.441 0 01-1.945-.466 4.544 4.544 0 01-1.61-1.335c-.152-.202-.303-.392-.414-.617a5.81 5.81 0 01-.37-.961 4.582 4.582 0 01-.014-2.298.124.124 0 00.006-.056.085.085 0 00-.027-.048 4.467 4.467 0 01-1.034-1.651 3.896 3.896 0 01-.251-1.192 5.189 5.189 0 01.141-1.6c.337-1.112.982-1.985 1.933-2.618.212-.141.413-.251.601-.33.215-.089.43-.164.646-.227a.098.098 0 00.065-.066 4.51 4.51 0 01.829-1.615 4.535 4.535 0 011.837-1.388z"/>
42
+ <clipPath id="cloud-clip">
43
+ <use href="#cloud-shape"/>
44
+ </clipPath>
45
+ </defs>
46
+
47
+ <!-- 云朵 layer (rotate + breath scale + distance scale, 都围绕 12,12) -->
48
+ <g id="cloud-group">
49
+ <g id="edge-system">
50
+ <use class="edge-contact" href="#cloud-shape" fill="none" stroke="#7d8aa7" stroke-width="1.4" stroke-linejoin="round" transform="translate(0 0.18)" filter="url(#edge-blur)"/>
51
+ <use class="edge-rim-soft" href="#cloud-shape" fill="none" stroke="#7A9DFF" stroke-width="1.2" stroke-linejoin="round" filter="url(#edge-blur)"/>
52
+ <use class="edge-rim-tight" href="#cloud-shape" fill="none" stroke="#aab3c6" stroke-width="0.8" stroke-linejoin="round"/>
53
+ </g>
54
+ <g filter="url(#drop)">
55
+ <use href="#cloud-shape" fill="url(#body-grad)"/>
56
+ <rect x="1" y="2" width="22" height="11" fill="url(#hi-grad)" clip-path="url(#cloud-clip)"/>
57
+ </g>
58
+ </g>
59
+
60
+ <!-- 眼睛 layer (独立, 1:1 跟鼠标) -->
61
+ <g id="eye-group">
62
+ <rect id="eye-l" x="7.7" y="9.5" width="2.6" height="5" rx="1.3" ry="1.3" fill="url(#eye-grad)" stroke="#ffffff" stroke-width="0.08" stroke-opacity="0.7"/>
63
+ <rect id="eye-r" x="13.7" y="9.5" width="2.6" height="5" rx="1.3" ry="1.3" fill="url(#eye-grad)" stroke="#ffffff" stroke-width="0.08" stroke-opacity="0.7"/>
64
+ </g>
65
+
66
+ <script type="application/ecmascript"><![CDATA[
67
+ const FACE_CENTER = { x: 12, y: 12 };
68
+
69
+ const svgEl = document.getElementById('cloudling');
70
+ const cloudGroup = document.getElementById('cloud-group');
71
+ const eyeGroup = document.getElementById('eye-group');
72
+ const eyeL = document.getElementById('eye-l');
73
+ const eyeR = document.getElementById('eye-r');
74
+ const eyeGrad = document.getElementById('eye-grad');
75
+
76
+ const EYE_BASE = {
77
+ l: { x: 7.7, y: 9.5, h: 5, rx: 1.3 },
78
+ r: { x: 13.7, y: 9.5, h: 5, rx: 1.3 }
79
+ };
80
+
81
+ const P = {
82
+ /* V1 锁定数值 */
83
+ SCALE_LOW: 0.96, BREATH_PERIOD: 5.0,
84
+ MAX_ROT_DEG: 20, CLOUD_LAG: 0.20,
85
+ EYE_MAX: 1.20, EYE_TRACK_TIME: 0.06, RECENTER_TIME: 0.45,
86
+ FLOW_PERIOD: 5.5, FLOW_AMP: 4,
87
+ BLINK_INTERVAL: 6.0, BLINK_DUR: 0.16,
88
+ /* V2 新增 */
89
+ DIST_NEAR: 6, DIST_FAR: 20, DIST_MAX_SCALE: 1.15, DIST_RECENTER: 0.5
90
+ };
91
+
92
+ let mouseInside = false;
93
+ let mouseVB = { x: 12, y: 12 };
94
+ let eyeOffset = { x: 0, y: 0 };
95
+ let cloudRotate = 0;
96
+ let curDistScale = 1.0;
97
+ let lastT = performance.now();
98
+ let lowPowerPaused = false;
99
+ let lowPowerPausedAt = 0;
100
+ let lowPowerPausedTotal = 0;
101
+ let rafId = 0;
102
+
103
+ let nextBlinkT = 3 + Math.random() * 2;
104
+ let blinkActive = false;
105
+ let blinkStartT = 0;
106
+
107
+ function scheduleLoop() {
108
+ if (!lowPowerPaused && !rafId) rafId = requestAnimationFrame(loop);
109
+ }
110
+
111
+ window.__clawdSetLowPowerPaused = paused => {
112
+ const next = !!paused;
113
+ if (lowPowerPaused === next) return;
114
+ lowPowerPaused = next;
115
+ if (next) {
116
+ lowPowerPausedAt = performance.now();
117
+ if (rafId) cancelAnimationFrame(rafId);
118
+ rafId = 0;
119
+ return;
120
+ }
121
+ if (lowPowerPausedAt) lowPowerPausedTotal += performance.now() - lowPowerPausedAt;
122
+ lowPowerPausedAt = 0;
123
+ lastT = performance.now();
124
+ scheduleLoop();
125
+ };
126
+
127
+ svgEl.addEventListener('mousemove', e => {
128
+ const r = svgEl.getBoundingClientRect();
129
+ /* viewBox -12 -12 48 48 → 反算 mouseVB */
130
+ mouseVB.x = (e.clientX - r.left) * (48 / r.width) - 12;
131
+ mouseVB.y = (e.clientY - r.top) * (48 / r.height) - 12;
132
+ mouseInside = true;
133
+ });
134
+ svgEl.addEventListener('mouseleave', () => mouseInside = false);
135
+ svgEl.addEventListener('mouseenter', () => mouseInside = true);
136
+
137
+ window.__cloudlingSetPointer = payload => {
138
+ if (!payload || !Number.isFinite(payload.x) || !Number.isFinite(payload.y)) return;
139
+ mouseVB.x = payload.x;
140
+ mouseVB.y = payload.y;
141
+ mouseInside = !!payload.inside;
142
+ };
143
+
144
+ function computeEyeTarget() {
145
+ if (!mouseInside) return { x: 0, y: 0 };
146
+ const dx = mouseVB.x - FACE_CENTER.x;
147
+ const dy = mouseVB.y - FACE_CENTER.y;
148
+ const dist = Math.hypot(dx, dy);
149
+ if (dist < 0.01) return { x: 0, y: 0 };
150
+ const sat = Math.tanh(dist / 6);
151
+ return {
152
+ x: (dx / dist) * sat * P.EYE_MAX,
153
+ y: (dy / dist) * sat * P.EYE_MAX
154
+ };
155
+ }
156
+
157
+ function getBlinkScale(t) {
158
+ if (!blinkActive && t > nextBlinkT) {
159
+ blinkActive = true;
160
+ blinkStartT = t;
161
+ }
162
+ if (blinkActive) {
163
+ const dt = t - blinkStartT;
164
+ const half = P.BLINK_DUR / 2;
165
+ if (dt < half) {
166
+ return 1 - 0.94 * (dt / half);
167
+ } else if (dt < P.BLINK_DUR) {
168
+ return 0.06 + 0.94 * ((dt - half) / half);
169
+ } else {
170
+ blinkActive = false;
171
+ nextBlinkT = t + P.BLINK_INTERVAL * (0.8 + Math.random() * 0.4);
172
+ }
173
+ }
174
+ return 1;
175
+ }
176
+
177
+ function applyBlink(rect, base, scale) {
178
+ const newH = Math.max(base.h * scale, 0.15);
179
+ const newY = base.y + (base.h - newH) / 2;
180
+ const newRx = Math.min(base.rx, newH / 2);
181
+ rect.setAttribute('height', newH.toFixed(3));
182
+ rect.setAttribute('y', newY.toFixed(3));
183
+ rect.setAttribute('rx', newRx.toFixed(3));
184
+ rect.setAttribute('ry', newRx.toFixed(3));
185
+ }
186
+
187
+ const startT = performance.now();
188
+ function loop(now) {
189
+ rafId = 0;
190
+ if (lowPowerPaused) return;
191
+ const dt = (now - lastT) / 1000;
192
+ lastT = now;
193
+ const t = (now - startT - lowPowerPausedTotal) / 1000;
194
+
195
+ /* 呼吸 scale */
196
+ const phase = (Math.cos(2 * Math.PI * t / P.BREATH_PERIOD) + 1) / 2;
197
+ const breathScale = P.SCALE_LOW + (1.0 - P.SCALE_LOW) * phase;
198
+
199
+ /* 眼睛位置 */
200
+ const target = computeEyeTarget();
201
+ const eyeTime = mouseInside ? P.EYE_TRACK_TIME : P.RECENTER_TIME;
202
+ const eyeRate = 1 - Math.exp(-dt / eyeTime);
203
+ eyeOffset.x += (target.x - eyeOffset.x) * eyeRate;
204
+ eyeOffset.y += (target.y - eyeOffset.y) * eyeRate;
205
+
206
+ /* 云朵 rotate (eye.x 驱动 + lag) */
207
+ const maxRot = P.MAX_ROT_DEG * Math.PI / 180;
208
+ const targetRot = (eyeOffset.x / Math.max(P.EYE_MAX, 0.01)) * maxRot;
209
+ cloudRotate += (targetRot - cloudRotate) * P.CLOUD_LAG;
210
+
211
+ /* V2: 距离驱动 scale */
212
+ let targetDistScale = 1.0;
213
+ if (mouseInside) {
214
+ const dx = mouseVB.x - FACE_CENTER.x;
215
+ const dy = mouseVB.y - FACE_CENTER.y;
216
+ const dist = Math.hypot(dx, dy);
217
+ if (dist > P.DIST_NEAR) {
218
+ const t01 = Math.min((dist - P.DIST_NEAR) / Math.max(P.DIST_FAR - P.DIST_NEAR, 0.01), 1);
219
+ targetDistScale = 1.0 + (P.DIST_MAX_SCALE - 1.0) * t01;
220
+ }
221
+ }
222
+ const distRate = 1 - Math.exp(-dt / P.DIST_RECENTER);
223
+ curDistScale += (targetDistScale - curDistScale) * distRate;
224
+
225
+ /* 应用 cloud transform — breath scale × distance scale 相乘 */
226
+ const finalScale = breathScale * curDistScale;
227
+ const angleDeg = cloudRotate * 180 / Math.PI;
228
+ cloudGroup.setAttribute(
229
+ 'transform',
230
+ `translate(12 12) rotate(${angleDeg.toFixed(3)}) scale(${finalScale.toFixed(4)}) translate(-12 -12)`
231
+ );
232
+
233
+ /* 眼睛 group transform */
234
+ const eyeScale = curDistScale;
235
+ eyeGroup.setAttribute(
236
+ 'transform',
237
+ `translate(${eyeOffset.x.toFixed(3)} ${eyeOffset.y.toFixed(3)}) translate(12 12) scale(${eyeScale.toFixed(4)}) translate(-12 -12)`
238
+ );
239
+
240
+ /* 眨眼 */
241
+ const blinkScale = getBlinkScale(t);
242
+ applyBlink(eyeL, EYE_BASE.l, blinkScale);
243
+ applyBlink(eyeR, EYE_BASE.r, blinkScale);
244
+
245
+ /* 流动渐变 */
246
+ const flowOffset = Math.sin(2 * Math.PI * t / P.FLOW_PERIOD) * P.FLOW_AMP;
247
+ eyeGrad.setAttribute('y1', (3 + flowOffset).toFixed(2));
248
+ eyeGrad.setAttribute('y2', (21 + flowOffset).toFixed(2));
249
+
250
+ scheduleLoop();
251
+ }
252
+
253
+ scheduleLoop();
254
+ ]]></script>
255
+ </svg>