@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,500 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <svg id="stage" xmlns="http://www.w3.org/2000/svg" viewBox="-6 -6 36 36" preserveAspectRatio="xMidYMid meet">
3
+ <g id="__cloudling-export-shims" opacity="0" pointer-events="none" style="display:none">
4
+ <g id="breath-group"></g>
5
+ <g id="hat-sway"></g>
6
+ <g id="root-svg"></g>
7
+ <g id="z-container"></g>
8
+ </g>
9
+ <script type="application/ecmascript"><![CDATA[
10
+ const CLOUD_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";
11
+
12
+ /* =========================================================================
13
+ * 默认锁定参数
14
+ * ========================================================================= */
15
+ const DEFAULTS = {
16
+ // 静帧摆位 (鹿鹿 2026-04-28 调好)
17
+ bodyTilt: -6,
18
+ hatTx: -2,
19
+ hatTy: -0.4,
20
+ hatRot: -10,
21
+ hatScale: 0.98,
22
+ maskTx: 0,
23
+ maskTy: -0.6,
24
+ maskRot: -9,
25
+ maskScale: 1.00,
26
+
27
+ // 主层: 呼吸
28
+ breathPeriod: 4,
29
+ breathLow: 0.96,
30
+ breathHigh: 1.02,
31
+
32
+ // 次层: 帽子独立 sway (异步)
33
+ hatSwayPeriod: 6,
34
+ hatSwayAmp: 2,
35
+
36
+ // 梦境层: Z 字
37
+ zEnable: 1,
38
+ zInterval: 1.8,
39
+ zLifetime: 2.9,
40
+ zStartX: 19.4,
41
+ zStartY: 3.6,
42
+ zRiseDist: 8,
43
+ zDriftX: 4,
44
+ zMaxSize: 1.6,
45
+ zMinSize: 1.3,
46
+ zStroke: 0.55,
47
+ zMaxOpacity: 0.45,
48
+ };
49
+
50
+ let params = { ...DEFAULTS };
51
+ let paused = false;
52
+
53
+ /* =========================================================================
54
+ * defs
55
+ * ========================================================================= */
56
+ function commonDefs(p) {
57
+ return `
58
+ <linearGradient id="${p}-body" x1="12" y1="2" x2="12" y2="22" gradientUnits="userSpaceOnUse">
59
+ <stop offset="0" stop-color="#93c5fd"/>
60
+ <stop offset="0.5" stop-color="#60a5fa"/>
61
+ <stop offset="1" stop-color="#3b82f6"/>
62
+ </linearGradient>
63
+ <linearGradient id="${p}-eye" x1="0" y1="3" x2="0" y2="21" gradientUnits="userSpaceOnUse">
64
+ <stop offset="0" stop-color="#B1A7FF"/>
65
+ <stop offset="0.5" stop-color="#7A9DFF"/>
66
+ <stop offset="1" stop-color="#3941FF"/>
67
+ </linearGradient>
68
+ <radialGradient id="${p}-hi" cx="0.45" cy="0.25" r="0.55">
69
+ <stop offset="0" stop-color="#93c5fd" stop-opacity="0.9"/>
70
+ <stop offset="1" stop-color="#93c5fd" stop-opacity="0"/>
71
+ </radialGradient>
72
+ <linearGradient id="${p}-hat" x1="11" y1="-3" x2="14" y2="6" gradientUnits="userSpaceOnUse">
73
+ <stop offset="0" stop-color="#7AA0FF"/>
74
+ <stop offset="0.45" stop-color="#3941FF"/>
75
+ <stop offset="1" stop-color="#1F2BC2"/>
76
+ </linearGradient>
77
+ <linearGradient id="${p}-cuff" x1="12" y1="3.0" x2="12" y2="8.0" gradientUnits="userSpaceOnUse">
78
+ <stop offset="0" stop-color="#93c5fd"/>
79
+ <stop offset="0.5" stop-color="#fafbff"/>
80
+ <stop offset="1" stop-color="#e8ecf6"/>
81
+ </linearGradient>
82
+ <linearGradient id="${p}-star-yellow" x1="0" y1="-2" x2="0" y2="2" gradientUnits="userSpaceOnUse">
83
+ <stop offset="0" stop-color="#FFE9A8"/>
84
+ <stop offset="0.5" stop-color="#FFD360"/>
85
+ <stop offset="1" stop-color="#F5A623"/>
86
+ </linearGradient>
87
+ <filter id="${p}-star-glow" x="-80%" y="-80%" width="260%" height="260%">
88
+ <feDropShadow dx="0" dy="0.4" stdDeviation="0.5" flood-color="#F5A623" flood-opacity="0.32"/>
89
+ </filter>
90
+ <linearGradient id="${p}-z-grad" x1="0" y1="0" x2="0" y2="1">
91
+ <stop offset="0" stop-color="#B1A7FF"/>
92
+ <stop offset="0.5" stop-color="#7A9DFF"/>
93
+ <stop offset="1" stop-color="#3941FF"/>
94
+ </linearGradient>
95
+ <filter id="${p}-z-glow" x="-50%" y="-50%" width="200%" height="200%">
96
+ <feDropShadow dx="0" dy="0.3" stdDeviation="0.35" flood-color="#7A9DFF" flood-opacity="0.45"/>
97
+ </filter>
98
+ <filter id="${p}-shadow" x="-50%" y="-50%" width="200%" height="200%">
99
+ <feDropShadow class="cloud-drop-shadow" dx="0" dy="0.6" stdDeviation="0.8" flood-color="#3941FF" flood-opacity="0.35"/>
100
+ </filter>
101
+ <filter id="${p}-edge-blur" x="-40%" y="-40%" width="180%" height="180%">
102
+ <feGaussianBlur stdDeviation="0.16"/>
103
+ </filter>
104
+ <filter id="${p}-soft" x="-30%" y="-30%" width="160%" height="160%">
105
+ <feGaussianBlur stdDeviation="0.18"/>
106
+ </filter>
107
+ <clipPath id="${p}-clip"><path d="${CLOUD_D}"/></clipPath>
108
+ `;
109
+ }
110
+
111
+ /* =========================================================================
112
+ * 部件 (从 sleeping-static-pose v1 复制)
113
+ * ========================================================================= */
114
+ function makeFluffyCuff(p, cx, cy, rx, ry, asym, bumps) {
115
+ let parts = [];
116
+ const circles = [];
117
+ for (let i = 0; i < bumps; i++) {
118
+ const ang = (i * 2 * Math.PI) / bumps - Math.PI / 2;
119
+ const x = cx + Math.cos(ang) * rx;
120
+ const y = cy + Math.sin(ang) * ry;
121
+ const sizeY = -Math.sin(ang);
122
+ const sizeBoost = 0.18 * (sizeY + 1) / 2 + 0.10;
123
+ const jitter = ((i * 13) % 7) / 35;
124
+ let r = 0.62 + sizeBoost + jitter;
125
+ if (asym !== 0) {
126
+ const leftRight = Math.cos(ang);
127
+ r *= 1 + asym * leftRight * 0.28;
128
+ }
129
+ circles.push({ x, y, r });
130
+ }
131
+ const innerRy = ry + 0.40;
132
+ parts.push(`<g filter="url(#${p}-soft)">`);
133
+ parts.push(`<ellipse cx="${cx}" cy="${cy}" rx="${(rx + 0.30).toFixed(2)}" ry="${(innerRy + 0.30).toFixed(2)}" fill="#7A9DFF" opacity="0.50"/>`);
134
+ circles.forEach(c => {
135
+ parts.push(`<circle cx="${c.x.toFixed(2)}" cy="${c.y.toFixed(2)}" r="${(c.r + 0.30).toFixed(2)}" fill="#7A9DFF" opacity="0.50"/>`);
136
+ });
137
+ parts.push(`</g>`);
138
+ parts.push(`<ellipse cx="${cx}" cy="${cy}" rx="${(rx + 0.15).toFixed(2)}" ry="${(innerRy + 0.15).toFixed(2)}" fill="#aab3c6" opacity="0.65"/>`);
139
+ circles.forEach(c => {
140
+ parts.push(`<circle cx="${c.x.toFixed(2)}" cy="${c.y.toFixed(2)}" r="${(c.r + 0.15).toFixed(2)}" fill="#aab3c6" opacity="0.65"/>`);
141
+ });
142
+ parts.push(`<ellipse cx="${cx}" cy="${cy}" rx="${rx.toFixed(2)}" ry="${innerRy.toFixed(2)}" fill="url(#${p}-cuff)"/>`);
143
+ circles.forEach(c => {
144
+ parts.push(`<circle cx="${c.x.toFixed(2)}" cy="${c.y.toFixed(2)}" r="${c.r.toFixed(2)}" fill="url(#${p}-cuff)"/>`);
145
+ });
146
+ parts.push(`<ellipse cx="${cx}" cy="${(cy - ry * 0.45).toFixed(2)}" rx="${(rx * 0.78).toFixed(2)}" ry="${(ry * 0.32).toFixed(2)}" fill="none" stroke="#ffffff" stroke-opacity="0.55" stroke-width="0.14"/>`);
147
+ return parts.join('');
148
+ }
149
+
150
+ function makeBentHat(bend) {
151
+ const baseLeft = { x: 8.5, y: 5.0 };
152
+ const baseRight = { x: 15.5, y: 5.0 };
153
+ const elbowX = 12 + bend * 2.2;
154
+ const elbowY = -0.5 + bend * 2.3;
155
+ const tipX = 11 - bend * 5.5;
156
+ const tipY = 0.0 + bend * 0.5;
157
+ const tipBottomX = tipX + 1.2 + bend * 0.4;
158
+ const tipBottomY = tipY + 1.4 + bend * 0.3;
159
+ const c1 = { x: baseRight.x + 0.5, y: baseRight.y - 1.5 };
160
+ const c2 = { x: elbowX + 0.8, y: elbowY + 1.0 };
161
+ const c3 = { x: elbowX - 0.5, y: elbowY - 0.8 };
162
+ const c4 = { x: tipX + 1.5, y: tipY - 0.5 };
163
+ const c5 = { x: tipX - 0.3, y: tipY + 0.6 };
164
+ const c6 = { x: tipBottomX - 1.0, y: tipBottomY };
165
+ const c7 = { x: 8.5, y: 4.0 };
166
+ return {
167
+ path: `M ${baseLeft.x} ${baseLeft.y} Q 12 5.55 ${baseRight.x} ${baseRight.y} C ${c1.x.toFixed(2)} ${c1.y.toFixed(2)} ${c2.x.toFixed(2)} ${c2.y.toFixed(2)} ${elbowX.toFixed(2)} ${elbowY.toFixed(2)} C ${c3.x.toFixed(2)} ${c3.y.toFixed(2)} ${c4.x.toFixed(2)} ${c4.y.toFixed(2)} ${tipX.toFixed(2)} ${tipY.toFixed(2)} C ${c5.x.toFixed(2)} ${c5.y.toFixed(2)} ${c6.x.toFixed(2)} ${c6.y.toFixed(2)} ${tipBottomX.toFixed(2)} ${tipBottomY.toFixed(2)} C ${c7.x.toFixed(2)} ${c7.y.toFixed(2)} 8.0 4.5 ${baseLeft.x} ${baseLeft.y} Z`
168
+ };
169
+ }
170
+
171
+ function makeStar(cx, cy, r, rotationDeg, cornerRound, chubbiness, innerRatio) {
172
+ const innerR = r * innerRatio;
173
+ const rotRad = (rotationDeg * Math.PI) / 180;
174
+ const verts = [];
175
+ for (let i = 0; i < 10; i++) {
176
+ const ang = (i * Math.PI / 5) + rotRad - Math.PI / 2;
177
+ const radius = i % 2 === 0 ? r : innerR;
178
+ verts.push({ x: cx + Math.cos(ang) * radius, y: cy + Math.sin(ang) * radius });
179
+ }
180
+ const inP = [], outP = [];
181
+ for (let i = 0; i < 10; i++) {
182
+ const prev = verts[(i - 1 + 10) % 10];
183
+ const cur = verts[i];
184
+ const next = verts[(i + 1) % 10];
185
+ const v1x = prev.x - cur.x, v1y = prev.y - cur.y;
186
+ const v1Len = Math.sqrt(v1x * v1x + v1y * v1y);
187
+ const v2x = next.x - cur.x, v2y = next.y - cur.y;
188
+ const v2Len = Math.sqrt(v2x * v2x + v2y * v2y);
189
+ const cr = Math.min(cornerRound, v1Len * 0.48, v2Len * 0.48);
190
+ inP[i] = { x: cur.x + (v1x / v1Len) * cr, y: cur.y + (v1y / v1Len) * cr };
191
+ outP[i] = { x: cur.x + (v2x / v2Len) * cr, y: cur.y + (v2y / v2Len) * cr };
192
+ }
193
+ let path = `M ${inP[0].x.toFixed(2)} ${inP[0].y.toFixed(2)} `;
194
+ for (let i = 0; i < 10; i++) {
195
+ path += `Q ${verts[i].x.toFixed(2)} ${verts[i].y.toFixed(2)} ${outP[i].x.toFixed(2)} ${outP[i].y.toFixed(2)} `;
196
+ const nextI = (i + 1) % 10;
197
+ const start = outP[i], end = inP[nextI];
198
+ if (chubbiness > 0) {
199
+ const midX = (start.x + end.x) / 2, midY = (start.y + end.y) / 2;
200
+ const dx = midX - cx, dy = midY - cy;
201
+ const len = Math.sqrt(dx * dx + dy * dy) || 1;
202
+ const offset = chubbiness * r * 0.32;
203
+ const ctrlX = midX + (dx / len) * offset, ctrlY = midY + (dy / len) * offset;
204
+ path += `Q ${ctrlX.toFixed(2)} ${ctrlY.toFixed(2)} ${end.x.toFixed(2)} ${end.y.toFixed(2)} `;
205
+ } else {
206
+ path += `L ${end.x.toFixed(2)} ${end.y.toFixed(2)} `;
207
+ }
208
+ }
209
+ path += 'Z';
210
+ return path;
211
+ }
212
+
213
+ const STAR_LOCKED = {
214
+ cx: 8.55, cy: 0.35, size: 1.00, rotation: 30,
215
+ cornerRound: 0.27, chubbiness: 0, innerRatio: 0.56
216
+ };
217
+
218
+ const SLEEP_MASK_PATH = {
219
+ d: "M488.728,341.587l-7.846-2.599c-0.405-16.992-7.605-47.34-48.625-70.941c-38.301-22.036-96.652-33.215-173.431-33.224c-76.822,0-135.146,11.165-173.438,33.183c-42.335,24.344-48.659,55.883-48.661,72.516c0,2.608,0.135,5.183,0.352,7.735h-2.649c-0.001,0-0.001,0-0.002,0c-9.635,0-17.446,7.811-17.448,17.445c-0.001,9.636,7.81,17.449,17.445,17.45l13,0.001c15.257,28.43,45.267,47.814,79.728,47.818c20.016-0.001,36.647-11.511,54.256-23.695c20.65-14.29,44.048-30.481,77.388-30.481c33.359,0.003,56.757,16.204,77.402,30.497c17.61,12.192,34.24,23.707,54.251,23.709h0.012c24.153,0,46.861-9.404,63.941-26.481c8.937-8.936,15.751-19.422,20.239-30.839l3.111,1.031c1.82,0.604,3.67,0.89,5.489,0.89c7.318,0,14.133-4.64,16.56-11.966C502.835,354.489,497.876,344.617,488.728,341.587Z",
220
+ bboxX: 15, bboxY: 232, bboxW: 487, bboxH: 200
221
+ };
222
+
223
+ function makeSleepMask(p, tx, ty, rot, scale) {
224
+ const cx = 12 + tx;
225
+ const cy = 12 + ty;
226
+ const W = 16.61 * scale;
227
+ const H = 7.55 * scale;
228
+ const eyeScale = 1.18;
229
+ const eyeRot = 5;
230
+ const eyeOffsetY = -0.25;
231
+ const lashW = 2.45;
232
+ const lashH = 0.70;
233
+ const lashStroke = 0.45;
234
+ const lashGap = 6.00 * scale;
235
+ const lashHairLen = 0.66;
236
+ const lashHairCount = 3;
237
+
238
+ const sx = W / SLEEP_MASK_PATH.bboxW;
239
+ const sy = H / SLEEP_MASK_PATH.bboxH;
240
+ const cxOrig = SLEEP_MASK_PATH.bboxX + SLEEP_MASK_PATH.bboxW / 2;
241
+ const cyOrig = SLEEP_MASK_PATH.bboxY + SLEEP_MASK_PATH.bboxH / 2;
242
+ const body = `<g transform="translate(${cx} ${cy}) scale(${sx.toFixed(5)} ${sy.toFixed(5)}) translate(${(-cxOrig).toFixed(2)} ${(-cyOrig).toFixed(2)})">
243
+ <path d="${SLEEP_MASK_PATH.d}" fill="#22305A" stroke="#000000" stroke-width="0.22" stroke-linejoin="round" stroke-linecap="round" vector-effect="non-scaling-stroke"/>
244
+ </g>`;
245
+
246
+ const arcLocal = `<path d="M ${(-lashW/2).toFixed(2)} 0 Q 0 ${(lashH * 2).toFixed(2)} ${(lashW/2).toFixed(2)} 0" fill="none" stroke="#FFFFFF" stroke-width="${lashStroke}" stroke-linecap="round"/>`;
247
+
248
+ const hairs = [];
249
+ const tPad = 0.18;
250
+ for (let i = 0; i < lashHairCount; i++) {
251
+ const t = tPad + (i / (lashHairCount - 1)) * (1 - 2 * tPad);
252
+ const sxh = (1-t)*(1-t)*(-lashW/2) + 2*(1-t)*t*0 + t*t*(lashW/2);
253
+ const syh = (1-t)*(1-t)*0 + 2*(1-t)*t*(2*lashH) + t*t*0;
254
+ const tangX = 2*(1-t)*(0 - (-lashW/2)) + 2*t*((lashW/2) - 0);
255
+ const tangY = 2*(1-t)*((2*lashH) - 0) + 2*t*(0 - (2*lashH));
256
+ const tLen = Math.hypot(tangX, tangY);
257
+ const normX1 = -tangY / tLen, normY1 = tangX / tLen;
258
+ const normX2 = tangY / tLen, normY2 = -tangX / tLen;
259
+ const useFirst = normY1 > 0;
260
+ const dirX = useFirst ? normX1 : normX2;
261
+ const dirY = useFirst ? normY1 : normY2;
262
+ const ex = sxh + dirX * lashHairLen;
263
+ const ey = syh + dirY * lashHairLen;
264
+ hairs.push(`<line x1="${sxh.toFixed(2)}" y1="${syh.toFixed(2)}" x2="${ex.toFixed(2)}" y2="${ey.toFixed(2)}" stroke="#FFFFFF" stroke-width="${(lashStroke * 0.85).toFixed(2)}" stroke-linecap="round" opacity="0.92"/>`);
265
+ }
266
+ const hairsLocal = hairs.join('');
267
+
268
+ const eyeL_cx = cx - lashGap / 2;
269
+ const eyeR_cx = cx + lashGap / 2;
270
+ const wrapEye = (ecx, sign) => `<g transform="translate(${ecx.toFixed(2)} ${(cy + eyeOffsetY).toFixed(2)}) rotate(${(sign * eyeRot).toFixed(2)}) scale(${eyeScale.toFixed(3)})">
271
+ ${arcLocal}
272
+ ${hairsLocal}
273
+ </g>`;
274
+ const lashes = wrapEye(eyeL_cx, -1) + wrapEye(eyeR_cx, +1);
275
+
276
+ return `
277
+ <g class="sleep-mask" transform="rotate(${rot} ${cx} ${cy})" filter="url(#${p}-shadow)">
278
+ ${body}
279
+ ${lashes}
280
+ </g>
281
+ `;
282
+ }
283
+
284
+ function makeCloudling(p) {
285
+ return `
286
+ <g class="cloudling-base">
287
+ <g class="edge-system">
288
+ <use class="edge-contact" href="#${p}-cloud-shape" fill="none" stroke="#7d8aa7" stroke-width="1.4" stroke-linejoin="round" transform="translate(0 0.18)" filter="url(#${p}-edge-blur)"/>
289
+ <use class="edge-rim-soft" href="#${p}-cloud-shape" fill="none" stroke="#7A9DFF" stroke-width="1.2" stroke-linejoin="round" filter="url(#${p}-edge-blur)"/>
290
+ <use class="edge-rim-tight" href="#${p}-cloud-shape" fill="none" stroke="#aab3c6" stroke-width="0.8" stroke-linejoin="round"/>
291
+ </g>
292
+ <g filter="url(#${p}-shadow)">
293
+ <use href="#${p}-cloud-shape" fill="url(#${p}-body)"/>
294
+ <rect x="1" y="2" width="22" height="11" fill="url(#${p}-hi)" clip-path="url(#${p}-clip)"/>
295
+ </g>
296
+ </g>
297
+ `;
298
+ }
299
+
300
+ /* =========================================================================
301
+ * Z 字 path - 苹果精致风手写体
302
+ * 流畅手写 Z = 上横 + 下笔斜对角(略带圆滑) + 下横
303
+ * 用 stroke 描边 + linecap=round + 紫蓝渐变 + glow
304
+ * ========================================================================= */
305
+ function makeZPath() {
306
+ // 在局部坐标 (-1, -1) 到 (1, 1) 区间内画 Z
307
+ // 上横: (-1, -1) → (1, -1) 但末端略弯 (Q 控制点)
308
+ // 斜杠: (1, -1) → (-1, 1) 略带 S 弧
309
+ // 下横: (-1, 1) → (1, 1) 末端略弯
310
+ return "M -1.0 -1.0 Q 0.0 -1.0 1.0 -1.0 Q 1.05 -0.85 0.6 -0.4 Q 0 0.1 -0.6 0.6 Q -1.05 0.95 -1.0 1.0 Q 0.0 1.0 1.0 1.0";
311
+ }
312
+
313
+ /* =========================================================================
314
+ * 渲染静态结构 + 用 JS RAF 驱动 transform / Z 字
315
+ * ========================================================================= */
316
+ const stage = document.getElementById('stage');
317
+ let svgRoot = null;
318
+ let cloudGroup = null;
319
+ let hatGroup = null;
320
+ let zContainer = null;
321
+
322
+ function renderStage() {
323
+ const p = 'sl';
324
+ const hatData = makeBentHat(0.4);
325
+ const cuffSVG = makeFluffyCuff(p, 12, 5.4, 3.7, 0.85, 0, 18);
326
+ const s = STAR_LOCKED;
327
+ const starPath = makeStar(s.cx, s.cy, s.size, s.rotation, s.cornerRound, s.chubbiness, s.innerRatio);
328
+ const cloudling = makeCloudling(p);
329
+ const mask = makeSleepMask(p, params.maskTx, params.maskTy, params.maskRot, params.maskScale);
330
+
331
+ // 帽子结构: 整顶帽子作为一个 group, transform-origin 在 cuff 底部 (12, 5.4)
332
+ // 帽子 sway 围绕这个点 rotate
333
+ const hatStaticTransform = `translate(${params.hatTx} ${params.hatTy}) rotate(${params.hatRot} 12 5.4) scale(${params.hatScale})`;
334
+ const hatInner = `
335
+ <g filter="url(#${p}-shadow)">
336
+ <path d="${hatData.path}" fill="url(#${p}-hat)"/>
337
+ </g>
338
+ <g class="cuff-fluffy">${cuffSVG}</g>
339
+ <g class="star-piece" filter="url(#${p}-star-glow)">
340
+ <path d="${starPath}" fill="url(#${p}-star-yellow)" stroke="#ffffff" stroke-width="0.06" stroke-opacity="0.7" stroke-linejoin="round"/>
341
+ </g>
342
+ `;
343
+
344
+ stage.innerHTML = `
345
+ <style>
346
+ .edge-rim-tight { stroke-opacity: .9; }
347
+ .edge-rim-soft { stroke-opacity: .75; }
348
+ .edge-contact { stroke-opacity: .9; }
349
+ @media (prefers-color-scheme: light) {
350
+ .edge-rim-tight { stroke-opacity: .95; }
351
+ .edge-rim-soft { stroke-opacity: .85; }
352
+ .edge-contact { stroke-opacity: .95; }
353
+ .cloud-drop-shadow { flood-opacity: .13; }
354
+ .food-cloud-shadow { flood-opacity: .28; flood-color: #5060FF; }
355
+ .food-cloud > path {
356
+ stroke: #5870E8 !important;
357
+ stroke-width: 1.2 !important;
358
+ stroke-opacity: .9 !important;
359
+ vector-effect: non-scaling-stroke !important;
360
+ }
361
+ }
362
+ </style>
363
+ <defs>
364
+ ${commonDefs(p)}
365
+ <path id="${p}-cloud-shape" d="${CLOUD_D}"/>
366
+ </defs>
367
+ <g class="scene" transform="rotate(${params.bodyTilt} 12 12)">
368
+ <!-- 主层: 呼吸 group, 包住 cloudling + hat + mask 整体 scale -->
369
+ <g id="breath-group" style="transform-box: fill-box;">
370
+ ${cloudling}
371
+ <!-- 帽子: 自己的 sway transform 套在静态摆位之上 -->
372
+ <g id="hat-static" transform="${hatStaticTransform}">
373
+ <g id="hat-sway" style="transform-origin: 12px 5.4px;">
374
+ ${hatInner}
375
+ </g>
376
+ </g>
377
+ ${mask}
378
+ </g>
379
+ <!-- 梦境层: Z 字 (在呼吸外, 不跟着主体起伏) -->
380
+ <g id="z-container"></g>
381
+ </g>
382
+ `;
383
+
384
+ svgRoot = document.getElementById('root-svg');
385
+ cloudGroup = document.getElementById('breath-group');
386
+ hatGroup = document.getElementById('hat-sway');
387
+ zContainer = document.getElementById('z-container');
388
+ }
389
+
390
+ /* =========================================================================
391
+ * Z 字 lifecycle 管理
392
+ * ========================================================================= */
393
+ const zList = []; // { bornAt, el }
394
+ let lastZSpawn = -999;
395
+
396
+ function spawnZ(now) {
397
+ const p = 'sl';
398
+ const el = document.createElementNS('http://www.w3.org/2000/svg', 'g');
399
+ el.setAttribute('class', 'z-particle');
400
+ el.innerHTML = `
401
+ <g filter="url(#${p}-z-glow)">
402
+ <path d="${makeZPath()}" fill="none" stroke="url(#${p}-z-grad)" stroke-width="${params.zStroke}" stroke-linecap="round" stroke-linejoin="round"/>
403
+ </g>
404
+ `;
405
+ zContainer.appendChild(el);
406
+ zList.push({ bornAt: now, el });
407
+ }
408
+
409
+ function updateZs(t) {
410
+ // 出生新 Z
411
+ if (params.zEnable && t - lastZSpawn >= params.zInterval) {
412
+ spawnZ(t);
413
+ lastZSpawn = t;
414
+ }
415
+
416
+ // 更新所有 Z 的 transform / opacity
417
+ for (let i = zList.length - 1; i >= 0; i--) {
418
+ const z = zList[i];
419
+ const age = t - z.bornAt;
420
+ const life = params.zLifetime;
421
+
422
+ if (age >= life) {
423
+ z.el.remove();
424
+ zList.splice(i, 1);
425
+ continue;
426
+ }
427
+
428
+ const u = age / life; // 0 → 1
429
+
430
+ // 位置: 起点 (zStartX, zStartY) → 飘升 + 右飘
431
+ const x = params.zStartX + params.zDriftX * u;
432
+ const y = params.zStartY - params.zRiseDist * u;
433
+
434
+ // 大小: 出生 max → 末段 min
435
+ const size = params.zMaxSize + (params.zMinSize - params.zMaxSize) * u;
436
+
437
+ // 透明度: 三段: 淡入 (0-0.18) → 平稳 (0.18-0.7) → 淡出 (0.7-1)
438
+ let opacity;
439
+ if (u < 0.18) {
440
+ opacity = (u / 0.18) * params.zMaxOpacity;
441
+ } else if (u < 0.7) {
442
+ opacity = params.zMaxOpacity;
443
+ } else {
444
+ opacity = params.zMaxOpacity * (1 - (u - 0.7) / 0.3);
445
+ }
446
+
447
+ // 微小的 sway (Z 字飘动时左右摆一点)
448
+ const sway = Math.sin(age * 2.0) * 0.3;
449
+
450
+ z.el.setAttribute('transform', `translate(${(x + sway).toFixed(2)} ${y.toFixed(2)}) scale(${size.toFixed(2)})`);
451
+ z.el.setAttribute('opacity', opacity.toFixed(3));
452
+ }
453
+ }
454
+
455
+ /* =========================================================================
456
+ * 主循环
457
+ * ========================================================================= */
458
+ let startT = performance.now();
459
+ let pausedAt = 0;
460
+ let elapsedOffset = 0;
461
+
462
+ function loop(now) {
463
+ if (paused) {
464
+ requestAnimationFrame(loop);
465
+ return;
466
+ }
467
+ const t = (now - startT) / 1000 + elapsedOffset;
468
+
469
+ // 主层呼吸: scale 在 breathLow .. breathHigh 之间正弦
470
+ const breathPhase = (Math.cos(2 * Math.PI * t / params.breathPeriod) + 1) / 2; // 0..1
471
+ const breathScale = params.breathLow + (params.breathHigh - params.breathLow) * breathPhase;
472
+ if (cloudGroup) {
473
+ cloudGroup.setAttribute('transform', `translate(12 12) scale(${breathScale.toFixed(4)}) translate(-12 -12)`);
474
+ }
475
+
476
+ // 次层帽子 sway: 异步 sin, 围绕 (12, 5.4) rotate
477
+ const hatPhase = Math.sin(2 * Math.PI * t / params.hatSwayPeriod);
478
+ const hatRotDeg = hatPhase * params.hatSwayAmp;
479
+ if (hatGroup) {
480
+ hatGroup.setAttribute('transform', `rotate(${hatRotDeg.toFixed(3)} 12 5.4)`);
481
+ }
482
+
483
+ // Z 字
484
+ updateZs(t);
485
+
486
+ requestAnimationFrame(loop);
487
+ }
488
+
489
+ function resetClock() {
490
+ startT = performance.now();
491
+ elapsedOffset = 0;
492
+ zList.forEach(z => z.el.remove());
493
+ zList.length = 0;
494
+ lastZSpawn = -999;
495
+ }
496
+
497
+ renderStage();
498
+ requestAnimationFrame(loop);
499
+ ]]></script>
500
+ </svg>