@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.
- package/README.md +4 -0
- package/assets/calico/calico-collapsing.apng +0 -0
- package/assets/calico/calico-dozing.apng +0 -0
- package/assets/calico/calico-error.apng +0 -0
- package/assets/calico/calico-happy.apng +0 -0
- package/assets/calico/calico-idle-follow.svg +324 -0
- package/assets/calico/calico-idle.apng +0 -0
- package/assets/calico/calico-mini-alert.apng +0 -0
- package/assets/calico/calico-mini-crabwalk.apng +0 -0
- package/assets/calico/calico-mini-enter.apng +0 -0
- package/assets/calico/calico-mini-happy.apng +0 -0
- package/assets/calico/calico-mini-idle.apng +0 -0
- package/assets/calico/calico-mini-peek.apng +0 -0
- package/assets/calico/calico-mini-sleep.apng +0 -0
- package/assets/calico/calico-notification.apng +0 -0
- package/assets/calico/calico-react-drag.apng +0 -0
- package/assets/calico/calico-react-left.apng +0 -0
- package/assets/calico/calico-react-poke.apng +0 -0
- package/assets/calico/calico-sleeping.apng +0 -0
- package/assets/calico/calico-thinking.apng +0 -0
- package/assets/calico/calico-waking.apng +0 -0
- package/assets/calico/calico-working-building.apng +0 -0
- package/assets/calico/calico-working-carrying.apng +0 -0
- package/assets/calico/calico-working-conducting.apng +0 -0
- package/assets/calico/calico-working-juggling.apng +0 -0
- package/assets/calico/calico-working-sweeping.apng +0 -0
- package/assets/calico/calico-working-typing.apng +0 -0
- package/assets/calico/calico-yawning.apng +0 -0
- package/assets/calico.svg +324 -0
- package/assets/clawd/LICENSE +22 -0
- package/assets/clawd/clawd-about-hero.svg +202 -0
- package/assets/clawd/clawd-collapse-sleep.svg +247 -0
- package/assets/clawd/clawd-error.svg +94 -0
- package/assets/clawd/clawd-happy.svg +161 -0
- package/assets/clawd/clawd-headphones-groove.svg +194 -0
- package/assets/clawd/clawd-idle-bubble.svg +180 -0
- package/assets/clawd/clawd-idle-collapse.svg +101 -0
- package/assets/clawd/clawd-idle-doze.svg +72 -0
- package/assets/clawd/clawd-idle-follow.svg +64 -0
- package/assets/clawd/clawd-idle-living.svg +196 -0
- package/assets/clawd/clawd-idle-look.svg +115 -0
- package/assets/clawd/clawd-idle-reading.svg +252 -0
- package/assets/clawd/clawd-idle-yawn.svg +158 -0
- package/assets/clawd/clawd-mini-alert.svg +129 -0
- package/assets/clawd/clawd-mini-crabwalk.svg +76 -0
- package/assets/clawd/clawd-mini-enter-sleep.svg +64 -0
- package/assets/clawd/clawd-mini-enter.svg +114 -0
- package/assets/clawd/clawd-mini-happy.svg +124 -0
- package/assets/clawd/clawd-mini-idle.svg +83 -0
- package/assets/clawd/clawd-mini-peek.svg +82 -0
- package/assets/clawd/clawd-mini-sleep.svg +112 -0
- package/assets/clawd/clawd-mini-typing.svg +153 -0
- package/assets/clawd/clawd-notification.svg +438 -0
- package/assets/clawd/clawd-react-annoyed.svg +167 -0
- package/assets/clawd/clawd-react-double-jump.svg +229 -0
- package/assets/clawd/clawd-react-double.svg +108 -0
- package/assets/clawd/clawd-react-drag.svg +102 -0
- package/assets/clawd/clawd-react-left.svg +102 -0
- package/assets/clawd/clawd-react-right.svg +102 -0
- package/assets/clawd/clawd-sleeping.svg +118 -0
- package/assets/clawd/clawd-static-base.svg +21 -0
- package/assets/clawd/clawd-wake.svg +277 -0
- package/assets/clawd/clawd-working-building.svg +329 -0
- package/assets/clawd/clawd-working-carrying.svg +178 -0
- package/assets/clawd/clawd-working-debugger.svg +245 -0
- package/assets/clawd/clawd-working-juggling.svg +183 -0
- package/assets/clawd/clawd-working-sweeping.svg +248 -0
- package/assets/clawd/clawd-working-thinking.svg +501 -0
- package/assets/clawd/clawd-working-typing-boss.svg +307 -0
- package/assets/clawd/clawd-working-typing.svg +273 -0
- package/assets/clawd/clawd-working-ultrathink.svg +166 -0
- package/assets/clawd/clawd-working-wizard.svg +356 -0
- package/assets/clawd.svg +64 -0
- package/assets/cloudling/cloudling/cloudling-attention.svg +380 -0
- package/assets/cloudling/cloudling/cloudling-building.svg +667 -0
- package/assets/cloudling/cloudling/cloudling-carrying.svg +604 -0
- package/assets/cloudling/cloudling/cloudling-conducting.svg +616 -0
- package/assets/cloudling/cloudling/cloudling-dozing-to-sleeping.svg +578 -0
- package/assets/cloudling/cloudling/cloudling-dozing.svg +358 -0
- package/assets/cloudling/cloudling/cloudling-error.svg +428 -0
- package/assets/cloudling/cloudling/cloudling-idle-reading.svg +658 -0
- package/assets/cloudling/cloudling/cloudling-idle-to-dozing.svg +568 -0
- package/assets/cloudling/cloudling/cloudling-idle-to-sleeping.svg +811 -0
- package/assets/cloudling/cloudling/cloudling-idle.svg +255 -0
- package/assets/cloudling/cloudling/cloudling-juggling.svg +648 -0
- package/assets/cloudling/cloudling/cloudling-mini-alert.svg +390 -0
- package/assets/cloudling/cloudling/cloudling-mini-crabwalk.svg +335 -0
- package/assets/cloudling/cloudling/cloudling-mini-enter-roll-in.svg +226 -0
- package/assets/cloudling/cloudling/cloudling-mini-enter-sleep.svg +232 -0
- package/assets/cloudling/cloudling/cloudling-mini-happy.svg +371 -0
- package/assets/cloudling/cloudling/cloudling-mini-idle.svg +279 -0
- package/assets/cloudling/cloudling/cloudling-mini-peek.svg +328 -0
- package/assets/cloudling/cloudling/cloudling-mini-sleep.svg +260 -0
- package/assets/cloudling/cloudling/cloudling-mini-typing.svg +692 -0
- package/assets/cloudling/cloudling/cloudling-notification.svg +494 -0
- package/assets/cloudling/cloudling/cloudling-react-drag.svg +83 -0
- package/assets/cloudling/cloudling/cloudling-sleeping-to-idle.svg +555 -0
- package/assets/cloudling/cloudling/cloudling-sleeping.svg +500 -0
- package/assets/cloudling/cloudling/cloudling-sweeping.svg +292 -0
- package/assets/cloudling/cloudling/cloudling-thinking.svg +720 -0
- package/assets/cloudling/cloudling/cloudling-typing.svg +354 -0
- package/assets/cloudling/cloudling-attention.svg +380 -0
- package/assets/cloudling/cloudling-building.svg +667 -0
- package/assets/cloudling/cloudling-carrying.svg +604 -0
- package/assets/cloudling/cloudling-conducting.svg +616 -0
- package/assets/cloudling/cloudling-dozing-to-sleeping.svg +578 -0
- package/assets/cloudling/cloudling-dozing.svg +358 -0
- package/assets/cloudling/cloudling-error.svg +428 -0
- package/assets/cloudling/cloudling-idle-reading.svg +658 -0
- package/assets/cloudling/cloudling-idle-to-dozing.svg +568 -0
- package/assets/cloudling/cloudling-idle-to-sleeping.svg +811 -0
- package/assets/cloudling/cloudling-idle.svg +255 -0
- package/assets/cloudling/cloudling-juggling.svg +648 -0
- package/assets/cloudling/cloudling-mini-alert.svg +390 -0
- package/assets/cloudling/cloudling-mini-crabwalk.svg +335 -0
- package/assets/cloudling/cloudling-mini-enter-roll-in.svg +226 -0
- package/assets/cloudling/cloudling-mini-enter-sleep.svg +232 -0
- package/assets/cloudling/cloudling-mini-happy.svg +371 -0
- package/assets/cloudling/cloudling-mini-idle.svg +279 -0
- package/assets/cloudling/cloudling-mini-peek.svg +328 -0
- package/assets/cloudling/cloudling-mini-sleep.svg +260 -0
- package/assets/cloudling/cloudling-mini-typing.svg +692 -0
- package/assets/cloudling/cloudling-notification.svg +494 -0
- package/assets/cloudling/cloudling-react-drag.svg +83 -0
- package/assets/cloudling/cloudling-sleeping-to-idle.svg +555 -0
- package/assets/cloudling/cloudling-sleeping.svg +500 -0
- package/assets/cloudling/cloudling-sweeping.svg +292 -0
- package/assets/cloudling/cloudling-thinking.svg +720 -0
- package/assets/cloudling/cloudling-typing.svg +354 -0
- package/assets/cloudling.svg +255 -0
- package/assets/ghost-attention.svg +19 -0
- package/assets/ghost-carrying.svg +19 -0
- package/assets/ghost-collapsing.svg +19 -0
- package/assets/ghost-dozing.svg +23 -0
- package/assets/ghost-error.svg +19 -0
- package/assets/ghost-happy.svg +23 -0
- package/assets/ghost-idle-action1.svg +19 -0
- package/assets/ghost-idle-action2.svg +19 -0
- package/assets/ghost-idle-action3.svg +19 -0
- package/assets/ghost-idle.svg +11 -0
- package/assets/ghost-juggling.svg +19 -0
- package/assets/ghost-notification.svg +19 -0
- package/assets/ghost-react-double.svg +23 -0
- package/assets/ghost-react-drag.svg +23 -0
- package/assets/ghost-react-left.svg +19 -0
- package/assets/ghost-react-right.svg +19 -0
- package/assets/ghost-sleeping.svg +19 -0
- package/assets/ghost-sweeping.svg +19 -0
- package/assets/ghost-thinking.svg +19 -0
- package/assets/ghost-waking.svg +19 -0
- package/assets/ghost-working.svg +19 -0
- package/assets/ghost-yawning.svg +23 -0
- package/assets/ghost.svg +11 -0
- package/assets/robot-attention.svg +19 -0
- package/assets/robot-carrying.svg +19 -0
- package/assets/robot-collapsing.svg +19 -0
- package/assets/robot-dozing.svg +23 -0
- package/assets/robot-error.svg +19 -0
- package/assets/robot-happy.svg +23 -0
- package/assets/robot-idle-action1.svg +19 -0
- package/assets/robot-idle-action2.svg +19 -0
- package/assets/robot-idle-action3.svg +19 -0
- package/assets/robot-idle.svg +11 -0
- package/assets/robot-juggling.svg +19 -0
- package/assets/robot-notification.svg +19 -0
- package/assets/robot-react-double.svg +23 -0
- package/assets/robot-react-drag.svg +23 -0
- package/assets/robot-react-left.svg +19 -0
- package/assets/robot-react-right.svg +19 -0
- package/assets/robot-sleeping.svg +19 -0
- package/assets/robot-sweeping.svg +19 -0
- package/assets/robot-thinking.svg +19 -0
- package/assets/robot-waking.svg +19 -0
- package/assets/robot-working.svg +19 -0
- package/assets/robot-yawning.svg +23 -0
- package/assets/robot.svg +11 -0
- package/manifest.json +889 -0
- package/package.json +18 -0
|
@@ -0,0 +1,358 @@
|
|
|
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="control-grid"></g>
|
|
5
|
+
<g id="readout"></g>
|
|
6
|
+
</g>
|
|
7
|
+
<script type="application/ecmascript"><![CDATA[
|
|
8
|
+
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-.740 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";
|
|
9
|
+
|
|
10
|
+
const SOFT_CLOUD_POINTS = [
|
|
11
|
+
{x:9.064,y:3.344},{x:9.825,y:3.106},{x:10.616,y:3.004},{x:11.413,y:3.040},
|
|
12
|
+
{x:12.192,y:3.210},{x:12.928,y:3.518},{x:13.599,y:3.948},{x:14.255,y:4.291},
|
|
13
|
+
{x:15.046,y:4.188},{x:15.843,y:4.224},{x:16.621,y:4.398},{x:17.357,y:4.706},
|
|
14
|
+
{x:18.029,y:5.134},{x:18.617,y:5.673},{x:19.103,y:6.306},{x:19.471,y:7.013},
|
|
15
|
+
{x:19.715,y:7.772},{x:19.812,y:8.564},{x:19.784,y:9.361},{x:19.797,y:10.105},
|
|
16
|
+
{x:20.287,y:10.735},{x:20.656,y:11.442},{x:20.897,y:12.202},{x:20.995,y:12.994},
|
|
17
|
+
{x:20.964,y:13.791},{x:20.791,y:14.569},{x:20.480,y:15.304},{x:20.052,y:15.977},
|
|
18
|
+
{x:19.516,y:16.567},{x:18.882,y:17.052},{x:18.174,y:17.418},{x:17.539,y:17.808},
|
|
19
|
+
{x:17.231,y:18.545},{x:16.805,y:19.218},{x:16.264,y:19.805},{x:15.633,y:20.292},
|
|
20
|
+
{x:14.925,y:20.660},{x:14.163,y:20.895},{x:13.372,y:20.996},{x:12.575,y:20.964},
|
|
21
|
+
{x:11.796,y:20.793},{x:11.061,y:20.483},{x:10.392,y:20.049},{x:9.736,y:19.714},
|
|
22
|
+
{x:8.946,y:19.811},{x:8.148,y:19.777},{x:7.370,y:19.606},{x:6.634,y:19.299},
|
|
23
|
+
{x:5.962,y:18.868},{x:5.377,y:18.326},{x:4.885,y:17.698},{x:4.522,y:16.989},
|
|
24
|
+
{x:4.283,y:16.228},{x:4.182,y:15.437},{x:4.220,y:14.640},{x:4.179,y:13.907},
|
|
25
|
+
{x:3.696,y:13.273},{x:3.332,y:12.563},{x:3.092,y:11.803},{x:3.003,y:11.010},
|
|
26
|
+
{x:3.038,y:10.213},{x:3.202,y:9.433},{x:3.514,y:8.699},{x:3.944,y:8.027},
|
|
27
|
+
{x:4.484,y:7.440},{x:5.114,y:6.951},{x:5.822,y:6.586},{x:6.445,y:6.191},
|
|
28
|
+
{x:6.752,y:5.455},{x:7.185,y:4.785},{x:7.723,y:4.197},{x:8.356,y:3.711},
|
|
29
|
+
];
|
|
30
|
+
|
|
31
|
+
const BASE = {
|
|
32
|
+
slump: 0.55,
|
|
33
|
+
topSag: 1.42,
|
|
34
|
+
shoulderSag: 0.34,
|
|
35
|
+
spread: 0.82,
|
|
36
|
+
floorY: 19.92,
|
|
37
|
+
floorFlatten: 0.56,
|
|
38
|
+
bottomSquish: 0.82,
|
|
39
|
+
leftLean: -0.18,
|
|
40
|
+
bodyTx: 0,
|
|
41
|
+
bodyTy: 1.15,
|
|
42
|
+
bodyRot: -1.5,
|
|
43
|
+
bodyScale: 1.04,
|
|
44
|
+
eyeY: 13.0,
|
|
45
|
+
eyeGap: 6.12,
|
|
46
|
+
eyeW: 2.70,
|
|
47
|
+
eyeH: 1.28,
|
|
48
|
+
eyeTilt: 7.7,
|
|
49
|
+
eyeStroke: 0.80,
|
|
50
|
+
eyeOpacity: 0.92,
|
|
51
|
+
haloY: 20.62,
|
|
52
|
+
haloRx: 10.8,
|
|
53
|
+
haloRy: 1.28,
|
|
54
|
+
haloOpacity: 0.34,
|
|
55
|
+
glowOpacity: 0.18,
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
const LOOP = {
|
|
59
|
+
period: 5.8,
|
|
60
|
+
breathAmp: 0.038,
|
|
61
|
+
pathBreath: 0.055,
|
|
62
|
+
nodCenter: 0.67,
|
|
63
|
+
nodWidth: 0.17,
|
|
64
|
+
nodDepth: 0.38,
|
|
65
|
+
nodRot: -0.85,
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
const CONTROL_CONFIG = [
|
|
69
|
+
{ key: 'period', label: '周期', min: 3.8, max: 10, step: 0.1, unit: 's' },
|
|
70
|
+
{ key: 'breathAmp', label: '呼吸', min: 0, max: 0.08, step: 0.001 },
|
|
71
|
+
{ key: 'pathBreath', label: '轮廓起伏', min: 0, max: 0.12, step: 0.001 },
|
|
72
|
+
{ key: 'nodCenter', label: '点头位置', min: 0.25, max: 0.90, step: 0.01 },
|
|
73
|
+
{ key: 'nodWidth', label: '点头宽度', min: 0.08, max: 0.28, step: 0.01 },
|
|
74
|
+
{ key: 'nodDepth', label: '点头下沉', min: 0, max: 0.9, step: 0.01 },
|
|
75
|
+
{ key: 'nodRot', label: '点头旋转', min: -2.5, max: 0.5, step: 0.05, unit: 'deg' },
|
|
76
|
+
];
|
|
77
|
+
|
|
78
|
+
const stage = document.getElementById('stage');
|
|
79
|
+
const readout = document.getElementById('readout');
|
|
80
|
+
const controlGrid = document.getElementById('control-grid');
|
|
81
|
+
let playing = true;
|
|
82
|
+
let startMs = performance.now();
|
|
83
|
+
let pausedT = 0;
|
|
84
|
+
let lowPowerPaused = false;
|
|
85
|
+
let rafId = 0;
|
|
86
|
+
|
|
87
|
+
function scheduleLoop() {
|
|
88
|
+
if (!lowPowerPaused && !rafId) rafId = requestAnimationFrame(loop);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
window.__clawdSetLowPowerPaused = paused => {
|
|
92
|
+
const next = !!paused;
|
|
93
|
+
if (lowPowerPaused === next) return;
|
|
94
|
+
lowPowerPaused = next;
|
|
95
|
+
if (next) {
|
|
96
|
+
pausedT = currentTime(performance.now());
|
|
97
|
+
playing = false;
|
|
98
|
+
if (rafId) cancelAnimationFrame(rafId);
|
|
99
|
+
rafId = 0;
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
playing = true;
|
|
103
|
+
startMs = performance.now() - pausedT * 1000;
|
|
104
|
+
scheduleLoop();
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
function clamp01(v) { return Math.max(0, Math.min(1, v)); }
|
|
108
|
+
function lerp(a, b, t) { return a + (b - a) * t; }
|
|
109
|
+
function smoothstep01(x) {
|
|
110
|
+
const t = clamp01(x);
|
|
111
|
+
return t * t * (3 - 2 * t);
|
|
112
|
+
}
|
|
113
|
+
function loopDistance(a, b) {
|
|
114
|
+
return Math.abs(((a - b + 0.5) % 1 + 1) % 1 - 0.5);
|
|
115
|
+
}
|
|
116
|
+
function loopBump(phase, center, width) {
|
|
117
|
+
const d = loopDistance(phase, center);
|
|
118
|
+
if (d >= width) return 0;
|
|
119
|
+
return 1 - smoothstep01(d / width);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
function catmullRomClosedPath(points, tension = 1) {
|
|
123
|
+
const q = (n) => n.toFixed(3);
|
|
124
|
+
let d = `M ${q(points[0].x)} ${q(points[0].y)}`;
|
|
125
|
+
const n = points.length;
|
|
126
|
+
for (let i = 0; i < n; i++) {
|
|
127
|
+
const p0 = points[(i - 1 + n) % n];
|
|
128
|
+
const p1 = points[i];
|
|
129
|
+
const p2 = points[(i + 1) % n];
|
|
130
|
+
const p3 = points[(i + 2) % n];
|
|
131
|
+
const c1x = p1.x + (p2.x - p0.x) * tension / 6;
|
|
132
|
+
const c1y = p1.y + (p2.y - p0.y) * tension / 6;
|
|
133
|
+
const c2x = p2.x - (p3.x - p1.x) * tension / 6;
|
|
134
|
+
const c2y = p2.y - (p3.y - p1.y) * tension / 6;
|
|
135
|
+
d += ` C ${q(c1x)} ${q(c1y)} ${q(c2x)} ${q(c2y)} ${q(p2.x)} ${q(p2.y)}`;
|
|
136
|
+
}
|
|
137
|
+
return `${d} Z`;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
function makeDozeCloudPath(amount) {
|
|
141
|
+
const mix = clamp01(amount);
|
|
142
|
+
if (mix <= 0.0001) return CLOUD_D;
|
|
143
|
+
const slump = mix;
|
|
144
|
+
const points = SOFT_CLOUD_POINTS.map((pt) => {
|
|
145
|
+
const dx = pt.x - 12;
|
|
146
|
+
const distX = Math.abs(dx);
|
|
147
|
+
const sign = dx === 0 ? 0 : Math.sign(dx);
|
|
148
|
+
const topWeight = Math.pow(clamp01((8.2 - pt.y) / 5.2), 0.95);
|
|
149
|
+
const crownCenter = clamp01(1 - distX / 6.6);
|
|
150
|
+
const shoulderWeight = clamp01((pt.y - 6.0) / 8.5) * clamp01((18.6 - pt.y) / 10.5);
|
|
151
|
+
const lowerWeight = Math.pow(clamp01((pt.y - 15.4) / 5.8), 0.78);
|
|
152
|
+
const sideWeight = clamp01((distX - 2.2) / 7.5) * clamp01((20.2 - pt.y) / 16.2);
|
|
153
|
+
|
|
154
|
+
const topSag = slump * topWeight * BASE.topSag * (0.70 + crownCenter * 0.42);
|
|
155
|
+
const shoulderSag = slump * shoulderWeight * BASE.shoulderSag;
|
|
156
|
+
const floorCurve = BASE.floorY + Math.pow(distX / 10, 2) * 0.46 - crownCenter * 0.04;
|
|
157
|
+
const floorPull = (floorCurve - pt.y) * lowerWeight * BASE.floorFlatten * slump;
|
|
158
|
+
const sideOut = sign * slump * BASE.spread * (0.18 + sideWeight * 0.76 + lowerWeight * 0.42);
|
|
159
|
+
const bottomInhale = lowerWeight * BASE.bottomSquish * slump * -0.34;
|
|
160
|
+
const lean = BASE.leftLean * slump * (0.15 + topWeight * 0.50 + shoulderWeight * 0.30);
|
|
161
|
+
|
|
162
|
+
return { x: pt.x + sideOut + lean, y: pt.y + topSag + shoulderSag + floorPull + bottomInhale };
|
|
163
|
+
});
|
|
164
|
+
return catmullRomClosedPath(points, lerp(0.95, 0.68, mix));
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
function defs(cloudD) {
|
|
168
|
+
return `
|
|
169
|
+
<linearGradient id="body-grad" x1="12" y1="2" x2="12" y2="22" gradientUnits="userSpaceOnUse">
|
|
170
|
+
<stop offset="0" stop-color="#93c5fd"/>
|
|
171
|
+
<stop offset="0.48" stop-color="#f5f6fa"/>
|
|
172
|
+
<stop offset="1" stop-color="#dde2ec"/>
|
|
173
|
+
</linearGradient>
|
|
174
|
+
<linearGradient id="eye-grad" x1="12" y1="9" x2="12" y2="15" gradientUnits="userSpaceOnUse">
|
|
175
|
+
<stop offset="0" stop-color="#B1A7FF"/>
|
|
176
|
+
<stop offset="0.55" stop-color="#7A9DFF"/>
|
|
177
|
+
<stop offset="1" stop-color="#3941FF"/>
|
|
178
|
+
</linearGradient>
|
|
179
|
+
<radialGradient id="hi-grad" cx="0.43" cy="0.22" r="0.62">
|
|
180
|
+
<stop offset="0" stop-color="#93c5fd" stop-opacity="0.82"/>
|
|
181
|
+
<stop offset="0.58" stop-color="#93c5fd" stop-opacity="0.28"/>
|
|
182
|
+
<stop offset="1" stop-color="#93c5fd" stop-opacity="0"/>
|
|
183
|
+
</radialGradient>
|
|
184
|
+
<radialGradient id="halo-grad" cx="50%" cy="50%" r="50%">
|
|
185
|
+
<stop offset="0" stop-color="#7A9DFF" stop-opacity="0.55"/>
|
|
186
|
+
<stop offset="0.54" stop-color="#3941FF" stop-opacity="0.24"/>
|
|
187
|
+
<stop offset="1" stop-color="#3941FF" stop-opacity="0"/>
|
|
188
|
+
</radialGradient>
|
|
189
|
+
<filter id="drop" x="-45%" y="-45%" width="190%" height="190%">
|
|
190
|
+
<feDropShadow class="cloud-drop-shadow" dx="0" dy="0.72" stdDeviation="0.92" flood-color="#3941FF" flood-opacity="0.35"/>
|
|
191
|
+
</filter>
|
|
192
|
+
<filter id="edge-blur" x="-40%" y="-40%" width="180%" height="180%">
|
|
193
|
+
<feGaussianBlur stdDeviation="0.16"/>
|
|
194
|
+
</filter>
|
|
195
|
+
<filter id="halo-blur" x="-80%" y="-120%" width="260%" height="340%">
|
|
196
|
+
<feGaussianBlur stdDeviation="0.42"/>
|
|
197
|
+
</filter>
|
|
198
|
+
<clipPath id="cloud-clip"><path d="${cloudD}"/></clipPath>
|
|
199
|
+
`;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
function sleepyEyePath(cx, cy, w, h, tiltDeg, flip) {
|
|
203
|
+
const x1 = cx - w / 2;
|
|
204
|
+
const x2 = cx + w / 2;
|
|
205
|
+
const mid = cy + h * 0.56;
|
|
206
|
+
const c1x = cx - w * 0.18;
|
|
207
|
+
const c2x = cx + w * 0.18;
|
|
208
|
+
const tilt = flip * tiltDeg * 0.018;
|
|
209
|
+
return `M ${x1.toFixed(2)} ${(cy - tilt).toFixed(2)} C ${c1x.toFixed(2)} ${mid.toFixed(2)} ${c2x.toFixed(2)} ${mid.toFixed(2)} ${x2.toFixed(2)} ${(cy + tilt).toFixed(2)}`;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
function makeEyes(frame) {
|
|
213
|
+
const lcx = 12 - BASE.eyeGap / 2;
|
|
214
|
+
const rcx = 12 + BASE.eyeGap / 2;
|
|
215
|
+
const cy = BASE.eyeY + frame.eyeDrop;
|
|
216
|
+
const h = BASE.eyeH + frame.eyeSoft;
|
|
217
|
+
const opacity = BASE.eyeOpacity * frame.eyeOpacity;
|
|
218
|
+
return `
|
|
219
|
+
<g fill="none" stroke="url(#eye-grad)" stroke-width="${(BASE.eyeStroke + frame.eyeStrokeAdd).toFixed(2)}" stroke-linecap="round" stroke-linejoin="round" opacity="${opacity.toFixed(3)}">
|
|
220
|
+
<path d="${sleepyEyePath(lcx, cy, BASE.eyeW, h, BASE.eyeTilt, -1)}"/>
|
|
221
|
+
<path d="${sleepyEyePath(rcx, cy, BASE.eyeW, h, BASE.eyeTilt, 1)}"/>
|
|
222
|
+
</g>
|
|
223
|
+
`;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
function computeFrame(t) {
|
|
227
|
+
const phase = (t / LOOP.period) % 1;
|
|
228
|
+
const inhale = (1 - Math.cos(phase * Math.PI * 2)) / 2;
|
|
229
|
+
const exhale = 1 - inhale;
|
|
230
|
+
const nod = loopBump(phase, LOOP.nodCenter, LOOP.nodWidth);
|
|
231
|
+
const microFloat = Math.sin(phase * Math.PI * 4 + 0.45) * 0.06;
|
|
232
|
+
const pathSlump = clamp01(BASE.slump - inhale * LOOP.pathBreath + nod * 0.045);
|
|
233
|
+
const bodyTy = BASE.bodyTy - inhale * LOOP.breathAmp * 4.6 + nod * LOOP.nodDepth + microFloat * 0.18;
|
|
234
|
+
const bodyRot = BASE.bodyRot + inhale * 0.22 + nod * LOOP.nodRot;
|
|
235
|
+
const sx = BASE.bodyScale + inhale * LOOP.breathAmp * 0.95 + nod * 0.014;
|
|
236
|
+
const sy = BASE.bodyScale + inhale * LOOP.breathAmp * 1.36 - nod * 0.024;
|
|
237
|
+
return {
|
|
238
|
+
phase,
|
|
239
|
+
inhale,
|
|
240
|
+
nod,
|
|
241
|
+
pathSlump,
|
|
242
|
+
bodyTy,
|
|
243
|
+
bodyRot,
|
|
244
|
+
sx,
|
|
245
|
+
sy,
|
|
246
|
+
eyeDrop: -inhale * 0.12 + nod * 0.18,
|
|
247
|
+
eyeSoft: inhale * 0.12 + nod * 0.10,
|
|
248
|
+
eyeStrokeAdd: inhale * 0.04 + nod * 0.03,
|
|
249
|
+
eyeOpacity: clamp01(1 + inhale * 0.04 - nod * 0.04),
|
|
250
|
+
haloOpacity: BASE.haloOpacity + exhale * 0.045 + nod * 0.055,
|
|
251
|
+
haloY: BASE.haloY + exhale * 0.08 + nod * 0.18,
|
|
252
|
+
haloRx: BASE.haloRx + exhale * 0.44 + nod * 0.32,
|
|
253
|
+
haloRy: BASE.haloRy + exhale * 0.08 + nod * 0.08,
|
|
254
|
+
};
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
function renderFrame(t) {
|
|
258
|
+
const frame = computeFrame(t);
|
|
259
|
+
const cloudD = makeDozeCloudPath(frame.pathSlump);
|
|
260
|
+
const bodyTransform = `translate(${BASE.bodyTx.toFixed(3)} ${frame.bodyTy.toFixed(3)}) translate(12 12) rotate(${frame.bodyRot.toFixed(3)}) scale(${frame.sx.toFixed(4)} ${frame.sy.toFixed(4)}) translate(-12 -12)`;
|
|
261
|
+
stage.innerHTML = `
|
|
262
|
+
<style>
|
|
263
|
+
.edge-rim-tight { stroke-opacity: .9; }
|
|
264
|
+
.edge-rim-soft { stroke-opacity: .75; }
|
|
265
|
+
.edge-contact { stroke-opacity: .9; }
|
|
266
|
+
.wrap {
|
|
267
|
+
width: min(980px, 100%);
|
|
268
|
+
display: grid;
|
|
269
|
+
gap: 18px;
|
|
270
|
+
}
|
|
271
|
+
.header {
|
|
272
|
+
display: flex;
|
|
273
|
+
justify-content: space-between;
|
|
274
|
+
gap: 14px;
|
|
275
|
+
align-items: end;
|
|
276
|
+
}
|
|
277
|
+
#stage {
|
|
278
|
+
width: min(540px, 82vw);
|
|
279
|
+
aspect-ratio: 1;
|
|
280
|
+
display: grid;
|
|
281
|
+
place-items: center;
|
|
282
|
+
}
|
|
283
|
+
.grid {
|
|
284
|
+
display: grid;
|
|
285
|
+
grid-template-columns: repeat(4, minmax(120px, 1fr));
|
|
286
|
+
gap: 10px 14px;
|
|
287
|
+
}
|
|
288
|
+
.field {
|
|
289
|
+
display: grid;
|
|
290
|
+
grid-template-columns: 1fr auto;
|
|
291
|
+
gap: 6px;
|
|
292
|
+
align-items: center;
|
|
293
|
+
color: rgba(255,255,255,.58);
|
|
294
|
+
font-size: 12px;
|
|
295
|
+
}
|
|
296
|
+
.field input { grid-column: 1 / -1; }
|
|
297
|
+
output {
|
|
298
|
+
min-width: 46px;
|
|
299
|
+
text-align: right;
|
|
300
|
+
color: rgba(255,255,255,.70);
|
|
301
|
+
font-size: 12px;
|
|
302
|
+
}
|
|
303
|
+
@media (prefers-color-scheme: light) {
|
|
304
|
+
.edge-rim-tight { stroke-opacity: .95; }
|
|
305
|
+
.edge-rim-soft { stroke-opacity: .85; }
|
|
306
|
+
.edge-contact { stroke-opacity: .95; }
|
|
307
|
+
.cloud-drop-shadow { flood-opacity: .13; }
|
|
308
|
+
output, .field { color: rgba(21,33,63,.62); }
|
|
309
|
+
.cloud-drop-shadow { flood-opacity: .13; }
|
|
310
|
+
.food-cloud-shadow { flood-opacity: .28; flood-color: #5060FF; }
|
|
311
|
+
.food-cloud > path {
|
|
312
|
+
stroke: #5870E8 !important;
|
|
313
|
+
stroke-width: 1.2 !important;
|
|
314
|
+
stroke-opacity: .9 !important;
|
|
315
|
+
vector-effect: non-scaling-stroke !important;
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
</style>
|
|
319
|
+
<defs>
|
|
320
|
+
${defs(cloudD)}
|
|
321
|
+
<path id="cloud-shape" d="${cloudD}"/>
|
|
322
|
+
</defs>
|
|
323
|
+
<g id="halo-layer">
|
|
324
|
+
<ellipse cx="12" cy="${frame.haloY.toFixed(2)}" rx="${frame.haloRx.toFixed(2)}" ry="${frame.haloRy.toFixed(2)}" fill="url(#halo-grad)" opacity="${frame.haloOpacity.toFixed(3)}" filter="url(#halo-blur)"/>
|
|
325
|
+
<ellipse cx="12" cy="${(frame.haloY + 0.05).toFixed(2)}" rx="${(frame.haloRx * 0.68).toFixed(2)}" ry="${(frame.haloRy * 0.46).toFixed(2)}" fill="#7A9DFF" opacity="${BASE.glowOpacity}" filter="url(#halo-blur)"/>
|
|
326
|
+
</g>
|
|
327
|
+
<g transform="${bodyTransform}">
|
|
328
|
+
<g id="edge-system">
|
|
329
|
+
<use class="edge-contact" href="#cloud-shape" fill="none" stroke="#7d8aa7" stroke-width="1.00" stroke-linejoin="round" transform="translate(0 0.16)" filter="url(#edge-blur)"/>
|
|
330
|
+
<use class="edge-rim-soft" href="#cloud-shape" fill="none" stroke="#7A9DFF" stroke-width="0.88" stroke-linejoin="round" filter="url(#edge-blur)"/>
|
|
331
|
+
<use class="edge-rim-tight" href="#cloud-shape" fill="none" stroke="#aab3c6" stroke-width="0.40" stroke-linejoin="round"/>
|
|
332
|
+
</g>
|
|
333
|
+
<g filter="url(#drop)">
|
|
334
|
+
<use href="#cloud-shape" fill="url(#body-grad)"/>
|
|
335
|
+
<rect x="1" y="2.2" width="22" height="10.4" fill="url(#hi-grad)" clip-path="url(#cloud-clip)" opacity=".88"/>
|
|
336
|
+
</g>
|
|
337
|
+
${makeEyes(frame)}
|
|
338
|
+
</g>
|
|
339
|
+
`;
|
|
340
|
+
readout.innerHTML = `<span>phase: <b>${frame.phase.toFixed(2)}</b></span><span>slump ${(frame.pathSlump * 100).toFixed(0)}%</span><span>inhale ${(frame.inhale * 100).toFixed(0)}%</span><span>nod ${(frame.nod * 100).toFixed(0)}%</span><span>rot ${frame.bodyRot.toFixed(2)}deg</span>`;
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
function currentTime(now) {
|
|
344
|
+
if (!playing) return pausedT;
|
|
345
|
+
return (now - startMs) / 1000;
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
function loop(now) {
|
|
349
|
+
rafId = 0;
|
|
350
|
+
if (lowPowerPaused) return;
|
|
351
|
+
renderFrame(currentTime(now));
|
|
352
|
+
scheduleLoop();
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
renderFrame(0);
|
|
356
|
+
scheduleLoop();
|
|
357
|
+
]]></script>
|
|
358
|
+
</svg>
|