@qingwu-ui/scroll-fab 0.9.0-beta.1
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/dist/index.cjs +420 -0
- package/dist/index.d.cts +111 -0
- package/dist/index.d.cts.map +1 -0
- package/dist/index.d.mts +111 -0
- package/dist/index.d.mts.map +1 -0
- package/dist/index.mjs +421 -0
- package/dist/index.mjs.map +1 -0
- package/dist/style.css +127 -0
- package/package.json +70 -0
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,421 @@
|
|
|
1
|
+
//#region ../../icon/icons.ts
|
|
2
|
+
const S = (d) => `<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">${d}</svg>`;
|
|
3
|
+
const S16 = (d) => `<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">${d}</svg>`;
|
|
4
|
+
const S18 = (d) => `<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">${d}</svg>`;
|
|
5
|
+
const S36 = (d) => `<svg xmlns="http://www.w3.org/2000/svg" width="36" height="36" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">${d}</svg>`;
|
|
6
|
+
S("<rect x=\"3\" y=\"4\" width=\"18\" height=\"18\" rx=\"2\"/><path d=\"M8 2v4M16 2v4M3 10h18\"/>");
|
|
7
|
+
S("<rect x=\"3\" y=\"4\" width=\"18\" height=\"18\" rx=\"2\" ry=\"2\"/><line x1=\"16\" y1=\"2\" x2=\"16\" y2=\"6\"/><line x1=\"8\" y1=\"2\" x2=\"8\" y2=\"6\"/><line x1=\"3\" y1=\"10\" x2=\"21\" y2=\"10\"/><line x1=\"8\" y1=\"14\" x2=\"8.01\" y2=\"14\"/>");
|
|
8
|
+
S("<circle cx=\"11\" cy=\"11\" r=\"8\"/><path d=\"m21 21-4.35-4.35\"/>");
|
|
9
|
+
S("<path d=\"M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4\"/><polyline points=\"17 8 12 3 7 8\"/><line x1=\"12\" y1=\"3\" x2=\"12\" y2=\"15\"/>");
|
|
10
|
+
S("<path d=\"M12 16V4\"/><path d=\"m6 9 6-5 6 5\"/><path d=\"M4 20h16\"/>");
|
|
11
|
+
S("<path d=\"M3 7h18M3 12h18M3 17h18\"/>");
|
|
12
|
+
S("<path d=\"M6 8a6 6 0 0 1 12 0c0 7 3 9 3 9H3s3-2 3-9\"/><path d=\"M10.3 21a1.94 1.94 0 0 0 3.4 0\"/>");
|
|
13
|
+
S("<line x1=\"18\" y1=\"6\" x2=\"6\" y2=\"18\"/><line x1=\"6\" y1=\"6\" x2=\"18\" y2=\"18\"/>");
|
|
14
|
+
S("<polyline points=\"20 6 9 17 4 12\"/>");
|
|
15
|
+
S("<line x1=\"12\" y1=\"5\" x2=\"12\" y2=\"19\"/><line x1=\"5\" y1=\"12\" x2=\"19\" y2=\"12\"/>");
|
|
16
|
+
S("<line x1=\"5\" y1=\"12\" x2=\"19\" y2=\"12\"/>");
|
|
17
|
+
S("<path d=\"M21 16V8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73l7 4a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16z\"/>");
|
|
18
|
+
S("<path d=\"M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7\"/><path d=\"M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z\"/>");
|
|
19
|
+
S("<path d=\"M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z\"/><polyline points=\"14 2 14 8 20 8\"/>");
|
|
20
|
+
S("<path d=\"M22 19a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h5l2 3h9a2 2 0 0 1 2 2z\"/>");
|
|
21
|
+
S("<circle cx=\"12\" cy=\"12\" r=\"10\"/><line x1=\"12\" y1=\"16\" x2=\"12\" y2=\"12\"/><line x1=\"12\" y1=\"8\" x2=\"12.01\" y2=\"8\"/>");
|
|
22
|
+
S("<path d=\"M10.29 3.86 1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z\"/><line x1=\"12\" y1=\"9\" x2=\"12\" y2=\"13\"/><line x1=\"12\" y1=\"17\" x2=\"12.01\" y2=\"17\"/>");
|
|
23
|
+
S("<polyline points=\"3 6 5 6 21 6\"/><path d=\"M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2\"/><line x1=\"10\" y1=\"11\" x2=\"10\" y2=\"17\"/><line x1=\"14\" y1=\"11\" x2=\"14\" y2=\"17\"/>");
|
|
24
|
+
S("<path d=\"M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4\"/><polyline points=\"7 10 12 15 17 10\"/><line x1=\"12\" y1=\"15\" x2=\"12\" y2=\"3\"/>");
|
|
25
|
+
S("<rect x=\"9\" y=\"9\" width=\"13\" height=\"13\" rx=\"2\" ry=\"2\"/><path d=\"M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1\"/>");
|
|
26
|
+
S("<path d=\"M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z\"/><circle cx=\"12\" cy=\"12\" r=\"3\"/>");
|
|
27
|
+
S("<polygon points=\"12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2\"/>");
|
|
28
|
+
S("<path d=\"M19 21l-7-5-7 5V5a2 2 0 0 1 2-2h10a2 2 0 0 1 2 2z\"/>");
|
|
29
|
+
S("<path d=\"M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6\"/><polyline points=\"15 3 21 3 21 9\"/><line x1=\"10\" y1=\"14\" x2=\"21\" y2=\"3\"/>");
|
|
30
|
+
S("<path d=\"M15 22v-4a4.8 4.8 0 0 0-1-3.5c3 0 6-2 6-5.5.08-1.25-.27-2.48-1-3.5.28-1.15.28-2.35 0-3.5 0 0-1 0-3 1.5-2.64-.5-5.36-.5-8 0C6 2 5 2 5 2c-.3 1.15-.3 2.35 0 3.5A5.403 5.403 0 0 0 4 9c0 3.5 3 5.5 6 5.5-.39.49-.68 1.05-.85 1.65-.17.6-.22 1.23-.15 1.85v4\"/><path d=\"M9 18c-4.51 2-5-2-7-2\"/>");
|
|
31
|
+
S16("<path d=\"m15 18-6-6 6-6\"/>");
|
|
32
|
+
S16("<path d=\"m9 18 6-6-6-6\"/>");
|
|
33
|
+
S("<polyline points=\"18 15 12 9 6 15\"/>");
|
|
34
|
+
const ICON_CHEVRON_DOWN = S("<polyline points=\"6 9 12 15 18 9\"/>");
|
|
35
|
+
S("<polyline points=\"16 18 22 12 16 6\"/><polyline points=\"8 6 2 12 8 18\"/>");
|
|
36
|
+
S("<polyline points=\"15 3 21 3 21 9\"/><polyline points=\"9 21 3 21 3 15\"/><line x1=\"21\" y1=\"3\" x2=\"14\" y2=\"10\"/><line x1=\"3\" y1=\"21\" x2=\"10\" y2=\"14\"/>");
|
|
37
|
+
S("<polyline points=\"4 8 4 3 9 3\"/><polyline points=\"20 16 20 21 15 21\"/><line x1=\"4\" y1=\"3\" x2=\"11\" y2=\"10\"/><line x1=\"20\" y1=\"21\" x2=\"13\" y2=\"14\"/>");
|
|
38
|
+
S("<circle cx=\"12\" cy=\"12\" r=\"10\"/><path d=\"M12 6v6l4 2\"/>");
|
|
39
|
+
S("<polyline points=\"4 7 4 4 20 4 20 7\"/><line x1=\"9\" y1=\"20\" x2=\"15\" y2=\"20\"/><line x1=\"12\" y1=\"4\" x2=\"12\" y2=\"20\"/>");
|
|
40
|
+
S("<path d=\"M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2\"/><circle cx=\"9\" cy=\"7\" r=\"4\"/><path d=\"M23 21v-2a4 4 0 0 0-3-3.87\"/><path d=\"M16 3.13a4 4 0 0 1 0 7.75\"/>");
|
|
41
|
+
S("<path d=\"M12 1a3 3 0 0 0-3 3v8a3 3 0 0 0 6 0V4a3 3 0 0 0-3-3z\"/><path d=\"M19 10v2a7 7 0 0 1-14 0v-2\"/><line x1=\"12\" y1=\"19\" x2=\"12\" y2=\"23\"/><line x1=\"8\" y1=\"23\" x2=\"16\" y2=\"23\"/>");
|
|
42
|
+
S("<path d=\"M9 18V5l12-2v13\"/><circle cx=\"6\" cy=\"18\" r=\"3\"/><circle cx=\"18\" cy=\"16\" r=\"3\"/>");
|
|
43
|
+
S("<polygon points=\"23 7 16 12 23 17 23 7\"/><rect x=\"1\" y=\"5\" width=\"15\" height=\"14\" rx=\"2\" ry=\"2\"/>");
|
|
44
|
+
S("<rect x=\"3\" y=\"3\" width=\"18\" height=\"18\" rx=\"2\" ry=\"2\"/><circle cx=\"8.5\" cy=\"8.5\" r=\"1.5\"/><polyline points=\"21 15 16 10 5 21\"/>");
|
|
45
|
+
S("<circle cx=\"12\" cy=\"12\" r=\"5\"/><line x1=\"12\" y1=\"1\" x2=\"12\" y2=\"3\"/><line x1=\"12\" y1=\"21\" x2=\"12\" y2=\"23\"/><line x1=\"4.22\" y1=\"4.22\" x2=\"5.64\" y2=\"5.64\"/><line x1=\"18.36\" y1=\"18.36\" x2=\"19.78\" y2=\"19.78\"/><line x1=\"1\" y1=\"12\" x2=\"3\" y2=\"12\"/><line x1=\"21\" y1=\"12\" x2=\"23\" y2=\"12\"/><line x1=\"4.22\" y1=\"19.78\" x2=\"5.64\" y2=\"18.36\"/><line x1=\"18.36\" y1=\"5.64\" x2=\"19.78\" y2=\"4.22\"/>");
|
|
46
|
+
S("<path d=\"M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z\"/>");
|
|
47
|
+
S("<rect width=\"18\" height=\"18\" x=\"3\" y=\"3\" rx=\"2\"/><path d=\"M9 3v18\"/><path d=\"m14 9-3 3 3 3\"/>");
|
|
48
|
+
S("<line x1=\"8\" y1=\"6\" x2=\"21\" y2=\"6\"/><line x1=\"8\" y1=\"12\" x2=\"21\" y2=\"12\"/><line x1=\"8\" y1=\"18\" x2=\"21\" y2=\"18\"/><line x1=\"3\" y1=\"6\" x2=\"3.01\" y2=\"6\"/><line x1=\"3\" y1=\"12\" x2=\"3.01\" y2=\"12\"/><line x1=\"3\" y1=\"18\" x2=\"3.01\" y2=\"18\"/>");
|
|
49
|
+
S("<path d=\"M12 2l2.4 7.2h7.6l-6 4.8 2.4 7.2-6.4-4.8-6.4 4.8 2.4-7.2-6-4.8h7.6z\"/>");
|
|
50
|
+
S("<polygon points=\"13 2 3 14 12 14 11 22 21 10 12 10 13 2\"/>");
|
|
51
|
+
S("<polyline points=\"23 4 23 10 17 10\"/><polyline points=\"1 20 1 14 7 14\"/><path d=\"M3.51 9a9 9 0 0 1 14.85-3.36L23 10M1 14l4.64 4.36A9 9 0 0 0 20.49 15\"/>");
|
|
52
|
+
S("<polyline points=\"1 4 1 10 7 10\"/><path d=\"M3.51 15a9 9 0 1 0 2.13-9.36L1 10\"/>");
|
|
53
|
+
S("<circle cx=\"12\" cy=\"12\" r=\"3\"/><path d=\"M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1 0 2.83 2 2 0 0 1-2.83 0l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-2 2 2 2 0 0 1-2-2v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83 0 2 2 0 0 1 0-2.83l.06-.06A1.65 1.65 0 0 0 4.68 15a1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1-2-2 2 2 0 0 1 2-2h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2 0 0 1 2.83 0l.06.06A1.65 1.65 0 0 0 9 4.68a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 2-2 2 2 0 0 1 2 2v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 0 2 2 0 0 1 0 2.83l-.06.06A1.65 1.65 0 0 0 19.4 9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 2 2 2 2 0 0 1-2 2h-.09a1.65 1.65 0 0 0-1.51 1z\"/>");
|
|
54
|
+
S("<path d=\"M3 3v5h5\"/><path d=\"M3.05 13A9 9 0 1 0 6.11 6.11L3 8\"/><path d=\"M12 7v5l4 2\"/>");
|
|
55
|
+
S("<line x1=\"22\" y1=\"2\" x2=\"11\" y2=\"13\"/><polygon points=\"22 2 15 22 11 13 2 9 22 2\"/>");
|
|
56
|
+
S("<path d=\"M5 8l6 6\"/><path d=\"M4 14l6-6 2-3\"/><path d=\"M2 5h12\"/><path d=\"M7 2h1\"/><path d=\"M22 22l-5-10-5 10\"/><path d=\"M14 18h6\"/>");
|
|
57
|
+
S("<path d=\"M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z\"/><polyline points=\"14 2 14 8 20 8\"/><line x1=\"16\" y1=\"13\" x2=\"8\" y2=\"13\"/><line x1=\"16\" y1=\"17\" x2=\"8\" y2=\"17\"/><polyline points=\"10 9 9 9 8 9\"/>");
|
|
58
|
+
S("<path d=\"M17 3a2.828 2.828 0 1 1 4 4L7.5 20.5 2 22l1.5-5.5L17 3z\"/>");
|
|
59
|
+
S("<circle cx=\"12\" cy=\"12\" r=\"1\"/><path d=\"M20 12a8 8 0 1 1-8-8\"/><path d=\"m16 2 4 4-4 4\"/>");
|
|
60
|
+
S("<path d=\"M20.59 13.41l-7.17 7.17a2 2 0 0 1-2.83 0L2 12V2h10l8.59 8.59a2 2 0 0 1 0 2.82z\"/><line x1=\"7\" y1=\"7\" x2=\"7.01\" y2=\"7\"/>");
|
|
61
|
+
S("<circle cx=\"9\" cy=\"12\" r=\"1\"/><circle cx=\"15\" cy=\"12\" r=\"1\"/><circle cx=\"9\" cy=\"6\" r=\"1\"/><circle cx=\"15\" cy=\"6\" r=\"1\"/><circle cx=\"9\" cy=\"18\" r=\"1\"/><circle cx=\"15\" cy=\"18\" r=\"1\"/>");
|
|
62
|
+
S("<rect x=\"2\" y=\"2\" width=\"8\" height=\"8\" rx=\"1\"/><rect x=\"14\" y=\"2\" width=\"8\" height=\"8\" rx=\"1\"/><rect x=\"2\" y=\"14\" width=\"8\" height=\"8\" rx=\"1\"/><rect x=\"14\" y=\"14\" width=\"8\" height=\"8\" rx=\"1\"/>");
|
|
63
|
+
S("<circle cx=\"12\" cy=\"12\" r=\"10\"/><line x1=\"2\" y1=\"12\" x2=\"22\" y2=\"12\"/><path d=\"M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10 15.3 15.3 0 0 1-4-10 15.3 15.3 0 0 1 4-10z\"/>");
|
|
64
|
+
S("<path d=\"M21.44 11.05l-9.19 9.19a6 6 0 0 1-8.49-8.49l9.19-9.19a4 4 0 0 1 5.66 5.66l-9.2 9.19a2 2 0 0 1-2.83-2.83l8.49-8.48\"/>");
|
|
65
|
+
S36("<path d=\"M12 16V4\"/><path d=\"m6 9 6-5 6 5\"/><path d=\"M4 20h16\"/>");
|
|
66
|
+
const S18c = (d) => `<svg class="qs-ico" xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">${d}</svg>`;
|
|
67
|
+
const S20c = (d) => `<svg class="qw-cal-svg" xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">${d}</svg>`;
|
|
68
|
+
S18c("<circle cx=\"11\" cy=\"11\" r=\"7\"/><path d=\"m20 20-3.2-3.2\"/>");
|
|
69
|
+
S18("<path d=\"M4 7h16M4 12h16M4 17h16\"/>");
|
|
70
|
+
S20c("<rect x=\"3\" y=\"4\" width=\"18\" height=\"18\" rx=\"2\"/><path d=\"M8 2v4M16 2v4M3 10h18\"/>");
|
|
71
|
+
S16("<path d=\"m15 18-6-6 6-6\"/>");
|
|
72
|
+
S16("<path d=\"m9 18 6-6-6-6\"/>");
|
|
73
|
+
//#endregion
|
|
74
|
+
//#region src/scroll-fab.ts
|
|
75
|
+
/** 青梧UI ScrollFab 悬浮滚动栏:默认“滚动到底部”,桌面悬停/触屏按住绕满描边一圈翻转为“返回顶部”,点击(轻点)直接执行当前模式动作 */
|
|
76
|
+
const RADIUS = 21;
|
|
77
|
+
const DEFAULT_LABEL_BOTTOM = "滚动到底部(悬停或按住绕圈可切换为返回顶部)";
|
|
78
|
+
const DEFAULT_LABEL_TOP = "返回顶部(悬停或按住绕圈可切换为滚动到底部)";
|
|
79
|
+
function easeInOutCubic(p) {
|
|
80
|
+
return p < .5 ? 4 * p * p * p : 1 - (-2 * p + 2) ** 3 / 2;
|
|
81
|
+
}
|
|
82
|
+
var ScrollFab = class {
|
|
83
|
+
el;
|
|
84
|
+
ring;
|
|
85
|
+
track;
|
|
86
|
+
icon;
|
|
87
|
+
len;
|
|
88
|
+
targetEl;
|
|
89
|
+
ringMs;
|
|
90
|
+
decayMs;
|
|
91
|
+
cancelPx;
|
|
92
|
+
animate;
|
|
93
|
+
labelBottom;
|
|
94
|
+
labelTop;
|
|
95
|
+
onModeChange;
|
|
96
|
+
mode = "to-bottom";
|
|
97
|
+
progress = 0;
|
|
98
|
+
hovering = false;
|
|
99
|
+
pressing = false;
|
|
100
|
+
flipped = false;
|
|
101
|
+
pointerId = null;
|
|
102
|
+
startX = 0;
|
|
103
|
+
startY = 0;
|
|
104
|
+
ringRaf = null;
|
|
105
|
+
lastTs = null;
|
|
106
|
+
scrollRaf = null;
|
|
107
|
+
flashTimer = null;
|
|
108
|
+
tapResetTimer = null;
|
|
109
|
+
touchTap = false;
|
|
110
|
+
ro = null;
|
|
111
|
+
constructor(options = {}) {
|
|
112
|
+
this.targetEl = options.target ?? null;
|
|
113
|
+
this.ringMs = Math.max(100, Math.min(5e3, options.ringDuration ?? 800));
|
|
114
|
+
this.decayMs = Math.max(100, options.decayDuration ?? 300);
|
|
115
|
+
this.cancelPx = Math.max(4, options.cancelThreshold ?? 10);
|
|
116
|
+
this.animate = options.animate ?? true;
|
|
117
|
+
this.labelBottom = options.ariaLabelToBottom ?? DEFAULT_LABEL_BOTTOM;
|
|
118
|
+
this.labelTop = options.ariaLabelToTop ?? DEFAULT_LABEL_TOP;
|
|
119
|
+
this.onModeChange = options.onModeChange;
|
|
120
|
+
this.el = document.createElement("button");
|
|
121
|
+
this.el.type = "button";
|
|
122
|
+
this.el.className = `qsf-root${options.className ? ` ${options.className}` : ""}`;
|
|
123
|
+
this.el.dataset.mode = this.mode;
|
|
124
|
+
this.el.setAttribute("aria-label", this.labelBottom);
|
|
125
|
+
this.el.style.setProperty("--qsf-size", `${Math.max(44, options.size ?? 48)}px`);
|
|
126
|
+
if (options.zIndex != null) this.el.style.setProperty("--qsf-z", String(options.zIndex));
|
|
127
|
+
if (options.position?.right != null) this.el.style.setProperty("--qsf-right", `${options.position.right}px`);
|
|
128
|
+
if (options.position?.bottom != null) this.el.style.setProperty("--qsf-bottom", `${options.position.bottom}px`);
|
|
129
|
+
if (options.position?.left != null) this.el.style.setProperty("--qsf-left", `${options.position.left}px`);
|
|
130
|
+
if (options.position?.top != null) this.el.style.setProperty("--qsf-top", `${options.position.top}px`);
|
|
131
|
+
const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
|
|
132
|
+
svg.setAttribute("class", "qsf-svg");
|
|
133
|
+
svg.setAttribute("viewBox", "0 0 48 48");
|
|
134
|
+
svg.setAttribute("aria-hidden", "true");
|
|
135
|
+
this.track = this.makeCircle("qsf-track");
|
|
136
|
+
this.ring = this.makeCircle("qsf-bar");
|
|
137
|
+
svg.append(this.track, this.ring);
|
|
138
|
+
let len = 2 * Math.PI * RADIUS;
|
|
139
|
+
try {
|
|
140
|
+
const measured = this.ring.getTotalLength();
|
|
141
|
+
if (Number.isFinite(measured)) len = measured;
|
|
142
|
+
} catch {}
|
|
143
|
+
this.len = len;
|
|
144
|
+
this.ring.style.strokeDasharray = String(this.len);
|
|
145
|
+
this.renderRing();
|
|
146
|
+
this.icon = document.createElement("span");
|
|
147
|
+
this.icon.className = "qsf-icon";
|
|
148
|
+
this.icon.setAttribute("aria-hidden", "true");
|
|
149
|
+
if (options.content != null) if (typeof options.content === "string") this.icon.innerHTML = options.content;
|
|
150
|
+
else this.icon.append(options.content);
|
|
151
|
+
else this.icon.innerHTML = ICON_CHEVRON_DOWN;
|
|
152
|
+
this.el.append(svg, this.icon);
|
|
153
|
+
document.body.append(this.el);
|
|
154
|
+
this.el.addEventListener("pointerenter", this.onEnter);
|
|
155
|
+
this.el.addEventListener("pointerleave", this.onLeave);
|
|
156
|
+
this.el.addEventListener("pointerdown", this.onDown);
|
|
157
|
+
this.el.addEventListener("pointermove", this.onMove);
|
|
158
|
+
this.el.addEventListener("pointerup", this.onUp);
|
|
159
|
+
this.el.addEventListener("pointercancel", this.onCancel);
|
|
160
|
+
this.el.addEventListener("keydown", this.onKey);
|
|
161
|
+
this.el.addEventListener("click", this.onClick);
|
|
162
|
+
window.addEventListener("resize", this.onRecheck);
|
|
163
|
+
(this.targetEl ?? window).addEventListener("scroll", this.onRecheck, { passive: true });
|
|
164
|
+
if (typeof ResizeObserver === "function") {
|
|
165
|
+
this.ro = new ResizeObserver(this.onRecheck);
|
|
166
|
+
this.ro.observe(this.targetEl ?? document.documentElement);
|
|
167
|
+
}
|
|
168
|
+
this.syncVisible();
|
|
169
|
+
}
|
|
170
|
+
/** 当前模式 */
|
|
171
|
+
get currentMode() {
|
|
172
|
+
return this.mode;
|
|
173
|
+
}
|
|
174
|
+
/** 内容懒加载等外部高度变化后可显式触发重估(滚动/resize 时也会自动重估) */
|
|
175
|
+
refresh() {
|
|
176
|
+
this.onRecheck();
|
|
177
|
+
}
|
|
178
|
+
scrollToBottom() {
|
|
179
|
+
this.animateScroll("bottom");
|
|
180
|
+
}
|
|
181
|
+
scrollToTop() {
|
|
182
|
+
this.scrollTo(0);
|
|
183
|
+
}
|
|
184
|
+
destroy() {
|
|
185
|
+
this.el.removeEventListener("pointerenter", this.onEnter);
|
|
186
|
+
this.el.removeEventListener("pointerleave", this.onLeave);
|
|
187
|
+
this.el.removeEventListener("pointerdown", this.onDown);
|
|
188
|
+
this.el.removeEventListener("pointermove", this.onMove);
|
|
189
|
+
this.el.removeEventListener("pointerup", this.onUp);
|
|
190
|
+
this.el.removeEventListener("pointercancel", this.onCancel);
|
|
191
|
+
this.el.removeEventListener("keydown", this.onKey);
|
|
192
|
+
this.el.removeEventListener("click", this.onClick);
|
|
193
|
+
window.removeEventListener("resize", this.onRecheck);
|
|
194
|
+
(this.targetEl ?? window).removeEventListener("scroll", this.onRecheck);
|
|
195
|
+
this.ro?.disconnect();
|
|
196
|
+
this.cancelScroll();
|
|
197
|
+
if (this.ringRaf != null) cancelAnimationFrame(this.ringRaf);
|
|
198
|
+
this.ringRaf = null;
|
|
199
|
+
if (this.flashTimer != null) clearTimeout(this.flashTimer);
|
|
200
|
+
if (this.tapResetTimer != null) clearTimeout(this.tapResetTimer);
|
|
201
|
+
this.el.remove();
|
|
202
|
+
}
|
|
203
|
+
makeCircle(cls) {
|
|
204
|
+
const c = document.createElementNS("http://www.w3.org/2000/svg", "circle");
|
|
205
|
+
c.setAttribute("class", cls);
|
|
206
|
+
c.setAttribute("cx", "24");
|
|
207
|
+
c.setAttribute("cy", "24");
|
|
208
|
+
c.setAttribute("r", String(RADIUS));
|
|
209
|
+
return c;
|
|
210
|
+
}
|
|
211
|
+
onEnter = (e) => {
|
|
212
|
+
if (e.pointerType !== "mouse") return;
|
|
213
|
+
this.hovering = true;
|
|
214
|
+
this.kickRing();
|
|
215
|
+
};
|
|
216
|
+
onLeave = (e) => {
|
|
217
|
+
if (e.pointerType !== "mouse") return;
|
|
218
|
+
this.hovering = false;
|
|
219
|
+
this.flipped = false;
|
|
220
|
+
};
|
|
221
|
+
onDown = (e) => {
|
|
222
|
+
if (e.button !== 0 || this.pointerId !== null) return;
|
|
223
|
+
if (e.pointerType === "mouse") return;
|
|
224
|
+
e.preventDefault();
|
|
225
|
+
this.touchTap = true;
|
|
226
|
+
if (this.tapResetTimer != null) clearTimeout(this.tapResetTimer);
|
|
227
|
+
this.pointerId = e.pointerId;
|
|
228
|
+
this.startX = e.clientX;
|
|
229
|
+
this.startY = e.clientY;
|
|
230
|
+
this.pressing = true;
|
|
231
|
+
this.flipped = false;
|
|
232
|
+
this.kickRing();
|
|
233
|
+
};
|
|
234
|
+
onMove = (e) => {
|
|
235
|
+
if (e.pointerId !== this.pointerId || !this.pressing) return;
|
|
236
|
+
if (Math.hypot(e.clientX - this.startX, e.clientY - this.startY) > this.cancelPx) {
|
|
237
|
+
this.pressing = false;
|
|
238
|
+
this.pointerId = null;
|
|
239
|
+
this.flipped = false;
|
|
240
|
+
}
|
|
241
|
+
};
|
|
242
|
+
onUp = (e) => {
|
|
243
|
+
if (e.pointerId !== this.pointerId) return;
|
|
244
|
+
this.pointerId = null;
|
|
245
|
+
this.pressing = false;
|
|
246
|
+
const consumed = this.flipped;
|
|
247
|
+
this.flipped = false;
|
|
248
|
+
this.tapResetTimer = setTimeout(() => {
|
|
249
|
+
this.touchTap = false;
|
|
250
|
+
this.tapResetTimer = null;
|
|
251
|
+
}, 400);
|
|
252
|
+
if (consumed) return;
|
|
253
|
+
this.doAction();
|
|
254
|
+
};
|
|
255
|
+
onCancel = (e) => {
|
|
256
|
+
if (e.pointerId !== this.pointerId) return;
|
|
257
|
+
this.pointerId = null;
|
|
258
|
+
this.pressing = false;
|
|
259
|
+
this.flipped = false;
|
|
260
|
+
};
|
|
261
|
+
onClick = (e) => {
|
|
262
|
+
if (this.touchTap) {
|
|
263
|
+
e.preventDefault();
|
|
264
|
+
e.stopPropagation();
|
|
265
|
+
return;
|
|
266
|
+
}
|
|
267
|
+
this.doAction();
|
|
268
|
+
};
|
|
269
|
+
onKey = (e) => {
|
|
270
|
+
if (e.repeat || e.key !== "Enter" && e.key !== " ") return;
|
|
271
|
+
e.preventDefault();
|
|
272
|
+
this.setMode(this.mode === "to-bottom" ? "to-top" : "to-bottom");
|
|
273
|
+
};
|
|
274
|
+
holding() {
|
|
275
|
+
return this.hovering || this.pressing;
|
|
276
|
+
}
|
|
277
|
+
kickRing() {
|
|
278
|
+
if (this.ringRaf != null) return;
|
|
279
|
+
this.lastTs = null;
|
|
280
|
+
this.ringRaf = requestAnimationFrame(this.stepRing);
|
|
281
|
+
}
|
|
282
|
+
stepRing = (ts) => {
|
|
283
|
+
const dt = this.lastTs == null ? 0 : ts - this.lastTs;
|
|
284
|
+
this.lastTs = ts;
|
|
285
|
+
if (this.holding()) {
|
|
286
|
+
if (!this.flipped) {
|
|
287
|
+
this.progress = Math.min(1, this.progress + dt / this.ringMs);
|
|
288
|
+
if (this.progress >= 1) this.completeRing();
|
|
289
|
+
}
|
|
290
|
+
} else this.progress = Math.max(0, this.progress - dt / this.decayMs);
|
|
291
|
+
this.renderRing();
|
|
292
|
+
if (this.holding() || this.progress > 0) this.ringRaf = requestAnimationFrame(this.stepRing);
|
|
293
|
+
else this.ringRaf = null;
|
|
294
|
+
};
|
|
295
|
+
/** 绕满一圈 = 纯模式翻转(不附带任何滚动) */
|
|
296
|
+
completeRing() {
|
|
297
|
+
this.flipped = true;
|
|
298
|
+
const next = this.mode === "to-bottom" ? "to-top" : "to-bottom";
|
|
299
|
+
this.setMode(next);
|
|
300
|
+
this.el.classList.add("is-complete");
|
|
301
|
+
if (this.flashTimer != null) clearTimeout(this.flashTimer);
|
|
302
|
+
this.flashTimer = setTimeout(() => {
|
|
303
|
+
this.el.classList.remove("is-complete");
|
|
304
|
+
this.flashTimer = null;
|
|
305
|
+
}, 350);
|
|
306
|
+
}
|
|
307
|
+
renderRing() {
|
|
308
|
+
const vis = this.progress > 0;
|
|
309
|
+
this.track.toggleAttribute("hidden", !vis);
|
|
310
|
+
this.ring.toggleAttribute("hidden", !vis);
|
|
311
|
+
this.ring.style.strokeDashoffset = String(this.len * (1 - this.progress));
|
|
312
|
+
}
|
|
313
|
+
setMode(mode) {
|
|
314
|
+
if (mode === this.mode) return;
|
|
315
|
+
this.mode = mode;
|
|
316
|
+
this.el.dataset.mode = mode;
|
|
317
|
+
this.el.setAttribute("aria-label", mode === "to-bottom" ? this.labelBottom : this.labelTop);
|
|
318
|
+
this.onModeChange?.(mode);
|
|
319
|
+
}
|
|
320
|
+
doAction() {
|
|
321
|
+
this.animateScroll(this.mode === "to-bottom" ? "bottom" : "top");
|
|
322
|
+
}
|
|
323
|
+
animateScroll(dest) {
|
|
324
|
+
this.cancelScroll();
|
|
325
|
+
if (!this.animate || this.reducedMotion()) {
|
|
326
|
+
this.setScrollPos(this.targetPos(dest));
|
|
327
|
+
return;
|
|
328
|
+
}
|
|
329
|
+
this.attachInterrupt();
|
|
330
|
+
let init = false;
|
|
331
|
+
let start = 0;
|
|
332
|
+
let end = 0;
|
|
333
|
+
let t0 = 0;
|
|
334
|
+
let dur = 0;
|
|
335
|
+
const step = (ts) => {
|
|
336
|
+
if (!init) {
|
|
337
|
+
init = true;
|
|
338
|
+
start = this.scrollPos();
|
|
339
|
+
end = this.targetPos(dest);
|
|
340
|
+
t0 = ts;
|
|
341
|
+
dur = Math.min(1200, Math.max(400, Math.abs(end - start) / 3));
|
|
342
|
+
} else if (dest === "bottom") {
|
|
343
|
+
const live = this.targetPos(dest);
|
|
344
|
+
if (Math.abs(live - end) > 2) {
|
|
345
|
+
end = live;
|
|
346
|
+
start = this.scrollPos();
|
|
347
|
+
t0 = ts;
|
|
348
|
+
dur = Math.min(1200, Math.max(400, Math.abs(end - start) / 3));
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
const p = Math.min(1, (ts - t0) / dur);
|
|
352
|
+
this.setScrollPos(start + (end - start) * easeInOutCubic(p));
|
|
353
|
+
if (p < 1) this.scrollRaf = requestAnimationFrame(step);
|
|
354
|
+
else {
|
|
355
|
+
this.scrollRaf = null;
|
|
356
|
+
this.detachInterrupt();
|
|
357
|
+
}
|
|
358
|
+
};
|
|
359
|
+
this.scrollRaf = requestAnimationFrame(step);
|
|
360
|
+
}
|
|
361
|
+
reducedMotion() {
|
|
362
|
+
return typeof matchMedia === "function" && matchMedia("(prefers-reduced-motion: reduce)").matches;
|
|
363
|
+
}
|
|
364
|
+
onInterrupt = () => {
|
|
365
|
+
this.cancelScroll();
|
|
366
|
+
};
|
|
367
|
+
interruptSource() {
|
|
368
|
+
return this.targetEl ?? window;
|
|
369
|
+
}
|
|
370
|
+
attachInterrupt() {
|
|
371
|
+
const src = this.interruptSource();
|
|
372
|
+
for (const ev of [
|
|
373
|
+
"wheel",
|
|
374
|
+
"touchstart",
|
|
375
|
+
"keydown"
|
|
376
|
+
]) src.addEventListener(ev, this.onInterrupt, { passive: true });
|
|
377
|
+
}
|
|
378
|
+
detachInterrupt() {
|
|
379
|
+
const src = this.interruptSource();
|
|
380
|
+
for (const ev of [
|
|
381
|
+
"wheel",
|
|
382
|
+
"touchstart",
|
|
383
|
+
"keydown"
|
|
384
|
+
]) src.removeEventListener(ev, this.onInterrupt);
|
|
385
|
+
}
|
|
386
|
+
cancelScroll() {
|
|
387
|
+
if (this.scrollRaf != null) {
|
|
388
|
+
cancelAnimationFrame(this.scrollRaf);
|
|
389
|
+
this.scrollRaf = null;
|
|
390
|
+
}
|
|
391
|
+
this.detachInterrupt();
|
|
392
|
+
}
|
|
393
|
+
maxScroll() {
|
|
394
|
+
if (this.targetEl) return Math.max(0, this.targetEl.scrollHeight - this.targetEl.clientHeight);
|
|
395
|
+
return Math.max(0, document.documentElement.scrollHeight - window.innerHeight);
|
|
396
|
+
}
|
|
397
|
+
targetPos(dest) {
|
|
398
|
+
return dest === "bottom" ? this.maxScroll() : 0;
|
|
399
|
+
}
|
|
400
|
+
scrollPos() {
|
|
401
|
+
if (this.targetEl) return this.targetEl.scrollTop;
|
|
402
|
+
return window.scrollY || document.documentElement.scrollTop || 0;
|
|
403
|
+
}
|
|
404
|
+
setScrollPos(v) {
|
|
405
|
+
if (this.targetEl) this.targetEl.scrollTop = v;
|
|
406
|
+
else window.scrollTo(0, v);
|
|
407
|
+
}
|
|
408
|
+
scrollTo(v) {
|
|
409
|
+
this.animateScroll(v === 0 ? "top" : "bottom");
|
|
410
|
+
}
|
|
411
|
+
onRecheck = () => {
|
|
412
|
+
this.syncVisible();
|
|
413
|
+
};
|
|
414
|
+
syncVisible() {
|
|
415
|
+
this.el.hidden = this.maxScroll() <= 1;
|
|
416
|
+
}
|
|
417
|
+
};
|
|
418
|
+
//#endregion
|
|
419
|
+
export { ScrollFab };
|
|
420
|
+
|
|
421
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.mjs","names":[],"sources":["../../../icon/icons.ts","../src/scroll-fab.ts"],"sourcesContent":["/* ============================================================\n 青梧 UI · 图标库\n ============================================================\n 零依赖 · 纯字符串常量 · tree-shakeable named exports\n 所有图标统一使用:viewBox=\"0 0 24 24\", stroke=\"currentColor\",\n stroke-width=\"2\", stroke-linecap=\"round\", stroke-linejoin=\"round\".\n\n 使用方式:\n import { ICON_CALENDAR, ICON_SEARCH } from \"@/icon/icons\";\n element.innerHTML = ICON_CALENDAR;\n 或者通过 dangerouslySetInnerHTML 渲染。\n ============================================================ */\n\nconst S = (d: string) =>\n `<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\">${d}</svg>`;\n\nconst S16 = (d: string) =>\n `<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\">${d}</svg>`;\n\nconst S18 = (d: string) =>\n `<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"18\" height=\"18\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\">${d}</svg>`;\n\nconst S20 = (d: string) =>\n `<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"20\" height=\"20\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\">${d}</svg>`;\n\nconst S36 = (d: string) =>\n `<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"36\" height=\"36\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.6\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\">${d}</svg>`;\n\n/* ---- 通用 UI 图标 ---- */\n\nexport const ICON_CALENDAR = S(\n '<rect x=\"3\" y=\"4\" width=\"18\" height=\"18\" rx=\"2\"/><path d=\"M8 2v4M16 2v4M3 10h18\"/>',\n);\n\nexport const ICON_CALENDAR_DOT = S(\n '<rect x=\"3\" y=\"4\" width=\"18\" height=\"18\" rx=\"2\" ry=\"2\"/><line x1=\"16\" y1=\"2\" x2=\"16\" y2=\"6\"/><line x1=\"8\" y1=\"2\" x2=\"8\" y2=\"6\"/><line x1=\"3\" y1=\"10\" x2=\"21\" y2=\"10\"/><line x1=\"8\" y1=\"14\" x2=\"8.01\" y2=\"14\"/>',\n);\n\nexport const ICON_SEARCH = S('<circle cx=\"11\" cy=\"11\" r=\"8\"/><path d=\"m21 21-4.35-4.35\"/>');\n\nexport const ICON_UPLOAD = S(\n '<path d=\"M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4\"/><polyline points=\"17 8 12 3 7 8\"/><line x1=\"12\" y1=\"3\" x2=\"12\" y2=\"15\"/>',\n);\n\nexport const ICON_UPLOAD_ARROW = S(\n '<path d=\"M12 16V4\"/><path d=\"m6 9 6-5 6 5\"/><path d=\"M4 20h16\"/>',\n);\n\nexport const ICON_MENU = S('<path d=\"M3 7h18M3 12h18M3 17h18\"/>');\n\nexport const ICON_BELL = S(\n '<path d=\"M6 8a6 6 0 0 1 12 0c0 7 3 9 3 9H3s3-2 3-9\"/><path d=\"M10.3 21a1.94 1.94 0 0 0 3.4 0\"/>',\n);\n\nexport const ICON_CLOSE = S(\n '<line x1=\"18\" y1=\"6\" x2=\"6\" y2=\"18\"/><line x1=\"6\" y1=\"6\" x2=\"18\" y2=\"18\"/>',\n);\n\nexport const ICON_CHECK = S('<polyline points=\"20 6 9 17 4 12\"/>');\n\nexport const ICON_PLUS = S(\n '<line x1=\"12\" y1=\"5\" x2=\"12\" y2=\"19\"/><line x1=\"5\" y1=\"12\" x2=\"19\" y2=\"12\"/>',\n);\n\nexport const ICON_MINUS = S('<line x1=\"5\" y1=\"12\" x2=\"19\" y2=\"12\"/>');\n\nexport const ICON_BOX = S(\n '<path d=\"M21 16V8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73l7 4a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16z\"/>',\n);\n\nexport const ICON_EDIT = S(\n '<path d=\"M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7\"/><path d=\"M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z\"/>',\n);\n\nexport const ICON_FILE = S(\n '<path d=\"M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z\"/><polyline points=\"14 2 14 8 20 8\"/>',\n);\n\nexport const ICON_FOLDER = S(\n '<path d=\"M22 19a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h5l2 3h9a2 2 0 0 1 2 2z\"/>',\n);\n\nexport const ICON_INFO = S(\n '<circle cx=\"12\" cy=\"12\" r=\"10\"/><line x1=\"12\" y1=\"16\" x2=\"12\" y2=\"12\"/><line x1=\"12\" y1=\"8\" x2=\"12.01\" y2=\"8\"/>',\n);\n\nexport const ICON_WARNING = S(\n '<path d=\"M10.29 3.86 1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z\"/><line x1=\"12\" y1=\"9\" x2=\"12\" y2=\"13\"/><line x1=\"12\" y1=\"17\" x2=\"12.01\" y2=\"17\"/>',\n);\n\nexport const ICON_TRASH = S(\n '<polyline points=\"3 6 5 6 21 6\"/><path d=\"M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2\"/><line x1=\"10\" y1=\"11\" x2=\"10\" y2=\"17\"/><line x1=\"14\" y1=\"11\" x2=\"14\" y2=\"17\"/>',\n);\n\nexport const ICON_DOWNLOAD = S(\n '<path d=\"M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4\"/><polyline points=\"7 10 12 15 17 10\"/><line x1=\"12\" y1=\"15\" x2=\"12\" y2=\"3\"/>',\n);\n\nexport const ICON_COPY = S(\n '<rect x=\"9\" y=\"9\" width=\"13\" height=\"13\" rx=\"2\" ry=\"2\"/><path d=\"M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1\"/>',\n);\n\nexport const ICON_EYE = S(\n '<path d=\"M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z\"/><circle cx=\"12\" cy=\"12\" r=\"3\"/>',\n);\n\nexport const ICON_STAR = S(\n '<polygon points=\"12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2\"/>',\n);\n\nexport const ICON_STAR_FILLED = `<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"currentColor\" stroke=\"none\" aria-hidden=\"true\"><polygon points=\"12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2\"/></svg>`;\n\nexport const ICON_BOOKMARK = S('<path d=\"M19 21l-7-5-7 5V5a2 2 0 0 1 2-2h10a2 2 0 0 1 2 2z\"/>');\n\nexport const ICON_EXTERNAL_LINK = S(\n '<path d=\"M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6\"/><polyline points=\"15 3 21 3 21 9\"/><line x1=\"10\" y1=\"14\" x2=\"21\" y2=\"3\"/>',\n);\nexport const ICON_GITHUB = S(\n '<path d=\"M15 22v-4a4.8 4.8 0 0 0-1-3.5c3 0 6-2 6-5.5.08-1.25-.27-2.48-1-3.5.28-1.15.28-2.35 0-3.5 0 0-1 0-3 1.5-2.64-.5-5.36-.5-8 0C6 2 5 2 5 2c-.3 1.15-.3 2.35 0 3.5A5.403 5.403 0 0 0 4 9c0 3.5 3 5.5 6 5.5-.39.49-.68 1.05-.85 1.65-.17.6-.22 1.23-.15 1.85v4\"/><path d=\"M9 18c-4.51 2-5-2-7-2\"/>',\n);\n\n/* ---- 导航箭头 ---- */\n\nexport const ICON_CHEVRON_LEFT = S16('<path d=\"m15 18-6-6 6-6\"/>');\nexport const ICON_CHEVRON_RIGHT = S16('<path d=\"m9 18 6-6-6-6\"/>');\nexport const ICON_CHEVRON_UP = S('<polyline points=\"18 15 12 9 6 15\"/>');\nexport const ICON_CHEVRON_DOWN = S('<polyline points=\"6 9 12 15 18 9\"/>');\nexport const ICON_CODE = S(\n '<polyline points=\"16 18 22 12 16 6\"/><polyline points=\"8 6 2 12 8 18\"/>',\n);\n\n/* ---- 全屏 ---- */\n\nexport const ICON_FULLSCREEN = S(\n '<polyline points=\"15 3 21 3 21 9\"/><polyline points=\"9 21 3 21 3 15\"/><line x1=\"21\" y1=\"3\" x2=\"14\" y2=\"10\"/><line x1=\"3\" y1=\"21\" x2=\"10\" y2=\"14\"/>',\n);\nexport const ICON_FULLSCREEN_EXIT = S(\n '<polyline points=\"4 8 4 3 9 3\"/><polyline points=\"20 16 20 21 15 21\"/><line x1=\"4\" y1=\"3\" x2=\"11\" y2=\"10\"/><line x1=\"20\" y1=\"21\" x2=\"13\" y2=\"14\"/>',\n);\n\n/* ---- 功能图标 ---- */\n\nexport const ICON_CLOCK = S('<circle cx=\"12\" cy=\"12\" r=\"10\"/><path d=\"M12 6v6l4 2\"/>');\nexport const ICON_TYPE = S(\n '<polyline points=\"4 7 4 4 20 4 20 7\"/><line x1=\"9\" y1=\"20\" x2=\"15\" y2=\"20\"/><line x1=\"12\" y1=\"4\" x2=\"12\" y2=\"20\"/>',\n);\nexport const ICON_USERS = S(\n '<path d=\"M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2\"/><circle cx=\"9\" cy=\"7\" r=\"4\"/><path d=\"M23 21v-2a4 4 0 0 0-3-3.87\"/><path d=\"M16 3.13a4 4 0 0 1 0 7.75\"/>',\n);\nexport const ICON_MICROPHONE = S(\n '<path d=\"M12 1a3 3 0 0 0-3 3v8a3 3 0 0 0 6 0V4a3 3 0 0 0-3-3z\"/><path d=\"M19 10v2a7 7 0 0 1-14 0v-2\"/><line x1=\"12\" y1=\"19\" x2=\"12\" y2=\"23\"/><line x1=\"8\" y1=\"23\" x2=\"16\" y2=\"23\"/>',\n);\nexport const ICON_MUSIC = S(\n '<path d=\"M9 18V5l12-2v13\"/><circle cx=\"6\" cy=\"18\" r=\"3\"/><circle cx=\"18\" cy=\"16\" r=\"3\"/>',\n);\nexport const ICON_VIDEO = S(\n '<polygon points=\"23 7 16 12 23 17 23 7\"/><rect x=\"1\" y=\"5\" width=\"15\" height=\"14\" rx=\"2\" ry=\"2\"/>',\n);\nexport const ICON_IMAGE = S(\n '<rect x=\"3\" y=\"3\" width=\"18\" height=\"18\" rx=\"2\" ry=\"2\"/><circle cx=\"8.5\" cy=\"8.5\" r=\"1.5\"/><polyline points=\"21 15 16 10 5 21\"/>',\n);\n\n/* ---- 主题 ---- */\n\nexport const ICON_SUN = S(\n '<circle cx=\"12\" cy=\"12\" r=\"5\"/><line x1=\"12\" y1=\"1\" x2=\"12\" y2=\"3\"/><line x1=\"12\" y1=\"21\" x2=\"12\" y2=\"23\"/><line x1=\"4.22\" y1=\"4.22\" x2=\"5.64\" y2=\"5.64\"/><line x1=\"18.36\" y1=\"18.36\" x2=\"19.78\" y2=\"19.78\"/><line x1=\"1\" y1=\"12\" x2=\"3\" y2=\"12\"/><line x1=\"21\" y1=\"12\" x2=\"23\" y2=\"12\"/><line x1=\"4.22\" y1=\"19.78\" x2=\"5.64\" y2=\"18.36\"/><line x1=\"18.36\" y1=\"5.64\" x2=\"19.78\" y2=\"4.22\"/>',\n);\nexport const ICON_MOON = S('<path d=\"M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z\"/>');\n\n/* ---- 侧边栏 ---- */\n\nexport const ICON_SIDEBAR_TOGGLE = S(\n '<rect width=\"18\" height=\"18\" x=\"3\" y=\"3\" rx=\"2\"/><path d=\"M9 3v18\"/><path d=\"m14 9-3 3 3 3\"/>',\n);\n\n/* ---- 编辑器图标 ---- */\n\nexport const ICON_LIST = S(\n '<line x1=\"8\" y1=\"6\" x2=\"21\" y2=\"6\"/><line x1=\"8\" y1=\"12\" x2=\"21\" y2=\"12\"/><line x1=\"8\" y1=\"18\" x2=\"21\" y2=\"18\"/><line x1=\"3\" y1=\"6\" x2=\"3.01\" y2=\"6\"/><line x1=\"3\" y1=\"12\" x2=\"3.01\" y2=\"12\"/><line x1=\"3\" y1=\"18\" x2=\"3.01\" y2=\"18\"/>',\n);\nexport const ICON_AI = S(\n '<path d=\"M12 2l2.4 7.2h7.6l-6 4.8 2.4 7.2-6.4-4.8-6.4 4.8 2.4-7.2-6-4.8h7.6z\"/>',\n);\nexport const ICON_BOLT = S('<polygon points=\"13 2 3 14 12 14 11 22 21 10 12 10 13 2\"/>');\nexport const ICON_REFRESH = S(\n '<polyline points=\"23 4 23 10 17 10\"/><polyline points=\"1 20 1 14 7 14\"/><path d=\"M3.51 9a9 9 0 0 1 14.85-3.36L23 10M1 14l4.64 4.36A9 9 0 0 0 20.49 15\"/>',\n);\nexport const ICON_RETRY = S(\n '<polyline points=\"1 4 1 10 7 10\"/><path d=\"M3.51 15a9 9 0 1 0 2.13-9.36L1 10\"/>',\n);\nexport const ICON_SETTINGS = S(\n '<circle cx=\"12\" cy=\"12\" r=\"3\"/><path d=\"M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1 0 2.83 2 2 0 0 1-2.83 0l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-2 2 2 2 0 0 1-2-2v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83 0 2 2 0 0 1 0-2.83l.06-.06A1.65 1.65 0 0 0 4.68 15a1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1-2-2 2 2 0 0 1 2-2h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2 0 0 1 2.83 0l.06.06A1.65 1.65 0 0 0 9 4.68a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 2-2 2 2 0 0 1 2 2v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 0 2 2 0 0 1 0 2.83l-.06.06A1.65 1.65 0 0 0 19.4 9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 2 2 2 2 0 0 1-2 2h-.09a1.65 1.65 0 0 0-1.51 1z\"/>',\n);\nexport const ICON_HISTORY = S(\n '<path d=\"M3 3v5h5\"/><path d=\"M3.05 13A9 9 0 1 0 6.11 6.11L3 8\"/><path d=\"M12 7v5l4 2\"/>',\n);\nexport const ICON_PUSH = S(\n '<line x1=\"22\" y1=\"2\" x2=\"11\" y2=\"13\"/><polygon points=\"22 2 15 22 11 13 2 9 22 2\"/>',\n);\nexport const ICON_TRANSLATE = S(\n '<path d=\"M5 8l6 6\"/><path d=\"M4 14l6-6 2-3\"/><path d=\"M2 5h12\"/><path d=\"M7 2h1\"/><path d=\"M22 22l-5-10-5 10\"/><path d=\"M14 18h6\"/>',\n);\nexport const ICON_SUMMARY = S(\n '<path d=\"M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z\"/><polyline points=\"14 2 14 8 20 8\"/><line x1=\"16\" y1=\"13\" x2=\"8\" y2=\"13\"/><line x1=\"16\" y1=\"17\" x2=\"8\" y2=\"17\"/><polyline points=\"10 9 9 9 8 9\"/>',\n);\nexport const ICON_RENAME = S('<path d=\"M17 3a2.828 2.828 0 1 1 4 4L7.5 20.5 2 22l1.5-5.5L17 3z\"/>');\nexport const ICON_SELECTION = S(\n '<circle cx=\"12\" cy=\"12\" r=\"1\"/><path d=\"M20 12a8 8 0 1 1-8-8\"/><path d=\"m16 2 4 4-4 4\"/>',\n);\nexport const ICON_TAG = S(\n '<path d=\"M20.59 13.41l-7.17 7.17a2 2 0 0 1-2.83 0L2 12V2h10l8.59 8.59a2 2 0 0 1 0 2.82z\"/><line x1=\"7\" y1=\"7\" x2=\"7.01\" y2=\"7\"/>',\n);\nexport const ICON_GRIP = S(\n '<circle cx=\"9\" cy=\"12\" r=\"1\"/><circle cx=\"15\" cy=\"12\" r=\"1\"/><circle cx=\"9\" cy=\"6\" r=\"1\"/><circle cx=\"15\" cy=\"6\" r=\"1\"/><circle cx=\"9\" cy=\"18\" r=\"1\"/><circle cx=\"15\" cy=\"18\" r=\"1\"/>',\n);\nexport const ICON_DIAGRAM = S(\n '<rect x=\"2\" y=\"2\" width=\"8\" height=\"8\" rx=\"1\"/><rect x=\"14\" y=\"2\" width=\"8\" height=\"8\" rx=\"1\"/><rect x=\"2\" y=\"14\" width=\"8\" height=\"8\" rx=\"1\"/><rect x=\"14\" y=\"14\" width=\"8\" height=\"8\" rx=\"1\"/>',\n);\nexport const ICON_LANGUAGE = S(\n '<circle cx=\"12\" cy=\"12\" r=\"10\"/><line x1=\"2\" y1=\"12\" x2=\"22\" y2=\"12\"/><path d=\"M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10 15.3 15.3 0 0 1-4-10 15.3 15.3 0 0 1 4-10z\"/>',\n);\nexport const ICON_PAPERCLIP = S(\n '<path d=\"M21.44 11.05l-9.19 9.19a6 6 0 0 1-8.49-8.49l9.19-9.19a4 4 0 0 1 5.66 5.66l-9.2 9.19a2 2 0 0 1-2.83-2.83l8.49-8.48\"/>',\n);\nexport const ICON_BROKEN_IMAGE =\n '<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"32\" height=\"32\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\"><rect x=\"2\" y=\"2\" width=\"20\" height=\"20\" rx=\"2\"/><path d=\"M2 14l4-4 4 4 4-4 4 4 4-4\"/><path d=\"M6 6h.01M18 6h.01\"/></svg>';\n\n/* ---- 包级别图标(用于 upload/search/calendar 包) ---- */\n\nexport const ICO_UPLOAD = S36('<path d=\"M12 16V4\"/><path d=\"m6 9 6-5 6 5\"/><path d=\"M4 20h16\"/>');\nconst S18c = (d: string) =>\n `<svg class=\"qs-ico\" xmlns=\"http://www.w3.org/2000/svg\" width=\"18\" height=\"18\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\">${d}</svg>`;\n\nconst S20c = (d: string) =>\n `<svg class=\"qw-cal-svg\" xmlns=\"http://www.w3.org/2000/svg\" width=\"20\" height=\"20\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\">${d}</svg>`;\n\nexport const ICO_SEARCH = S18c('<circle cx=\"11\" cy=\"11\" r=\"7\"/><path d=\"m20 20-3.2-3.2\"/>');\nexport const ICO_MENU = S18('<path d=\"M4 7h16M4 12h16M4 17h16\"/>');\nexport const ICO_CALENDAR = S20c(\n '<rect x=\"3\" y=\"4\" width=\"18\" height=\"18\" rx=\"2\"/><path d=\"M8 2v4M16 2v4M3 10h18\"/>',\n);\nexport const ICO_ARROW_LEFT = S16('<path d=\"m15 18-6-6 6-6\"/>');\nexport const ICO_ARROW_RIGHT = S16('<path d=\"m9 18 6-6-6-6\"/>');\n\n/* ---- 搜索插图(大尺寸 art) ---- */\n\nexport const SEARCH_ART =\n '<svg viewBox=\"0 0 160 140\" role=\"img\" aria-label=\"Search illustration\">' +\n '<ellipse class=\"qs-art-shadow\" cx=\"80\" cy=\"124\" rx=\"46\" ry=\"6\"/>' +\n '<g class=\"qs-art-float\">' +\n '<rect class=\"qs-art-paper\" x=\"40\" y=\"20\" width=\"80\" height=\"82\" rx=\"13\"/>' +\n '<line class=\"qs-art-row-accent\" x1=\"54\" y1=\"42\" x2=\"92\" y2=\"42\"/>' +\n '<line class=\"qs-art-row\" x1=\"54\" y1=\"56\" x2=\"106\" y2=\"56\"/>' +\n '<line class=\"qs-art-row\" x1=\"54\" y1=\"70\" x2=\"84\" y2=\"70\"/>' +\n '<circle class=\"qs-art-glass\" cx=\"96\" cy=\"86\" r=\"23\"/>' +\n '<line class=\"qs-art-handle\" x1=\"113\" y1=\"103\" x2=\"129\" y2=\"119\"/>' +\n '<text class=\"qs-art-seal\" x=\"96\" y=\"94\" text-anchor=\"middle\" font-size=\"24\">寻</text>' +\n \"</g>\" +\n '<circle class=\"qs-art-dot-a qs-art-twinkle\" cx=\"34\" cy=\"32\" r=\"3\"/>' +\n '<circle class=\"qs-art-dot-b qs-art-twinkle\" cx=\"126\" cy=\"40\" r=\"2.6\" style=\"animation-delay:.8s\"/>' +\n '<circle class=\"qs-art-dot-c qs-art-twinkle\" cx=\"30\" cy=\"98\" r=\"2.4\" style=\"animation-delay:1.5s\"/>' +\n \"</svg>\";\n","/** 青梧UI ScrollFab 悬浮滚动栏:默认“滚动到底部”,桌面悬停/触屏按住绕满描边一圈翻转为“返回顶部”,点击(轻点)直接执行当前模式动作 */\n\nimport { ICON_CHEVRON_DOWN } from \"../../../icon/icons\";\nimport type { ScrollFabMode, ScrollFabOptions } from \"./types\";\n\nconst RADIUS = 21;\nconst DEFAULT_LABEL_BOTTOM = \"滚动到底部(悬停或按住绕圈可切换为返回顶部)\";\nconst DEFAULT_LABEL_TOP = \"返回顶部(悬停或按住绕圈可切换为滚动到底部)\";\n\nfunction easeInOutCubic(p: number): number {\n return p < 0.5 ? 4 * p * p * p : 1 - (-2 * p + 2) ** 3 / 2;\n}\n\nexport class ScrollFab {\n readonly el: HTMLButtonElement;\n\n private readonly ring: SVGCircleElement;\n private readonly track: SVGCircleElement;\n private readonly icon: HTMLElement;\n private readonly len: number;\n\n private readonly targetEl: HTMLElement | null;\n private readonly ringMs: number;\n private readonly decayMs: number;\n private readonly cancelPx: number;\n private readonly animate: boolean;\n private readonly labelBottom: string;\n private readonly labelTop: string;\n private readonly onModeChange?: (mode: ScrollFabMode) => void;\n\n private mode: ScrollFabMode = \"to-bottom\";\n private progress = 0;\n private hovering = false;\n private pressing = false;\n private flipped = false;\n private pointerId: number | null = null;\n private startX = 0;\n private startY = 0;\n private ringRaf: number | null = null;\n private lastTs: number | null = null;\n private scrollRaf: number | null = null;\n private flashTimer: ReturnType<typeof setTimeout> | null = null;\n private tapResetTimer: ReturnType<typeof setTimeout> | null = null;\n private touchTap = false;\n private ro: ResizeObserver | null = null;\n\n constructor(options: ScrollFabOptions = {}) {\n this.targetEl = options.target ?? null;\n this.ringMs = Math.max(100, Math.min(5000, options.ringDuration ?? 800));\n this.decayMs = Math.max(100, options.decayDuration ?? 300);\n this.cancelPx = Math.max(4, options.cancelThreshold ?? 10);\n this.animate = options.animate ?? true;\n this.labelBottom = options.ariaLabelToBottom ?? DEFAULT_LABEL_BOTTOM;\n this.labelTop = options.ariaLabelToTop ?? DEFAULT_LABEL_TOP;\n this.onModeChange = options.onModeChange;\n\n this.el = document.createElement(\"button\");\n this.el.type = \"button\";\n this.el.className = `qsf-root${options.className ? ` ${options.className}` : \"\"}`;\n this.el.dataset.mode = this.mode;\n this.el.setAttribute(\"aria-label\", this.labelBottom);\n this.el.style.setProperty(\"--qsf-size\", `${Math.max(44, options.size ?? 48)}px`);\n if (options.zIndex != null) this.el.style.setProperty(\"--qsf-z\", String(options.zIndex));\n if (options.position?.right != null)\n this.el.style.setProperty(\"--qsf-right\", `${options.position.right}px`);\n if (options.position?.bottom != null)\n this.el.style.setProperty(\"--qsf-bottom\", `${options.position.bottom}px`);\n if (options.position?.left != null)\n this.el.style.setProperty(\"--qsf-left\", `${options.position.left}px`);\n if (options.position?.top != null)\n this.el.style.setProperty(\"--qsf-top\", `${options.position.top}px`);\n\n const svg = document.createElementNS(\"http://www.w3.org/2000/svg\", \"svg\");\n svg.setAttribute(\"class\", \"qsf-svg\");\n svg.setAttribute(\"viewBox\", \"0 0 48 48\");\n svg.setAttribute(\"aria-hidden\", \"true\");\n this.track = this.makeCircle(\"qsf-track\");\n this.ring = this.makeCircle(\"qsf-bar\");\n svg.append(this.track, this.ring);\n let len = 2 * Math.PI * RADIUS;\n try {\n // 浏览器对未渲染 SVG 抛错(happy-dom 返回 NaN),失败时用几何公式回退\n const measured = this.ring.getTotalLength();\n if (Number.isFinite(measured)) len = measured;\n } catch {\n /* 回退 2πr */\n }\n this.len = len;\n this.ring.style.strokeDasharray = String(this.len);\n this.renderRing();\n\n this.icon = document.createElement(\"span\");\n this.icon.className = \"qsf-icon\";\n this.icon.setAttribute(\"aria-hidden\", \"true\");\n if (options.content != null) {\n if (typeof options.content === \"string\") this.icon.innerHTML = options.content;\n else this.icon.append(options.content);\n } else {\n this.icon.innerHTML = ICON_CHEVRON_DOWN;\n }\n\n this.el.append(svg, this.icon);\n document.body.append(this.el);\n\n this.el.addEventListener(\"pointerenter\", this.onEnter);\n this.el.addEventListener(\"pointerleave\", this.onLeave);\n this.el.addEventListener(\"pointerdown\", this.onDown);\n this.el.addEventListener(\"pointermove\", this.onMove);\n this.el.addEventListener(\"pointerup\", this.onUp);\n this.el.addEventListener(\"pointercancel\", this.onCancel);\n this.el.addEventListener(\"keydown\", this.onKey);\n this.el.addEventListener(\"click\", this.onClick);\n window.addEventListener(\"resize\", this.onRecheck);\n (this.targetEl ?? window).addEventListener(\"scroll\", this.onRecheck, { passive: true });\n if (typeof ResizeObserver === \"function\") {\n this.ro = new ResizeObserver(this.onRecheck);\n this.ro.observe(this.targetEl ?? document.documentElement);\n }\n this.syncVisible();\n }\n\n /** 当前模式 */\n get currentMode(): ScrollFabMode {\n return this.mode;\n }\n\n /** 内容懒加载等外部高度变化后可显式触发重估(滚动/resize 时也会自动重估) */\n refresh(): void {\n this.onRecheck();\n }\n\n scrollToBottom(): void {\n this.animateScroll(\"bottom\");\n }\n\n scrollToTop(): void {\n this.scrollTo(0);\n }\n\n destroy(): void {\n this.el.removeEventListener(\"pointerenter\", this.onEnter);\n this.el.removeEventListener(\"pointerleave\", this.onLeave);\n this.el.removeEventListener(\"pointerdown\", this.onDown);\n this.el.removeEventListener(\"pointermove\", this.onMove);\n this.el.removeEventListener(\"pointerup\", this.onUp);\n this.el.removeEventListener(\"pointercancel\", this.onCancel);\n this.el.removeEventListener(\"keydown\", this.onKey);\n this.el.removeEventListener(\"click\", this.onClick);\n window.removeEventListener(\"resize\", this.onRecheck);\n (this.targetEl ?? window).removeEventListener(\"scroll\", this.onRecheck);\n this.ro?.disconnect();\n this.cancelScroll();\n if (this.ringRaf != null) cancelAnimationFrame(this.ringRaf);\n this.ringRaf = null;\n if (this.flashTimer != null) clearTimeout(this.flashTimer);\n if (this.tapResetTimer != null) clearTimeout(this.tapResetTimer);\n this.el.remove();\n }\n\n private makeCircle(cls: string): SVGCircleElement {\n const c = document.createElementNS(\"http://www.w3.org/2000/svg\", \"circle\");\n c.setAttribute(\"class\", cls);\n c.setAttribute(\"cx\", \"24\");\n c.setAttribute(\"cy\", \"24\");\n c.setAttribute(\"r\", String(RADIUS));\n return c;\n }\n\n /* ---------------- 输入状态机 ----------------\n * 桌面(mouse):pointerenter 推进描边 / pointerleave 衰减倒转;click 恒为动作\n * 触屏(非 mouse):pointerdown 按住推进(首指针独占,位移超阈值放弃并放行手势);\n * 轻点 = 动作;绕满翻转过 → 本次释放被消费(不在同一手势里翻转+滚动)\n */\n\n private onEnter = (e: PointerEvent): void => {\n if (e.pointerType !== \"mouse\") return;\n this.hovering = true;\n this.kickRing();\n };\n\n private onLeave = (e: PointerEvent): void => {\n if (e.pointerType !== \"mouse\") return;\n this.hovering = false;\n this.flipped = false;\n };\n\n private onDown = (e: PointerEvent): void => {\n if (e.button !== 0 || this.pointerId !== null) return;\n // 桌面描边由 hover 驱动,鼠标按压不接管\n if (e.pointerType === \"mouse\") return;\n e.preventDefault();\n this.touchTap = true;\n if (this.tapResetTimer != null) clearTimeout(this.tapResetTimer);\n this.pointerId = e.pointerId;\n this.startX = e.clientX;\n this.startY = e.clientY;\n this.pressing = true;\n this.flipped = false;\n this.kickRing();\n };\n\n private onMove = (e: PointerEvent): void => {\n if (e.pointerId !== this.pointerId || !this.pressing) return;\n // 超阈值 = 放弃描边且放行页面手势(不拦截 move,浏览器原生滚动照常)\n if (Math.hypot(e.clientX - this.startX, e.clientY - this.startY) > this.cancelPx) {\n this.pressing = false;\n this.pointerId = null;\n this.flipped = false;\n }\n };\n\n private onUp = (e: PointerEvent): void => {\n if (e.pointerId !== this.pointerId) return;\n this.pointerId = null;\n this.pressing = false;\n const consumed = this.flipped;\n this.flipped = false;\n // ghost click 兜底标记延迟复位,避免吞掉下一次真实鼠标点击\n this.tapResetTimer = setTimeout(() => {\n this.touchTap = false;\n this.tapResetTimer = null;\n }, 400);\n if (consumed) return;\n this.doAction();\n };\n\n private onCancel = (e: PointerEvent): void => {\n if (e.pointerId !== this.pointerId) return;\n this.pointerId = null;\n this.pressing = false;\n this.flipped = false;\n };\n\n private onClick = (e: MouseEvent): void => {\n // 触屏 pointerdown 已 preventDefault,正常不合成 click;此处兜底吞掉,防双触发\n if (this.touchTap) {\n e.preventDefault();\n e.stopPropagation();\n return;\n }\n this.doAction();\n };\n\n private onKey = (e: KeyboardEvent): void => {\n if (e.repeat || (e.key !== \"Enter\" && e.key !== \" \")) return;\n e.preventDefault();\n this.setMode(this.mode === \"to-bottom\" ? \"to-top\" : \"to-bottom\");\n };\n\n /* ---------------- 描边 rAF:悬停/按住推进 · 离开/松手衰减倒转 ---------------- */\n\n private holding(): boolean {\n return this.hovering || this.pressing;\n }\n\n private kickRing(): void {\n if (this.ringRaf != null) return;\n this.lastTs = null;\n this.ringRaf = requestAnimationFrame(this.stepRing);\n }\n\n private stepRing = (ts: number): void => {\n const dt = this.lastTs == null ? 0 : ts - this.lastTs;\n this.lastTs = ts;\n if (this.holding()) {\n if (!this.flipped) {\n this.progress = Math.min(1, this.progress + dt / this.ringMs);\n if (this.progress >= 1) this.completeRing();\n }\n } else {\n this.progress = Math.max(0, this.progress - dt / this.decayMs);\n }\n this.renderRing();\n if (this.holding() || this.progress > 0) {\n this.ringRaf = requestAnimationFrame(this.stepRing);\n } else {\n this.ringRaf = null;\n }\n };\n\n /** 绕满一圈 = 纯模式翻转(不附带任何滚动) */\n private completeRing(): void {\n this.flipped = true;\n const next: ScrollFabMode = this.mode === \"to-bottom\" ? \"to-top\" : \"to-bottom\";\n this.setMode(next);\n this.el.classList.add(\"is-complete\");\n if (this.flashTimer != null) clearTimeout(this.flashTimer);\n this.flashTimer = setTimeout(() => {\n this.el.classList.remove(\"is-complete\");\n this.flashTimer = null;\n }, 350);\n }\n\n private renderRing(): void {\n const vis = this.progress > 0;\n this.track.toggleAttribute(\"hidden\", !vis);\n this.ring.toggleAttribute(\"hidden\", !vis);\n this.ring.style.strokeDashoffset = String(this.len * (1 - this.progress));\n }\n\n private setMode(mode: ScrollFabMode): void {\n if (mode === this.mode) return;\n this.mode = mode;\n this.el.dataset.mode = mode;\n this.el.setAttribute(\"aria-label\", mode === \"to-bottom\" ? this.labelBottom : this.labelTop);\n this.onModeChange?.(mode);\n }\n\n /* ---------------- 程序滚动:rAF 缓动 + 打断 + 懒加载追击 ---------------- */\n\n private doAction(): void {\n this.animateScroll(this.mode === \"to-bottom\" ? \"bottom\" : \"top\");\n }\n\n private animateScroll(dest: \"bottom\" | \"top\"): void {\n this.cancelScroll();\n if (!this.animate || this.reducedMotion()) {\n this.setScrollPos(this.targetPos(dest));\n return;\n }\n this.attachInterrupt();\n let init = false;\n let start = 0;\n let end = 0;\n let t0 = 0;\n let dur = 0;\n const step = (ts: number): void => {\n if (!init) {\n init = true;\n start = this.scrollPos();\n end = this.targetPos(dest);\n t0 = ts;\n dur = Math.min(1200, Math.max(400, Math.abs(end - start) / 3));\n } else if (dest === \"bottom\") {\n // 追击:懒加载变长时以当前实测底端为准重设行程\n const live = this.targetPos(dest);\n if (Math.abs(live - end) > 2) {\n end = live;\n start = this.scrollPos();\n t0 = ts;\n dur = Math.min(1200, Math.max(400, Math.abs(end - start) / 3));\n }\n }\n const p = Math.min(1, (ts - t0) / dur);\n this.setScrollPos(start + (end - start) * easeInOutCubic(p));\n if (p < 1) this.scrollRaf = requestAnimationFrame(step);\n else {\n this.scrollRaf = null;\n this.detachInterrupt();\n }\n };\n this.scrollRaf = requestAnimationFrame(step);\n }\n\n private reducedMotion(): boolean {\n return (\n typeof matchMedia === \"function\" && matchMedia(\"(prefers-reduced-motion: reduce)\").matches\n );\n }\n\n private onInterrupt = (): void => {\n this.cancelScroll();\n };\n\n private interruptSource(): Window | HTMLElement {\n return this.targetEl ?? window;\n }\n\n private attachInterrupt(): void {\n const src = this.interruptSource();\n for (const ev of [\"wheel\", \"touchstart\", \"keydown\"])\n src.addEventListener(ev, this.onInterrupt, { passive: true });\n }\n\n private detachInterrupt(): void {\n const src = this.interruptSource();\n for (const ev of [\"wheel\", \"touchstart\", \"keydown\"])\n src.removeEventListener(ev, this.onInterrupt);\n }\n\n private cancelScroll(): void {\n if (this.scrollRaf != null) {\n cancelAnimationFrame(this.scrollRaf);\n this.scrollRaf = null;\n }\n this.detachInterrupt();\n }\n\n /* ---------------- 度量与可见性(可滚动才渲染) ---------------- */\n\n private maxScroll(): number {\n if (this.targetEl) return Math.max(0, this.targetEl.scrollHeight - this.targetEl.clientHeight);\n return Math.max(0, document.documentElement.scrollHeight - window.innerHeight);\n }\n\n private targetPos(dest: \"bottom\" | \"top\"): number {\n return dest === \"bottom\" ? this.maxScroll() : 0;\n }\n\n private scrollPos(): number {\n if (this.targetEl) return this.targetEl.scrollTop;\n return window.scrollY || document.documentElement.scrollTop || 0;\n }\n\n private setScrollPos(v: number): void {\n if (this.targetEl) this.targetEl.scrollTop = v;\n else window.scrollTo(0, v);\n }\n\n private scrollTo(v: number): void {\n this.animateScroll(v === 0 ? \"top\" : \"bottom\");\n }\n\n private onRecheck = (): void => {\n this.syncVisible();\n };\n\n private syncVisible(): void {\n this.el.hidden = this.maxScroll() <= 1;\n }\n}\n"],"mappings":"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"}
|