@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,568 @@
|
|
|
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="btn-play"></g>
|
|
5
|
+
<g id="duration-total"></g>
|
|
6
|
+
<g id="motion-controls"></g>
|
|
7
|
+
<g id="motion-summary"></g>
|
|
8
|
+
<g id="phase"></g>
|
|
9
|
+
<g id="scrubber"></g>
|
|
10
|
+
<g id="time"></g>
|
|
11
|
+
<g id="timing-controls"></g>
|
|
12
|
+
</g>
|
|
13
|
+
<script type="application/ecmascript"><![CDATA[
|
|
14
|
+
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";
|
|
15
|
+
|
|
16
|
+
const SOFT_CLOUD_POINTS = [
|
|
17
|
+
{x:9.064,y:3.344},{x:9.825,y:3.106},{x:10.616,y:3.004},{x:11.413,y:3.040},
|
|
18
|
+
{x:12.192,y:3.210},{x:12.928,y:3.518},{x:13.599,y:3.948},{x:14.255,y:4.291},
|
|
19
|
+
{x:15.046,y:4.188},{x:15.843,y:4.224},{x:16.621,y:4.398},{x:17.357,y:4.706},
|
|
20
|
+
{x:18.029,y:5.134},{x:18.617,y:5.673},{x:19.103,y:6.306},{x:19.471,y:7.013},
|
|
21
|
+
{x:19.715,y:7.772},{x:19.812,y:8.564},{x:19.784,y:9.361},{x:19.797,y:10.105},
|
|
22
|
+
{x:20.287,y:10.735},{x:20.656,y:11.442},{x:20.897,y:12.202},{x:20.995,y:12.994},
|
|
23
|
+
{x:20.964,y:13.791},{x:20.791,y:14.569},{x:20.480,y:15.304},{x:20.052,y:15.977},
|
|
24
|
+
{x:19.516,y:16.567},{x:18.882,y:17.052},{x:18.174,y:17.418},{x:17.539,y:17.808},
|
|
25
|
+
{x:17.231,y:18.545},{x:16.805,y:19.218},{x:16.264,y:19.805},{x:15.633,y:20.292},
|
|
26
|
+
{x:14.925,y:20.660},{x:14.163,y:20.895},{x:13.372,y:20.996},{x:12.575,y:20.964},
|
|
27
|
+
{x:11.796,y:20.793},{x:11.061,y:20.483},{x:10.392,y:20.049},{x:9.736,y:19.714},
|
|
28
|
+
{x:8.946,y:19.811},{x:8.148,y:19.777},{x:7.370,y:19.606},{x:6.634,y:19.299},
|
|
29
|
+
{x:5.962,y:18.868},{x:5.377,y:18.326},{x:4.885,y:17.698},{x:4.522,y:16.989},
|
|
30
|
+
{x:4.283,y:16.228},{x:4.182,y:15.437},{x:4.220,y:14.640},{x:4.179,y:13.907},
|
|
31
|
+
{x:3.696,y:13.273},{x:3.332,y:12.563},{x:3.092,y:11.803},{x:3.003,y:11.010},
|
|
32
|
+
{x:3.038,y:10.213},{x:3.202,y:9.433},{x:3.514,y:8.699},{x:3.944,y:8.027},
|
|
33
|
+
{x:4.484,y:7.440},{x:5.114,y:6.951},{x:5.822,y:6.586},{x:6.445,y:6.191},
|
|
34
|
+
{x:6.752,y:5.455},{x:7.185,y:4.785},{x:7.723,y:4.197},{x:8.356,y:3.711},
|
|
35
|
+
];
|
|
36
|
+
|
|
37
|
+
let DURATION = 9.03;
|
|
38
|
+
const DOZE = {
|
|
39
|
+
slump: 0.55,
|
|
40
|
+
topSag: 1.42,
|
|
41
|
+
shoulderSag: 0.34,
|
|
42
|
+
spread: 0.82,
|
|
43
|
+
floorY: 19.92,
|
|
44
|
+
floorFlatten: 0.56,
|
|
45
|
+
bottomSquish: 0.82,
|
|
46
|
+
leftLean: -0.18,
|
|
47
|
+
bodyTx: 0,
|
|
48
|
+
bodyTy: 1.15,
|
|
49
|
+
bodyRot: -1.5,
|
|
50
|
+
bodyScale: 1.04,
|
|
51
|
+
eyeY: 13.0,
|
|
52
|
+
eyeGap: 6.12,
|
|
53
|
+
eyeW: 2.70,
|
|
54
|
+
eyeH: 1.28,
|
|
55
|
+
eyeTilt: 7.7,
|
|
56
|
+
eyeStroke: 0.80,
|
|
57
|
+
eyeOpacity: 0.92,
|
|
58
|
+
haloY: 20.62,
|
|
59
|
+
haloRx: 10.8,
|
|
60
|
+
haloRy: 1.28,
|
|
61
|
+
haloOpacity: 0.34,
|
|
62
|
+
glowOpacity: 0.18,
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
const stage = document.getElementById('stage');
|
|
66
|
+
const scrubber = document.getElementById('scrubber');
|
|
67
|
+
const timeOut = document.getElementById('time');
|
|
68
|
+
const phaseEl = document.getElementById('phase');
|
|
69
|
+
const timingControls = document.getElementById('timing-controls');
|
|
70
|
+
const durationTotal = document.getElementById('duration-total');
|
|
71
|
+
const motionControls = document.getElementById('motion-controls');
|
|
72
|
+
const motionSummary = document.getElementById('motion-summary');
|
|
73
|
+
let playing = true;
|
|
74
|
+
let start = performance.now();
|
|
75
|
+
let pausedAt = 0;
|
|
76
|
+
let manualT = 0;
|
|
77
|
+
|
|
78
|
+
const TIMING_CONTROLS = [
|
|
79
|
+
{ key: 'idleHold', label: '起始停顿', min: 0.10, max: 1.20, step: 0.01 },
|
|
80
|
+
{ key: 'halfClose1', label: '半闭 1', min: 0.25, max: 1.60, step: 0.01 },
|
|
81
|
+
{ key: 'wake1', label: '睁眼 1', min: 0.12, max: 0.80, step: 0.01 },
|
|
82
|
+
{ key: 'hold1', label: '撑住 1', min: 0.10, max: 1.40, step: 0.01 },
|
|
83
|
+
{ key: 'halfClose2', label: '半闭 2', min: 0.25, max: 1.80, step: 0.01 },
|
|
84
|
+
{ key: 'wake2', label: '睁眼 2', min: 0.12, max: 0.90, step: 0.01 },
|
|
85
|
+
{ key: 'hold2', label: '撑住 2', min: 0.10, max: 1.50, step: 0.01 },
|
|
86
|
+
{ key: 'finalClose', label: '最终闭眼', min: 0.25, max: 1.70, step: 0.01 },
|
|
87
|
+
{ key: 'slump', label: '软塌下移', min: 0.45, max: 2.20, step: 0.01 },
|
|
88
|
+
{ key: 'finalHold', label: '末尾停顿', min: 0.05, max: 0.80, step: 0.01 },
|
|
89
|
+
];
|
|
90
|
+
|
|
91
|
+
const TIMING = {
|
|
92
|
+
idleHold: 0.35,
|
|
93
|
+
halfClose1: 1.00,
|
|
94
|
+
wake1: 0.30,
|
|
95
|
+
hold1: 0.80,
|
|
96
|
+
halfClose2: 1.59,
|
|
97
|
+
wake2: 0.55,
|
|
98
|
+
hold2: 1.00,
|
|
99
|
+
finalClose: 1.32,
|
|
100
|
+
slump: 1.88,
|
|
101
|
+
finalHold: 0.24,
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
const MOTION_CONTROLS = [
|
|
105
|
+
{ key: 'breathAmp', label: '呼吸幅度', min: 0, max: 0.04, step: 0.001, format: (v) => `${(v * 100).toFixed(1)}%` },
|
|
106
|
+
{ key: 'breathPeriod', label: '呼吸周期', min: 1.80, max: 6.00, step: 0.05, format: (v) => `${v.toFixed(2)}s` },
|
|
107
|
+
];
|
|
108
|
+
|
|
109
|
+
const MOTION = {
|
|
110
|
+
breathAmp: 0.018,
|
|
111
|
+
breathPeriod: 3.80,
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
function clamp01(v) { return Math.max(0, Math.min(1, v)); }
|
|
115
|
+
function lerp(a, b, t) { return a + (b - a) * t; }
|
|
116
|
+
function progress(t, a, b) { return clamp01((t - a) / (b - a)); }
|
|
117
|
+
function smoothstep(a, b, t) {
|
|
118
|
+
const x = progress(t, a, b);
|
|
119
|
+
return x * x * (3 - 2 * x);
|
|
120
|
+
}
|
|
121
|
+
function easeInOut(t) {
|
|
122
|
+
return t < .5 ? 4*t*t*t : 1 - Math.pow(-2*t + 2, 3) / 2;
|
|
123
|
+
}
|
|
124
|
+
function easeOutBack(t) {
|
|
125
|
+
const c1 = 1.18;
|
|
126
|
+
const c3 = c1 + 1;
|
|
127
|
+
return 1 + c3 * Math.pow(t - 1, 3) + c1 * Math.pow(t - 1, 2);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
function buildTimeline() {
|
|
131
|
+
let t = 0;
|
|
132
|
+
const tl = {};
|
|
133
|
+
tl.idleEnd = t += TIMING.idleHold;
|
|
134
|
+
tl.half1Start = tl.idleEnd;
|
|
135
|
+
tl.half1Peak = t += TIMING.halfClose1;
|
|
136
|
+
tl.wake1End = t += TIMING.wake1;
|
|
137
|
+
tl.hold1End = t += TIMING.hold1;
|
|
138
|
+
tl.half2Start = tl.hold1End;
|
|
139
|
+
tl.half2Peak = t += TIMING.halfClose2;
|
|
140
|
+
tl.wake2End = t += TIMING.wake2;
|
|
141
|
+
tl.hold2End = t += TIMING.hold2;
|
|
142
|
+
tl.closeStart = tl.hold2End;
|
|
143
|
+
tl.closeEnd = t += TIMING.finalClose;
|
|
144
|
+
tl.slumpStart = tl.closeEnd;
|
|
145
|
+
tl.slumpEnd = t += TIMING.slump;
|
|
146
|
+
tl.duration = t += TIMING.finalHold;
|
|
147
|
+
tl.haloStart = tl.slumpStart + TIMING.slump * 0.13;
|
|
148
|
+
tl.dropStart = tl.slumpStart + TIMING.slump * 0.03;
|
|
149
|
+
return tl;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
let timeline = buildTimeline();
|
|
153
|
+
DURATION = timeline.duration;
|
|
154
|
+
|
|
155
|
+
function syncDuration() {
|
|
156
|
+
timeline = buildTimeline();
|
|
157
|
+
DURATION = timeline.duration;
|
|
158
|
+
scrubber.max = DURATION.toFixed(2);
|
|
159
|
+
if (manualT > DURATION) manualT = DURATION;
|
|
160
|
+
durationTotal.textContent = `total ${DURATION.toFixed(2)} s`;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
function catmullRomClosedPath(points, tension = 1) {
|
|
164
|
+
const q = (n) => n.toFixed(3);
|
|
165
|
+
let d = `M ${q(points[0].x)} ${q(points[0].y)}`;
|
|
166
|
+
const n = points.length;
|
|
167
|
+
for (let i = 0; i < n; i++) {
|
|
168
|
+
const p0 = points[(i - 1 + n) % n];
|
|
169
|
+
const p1 = points[i];
|
|
170
|
+
const p2 = points[(i + 1) % n];
|
|
171
|
+
const p3 = points[(i + 2) % n];
|
|
172
|
+
const c1x = p1.x + (p2.x - p0.x) * tension / 6;
|
|
173
|
+
const c1y = p1.y + (p2.y - p0.y) * tension / 6;
|
|
174
|
+
const c2x = p2.x - (p3.x - p1.x) * tension / 6;
|
|
175
|
+
const c2y = p2.y - (p3.y - p1.y) * tension / 6;
|
|
176
|
+
d += ` C ${q(c1x)} ${q(c1y)} ${q(c2x)} ${q(c2y)} ${q(p2.x)} ${q(p2.y)}`;
|
|
177
|
+
}
|
|
178
|
+
return `${d} Z`;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
function makeDozeCloudPath(amount) {
|
|
182
|
+
const mix = clamp01(amount);
|
|
183
|
+
if (mix <= 0.0001) return CLOUD_D;
|
|
184
|
+
const slump = DOZE.slump * mix;
|
|
185
|
+
const points = SOFT_CLOUD_POINTS.map((pt) => {
|
|
186
|
+
const dx = pt.x - 12;
|
|
187
|
+
const distX = Math.abs(dx);
|
|
188
|
+
const sign = dx === 0 ? 0 : Math.sign(dx);
|
|
189
|
+
const topWeight = Math.pow(clamp01((8.2 - pt.y) / 5.2), 0.95);
|
|
190
|
+
const crownCenter = clamp01(1 - distX / 6.6);
|
|
191
|
+
const shoulderWeight = clamp01((pt.y - 6.0) / 8.5) * clamp01((18.6 - pt.y) / 10.5);
|
|
192
|
+
const lowerWeight = Math.pow(clamp01((pt.y - 15.4) / 5.8), 0.78);
|
|
193
|
+
const sideWeight = clamp01((distX - 2.2) / 7.5) * clamp01((20.2 - pt.y) / 16.2);
|
|
194
|
+
|
|
195
|
+
const topSag = slump * topWeight * DOZE.topSag * (0.70 + crownCenter * 0.42);
|
|
196
|
+
const shoulderSag = slump * shoulderWeight * DOZE.shoulderSag;
|
|
197
|
+
const floorCurve = DOZE.floorY + Math.pow(distX / 10, 2) * 0.46 - crownCenter * 0.04;
|
|
198
|
+
const floorPull = (floorCurve - pt.y) * lowerWeight * DOZE.floorFlatten * slump;
|
|
199
|
+
const sideOut = sign * slump * DOZE.spread * (0.18 + sideWeight * 0.76 + lowerWeight * 0.42);
|
|
200
|
+
const bottomInhale = lowerWeight * DOZE.bottomSquish * slump * -0.34;
|
|
201
|
+
const lean = DOZE.leftLean * slump * (0.15 + topWeight * 0.50 + shoulderWeight * 0.30);
|
|
202
|
+
|
|
203
|
+
return {
|
|
204
|
+
x: pt.x + sideOut + lean,
|
|
205
|
+
y: pt.y + topSag + shoulderSag + floorPull + bottomInhale
|
|
206
|
+
};
|
|
207
|
+
});
|
|
208
|
+
return catmullRomClosedPath(points, lerp(0.95, 0.68, mix));
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
function defs(cloudD) {
|
|
212
|
+
return `
|
|
213
|
+
<linearGradient id="body-grad" x1="12" y1="2" x2="12" y2="22" gradientUnits="userSpaceOnUse">
|
|
214
|
+
<stop offset="0" stop-color="#93c5fd"/>
|
|
215
|
+
<stop offset="0.48" stop-color="#f5f6fa"/>
|
|
216
|
+
<stop offset="1" stop-color="#dde2ec"/>
|
|
217
|
+
</linearGradient>
|
|
218
|
+
<linearGradient id="eye-grad" x1="12" y1="8" x2="12" y2="15" gradientUnits="userSpaceOnUse">
|
|
219
|
+
<stop offset="0" stop-color="#B1A7FF"/>
|
|
220
|
+
<stop offset="0.55" stop-color="#7A9DFF"/>
|
|
221
|
+
<stop offset="1" stop-color="#3941FF"/>
|
|
222
|
+
</linearGradient>
|
|
223
|
+
<radialGradient id="hi-grad" cx="0.43" cy="0.22" r="0.62">
|
|
224
|
+
<stop offset="0" stop-color="#93c5fd" stop-opacity="0.82"/>
|
|
225
|
+
<stop offset="0.58" stop-color="#93c5fd" stop-opacity="0.28"/>
|
|
226
|
+
<stop offset="1" stop-color="#93c5fd" stop-opacity="0"/>
|
|
227
|
+
</radialGradient>
|
|
228
|
+
<radialGradient id="halo-grad" cx="50%" cy="50%" r="50%">
|
|
229
|
+
<stop offset="0" stop-color="#7A9DFF" stop-opacity="0.55"/>
|
|
230
|
+
<stop offset="0.54" stop-color="#3941FF" stop-opacity="0.24"/>
|
|
231
|
+
<stop offset="1" stop-color="#3941FF" stop-opacity="0"/>
|
|
232
|
+
</radialGradient>
|
|
233
|
+
<filter id="drop" x="-45%" y="-45%" width="190%" height="190%">
|
|
234
|
+
<feDropShadow dx="0" dy="0.72" stdDeviation="0.92" flood-color="#3941FF" flood-opacity="0.35"/>
|
|
235
|
+
</filter>
|
|
236
|
+
<filter id="edge-blur" x="-40%" y="-40%" width="180%" height="180%">
|
|
237
|
+
<feGaussianBlur stdDeviation="0.16"/>
|
|
238
|
+
</filter>
|
|
239
|
+
<filter id="halo-blur" x="-80%" y="-120%" width="260%" height="340%">
|
|
240
|
+
<feGaussianBlur stdDeviation="0.42"/>
|
|
241
|
+
</filter>
|
|
242
|
+
<clipPath id="cloud-clip"><path d="${cloudD}"/></clipPath>
|
|
243
|
+
`;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
function capsulePath(cx, cy, w, h) {
|
|
247
|
+
const r = w / 2;
|
|
248
|
+
const top = cy - h / 2;
|
|
249
|
+
const bottom = cy + h / 2;
|
|
250
|
+
return [
|
|
251
|
+
`M ${cx.toFixed(2)} ${top.toFixed(2)}`,
|
|
252
|
+
`C ${(cx + r).toFixed(2)} ${top.toFixed(2)} ${(cx + r).toFixed(2)} ${(top + r).toFixed(2)} ${(cx + r).toFixed(2)} ${(top + r).toFixed(2)}`,
|
|
253
|
+
`L ${(cx + r).toFixed(2)} ${(bottom - r).toFixed(2)}`,
|
|
254
|
+
`C ${(cx + r).toFixed(2)} ${bottom.toFixed(2)} ${cx.toFixed(2)} ${bottom.toFixed(2)} ${cx.toFixed(2)} ${bottom.toFixed(2)}`,
|
|
255
|
+
`C ${(cx - r).toFixed(2)} ${bottom.toFixed(2)} ${(cx - r).toFixed(2)} ${(bottom - r).toFixed(2)} ${(cx - r).toFixed(2)} ${(bottom - r).toFixed(2)}`,
|
|
256
|
+
`L ${(cx - r).toFixed(2)} ${(top + r).toFixed(2)}`,
|
|
257
|
+
`C ${(cx - r).toFixed(2)} ${top.toFixed(2)} ${cx.toFixed(2)} ${top.toFixed(2)} ${cx.toFixed(2)} ${top.toFixed(2)} Z`
|
|
258
|
+
].join(' ');
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
function roundedRectPath(cx, cy, w, h) {
|
|
262
|
+
const r = Math.min(w, h) / 2;
|
|
263
|
+
const k = 0.5522847498;
|
|
264
|
+
const left = cx - w / 2;
|
|
265
|
+
const right = cx + w / 2;
|
|
266
|
+
const top = cy - h / 2;
|
|
267
|
+
const bottom = cy + h / 2;
|
|
268
|
+
const c = r * k;
|
|
269
|
+
return [
|
|
270
|
+
`M ${(left + r).toFixed(2)} ${top.toFixed(2)}`,
|
|
271
|
+
`L ${(right - r).toFixed(2)} ${top.toFixed(2)}`,
|
|
272
|
+
`C ${(right - r + c).toFixed(2)} ${top.toFixed(2)} ${right.toFixed(2)} ${(top + r - c).toFixed(2)} ${right.toFixed(2)} ${(top + r).toFixed(2)}`,
|
|
273
|
+
`L ${right.toFixed(2)} ${(bottom - r).toFixed(2)}`,
|
|
274
|
+
`C ${right.toFixed(2)} ${(bottom - r + c).toFixed(2)} ${(right - r + c).toFixed(2)} ${bottom.toFixed(2)} ${(right - r).toFixed(2)} ${bottom.toFixed(2)}`,
|
|
275
|
+
`L ${(left + r).toFixed(2)} ${bottom.toFixed(2)}`,
|
|
276
|
+
`C ${(left + r - c).toFixed(2)} ${bottom.toFixed(2)} ${left.toFixed(2)} ${(bottom - r + c).toFixed(2)} ${left.toFixed(2)} ${(bottom - r).toFixed(2)}`,
|
|
277
|
+
`L ${left.toFixed(2)} ${(top + r).toFixed(2)}`,
|
|
278
|
+
`C ${left.toFixed(2)} ${(top + r - c).toFixed(2)} ${(left + r - c).toFixed(2)} ${top.toFixed(2)} ${(left + r).toFixed(2)} ${top.toFixed(2)} Z`
|
|
279
|
+
].join(' ');
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
function closedEyeStrokePath(cx, cy, w, h, tiltDeg, flip) {
|
|
283
|
+
const x1 = cx - w / 2;
|
|
284
|
+
const x2 = cx + w / 2;
|
|
285
|
+
const mid = cy + h * 0.56;
|
|
286
|
+
const c1x = cx - w * 0.18;
|
|
287
|
+
const c2x = cx + w * 0.18;
|
|
288
|
+
const tilt = flip * tiltDeg * 0.018;
|
|
289
|
+
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)}`;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
function makeClippedCapsuleEye(cx, cy, w, h, lid, clipId) {
|
|
293
|
+
const d = capsulePath(cx, cy, w, h);
|
|
294
|
+
const cut = clamp01(lid);
|
|
295
|
+
if (cut <= 0.012) return `<path d="${d}"/>`;
|
|
296
|
+
|
|
297
|
+
const cutY = cy - h / 2 + h * cut;
|
|
298
|
+
return `
|
|
299
|
+
<clipPath id="${clipId}" clipPathUnits="userSpaceOnUse">
|
|
300
|
+
<rect x="${(cx - w / 2 - 0.18).toFixed(2)}" y="${cutY.toFixed(2)}" width="${(w + 0.36).toFixed(2)}" height="${(h + 0.48).toFixed(2)}"/>
|
|
301
|
+
</clipPath>
|
|
302
|
+
<path d="${d}" clip-path="url(#${clipId})"/>`;
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
function makeClosingSlitEye(cx, cy, w, h, close, clipId) {
|
|
306
|
+
const u = clamp01(close);
|
|
307
|
+
const squeeze = smoothstep(0.42, 0.84, u);
|
|
308
|
+
if (squeeze <= 0.001) return makeClippedCapsuleEye(cx, cy, w, h, u * 0.96, clipId);
|
|
309
|
+
|
|
310
|
+
const cutY = cy - h / 2 + h * u * 0.96;
|
|
311
|
+
const bottomY = cy + h / 2;
|
|
312
|
+
const visibleH = Math.max(0.34, bottomY - cutY);
|
|
313
|
+
const visibleCy = cutY + visibleH / 2;
|
|
314
|
+
const slitH = lerp(visibleH, 0.34, squeeze);
|
|
315
|
+
const slitW = lerp(w, w * 0.90, squeeze);
|
|
316
|
+
const slitCy = lerp(visibleCy, cy + 0.25, squeeze);
|
|
317
|
+
return `<path d="${roundedRectPath(cx, slitCy, slitW, slitH)}"/>`;
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
function makeEyes(eye) {
|
|
321
|
+
const fillGap = 6.00;
|
|
322
|
+
const fillCy = 12.00;
|
|
323
|
+
const fillW = 2.60;
|
|
324
|
+
const fillH = 5.00;
|
|
325
|
+
const fillLeft = 12 - fillGap / 2;
|
|
326
|
+
const fillRight = 12 + fillGap / 2;
|
|
327
|
+
|
|
328
|
+
const closedGap = lerp(5.84, DOZE.eyeGap, eye.drop);
|
|
329
|
+
const closedCy = lerp(12.12, DOZE.eyeY, eye.drop);
|
|
330
|
+
const closedW = lerp(2.28, DOZE.eyeW, eye.drop);
|
|
331
|
+
const closedH = lerp(0.72, DOZE.eyeH, eye.drop);
|
|
332
|
+
const closedTilt = lerp(2.8, DOZE.eyeTilt, eye.drop);
|
|
333
|
+
const closedStroke = lerp(0.62, DOZE.eyeStroke, eye.drop);
|
|
334
|
+
const closedLeft = 12 - closedGap / 2;
|
|
335
|
+
const closedRight = 12 + closedGap / 2;
|
|
336
|
+
|
|
337
|
+
return `
|
|
338
|
+
<g id="eyes-morph">
|
|
339
|
+
<g fill="url(#eye-grad)" stroke="#ffffff" stroke-width="0.08" stroke-opacity="0.7" opacity="${eye.fillOpacity.toFixed(3)}">
|
|
340
|
+
${eye.finalClose > 0.001
|
|
341
|
+
? makeClosingSlitEye(fillLeft, fillCy, fillW, fillH, eye.finalClose, 'eye-l-clip')
|
|
342
|
+
: makeClippedCapsuleEye(fillLeft, fillCy, fillW, fillH, eye.lid, 'eye-l-clip')}
|
|
343
|
+
${eye.finalClose > 0.001
|
|
344
|
+
? makeClosingSlitEye(fillRight, fillCy, fillW, fillH, eye.finalClose, 'eye-r-clip')
|
|
345
|
+
: makeClippedCapsuleEye(fillRight, fillCy, fillW, fillH, eye.lid, 'eye-r-clip')}
|
|
346
|
+
</g>
|
|
347
|
+
<g id="eyes-doze" fill="none" stroke="url(#eye-grad)" stroke-width="${closedStroke.toFixed(2)}" stroke-linecap="round" stroke-linejoin="round" opacity="${(eye.closedOpacity * DOZE.eyeOpacity).toFixed(3)}">
|
|
348
|
+
<path d="${closedEyeStrokePath(closedLeft, closedCy, closedW, closedH, closedTilt, -1)}"/>
|
|
349
|
+
<path d="${closedEyeStrokePath(closedRight, closedCy, closedW, closedH, closedTilt, 1)}"/>
|
|
350
|
+
</g>
|
|
351
|
+
</g>
|
|
352
|
+
`;
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
function dozeBeat(t, start, peak, end) {
|
|
356
|
+
return Math.min(smoothstep(start, peak, t), 1 - smoothstep(peak, end, t));
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
function computeFrame(t) {
|
|
360
|
+
const tl = timeline;
|
|
361
|
+
const beatA = dozeBeat(t, tl.half1Start, tl.half1Peak, tl.wake1End);
|
|
362
|
+
const beatB = dozeBeat(t, tl.half2Start, tl.half2Peak, tl.wake2End);
|
|
363
|
+
const closeU = smoothstep(tl.closeStart, tl.closeEnd, t);
|
|
364
|
+
const pathU = easeInOut(progress(t, tl.slumpStart, tl.slumpEnd));
|
|
365
|
+
const settleU = easeInOut(progress(t, tl.dropStart, tl.slumpEnd));
|
|
366
|
+
const wakeA = Math.sin(progress(t, tl.half1Peak, tl.wake1End) * Math.PI) * 0.42;
|
|
367
|
+
const wakeB = Math.sin(progress(t, tl.half2Peak, tl.wake2End) * Math.PI) * 0.34;
|
|
368
|
+
const nodU = Math.max(beatA * 0.72, beatB * 1.05);
|
|
369
|
+
const reboundU = wakeA + wakeB;
|
|
370
|
+
const haloU = smoothstep(tl.haloStart, tl.slumpEnd, t);
|
|
371
|
+
const eyeDropU = easeInOut(progress(t, tl.dropStart, tl.slumpEnd));
|
|
372
|
+
const breathWave = Math.sin((t / MOTION.breathPeriod) * Math.PI * 2);
|
|
373
|
+
const breathFade = lerp(1, 0.72, pathU);
|
|
374
|
+
const breath = breathWave * MOTION.breathAmp * breathFade;
|
|
375
|
+
const sleepyU = Math.max(beatA * 0.82, beatB, closeU);
|
|
376
|
+
const lid = beatA * 0.56 + beatB * 0.66 + closeU * 0.96;
|
|
377
|
+
const fillOpacity = 1 - smoothstep(0.58, 0.88, closeU);
|
|
378
|
+
const closedOpacity = smoothstep(0.58, 1.0, closeU);
|
|
379
|
+
|
|
380
|
+
return {
|
|
381
|
+
pathU,
|
|
382
|
+
haloU,
|
|
383
|
+
eye: {
|
|
384
|
+
sleepy: sleepyU,
|
|
385
|
+
lid,
|
|
386
|
+
finalClose: closeU,
|
|
387
|
+
fillOpacity,
|
|
388
|
+
closedOpacity,
|
|
389
|
+
drop: eyeDropU,
|
|
390
|
+
},
|
|
391
|
+
bodyTx: lerp(0, DOZE.bodyTx, settleU) - 0.05 * nodU,
|
|
392
|
+
bodyTy: lerp(0, DOZE.bodyTy, settleU) + 0.74 * nodU - 0.18 * reboundU,
|
|
393
|
+
bodyRot: lerp(0, DOZE.bodyRot, settleU) - 2.1 * nodU + 0.42 * reboundU,
|
|
394
|
+
bodySx: lerp(1, DOZE.bodyScale, settleU) + 0.025 * nodU + breath * 0.58,
|
|
395
|
+
bodySy: lerp(1, DOZE.bodyScale, settleU) - 0.045 * nodU + breath,
|
|
396
|
+
breath,
|
|
397
|
+
};
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
function phaseName(t) {
|
|
401
|
+
const tl = timeline;
|
|
402
|
+
if (t < tl.idleEnd) return 'idle';
|
|
403
|
+
if (t < tl.half1Peak) return 'half close 1';
|
|
404
|
+
if (t < tl.wake1End) return 'wake 1';
|
|
405
|
+
if (t < tl.hold1End) return 'holding on';
|
|
406
|
+
if (t < tl.half2Peak) return 'half close 2';
|
|
407
|
+
if (t < tl.wake2End) return 'wake 2';
|
|
408
|
+
if (t < tl.hold2End) return 'holding on';
|
|
409
|
+
if (t < tl.closeEnd) return 'natural close';
|
|
410
|
+
if (t < tl.slumpEnd) return 'soft slump';
|
|
411
|
+
return 'dozing hold';
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
function renderFrame(t) {
|
|
415
|
+
const f = computeFrame(t);
|
|
416
|
+
const cloudD = makeDozeCloudPath(f.pathU);
|
|
417
|
+
const bodyTransform = `translate(${f.bodyTx.toFixed(3)} ${f.bodyTy.toFixed(3)}) translate(12 12) rotate(${f.bodyRot.toFixed(3)}) scale(${f.bodySx.toFixed(4)} ${f.bodySy.toFixed(4)}) translate(-12 -12)`;
|
|
418
|
+
stage.innerHTML = `
|
|
419
|
+
<style>
|
|
420
|
+
.edge-rim-tight { stroke-opacity: .9; }
|
|
421
|
+
.edge-rim-soft { stroke-opacity: .75; }
|
|
422
|
+
.edge-contact { stroke-opacity: .9; }
|
|
423
|
+
.page {
|
|
424
|
+
width: min(1040px, 100%);
|
|
425
|
+
display: grid;
|
|
426
|
+
gap: 18px;
|
|
427
|
+
}
|
|
428
|
+
.header {
|
|
429
|
+
display: flex;
|
|
430
|
+
align-items: baseline;
|
|
431
|
+
justify-content: space-between;
|
|
432
|
+
gap: 16px;
|
|
433
|
+
}
|
|
434
|
+
.timeline, .tuning {
|
|
435
|
+
border: 1px solid rgba(149, 170, 255, .22);
|
|
436
|
+
background: rgba(5, 10, 24, .46);
|
|
437
|
+
box-shadow: 0 20px 80px rgba(0, 0, 0, .34);
|
|
438
|
+
border-radius: 8px;
|
|
439
|
+
}
|
|
440
|
+
.timeline {
|
|
441
|
+
padding: 13px;
|
|
442
|
+
display: grid;
|
|
443
|
+
grid-template-columns: 70px 1fr 72px auto auto auto;
|
|
444
|
+
gap: 10px;
|
|
445
|
+
align-items: center;
|
|
446
|
+
}
|
|
447
|
+
.timeline span, .timeline output {
|
|
448
|
+
color: rgba(226, 234, 255, .76);
|
|
449
|
+
font: 11px ui-monospace, SFMono-Regular, Consolas, monospace;
|
|
450
|
+
}
|
|
451
|
+
.phase {
|
|
452
|
+
display: flex;
|
|
453
|
+
gap: 10px;
|
|
454
|
+
flex-wrap: wrap;
|
|
455
|
+
color: rgba(226, 234, 255, .54);
|
|
456
|
+
font-size: 11px;
|
|
457
|
+
}
|
|
458
|
+
.phase b {
|
|
459
|
+
color: rgba(235, 240, 255, .84);
|
|
460
|
+
font-weight: 650;
|
|
461
|
+
}
|
|
462
|
+
.tuning {
|
|
463
|
+
padding: 14px;
|
|
464
|
+
display: grid;
|
|
465
|
+
gap: 12px;
|
|
466
|
+
}
|
|
467
|
+
.tuning-head {
|
|
468
|
+
display: flex;
|
|
469
|
+
align-items: center;
|
|
470
|
+
justify-content: space-between;
|
|
471
|
+
gap: 12px;
|
|
472
|
+
color: rgba(226, 234, 255, .82);
|
|
473
|
+
font-size: 12px;
|
|
474
|
+
font-weight: 650;
|
|
475
|
+
}
|
|
476
|
+
.tuning-head output {
|
|
477
|
+
color: rgba(226, 234, 255, .62);
|
|
478
|
+
font: 11px ui-monospace, SFMono-Regular, Consolas, monospace;
|
|
479
|
+
font-weight: 500;
|
|
480
|
+
}
|
|
481
|
+
.timing-controls {
|
|
482
|
+
display: grid;
|
|
483
|
+
grid-template-columns: repeat(2, minmax(0, 1fr));
|
|
484
|
+
gap: 11px 16px;
|
|
485
|
+
}
|
|
486
|
+
.timing-row {
|
|
487
|
+
display: grid;
|
|
488
|
+
grid-template-columns: 92px 1fr 56px;
|
|
489
|
+
gap: 10px;
|
|
490
|
+
align-items: center;
|
|
491
|
+
}
|
|
492
|
+
.timing-row label {
|
|
493
|
+
color: rgba(226, 234, 255, .72);
|
|
494
|
+
font-size: 12px;
|
|
495
|
+
}
|
|
496
|
+
.timing-row output {
|
|
497
|
+
color: rgba(226, 234, 255, .66);
|
|
498
|
+
font: 11px ui-monospace, SFMono-Regular, Consolas, monospace;
|
|
499
|
+
text-align: right;
|
|
500
|
+
}
|
|
501
|
+
@media (prefers-color-scheme: light) {
|
|
502
|
+
.edge-rim-tight { stroke-opacity: .95; }
|
|
503
|
+
.edge-rim-soft { stroke-opacity: .85; }
|
|
504
|
+
.edge-contact { stroke-opacity: .95; }
|
|
505
|
+
.cloud-drop-shadow { flood-opacity: .13; }
|
|
506
|
+
.timeline, .tuning {
|
|
507
|
+
background: rgba(255, 255, 255, .68);
|
|
508
|
+
border-color: rgba(82, 111, 190, .22);
|
|
509
|
+
box-shadow: 0 20px 70px rgba(58, 78, 130, .14);
|
|
510
|
+
}
|
|
511
|
+
.phase, .timeline span, .timeline output, .tuning-head output, .timing-row label, .timing-row output { color: rgba(21, 33, 63, .66); }
|
|
512
|
+
.phase b { color: rgba(21, 33, 63, .86); }
|
|
513
|
+
.food-cloud-shadow { flood-opacity: .28; flood-color: #5060FF; }
|
|
514
|
+
.food-cloud > path {
|
|
515
|
+
stroke: #5870E8 !important;
|
|
516
|
+
stroke-width: 1.2 !important;
|
|
517
|
+
stroke-opacity: .9 !important;
|
|
518
|
+
vector-effect: non-scaling-stroke !important;
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
</style>
|
|
522
|
+
<defs>
|
|
523
|
+
${defs(cloudD)}
|
|
524
|
+
<path id="cloud-shape" d="${cloudD}"/>
|
|
525
|
+
</defs>
|
|
526
|
+
<g id="halo-layer" opacity="${f.haloU.toFixed(3)}">
|
|
527
|
+
<ellipse cx="12" cy="${DOZE.haloY}" rx="${lerp(7.8, DOZE.haloRx, f.haloU).toFixed(2)}" ry="${lerp(.72, DOZE.haloRy, f.haloU).toFixed(2)}" fill="url(#halo-grad)" opacity="${DOZE.haloOpacity}" filter="url(#halo-blur)"/>
|
|
528
|
+
<ellipse cx="12" cy="${(DOZE.haloY + 0.05).toFixed(2)}" rx="${(DOZE.haloRx * 0.68).toFixed(2)}" ry="${(DOZE.haloRy * 0.46).toFixed(2)}" fill="#7A9DFF" opacity="${DOZE.glowOpacity}" filter="url(#halo-blur)"/>
|
|
529
|
+
</g>
|
|
530
|
+
<g id="body-pose" transform="${bodyTransform}">
|
|
531
|
+
<g id="edge-system">
|
|
532
|
+
<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)"/>
|
|
533
|
+
<use class="edge-rim-soft" href="#cloud-shape" fill="none" stroke="#7A9DFF" stroke-width="0.88" stroke-linejoin="round" filter="url(#edge-blur)"/>
|
|
534
|
+
<use class="edge-rim-tight" href="#cloud-shape" fill="none" stroke="#aab3c6" stroke-width="0.40" stroke-linejoin="round"/>
|
|
535
|
+
</g>
|
|
536
|
+
<g filter="url(#drop)">
|
|
537
|
+
<use href="#cloud-shape" fill="url(#body-grad)"/>
|
|
538
|
+
<rect x="1" y="2.2" width="22" height="10.4" fill="url(#hi-grad)" clip-path="url(#cloud-clip)" opacity=".88"/>
|
|
539
|
+
</g>
|
|
540
|
+
${makeEyes(f.eye)}
|
|
541
|
+
</g>
|
|
542
|
+
`;
|
|
543
|
+
scrubber.value = t.toFixed(2);
|
|
544
|
+
timeOut.value = `${t.toFixed(2)} s`;
|
|
545
|
+
phaseEl.innerHTML = `<span>phase: <b>${phaseName(t)}</b></span><span>path ${(f.pathU * 100).toFixed(0)}%</span><span>halo ${(f.haloU * 100).toFixed(0)}%</span><span>breath ${(f.breath * 100).toFixed(1)}%</span>`;
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
function currentTime(now) {
|
|
549
|
+
if (!playing) return manualT;
|
|
550
|
+
const t = ((now - start) / 1000);
|
|
551
|
+
return Math.min(t, DURATION);
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
function loop(now) {
|
|
555
|
+
const t = currentTime(now);
|
|
556
|
+
renderFrame(t);
|
|
557
|
+
if (playing && t >= DURATION) {
|
|
558
|
+
playing = false;
|
|
559
|
+
manualT = DURATION;
|
|
560
|
+
{ const __btn = document.getElementById('btn-play'); if (__btn) __btn.textContent = 'Play'; }
|
|
561
|
+
}
|
|
562
|
+
requestAnimationFrame(loop);
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
renderFrame(0);
|
|
566
|
+
requestAnimationFrame(loop);
|
|
567
|
+
]]></script>
|
|
568
|
+
</svg>
|