@qingwu-ui/scroll-fab 0.9.0-beta.2 → 0.9.0-beta.3
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 +19 -0
- package/dist/index.cjs +71 -21
- package/dist/index.d.cts +24 -1
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts +24 -1
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +71 -21
- package/dist/index.mjs.map +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -4,6 +4,9 @@
|
|
|
4
4
|
|
|
5
5
|
- **绕圈翻转**:桌面悬停 / 触屏按住推进描边进度,绕满一圈在「滚动到底部 / 返回顶部」间纯切换(不附带滚动),未绕满移开/松手则描边衰减倒转
|
|
6
6
|
- **点击与描边正交**:轻点恒直接执行当前模式动作;触屏绕满后的释放被消费,避免同一手势既翻转又滚动
|
|
7
|
+
- **键盘语义正确**:Enter/Space 执行当前模式动作(原生按钮激活);双模式下方向键 `ArrowUp/ArrowDown` 翻转模式
|
|
8
|
+
- **单模式 / 阈值显隐**:`modes` 允许只跑「返回顶部」,`showThreshold` 滚过阈值才出现、不打扰首屏
|
|
9
|
+
- **进度回调**:`onScroll(pct)` 实时吐出 0..1 滚动进度
|
|
7
10
|
- **Lenis 可插拔**:内置 rAF 缓动,可选接入 [Lenis](https://github.com/darkroomengineering/lenis)(未安装静默回退)
|
|
8
11
|
- **异形形状**:银杏叶等自定义外形,进度描边沿外扩轮廓完全包裹
|
|
9
12
|
- 程序滚动支持 wheel / touch / keydown 打断、懒加载变长追击
|
|
@@ -68,6 +71,19 @@ new ScrollFab({
|
|
|
68
71
|
|
|
69
72
|
异形模式下圆形外壳自动退场(背景/边框/圆角),阴影改用 `drop-shadow` 跟随叶片真实形状;命中区仍为矩形以保证 44px 触控目标。
|
|
70
73
|
|
|
74
|
+
### 单模式 / 阈值显隐 / 进度回调
|
|
75
|
+
|
|
76
|
+
```ts
|
|
77
|
+
// 只跑「返回顶部」:首屏不打扰,滚过 400px 才出现;实时读滚动进度
|
|
78
|
+
new ScrollFab({
|
|
79
|
+
modes: ["to-top"],
|
|
80
|
+
showThreshold: 400,
|
|
81
|
+
onScroll: (pct) => console.log(`${Math.round(pct * 100)}%`),
|
|
82
|
+
});
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
单模式没有绕环翻转与描边,视觉上即普通按钮;键盘 Enter/Space 执行动作。
|
|
86
|
+
|
|
71
87
|
## API
|
|
72
88
|
|
|
73
89
|
```ts
|
|
@@ -90,6 +106,9 @@ interface ScrollFabOptions {
|
|
|
90
106
|
ariaLabelToBottom?: string;
|
|
91
107
|
ariaLabelToTop?: string;
|
|
92
108
|
onModeChange?: (mode: "to-bottom" | "to-top") => void;
|
|
109
|
+
modes?: ("to-bottom" | "to-top")[]; // 缺省双模式;初始取首项;单模式无绕环/描边;空配置抛 TypeError
|
|
110
|
+
showThreshold?: number; // 显示所需最小 scrollPos px,默认 0(行为不变),负数按 0
|
|
111
|
+
onScroll?: (pct: number) => void; // 0..1 小数,rAF 节流,隐藏期间照吐,构造/refresh 后补发
|
|
93
112
|
}
|
|
94
113
|
```
|
|
95
114
|
|
package/dist/index.cjs
CHANGED
|
@@ -79,6 +79,8 @@ const RADIUS = 21;
|
|
|
79
79
|
const FALLBACK_PATH_LEN = 100;
|
|
80
80
|
const DEFAULT_LABEL_BOTTOM = "滚动到底部(悬停或按住绕圈可切换为返回顶部)";
|
|
81
81
|
const DEFAULT_LABEL_TOP = "返回顶部(悬停或按住绕圈可切换为滚动到底部)";
|
|
82
|
+
const STATIC_LABEL_BOTTOM = "滚动到底部";
|
|
83
|
+
const STATIC_LABEL_TOP = "返回顶部";
|
|
82
84
|
function easeInOutCubic(p) {
|
|
83
85
|
return p < .5 ? 4 * p * p * p : 1 - (-2 * p + 2) ** 3 / 2;
|
|
84
86
|
}
|
|
@@ -102,13 +104,17 @@ var ScrollFab = class {
|
|
|
102
104
|
labelTop;
|
|
103
105
|
onModeChange;
|
|
104
106
|
onLenisReady;
|
|
107
|
+
onScroll;
|
|
108
|
+
modes;
|
|
109
|
+
single;
|
|
110
|
+
threshold;
|
|
105
111
|
/** 外部注入:宿主实例,组件永不销毁 */
|
|
106
112
|
externalLenis = null;
|
|
107
113
|
/** 自建配置:null = 不走 lenis;true 形态也归一成对象 */
|
|
108
114
|
ownLenisConfig = null;
|
|
109
115
|
ownLenis = null;
|
|
110
116
|
ownLenisPromise = null;
|
|
111
|
-
mode
|
|
117
|
+
mode;
|
|
112
118
|
progress = 0;
|
|
113
119
|
hovering = false;
|
|
114
120
|
pressing = false;
|
|
@@ -128,6 +134,8 @@ var ScrollFab = class {
|
|
|
128
134
|
ro = null;
|
|
129
135
|
actionSeq = 0;
|
|
130
136
|
destroyed = false;
|
|
137
|
+
emitRaf = null;
|
|
138
|
+
lastPct = -1;
|
|
131
139
|
constructor(options = {}) {
|
|
132
140
|
this.targetEl = options.target ?? null;
|
|
133
141
|
this.shape = options.shape ?? null;
|
|
@@ -135,10 +143,15 @@ var ScrollFab = class {
|
|
|
135
143
|
this.decayMs = Math.max(100, options.decayDuration ?? 300);
|
|
136
144
|
this.cancelPx = Math.max(4, options.cancelThreshold ?? 10);
|
|
137
145
|
this.animate = options.animate ?? true;
|
|
138
|
-
this.
|
|
139
|
-
this.labelTop = options.ariaLabelToTop ?? DEFAULT_LABEL_TOP;
|
|
146
|
+
this.threshold = Math.max(0, options.showThreshold ?? 0);
|
|
140
147
|
this.onModeChange = options.onModeChange;
|
|
141
148
|
this.onLenisReady = options.onLenisReady;
|
|
149
|
+
this.onScroll = options.onScroll;
|
|
150
|
+
this.modes = this.normalizeModes(options.modes);
|
|
151
|
+
this.single = this.modes.length === 1;
|
|
152
|
+
this.mode = this.modes[0];
|
|
153
|
+
this.labelBottom = options.ariaLabelToBottom ?? (this.single ? STATIC_LABEL_BOTTOM : DEFAULT_LABEL_BOTTOM);
|
|
154
|
+
this.labelTop = options.ariaLabelToTop ?? (this.single ? STATIC_LABEL_TOP : DEFAULT_LABEL_TOP);
|
|
142
155
|
if (isLenisInstance(options.lenis)) this.externalLenis = options.lenis;
|
|
143
156
|
else if (options.lenis != null && options.lenis !== false) {
|
|
144
157
|
const cfg = options.lenis === true ? {} : options.lenis;
|
|
@@ -152,7 +165,7 @@ var ScrollFab = class {
|
|
|
152
165
|
this.el.className = `qsf-root${options.className ? ` ${options.className}` : ""}`;
|
|
153
166
|
this.el.dataset.mode = this.mode;
|
|
154
167
|
if (this.shape) this.el.dataset.shape = "on";
|
|
155
|
-
this.el.setAttribute("aria-label", this.labelBottom);
|
|
168
|
+
this.el.setAttribute("aria-label", this.mode === "to-bottom" ? this.labelBottom : this.labelTop);
|
|
156
169
|
this.el.style.setProperty("--qsf-size", `${Math.max(44, options.size ?? 48)}px`);
|
|
157
170
|
if (options.zIndex != null) this.el.style.setProperty("--qsf-z", String(options.zIndex));
|
|
158
171
|
if (options.position?.right != null) this.el.style.setProperty("--qsf-right", `${options.position.right}px`);
|
|
@@ -197,12 +210,14 @@ var ScrollFab = class {
|
|
|
197
210
|
else this.icon.innerHTML = ICON_CHEVRON_DOWN;
|
|
198
211
|
this.el.append(svg, this.icon);
|
|
199
212
|
document.body.append(this.el);
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
213
|
+
if (!this.single) {
|
|
214
|
+
this.el.addEventListener("pointerenter", this.onEnter);
|
|
215
|
+
this.el.addEventListener("pointerleave", this.onLeave);
|
|
216
|
+
this.el.addEventListener("pointerdown", this.onDown);
|
|
217
|
+
this.el.addEventListener("pointermove", this.onMove);
|
|
218
|
+
this.el.addEventListener("pointerup", this.onUp);
|
|
219
|
+
this.el.addEventListener("pointercancel", this.onCancel);
|
|
220
|
+
}
|
|
206
221
|
this.el.addEventListener("keydown", this.onKey);
|
|
207
222
|
this.el.addEventListener("click", this.onClick);
|
|
208
223
|
window.addEventListener("resize", this.onRecheck);
|
|
@@ -212,14 +227,17 @@ var ScrollFab = class {
|
|
|
212
227
|
this.ro.observe(this.targetEl ?? document.documentElement);
|
|
213
228
|
}
|
|
214
229
|
this.syncVisible();
|
|
230
|
+
this.emitScroll();
|
|
215
231
|
}
|
|
216
232
|
/** 当前模式 */
|
|
217
233
|
get currentMode() {
|
|
218
234
|
return this.mode;
|
|
219
235
|
}
|
|
220
|
-
/** 内容懒加载等外部高度变化后可显式触发重估(滚动/resize
|
|
236
|
+
/** 内容懒加载等外部高度变化后可显式触发重估(滚动/resize 时也会自动重估),并强制补发一次进度 */
|
|
221
237
|
refresh() {
|
|
222
|
-
this.
|
|
238
|
+
this.syncVisible();
|
|
239
|
+
this.lastPct = -1;
|
|
240
|
+
this.emitScroll();
|
|
223
241
|
}
|
|
224
242
|
scrollToBottom() {
|
|
225
243
|
this.animateScroll("bottom");
|
|
@@ -229,12 +247,14 @@ var ScrollFab = class {
|
|
|
229
247
|
}
|
|
230
248
|
destroy() {
|
|
231
249
|
this.destroyed = true;
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
250
|
+
if (!this.single) {
|
|
251
|
+
this.el.removeEventListener("pointerenter", this.onEnter);
|
|
252
|
+
this.el.removeEventListener("pointerleave", this.onLeave);
|
|
253
|
+
this.el.removeEventListener("pointerdown", this.onDown);
|
|
254
|
+
this.el.removeEventListener("pointermove", this.onMove);
|
|
255
|
+
this.el.removeEventListener("pointerup", this.onUp);
|
|
256
|
+
this.el.removeEventListener("pointercancel", this.onCancel);
|
|
257
|
+
}
|
|
238
258
|
this.el.removeEventListener("keydown", this.onKey);
|
|
239
259
|
this.el.removeEventListener("click", this.onClick);
|
|
240
260
|
window.removeEventListener("resize", this.onRecheck);
|
|
@@ -246,10 +266,20 @@ var ScrollFab = class {
|
|
|
246
266
|
this.ownLenis = null;
|
|
247
267
|
if (this.ringRaf != null) cancelAnimationFrame(this.ringRaf);
|
|
248
268
|
this.ringRaf = null;
|
|
269
|
+
if (this.emitRaf != null) cancelAnimationFrame(this.emitRaf);
|
|
270
|
+
this.emitRaf = null;
|
|
249
271
|
if (this.flashTimer != null) clearTimeout(this.flashTimer);
|
|
250
272
|
if (this.tapResetTimer != null) clearTimeout(this.tapResetTimer);
|
|
251
273
|
this.el.remove();
|
|
252
274
|
}
|
|
275
|
+
/** 规范化 modes:缺省双模式;去重、过滤非法值;结果为空直接抛错 */
|
|
276
|
+
normalizeModes(input) {
|
|
277
|
+
if (!input) return ["to-bottom", "to-top"];
|
|
278
|
+
const out = [];
|
|
279
|
+
for (const m of input) if ((m === "to-bottom" || m === "to-top") && !out.includes(m)) out.push(m);
|
|
280
|
+
if (out.length === 0) throw new TypeError("ScrollFab: modes 至少需要一个有效模式('to-bottom' | 'to-top')");
|
|
281
|
+
return out;
|
|
282
|
+
}
|
|
253
283
|
makeCircle(cls) {
|
|
254
284
|
const c = document.createElementNS(SVG_NS, "circle");
|
|
255
285
|
c.setAttribute("class", cls);
|
|
@@ -323,9 +353,12 @@ var ScrollFab = class {
|
|
|
323
353
|
this.doAction();
|
|
324
354
|
};
|
|
325
355
|
onKey = (e) => {
|
|
326
|
-
if (e.repeat || e.key !== "
|
|
356
|
+
if (e.repeat || e.key !== "ArrowUp" && e.key !== "ArrowDown") return;
|
|
357
|
+
if (this.single) return;
|
|
358
|
+
const want = e.key === "ArrowUp" ? "to-top" : "to-bottom";
|
|
359
|
+
if (want === this.mode) return;
|
|
327
360
|
e.preventDefault();
|
|
328
|
-
this.setMode(
|
|
361
|
+
this.setMode(want);
|
|
329
362
|
};
|
|
330
363
|
holding() {
|
|
331
364
|
return this.hovering || this.pressing;
|
|
@@ -588,9 +621,26 @@ var ScrollFab = class {
|
|
|
588
621
|
}
|
|
589
622
|
onRecheck = () => {
|
|
590
623
|
this.syncVisible();
|
|
624
|
+
this.scheduleScroll();
|
|
591
625
|
};
|
|
592
626
|
syncVisible() {
|
|
593
|
-
this.el.hidden = this.maxScroll() <= 1;
|
|
627
|
+
this.el.hidden = this.maxScroll() <= 1 || this.scrollPos() < this.threshold;
|
|
628
|
+
}
|
|
629
|
+
/** rAF 节流:一帧内多次重估只吐一次;按钮隐藏期间照常调度 */
|
|
630
|
+
scheduleScroll() {
|
|
631
|
+
if (this.emitRaf != null || this.destroyed) return;
|
|
632
|
+
this.emitRaf = requestAnimationFrame(() => {
|
|
633
|
+
this.emitRaf = null;
|
|
634
|
+
this.emitScroll();
|
|
635
|
+
});
|
|
636
|
+
}
|
|
637
|
+
emitScroll() {
|
|
638
|
+
if (this.destroyed) return;
|
|
639
|
+
const max = this.maxScroll();
|
|
640
|
+
const pct = max <= 0 ? 0 : Math.min(1, Math.max(0, this.scrollPos() / max));
|
|
641
|
+
if (pct === this.lastPct) return;
|
|
642
|
+
this.lastPct = pct;
|
|
643
|
+
this.onScroll?.(pct);
|
|
594
644
|
}
|
|
595
645
|
};
|
|
596
646
|
//#endregion
|
package/dist/index.d.cts
CHANGED
|
@@ -86,6 +86,18 @@ interface ScrollFabOptions {
|
|
|
86
86
|
ariaLabelToTop?: string;
|
|
87
87
|
/** 模式翻转回调 */
|
|
88
88
|
onModeChange?: (mode: ScrollFabMode) => void;
|
|
89
|
+
/**
|
|
90
|
+
* 启用的模式集合(按数组顺序);缺省 ["to-bottom", "to-top"]。
|
|
91
|
+
* 初始模式取首项;只传一个即为单模式(无绕环翻转、无描边、静态 aria 文案);空数组/全非法抛 TypeError。
|
|
92
|
+
*/
|
|
93
|
+
modes?: ScrollFabMode[];
|
|
94
|
+
/** 显示所需的最小已滚动距离 px:scrollPos < 该值时按钮隐藏(默认 0,行为不变);负数按 0 */
|
|
95
|
+
showThreshold?: number;
|
|
96
|
+
/**
|
|
97
|
+
* 滚动进度回调:0..1 小数(maxScroll 为 0 时吐 0),scroll/resize/程序滚动时 rAF 节流触发,
|
|
98
|
+
* 按钮隐藏期间照常吐,构造完成与 refresh() 后各补发一次。
|
|
99
|
+
*/
|
|
100
|
+
onScroll?: (pct: number) => void;
|
|
89
101
|
}
|
|
90
102
|
//#endregion
|
|
91
103
|
//#region src/scroll-fab.d.ts
|
|
@@ -106,6 +118,10 @@ declare class ScrollFab {
|
|
|
106
118
|
private readonly labelTop;
|
|
107
119
|
private readonly onModeChange?;
|
|
108
120
|
private readonly onLenisReady?;
|
|
121
|
+
private readonly onScroll?;
|
|
122
|
+
private readonly modes;
|
|
123
|
+
private readonly single;
|
|
124
|
+
private readonly threshold;
|
|
109
125
|
/** 外部注入:宿主实例,组件永不销毁 */
|
|
110
126
|
private externalLenis;
|
|
111
127
|
/** 自建配置:null = 不走 lenis;true 形态也归一成对象 */
|
|
@@ -132,14 +148,18 @@ declare class ScrollFab {
|
|
|
132
148
|
private ro;
|
|
133
149
|
private actionSeq;
|
|
134
150
|
private destroyed;
|
|
151
|
+
private emitRaf;
|
|
152
|
+
private lastPct;
|
|
135
153
|
constructor(options?: ScrollFabOptions);
|
|
136
154
|
/** 当前模式 */
|
|
137
155
|
get currentMode(): ScrollFabMode;
|
|
138
|
-
/** 内容懒加载等外部高度变化后可显式触发重估(滚动/resize
|
|
156
|
+
/** 内容懒加载等外部高度变化后可显式触发重估(滚动/resize 时也会自动重估),并强制补发一次进度 */
|
|
139
157
|
refresh(): void;
|
|
140
158
|
scrollToBottom(): void;
|
|
141
159
|
scrollToTop(): void;
|
|
142
160
|
destroy(): void;
|
|
161
|
+
/** 规范化 modes:缺省双模式;去重、过滤非法值;结果为空直接抛错 */
|
|
162
|
+
private normalizeModes;
|
|
143
163
|
private makeCircle;
|
|
144
164
|
private makePath;
|
|
145
165
|
private onEnter;
|
|
@@ -190,6 +210,9 @@ declare class ScrollFab {
|
|
|
190
210
|
private setScrollPos;
|
|
191
211
|
private onRecheck;
|
|
192
212
|
private syncVisible;
|
|
213
|
+
/** rAF 节流:一帧内多次重估只吐一次;按钮隐藏期间照常调度 */
|
|
214
|
+
private scheduleScroll;
|
|
215
|
+
private emitScroll;
|
|
193
216
|
}
|
|
194
217
|
//#endregion
|
|
195
218
|
export { type LenisLike, ScrollFab, type ScrollFabLenisConfig, type ScrollFabMode, type ScrollFabOptions, type ScrollFabPosition, type ScrollFabShape };
|
package/dist/index.d.cts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.cts","names":[],"sources":["../src/types.ts","../src/scroll-fab.ts"],"mappings":";KAAY;;UAGK;EACf;EACA;EACA;EACA;;;UAIe;EACf,WACE,0BAA0B,aAC1B,UAAU;EAEZ,OAAO;EACP;EACA;EACA;EACA,MAAM,eAAe,cAAc;EACnC,OAAO,eAAe,cAAc;EACpC;;;UAIe;EACf,UAAU,cAAc;EACxB,UAAU;EACV;EACA,UAAU;;EAEV;EACA;EACA;EACA;GACC;;;;;;;;UASc;;EAEf;;EAEA;;EAEA;;EAEA;;UAGe;;EAEf,SAAS;;EAET;;EAEA;;EAEA;;EAEA;;EAEA,mBAAmB;;EAEnB,WAAW;;EAEX;;EAEA;;EAEA;;;;;;;;EAQA,kBAAkB,uBAAuB;;EAEzC,QAAQ;;EAER,gBAAgB,OAAO;;EAEvB;;EAEA;;EAEA,gBAAgB,MAAM;;;;
|
|
1
|
+
{"version":3,"file":"index.d.cts","names":[],"sources":["../src/types.ts","../src/scroll-fab.ts"],"mappings":";KAAY;;UAGK;EACf;EACA;EACA;EACA;;;UAIe;EACf,WACE,0BAA0B,aAC1B,UAAU;EAEZ,OAAO;EACP;EACA;EACA;EACA,MAAM,eAAe,cAAc;EACnC,OAAO,eAAe,cAAc;EACpC;;;UAIe;EACf,UAAU,cAAc;EACxB,UAAU;EACV;EACA,UAAU;;EAEV;EACA;EACA;EACA;GACC;;;;;;;;UASc;;EAEf;;EAEA;;EAEA;;EAEA;;UAGe;;EAEf,SAAS;;EAET;;EAEA;;EAEA;;EAEA;;EAEA,mBAAmB;;EAEnB,WAAW;;EAEX;;EAEA;;EAEA;;;;;;;;EAQA,kBAAkB,uBAAuB;;EAEzC,QAAQ;;EAER,gBAAgB,OAAO;;EAEvB;;EAEA;;EAEA,gBAAgB,MAAM;;;;;EAKtB,QAAQ;;EAER;;;;;EAKA,YAAY;;;;cC3ED;WACF,IAAI;UAEL;UACA;UACA;mBACS;UACT;mBAES;mBACA;mBACA;mBACA;mBACA;mBACA;mBACA;mBACA;mBACA;mBACA;mBACA;mBACA;mBACA;mBACA;;UAGT;;UAEA;UACA;UACA;UAEA;UACA;UACA;UACA;UACA;UACA;UACA;UACA;UACA;UACA;UACA;UACA;UACA;UACA;UACA;UACA;UACA;UACA;UACA;UACA;UACA;UACA;EAER,YAAY,UAAS;;MA2HjB,eAAe;;EAKnB;EAMA;EAIA;EAIA;;UA8BQ;UAcA;UASA;UAcA;UAMA;UAMA;UAeA;UAUA;UAeA;UAOA;UAUA;UAYA;UAIA;UAMA;;UAoBA;UAYA;UAOA;UAUA;UAIA;;UAyBA;;;;;;UA6BA;;UA6BA;UAqBA;;UAQA;UAMA;UASA;UAmCA;UAMA;UAIA;;UAKA;UAMA;;UAOA;UAMA;UAMA;UAeA;UAKA;UAIA;UAKA;UAKA;UAKA;;UAKA;UAQA"}
|
package/dist/index.d.mts
CHANGED
|
@@ -86,6 +86,18 @@ interface ScrollFabOptions {
|
|
|
86
86
|
ariaLabelToTop?: string;
|
|
87
87
|
/** 模式翻转回调 */
|
|
88
88
|
onModeChange?: (mode: ScrollFabMode) => void;
|
|
89
|
+
/**
|
|
90
|
+
* 启用的模式集合(按数组顺序);缺省 ["to-bottom", "to-top"]。
|
|
91
|
+
* 初始模式取首项;只传一个即为单模式(无绕环翻转、无描边、静态 aria 文案);空数组/全非法抛 TypeError。
|
|
92
|
+
*/
|
|
93
|
+
modes?: ScrollFabMode[];
|
|
94
|
+
/** 显示所需的最小已滚动距离 px:scrollPos < 该值时按钮隐藏(默认 0,行为不变);负数按 0 */
|
|
95
|
+
showThreshold?: number;
|
|
96
|
+
/**
|
|
97
|
+
* 滚动进度回调:0..1 小数(maxScroll 为 0 时吐 0),scroll/resize/程序滚动时 rAF 节流触发,
|
|
98
|
+
* 按钮隐藏期间照常吐,构造完成与 refresh() 后各补发一次。
|
|
99
|
+
*/
|
|
100
|
+
onScroll?: (pct: number) => void;
|
|
89
101
|
}
|
|
90
102
|
//#endregion
|
|
91
103
|
//#region src/scroll-fab.d.ts
|
|
@@ -106,6 +118,10 @@ declare class ScrollFab {
|
|
|
106
118
|
private readonly labelTop;
|
|
107
119
|
private readonly onModeChange?;
|
|
108
120
|
private readonly onLenisReady?;
|
|
121
|
+
private readonly onScroll?;
|
|
122
|
+
private readonly modes;
|
|
123
|
+
private readonly single;
|
|
124
|
+
private readonly threshold;
|
|
109
125
|
/** 外部注入:宿主实例,组件永不销毁 */
|
|
110
126
|
private externalLenis;
|
|
111
127
|
/** 自建配置:null = 不走 lenis;true 形态也归一成对象 */
|
|
@@ -132,14 +148,18 @@ declare class ScrollFab {
|
|
|
132
148
|
private ro;
|
|
133
149
|
private actionSeq;
|
|
134
150
|
private destroyed;
|
|
151
|
+
private emitRaf;
|
|
152
|
+
private lastPct;
|
|
135
153
|
constructor(options?: ScrollFabOptions);
|
|
136
154
|
/** 当前模式 */
|
|
137
155
|
get currentMode(): ScrollFabMode;
|
|
138
|
-
/** 内容懒加载等外部高度变化后可显式触发重估(滚动/resize
|
|
156
|
+
/** 内容懒加载等外部高度变化后可显式触发重估(滚动/resize 时也会自动重估),并强制补发一次进度 */
|
|
139
157
|
refresh(): void;
|
|
140
158
|
scrollToBottom(): void;
|
|
141
159
|
scrollToTop(): void;
|
|
142
160
|
destroy(): void;
|
|
161
|
+
/** 规范化 modes:缺省双模式;去重、过滤非法值;结果为空直接抛错 */
|
|
162
|
+
private normalizeModes;
|
|
143
163
|
private makeCircle;
|
|
144
164
|
private makePath;
|
|
145
165
|
private onEnter;
|
|
@@ -190,6 +210,9 @@ declare class ScrollFab {
|
|
|
190
210
|
private setScrollPos;
|
|
191
211
|
private onRecheck;
|
|
192
212
|
private syncVisible;
|
|
213
|
+
/** rAF 节流:一帧内多次重估只吐一次;按钮隐藏期间照常调度 */
|
|
214
|
+
private scheduleScroll;
|
|
215
|
+
private emitScroll;
|
|
193
216
|
}
|
|
194
217
|
//#endregion
|
|
195
218
|
export { type LenisLike, ScrollFab, type ScrollFabLenisConfig, type ScrollFabMode, type ScrollFabOptions, type ScrollFabPosition, type ScrollFabShape };
|
package/dist/index.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.mts","names":[],"sources":["../src/types.ts","../src/scroll-fab.ts"],"mappings":";KAAY;;UAGK;EACf;EACA;EACA;EACA;;;UAIe;EACf,WACE,0BAA0B,aAC1B,UAAU;EAEZ,OAAO;EACP;EACA;EACA;EACA,MAAM,eAAe,cAAc;EACnC,OAAO,eAAe,cAAc;EACpC;;;UAIe;EACf,UAAU,cAAc;EACxB,UAAU;EACV;EACA,UAAU;;EAEV;EACA;EACA;EACA;GACC;;;;;;;;UASc;;EAEf;;EAEA;;EAEA;;EAEA;;UAGe;;EAEf,SAAS;;EAET;;EAEA;;EAEA;;EAEA;;EAEA,mBAAmB;;EAEnB,WAAW;;EAEX;;EAEA;;EAEA;;;;;;;;EAQA,kBAAkB,uBAAuB;;EAEzC,QAAQ;;EAER,gBAAgB,OAAO;;EAEvB;;EAEA;;EAEA,gBAAgB,MAAM;;;;
|
|
1
|
+
{"version":3,"file":"index.d.mts","names":[],"sources":["../src/types.ts","../src/scroll-fab.ts"],"mappings":";KAAY;;UAGK;EACf;EACA;EACA;EACA;;;UAIe;EACf,WACE,0BAA0B,aAC1B,UAAU;EAEZ,OAAO;EACP;EACA;EACA;EACA,MAAM,eAAe,cAAc;EACnC,OAAO,eAAe,cAAc;EACpC;;;UAIe;EACf,UAAU,cAAc;EACxB,UAAU;EACV;EACA,UAAU;;EAEV;EACA;EACA;EACA;GACC;;;;;;;;UASc;;EAEf;;EAEA;;EAEA;;EAEA;;UAGe;;EAEf,SAAS;;EAET;;EAEA;;EAEA;;EAEA;;EAEA,mBAAmB;;EAEnB,WAAW;;EAEX;;EAEA;;EAEA;;;;;;;;EAQA,kBAAkB,uBAAuB;;EAEzC,QAAQ;;EAER,gBAAgB,OAAO;;EAEvB;;EAEA;;EAEA,gBAAgB,MAAM;;;;;EAKtB,QAAQ;;EAER;;;;;EAKA,YAAY;;;;cC3ED;WACF,IAAI;UAEL;UACA;UACA;mBACS;UACT;mBAES;mBACA;mBACA;mBACA;mBACA;mBACA;mBACA;mBACA;mBACA;mBACA;mBACA;mBACA;mBACA;mBACA;;UAGT;;UAEA;UACA;UACA;UAEA;UACA;UACA;UACA;UACA;UACA;UACA;UACA;UACA;UACA;UACA;UACA;UACA;UACA;UACA;UACA;UACA;UACA;UACA;UACA;UACA;UACA;EAER,YAAY,UAAS;;MA2HjB,eAAe;;EAKnB;EAMA;EAIA;EAIA;;UA8BQ;UAcA;UASA;UAcA;UAMA;UAMA;UAeA;UAUA;UAeA;UAOA;UAUA;UAYA;UAIA;UAMA;;UAoBA;UAYA;UAOA;UAUA;UAIA;;UAyBA;;;;;;UA6BA;;UA6BA;UAqBA;;UAQA;UAMA;UASA;UAmCA;UAMA;UAIA;;UAKA;UAMA;;UAOA;UAMA;UAMA;UAeA;UAKA;UAIA;UAKA;UAKA;UAKA;;UAKA;UAQA"}
|
package/dist/index.mjs
CHANGED
|
@@ -78,6 +78,8 @@ const RADIUS = 21;
|
|
|
78
78
|
const FALLBACK_PATH_LEN = 100;
|
|
79
79
|
const DEFAULT_LABEL_BOTTOM = "滚动到底部(悬停或按住绕圈可切换为返回顶部)";
|
|
80
80
|
const DEFAULT_LABEL_TOP = "返回顶部(悬停或按住绕圈可切换为滚动到底部)";
|
|
81
|
+
const STATIC_LABEL_BOTTOM = "滚动到底部";
|
|
82
|
+
const STATIC_LABEL_TOP = "返回顶部";
|
|
81
83
|
function easeInOutCubic(p) {
|
|
82
84
|
return p < .5 ? 4 * p * p * p : 1 - (-2 * p + 2) ** 3 / 2;
|
|
83
85
|
}
|
|
@@ -101,13 +103,17 @@ var ScrollFab = class {
|
|
|
101
103
|
labelTop;
|
|
102
104
|
onModeChange;
|
|
103
105
|
onLenisReady;
|
|
106
|
+
onScroll;
|
|
107
|
+
modes;
|
|
108
|
+
single;
|
|
109
|
+
threshold;
|
|
104
110
|
/** 外部注入:宿主实例,组件永不销毁 */
|
|
105
111
|
externalLenis = null;
|
|
106
112
|
/** 自建配置:null = 不走 lenis;true 形态也归一成对象 */
|
|
107
113
|
ownLenisConfig = null;
|
|
108
114
|
ownLenis = null;
|
|
109
115
|
ownLenisPromise = null;
|
|
110
|
-
mode
|
|
116
|
+
mode;
|
|
111
117
|
progress = 0;
|
|
112
118
|
hovering = false;
|
|
113
119
|
pressing = false;
|
|
@@ -127,6 +133,8 @@ var ScrollFab = class {
|
|
|
127
133
|
ro = null;
|
|
128
134
|
actionSeq = 0;
|
|
129
135
|
destroyed = false;
|
|
136
|
+
emitRaf = null;
|
|
137
|
+
lastPct = -1;
|
|
130
138
|
constructor(options = {}) {
|
|
131
139
|
this.targetEl = options.target ?? null;
|
|
132
140
|
this.shape = options.shape ?? null;
|
|
@@ -134,10 +142,15 @@ var ScrollFab = class {
|
|
|
134
142
|
this.decayMs = Math.max(100, options.decayDuration ?? 300);
|
|
135
143
|
this.cancelPx = Math.max(4, options.cancelThreshold ?? 10);
|
|
136
144
|
this.animate = options.animate ?? true;
|
|
137
|
-
this.
|
|
138
|
-
this.labelTop = options.ariaLabelToTop ?? DEFAULT_LABEL_TOP;
|
|
145
|
+
this.threshold = Math.max(0, options.showThreshold ?? 0);
|
|
139
146
|
this.onModeChange = options.onModeChange;
|
|
140
147
|
this.onLenisReady = options.onLenisReady;
|
|
148
|
+
this.onScroll = options.onScroll;
|
|
149
|
+
this.modes = this.normalizeModes(options.modes);
|
|
150
|
+
this.single = this.modes.length === 1;
|
|
151
|
+
this.mode = this.modes[0];
|
|
152
|
+
this.labelBottom = options.ariaLabelToBottom ?? (this.single ? STATIC_LABEL_BOTTOM : DEFAULT_LABEL_BOTTOM);
|
|
153
|
+
this.labelTop = options.ariaLabelToTop ?? (this.single ? STATIC_LABEL_TOP : DEFAULT_LABEL_TOP);
|
|
141
154
|
if (isLenisInstance(options.lenis)) this.externalLenis = options.lenis;
|
|
142
155
|
else if (options.lenis != null && options.lenis !== false) {
|
|
143
156
|
const cfg = options.lenis === true ? {} : options.lenis;
|
|
@@ -151,7 +164,7 @@ var ScrollFab = class {
|
|
|
151
164
|
this.el.className = `qsf-root${options.className ? ` ${options.className}` : ""}`;
|
|
152
165
|
this.el.dataset.mode = this.mode;
|
|
153
166
|
if (this.shape) this.el.dataset.shape = "on";
|
|
154
|
-
this.el.setAttribute("aria-label", this.labelBottom);
|
|
167
|
+
this.el.setAttribute("aria-label", this.mode === "to-bottom" ? this.labelBottom : this.labelTop);
|
|
155
168
|
this.el.style.setProperty("--qsf-size", `${Math.max(44, options.size ?? 48)}px`);
|
|
156
169
|
if (options.zIndex != null) this.el.style.setProperty("--qsf-z", String(options.zIndex));
|
|
157
170
|
if (options.position?.right != null) this.el.style.setProperty("--qsf-right", `${options.position.right}px`);
|
|
@@ -196,12 +209,14 @@ var ScrollFab = class {
|
|
|
196
209
|
else this.icon.innerHTML = ICON_CHEVRON_DOWN;
|
|
197
210
|
this.el.append(svg, this.icon);
|
|
198
211
|
document.body.append(this.el);
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
212
|
+
if (!this.single) {
|
|
213
|
+
this.el.addEventListener("pointerenter", this.onEnter);
|
|
214
|
+
this.el.addEventListener("pointerleave", this.onLeave);
|
|
215
|
+
this.el.addEventListener("pointerdown", this.onDown);
|
|
216
|
+
this.el.addEventListener("pointermove", this.onMove);
|
|
217
|
+
this.el.addEventListener("pointerup", this.onUp);
|
|
218
|
+
this.el.addEventListener("pointercancel", this.onCancel);
|
|
219
|
+
}
|
|
205
220
|
this.el.addEventListener("keydown", this.onKey);
|
|
206
221
|
this.el.addEventListener("click", this.onClick);
|
|
207
222
|
window.addEventListener("resize", this.onRecheck);
|
|
@@ -211,14 +226,17 @@ var ScrollFab = class {
|
|
|
211
226
|
this.ro.observe(this.targetEl ?? document.documentElement);
|
|
212
227
|
}
|
|
213
228
|
this.syncVisible();
|
|
229
|
+
this.emitScroll();
|
|
214
230
|
}
|
|
215
231
|
/** 当前模式 */
|
|
216
232
|
get currentMode() {
|
|
217
233
|
return this.mode;
|
|
218
234
|
}
|
|
219
|
-
/** 内容懒加载等外部高度变化后可显式触发重估(滚动/resize
|
|
235
|
+
/** 内容懒加载等外部高度变化后可显式触发重估(滚动/resize 时也会自动重估),并强制补发一次进度 */
|
|
220
236
|
refresh() {
|
|
221
|
-
this.
|
|
237
|
+
this.syncVisible();
|
|
238
|
+
this.lastPct = -1;
|
|
239
|
+
this.emitScroll();
|
|
222
240
|
}
|
|
223
241
|
scrollToBottom() {
|
|
224
242
|
this.animateScroll("bottom");
|
|
@@ -228,12 +246,14 @@ var ScrollFab = class {
|
|
|
228
246
|
}
|
|
229
247
|
destroy() {
|
|
230
248
|
this.destroyed = true;
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
249
|
+
if (!this.single) {
|
|
250
|
+
this.el.removeEventListener("pointerenter", this.onEnter);
|
|
251
|
+
this.el.removeEventListener("pointerleave", this.onLeave);
|
|
252
|
+
this.el.removeEventListener("pointerdown", this.onDown);
|
|
253
|
+
this.el.removeEventListener("pointermove", this.onMove);
|
|
254
|
+
this.el.removeEventListener("pointerup", this.onUp);
|
|
255
|
+
this.el.removeEventListener("pointercancel", this.onCancel);
|
|
256
|
+
}
|
|
237
257
|
this.el.removeEventListener("keydown", this.onKey);
|
|
238
258
|
this.el.removeEventListener("click", this.onClick);
|
|
239
259
|
window.removeEventListener("resize", this.onRecheck);
|
|
@@ -245,10 +265,20 @@ var ScrollFab = class {
|
|
|
245
265
|
this.ownLenis = null;
|
|
246
266
|
if (this.ringRaf != null) cancelAnimationFrame(this.ringRaf);
|
|
247
267
|
this.ringRaf = null;
|
|
268
|
+
if (this.emitRaf != null) cancelAnimationFrame(this.emitRaf);
|
|
269
|
+
this.emitRaf = null;
|
|
248
270
|
if (this.flashTimer != null) clearTimeout(this.flashTimer);
|
|
249
271
|
if (this.tapResetTimer != null) clearTimeout(this.tapResetTimer);
|
|
250
272
|
this.el.remove();
|
|
251
273
|
}
|
|
274
|
+
/** 规范化 modes:缺省双模式;去重、过滤非法值;结果为空直接抛错 */
|
|
275
|
+
normalizeModes(input) {
|
|
276
|
+
if (!input) return ["to-bottom", "to-top"];
|
|
277
|
+
const out = [];
|
|
278
|
+
for (const m of input) if ((m === "to-bottom" || m === "to-top") && !out.includes(m)) out.push(m);
|
|
279
|
+
if (out.length === 0) throw new TypeError("ScrollFab: modes 至少需要一个有效模式('to-bottom' | 'to-top')");
|
|
280
|
+
return out;
|
|
281
|
+
}
|
|
252
282
|
makeCircle(cls) {
|
|
253
283
|
const c = document.createElementNS(SVG_NS, "circle");
|
|
254
284
|
c.setAttribute("class", cls);
|
|
@@ -322,9 +352,12 @@ var ScrollFab = class {
|
|
|
322
352
|
this.doAction();
|
|
323
353
|
};
|
|
324
354
|
onKey = (e) => {
|
|
325
|
-
if (e.repeat || e.key !== "
|
|
355
|
+
if (e.repeat || e.key !== "ArrowUp" && e.key !== "ArrowDown") return;
|
|
356
|
+
if (this.single) return;
|
|
357
|
+
const want = e.key === "ArrowUp" ? "to-top" : "to-bottom";
|
|
358
|
+
if (want === this.mode) return;
|
|
326
359
|
e.preventDefault();
|
|
327
|
-
this.setMode(
|
|
360
|
+
this.setMode(want);
|
|
328
361
|
};
|
|
329
362
|
holding() {
|
|
330
363
|
return this.hovering || this.pressing;
|
|
@@ -587,9 +620,26 @@ var ScrollFab = class {
|
|
|
587
620
|
}
|
|
588
621
|
onRecheck = () => {
|
|
589
622
|
this.syncVisible();
|
|
623
|
+
this.scheduleScroll();
|
|
590
624
|
};
|
|
591
625
|
syncVisible() {
|
|
592
|
-
this.el.hidden = this.maxScroll() <= 1;
|
|
626
|
+
this.el.hidden = this.maxScroll() <= 1 || this.scrollPos() < this.threshold;
|
|
627
|
+
}
|
|
628
|
+
/** rAF 节流:一帧内多次重估只吐一次;按钮隐藏期间照常调度 */
|
|
629
|
+
scheduleScroll() {
|
|
630
|
+
if (this.emitRaf != null || this.destroyed) return;
|
|
631
|
+
this.emitRaf = requestAnimationFrame(() => {
|
|
632
|
+
this.emitRaf = null;
|
|
633
|
+
this.emitScroll();
|
|
634
|
+
});
|
|
635
|
+
}
|
|
636
|
+
emitScroll() {
|
|
637
|
+
if (this.destroyed) return;
|
|
638
|
+
const max = this.maxScroll();
|
|
639
|
+
const pct = max <= 0 ? 0 : Math.min(1, Math.max(0, this.scrollPos() / max));
|
|
640
|
+
if (pct === this.lastPct) return;
|
|
641
|
+
this.lastPct = pct;
|
|
642
|
+
this.onScroll?.(pct);
|
|
593
643
|
}
|
|
594
644
|
};
|
|
595
645
|
//#endregion
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +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 {\n LenisLike,\n ScrollFabLenisConfig,\n ScrollFabMode,\n ScrollFabOptions,\n ScrollFabShape,\n} from \"./types\";\n\nconst SVG_NS = \"http://www.w3.org/2000/svg\";\nconst RADIUS = 21;\nconst FALLBACK_PATH_LEN = 100;\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\nfunction isLenisInstance(v: unknown): v is LenisLike {\n return (\n typeof v === \"object\" &&\n v !== null &&\n typeof (v as LenisLike).scrollTo === \"function\"\n );\n}\n\nexport class ScrollFab {\n readonly el: HTMLButtonElement;\n\n private ring: SVGGeometryElement;\n private track: SVGGeometryElement;\n private fillPath: SVGPathElement | null = null;\n private readonly icon: HTMLElement;\n private len: number;\n\n private readonly targetEl: HTMLElement | null;\n private readonly shape: ScrollFabShape | 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 private readonly onLenisReady?: (lenis: LenisLike) => void;\n\n /** 外部注入:宿主实例,组件永不销毁 */\n private externalLenis: LenisLike | null = null;\n /** 自建配置:null = 不走 lenis;true 形态也归一成对象 */\n private ownLenisConfig: ScrollFabLenisConfig | null = null;\n private ownLenis: LenisLike | null = null;\n private ownLenisPromise: Promise<LenisLike | null> | null = null;\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 lenisLoopRaf: number | null = null;\n private lenisScrolling = false;\n private lenisDest: \"bottom\" | \"top\" | 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 private actionSeq = 0;\n private destroyed = false;\n\n constructor(options: ScrollFabOptions = {}) {\n this.targetEl = options.target ?? null;\n this.shape = options.shape ?? 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 this.onLenisReady = options.onLenisReady;\n\n if (isLenisInstance(options.lenis)) {\n this.externalLenis = options.lenis;\n } else if (options.lenis != null && options.lenis !== false) {\n const cfg = options.lenis === true ? {} : options.lenis;\n // 自建实例默认不劫持宿主滚轮:只驱动按钮触发的程序滚动\n this.ownLenisConfig = { smoothWheel: false, ...cfg };\n }\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 if (this.shape) this.el.dataset.shape = \"on\";\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(SVG_NS, \"svg\");\n svg.setAttribute(\"class\", \"qsf-svg\");\n svg.setAttribute(\"viewBox\", this.shape?.viewBox ?? \"0 0 48 48\");\n svg.setAttribute(\"aria-hidden\", \"true\");\n\n if (this.shape) {\n this.fillPath = document.createElementNS(SVG_NS, \"path\");\n this.fillPath.setAttribute(\"class\", \"qsf-shape-fill\");\n this.fillPath.setAttribute(\"d\", this.shape.fill);\n this.track = this.makePath(\"qsf-track\", this.shape.outline);\n this.ring = this.makePath(\"qsf-bar\", this.shape.outline);\n if (this.shape.outlineTransform) {\n this.track.setAttribute(\"transform\", this.shape.outlineTransform);\n this.ring.setAttribute(\"transform\", this.shape.outlineTransform);\n }\n svg.append(this.fillPath, this.track, this.ring);\n } else {\n this.track = this.makeCircle(\"qsf-track\");\n this.ring = this.makeCircle(\"qsf-bar\");\n svg.append(this.track, this.ring);\n }\n\n let len: number;\n if (this.shape) {\n len = FALLBACK_PATH_LEN;\n } else {\n len = 2 * Math.PI * RADIUS;\n }\n try {\n // 浏览器对未渲染 SVG 抛错(happy-dom 返回 NaN),失败时用回退值\n const measured = this.ring.getTotalLength();\n if (Number.isFinite(measured) && measured > 0) len = measured;\n } catch {\n /* 几何/经验回退 */\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.animateScroll(\"top\");\n }\n\n destroy(): void {\n this.destroyed = true;\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 this.stopLenisLoop();\n // 只销毁自建实例;外部宿主实例交还宿主\n this.ownLenis?.destroy?.();\n this.ownLenis = null;\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(SVG_NS, \"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 private makePath(cls: string, d: string): SVGPathElement {\n const p = document.createElementNS(SVG_NS, \"path\");\n p.setAttribute(\"class\", cls);\n p.setAttribute(\"d\", d);\n return p;\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 /* ---------------- 程序滚动:Lenis 后端可插拔,缺省内置 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 const seq = ++this.actionSeq;\n this.cancelScroll();\n if (!this.animate || this.reducedMotion()) {\n this.setScrollPos(this.targetPos(dest));\n return;\n }\n if (this.externalLenis) {\n this.scrollWithLenis(this.externalLenis, dest, false);\n return;\n }\n if (this.ownLenisConfig) {\n const seq = ++this.actionSeq;\n void this.resolveOwnLenis().then((lenis) => {\n // await 期间可能已被新动作或 destroy 取代\n if (this.destroyed || seq !== this.actionSeq) return;\n if (lenis) this.scrollWithLenis(lenis, dest, true);\n else this.animateRaf(dest);\n });\n return;\n }\n this.animateRaf(dest);\n }\n\n /** 首次点击时动态 import lenis 并自建实例;未安装(或加载失败)→ null 永久回退 rAF */\n private resolveOwnLenis(): Promise<LenisLike | null> {\n if (this.ownLenis) return Promise.resolve(this.ownLenis);\n if (this.ownLenisPromise) return this.ownLenisPromise;\n this.ownLenisPromise = import(\"lenis\")\n .then((mod) => {\n const LenisCtor =\n (mod as { default?: new (opts: Record<string, unknown>) => LenisLike }).default ??\n (mod as unknown as new (opts: Record<string, unknown>) => LenisLike);\n const cfg: Record<string, unknown> = { ...this.ownLenisConfig };\n // target 容器映射为 Lenis wrapper;window 形态留空(Lenis 默认即 window)\n if (this.targetEl) cfg.wrapper = this.targetEl;\n this.ownLenis = new LenisCtor(cfg);\n this.onLenisReady?.(this.ownLenis);\n return this.ownLenis;\n })\n .catch(() => {\n // lenis 未安装/不可用:放弃 lenis 路线,避免每次点击重复尝试\n this.ownLenisConfig = null;\n this.ownLenisPromise = null;\n return null;\n });\n return this.ownLenisPromise;\n }\n\n /**\n * 用 Lenis 执行程序滚动。\n * 自建实例(own):组件自跑 raf loop(Lenis 需外部驱动 raf),保留 wheel/touch/keydown 打断与到底追击;\n * 外部实例:宿主自带 raf loop 与手势处理,组件只发 scrollTo,不追击不打断。\n */\n private scrollWithLenis(\n lenis: LenisLike,\n dest: \"bottom\" | \"top\",\n own: boolean,\n ): void {\n const immediate = !this.animate || this.reducedMotion();\n const target = this.targetPos(dest);\n this.lenisDest = dest;\n if (own) {\n // 上次打断可能调过 stop,发起前复位\n (lenis as LenisLike & { start?: () => void }).start?.();\n lenis.scrollTo(target, {\n immediate,\n onComplete: () => this.endLenisScroll(),\n });\n if (!immediate) {\n this.lenisScrolling = true;\n this.attachInterrupt();\n this.startLenisLoop(lenis, dest);\n } else {\n this.endLenisScroll();\n }\n } else {\n lenis.scrollTo(target, { immediate });\n this.lenisDest = null;\n }\n }\n\n /** 自建实例的 raf 驱动 + 懒加载到底追击(对齐内置 rAF 语义) */\n private startLenisLoop(lenis: LenisLike, dest: \"bottom\" | \"top\"): void {\n let lastEnd = this.targetPos(dest);\n const loop = (time: number): void => {\n if (!this.lenisScrolling) return;\n if (dest === \"bottom\") {\n const live = this.targetPos(\"bottom\");\n if (Math.abs(live - lastEnd) > 2) {\n // 内容变长:追击新底端(force 覆盖当前程序动画)\n lastEnd = live;\n lenis.scrollTo(live, {\n force: true,\n onComplete: () => this.endLenisScroll(),\n });\n }\n }\n lenis.raf?.(time);\n this.lenisLoopRaf = requestAnimationFrame(loop);\n };\n this.lenisLoopRaf = requestAnimationFrame(loop);\n }\n\n private stopLenisLoop(): void {\n if (this.lenisLoopRaf != null) {\n cancelAnimationFrame(this.lenisLoopRaf);\n this.lenisLoopRaf = null;\n }\n }\n\n /** 打断自建 Lenis 程序滚动:stop 冻结动画(下次发起前 start 复位) */\n private onLenisInterrupt = (): void => {\n if (!this.lenisScrolling) return;\n (this.ownLenis as LenisLike & { stop?: () => void } | null)?.stop?.();\n this.endLenisScroll();\n };\n\n private endLenisScroll(): void {\n this.lenisScrolling = false;\n this.lenisDest = null;\n this.stopLenisLoop();\n this.detachInterrupt();\n }\n\n /* ---------------- 内置 rAF:缓动 + 打断 + 懒加载追击 ---------------- */\n\n private animateRaf(dest: \"bottom\" | \"top\"): void {\n this.attachRafInterrupt();\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.detachRafInterrupt();\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 /** 自建 Lenis 路线的打断监听 */\n private attachInterrupt(): void {\n const src = this.interruptSource();\n for (const ev of [\"wheel\", \"touchstart\", \"keydown\"])\n src.addEventListener(ev, this.onLenisInterrupt, { 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.onLenisInterrupt);\n }\n\n /** 内置 rAF 路线的打断监听 */\n private attachRafInterrupt(): 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 detachRafInterrupt(): 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 if (this.lenisScrolling) this.endLenisScroll();\n const src = this.interruptSource();\n for (const ev of [\"wheel\", \"touchstart\", \"keydown\"]) {\n src.removeEventListener(ev, this.onInterrupt);\n src.removeEventListener(ev, this.onLenisInterrupt);\n }\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 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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"}
|
|
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 {\n LenisLike,\n ScrollFabLenisConfig,\n ScrollFabMode,\n ScrollFabOptions,\n ScrollFabShape,\n} from \"./types\";\n\nconst SVG_NS = \"http://www.w3.org/2000/svg\";\nconst RADIUS = 21;\nconst FALLBACK_PATH_LEN = 100;\nconst DEFAULT_LABEL_BOTTOM = \"滚动到底部(悬停或按住绕圈可切换为返回顶部)\";\nconst DEFAULT_LABEL_TOP = \"返回顶部(悬停或按住绕圈可切换为滚动到底部)\";\nconst STATIC_LABEL_BOTTOM = \"滚动到底部\";\nconst STATIC_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\nfunction isLenisInstance(v: unknown): v is LenisLike {\n return (\n typeof v === \"object\" &&\n v !== null &&\n typeof (v as LenisLike).scrollTo === \"function\"\n );\n}\n\nexport class ScrollFab {\n readonly el: HTMLButtonElement;\n\n private ring: SVGGeometryElement;\n private track: SVGGeometryElement;\n private fillPath: SVGPathElement | null = null;\n private readonly icon: HTMLElement;\n private len: number;\n\n private readonly targetEl: HTMLElement | null;\n private readonly shape: ScrollFabShape | 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 private readonly onLenisReady?: (lenis: LenisLike) => void;\n private readonly onScroll?: (pct: number) => void;\n private readonly modes: ScrollFabMode[];\n private readonly single: boolean;\n private readonly threshold: number;\n\n /** 外部注入:宿主实例,组件永不销毁 */\n private externalLenis: LenisLike | null = null;\n /** 自建配置:null = 不走 lenis;true 形态也归一成对象 */\n private ownLenisConfig: ScrollFabLenisConfig | null = null;\n private ownLenis: LenisLike | null = null;\n private ownLenisPromise: Promise<LenisLike | null> | null = null;\n\n private mode: ScrollFabMode;\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 lenisLoopRaf: number | null = null;\n private lenisScrolling = false;\n private lenisDest: \"bottom\" | \"top\" | 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 private actionSeq = 0;\n private destroyed = false;\n private emitRaf: number | null = null;\n private lastPct = -1;\n\n constructor(options: ScrollFabOptions = {}) {\n this.targetEl = options.target ?? null;\n this.shape = options.shape ?? 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.threshold = Math.max(0, options.showThreshold ?? 0);\n this.onModeChange = options.onModeChange;\n this.onLenisReady = options.onLenisReady;\n this.onScroll = options.onScroll;\n\n this.modes = this.normalizeModes(options.modes);\n this.single = this.modes.length === 1;\n this.mode = this.modes[0]!;\n\n this.labelBottom =\n options.ariaLabelToBottom ?? (this.single ? STATIC_LABEL_BOTTOM : DEFAULT_LABEL_BOTTOM);\n this.labelTop = options.ariaLabelToTop ?? (this.single ? STATIC_LABEL_TOP : DEFAULT_LABEL_TOP);\n\n if (isLenisInstance(options.lenis)) {\n this.externalLenis = options.lenis;\n } else if (options.lenis != null && options.lenis !== false) {\n const cfg = options.lenis === true ? {} : options.lenis;\n // 自建实例默认不劫持宿主滚轮:只驱动按钮触发的程序滚动\n this.ownLenisConfig = { smoothWheel: false, ...cfg };\n }\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 if (this.shape) this.el.dataset.shape = \"on\";\n this.el.setAttribute(\n \"aria-label\",\n this.mode === \"to-bottom\" ? this.labelBottom : this.labelTop,\n );\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(SVG_NS, \"svg\");\n svg.setAttribute(\"class\", \"qsf-svg\");\n svg.setAttribute(\"viewBox\", this.shape?.viewBox ?? \"0 0 48 48\");\n svg.setAttribute(\"aria-hidden\", \"true\");\n\n if (this.shape) {\n this.fillPath = document.createElementNS(SVG_NS, \"path\");\n this.fillPath.setAttribute(\"class\", \"qsf-shape-fill\");\n this.fillPath.setAttribute(\"d\", this.shape.fill);\n this.track = this.makePath(\"qsf-track\", this.shape.outline);\n this.ring = this.makePath(\"qsf-bar\", this.shape.outline);\n if (this.shape.outlineTransform) {\n this.track.setAttribute(\"transform\", this.shape.outlineTransform);\n this.ring.setAttribute(\"transform\", this.shape.outlineTransform);\n }\n svg.append(this.fillPath, this.track, this.ring);\n } else {\n this.track = this.makeCircle(\"qsf-track\");\n this.ring = this.makeCircle(\"qsf-bar\");\n svg.append(this.track, this.ring);\n }\n\n let len: number;\n if (this.shape) {\n len = FALLBACK_PATH_LEN;\n } else {\n len = 2 * Math.PI * RADIUS;\n }\n try {\n // 浏览器对未渲染 SVG 抛错(happy-dom 返回 NaN),失败时用回退值\n const measured = this.ring.getTotalLength();\n if (Number.isFinite(measured) && measured > 0) len = measured;\n } catch {\n /* 几何/经验回退 */\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 // 单模式没有绕环翻转:不绑绕环手势,只保留按钮激活与(双模式的)方向键翻转\n if (!this.single) {\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 }\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 this.emitScroll();\n }\n\n /** 当前模式 */\n get currentMode(): ScrollFabMode {\n return this.mode;\n }\n\n /** 内容懒加载等外部高度变化后可显式触发重估(滚动/resize 时也会自动重估),并强制补发一次进度 */\n refresh(): void {\n this.syncVisible();\n this.lastPct = -1;\n this.emitScroll();\n }\n\n scrollToBottom(): void {\n this.animateScroll(\"bottom\");\n }\n\n scrollToTop(): void {\n this.animateScroll(\"top\");\n }\n\n destroy(): void {\n this.destroyed = true;\n if (!this.single) {\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 }\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 this.stopLenisLoop();\n // 只销毁自建实例;外部宿主实例交还宿主\n this.ownLenis?.destroy?.();\n this.ownLenis = null;\n if (this.ringRaf != null) cancelAnimationFrame(this.ringRaf);\n this.ringRaf = null;\n if (this.emitRaf != null) cancelAnimationFrame(this.emitRaf);\n this.emitRaf = null;\n if (this.flashTimer != null) clearTimeout(this.flashTimer);\n if (this.tapResetTimer != null) clearTimeout(this.tapResetTimer);\n this.el.remove();\n }\n\n /** 规范化 modes:缺省双模式;去重、过滤非法值;结果为空直接抛错 */\n private normalizeModes(input?: ScrollFabMode[]): ScrollFabMode[] {\n if (!input) return [\"to-bottom\", \"to-top\"];\n const out: ScrollFabMode[] = [];\n for (const m of input) {\n if ((m === \"to-bottom\" || m === \"to-top\") && !out.includes(m)) out.push(m);\n }\n if (out.length === 0) {\n throw new TypeError(\n \"ScrollFab: modes 至少需要一个有效模式('to-bottom' | 'to-top')\",\n );\n }\n return out;\n }\n\n private makeCircle(cls: string): SVGCircleElement {\n const c = document.createElementNS(SVG_NS, \"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 private makePath(cls: string, d: string): SVGPathElement {\n const p = document.createElementNS(SVG_NS, \"path\");\n p.setAttribute(\"class\", cls);\n p.setAttribute(\"d\", d);\n return p;\n }\n\n /* ---------------- 输入状态机 ----------------\n * 桌面(mouse):pointerenter 推进描边 / pointerleave 衰减倒转;click 恒为动作\n * 触屏(非 mouse):pointerdown 按住推进(首指针独占,位移超阈值放弃并放行手势);\n * 轻点 = 动作;绕满翻转过 → 本次释放被消费(不在同一手势里翻转+滚动)\n * 键盘:Enter/Space 走原生按钮激活(click → doAction);双模式下方向键翻转模式\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 !== \"ArrowUp\" && e.key !== \"ArrowDown\")) return;\n if (this.single) return;\n const want: ScrollFabMode = e.key === \"ArrowUp\" ? \"to-top\" : \"to-bottom\";\n if (want === this.mode) return;\n // 翻转与滚动正交:拦截方向键的原生页面滚动,只做模式翻转\n e.preventDefault();\n this.setMode(want);\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 /* ---------------- 程序滚动:Lenis 后端可插拔,缺省内置 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 const seq = ++this.actionSeq;\n this.cancelScroll();\n if (!this.animate || this.reducedMotion()) {\n this.setScrollPos(this.targetPos(dest));\n return;\n }\n if (this.externalLenis) {\n this.scrollWithLenis(this.externalLenis, dest, false);\n return;\n }\n if (this.ownLenisConfig) {\n const seq = ++this.actionSeq;\n void this.resolveOwnLenis().then((lenis) => {\n // await 期间可能已被新动作或 destroy 取代\n if (this.destroyed || seq !== this.actionSeq) return;\n if (lenis) this.scrollWithLenis(lenis, dest, true);\n else this.animateRaf(dest);\n });\n return;\n }\n this.animateRaf(dest);\n }\n\n /** 首次点击时动态 import lenis 并自建实例;未安装(或加载失败)→ null 永久回退 rAF */\n private resolveOwnLenis(): Promise<LenisLike | null> {\n if (this.ownLenis) return Promise.resolve(this.ownLenis);\n if (this.ownLenisPromise) return this.ownLenisPromise;\n this.ownLenisPromise = import(\"lenis\")\n .then((mod) => {\n const LenisCtor =\n (mod as { default?: new (opts: Record<string, unknown>) => LenisLike }).default ??\n (mod as unknown as new (opts: Record<string,unknown>) => LenisLike);\n const cfg: Record<string, unknown> = { ...this.ownLenisConfig };\n // target 容器映射为 Lenis wrapper;window 形态留空(Lenis 默认即 window)\n if (this.targetEl) cfg.wrapper = this.targetEl;\n this.ownLenis = new LenisCtor(cfg);\n this.onLenisReady?.(this.ownLenis);\n return this.ownLenis\n })\n .catch(() => {\n // lenis 未安装/不可用:放弃 lenis 路线,避免每次点击重复尝试\n this.ownLenisConfig = null;\n this.ownLenisPromise = null;\n return null;\n });\n return this.ownLenisPromise;\n }\n\n /**\n * 用 Lenis 执行程序滚动。\n * 自建实例(own):组件自跑 raf loop(Lenis 需外部驱动 raf),保留 wheel/touch/keydown 打断与到底追击;\n * 外部实例:宿主自带 raf loop 与手势处理,组件只发 scrollTo,不追击不打断。\n */\n private scrollWithLenis(\n lenis: LenisLike,\n dest: \"bottom\" | \"top\",\n own: boolean,\n ): void {\n const immediate = !this.animate || this.reducedMotion();\n const target = this.targetPos(dest);\n this.lenisDest = dest;\n if (own) {\n // 上次打断可能调过 stop,发起前复位\n (lenis as LenisLike & { start?: () => void }).start?.();\n lenis.scrollTo(target, {\n immediate,\n onComplete: () => this.endLenisScroll(),\n });\n if (!immediate) {\n this.lenisScrolling = true;\n this.attachInterrupt();\n this.startLenisLoop(lenis, dest);\n } else {\n this.endLenisScroll();\n }\n } else {\n lenis.scrollTo(target, { immediate });\n this.lenisDest = null;\n }\n }\n\n /** 自建实例的 raf 驱动 + 懒加载到底追击(对齐内置 rAF 语义) */\n private startLenisLoop(lenis: LenisLike, dest: \"bottom\" | \"top\"): void {\n let lastEnd = this.targetPos(dest);\n const loop = (time: number): void => {\n if (!this.lenisScrolling) return;\n if (dest === \"bottom\") {\n const live = this.targetPos(\"bottom\");\n if (Math.abs(live - lastEnd) > 2) {\n // 内容变长:追击新底端(force 覆盖当前程序动画)\n lastEnd = live;\n lenis.scrollTo(live, {\n force: true,\n onComplete: () => this.endLenisScroll(),\n });\n }\n }\n lenis.raf?.(time);\n this.lenisLoopRaf = requestAnimationFrame(loop);\n };\n this.lenisLoopRaf = requestAnimationFrame(loop);\n }\n\n private stopLenisLoop(): void {\n if (this.lenisLoopRaf != null) {\n cancelAnimationFrame(this.lenisLoopRaf);\n this.lenisLoopRaf = null;\n }\n }\n\n /** 打断自建 Lenis 程序滚动:stop 冻结动画(下次发起前 start 复位) */\n private onLenisInterrupt = (): void => {\n if (!this.lenisScrolling) return;\n (this.ownLenis as (LenisLike & { stop?: () => void }) | null)?.stop?.();\n this.endLenisScroll();\n };\n\n private endLenisScroll(): void {\n this.lenisScrolling = false;\n this.lenisDest = null;\n this.stopLenisLoop();\n this.detachInterrupt();\n }\n\n /* ---------------- 内置 rAF:缓动 + 打断 + 懒加载追击 ---------------- */\n\n private animateRaf(dest: \"bottom\" | \"top\"): void {\n this.attachRafInterrupt();\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.detachRafInterrupt();\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 /** 自建 Lenis 路线的打断监听 */\n private attachInterrupt(): void {\n const src = this.interruptSource();\n for (const ev of [\"wheel\", \"touchstart\", \"keydown\"])\n src.addEventListener(ev, this.onLenisInterrupt, { 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.onLenisInterrupt);\n }\n\n /** 内置 rAF 路线的打断监听 */\n private attachRafInterrupt(): 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 detachRafInterrupt(): 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 if (this.lenisScrolling) this.endLenisScroll();\n const src = this.interruptSource();\n for (const ev of [\"wheel\", \"touchstart\", \"keydown\"]) {\n src.removeEventListener(ev, this.onInterrupt);\n src.removeEventListener(ev, this.onLenisInterrupt);\n }\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 onRecheck = (): void => {\n this.syncVisible();\n this.scheduleScroll();\n };\n\n private syncVisible(): void {\n this.el.hidden = this.maxScroll() <= 1 || this.scrollPos() < this.threshold;\n }\n\n /** rAF 节流:一帧内多次重估只吐一次;按钮隐藏期间照常调度 */\n private scheduleScroll(): void {\n if (this.emitRaf != null || this.destroyed) return;\n this.emitRaf = requestAnimationFrame(() => {\n this.emitRaf = null;\n this.emitScroll();\n });\n }\n\n private emitScroll(): void {\n if (this.destroyed) return;\n const max = this.maxScroll();\n const pct = max <= 0 ? 0 : Math.min(1, Math.max(0, this.scrollPos() / max));\n if (pct === this.lastPct) return;\n this.lastPct = pct;\n this.onScroll?.(pct);\n }\n}\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@qingwu-ui/scroll-fab",
|
|
3
|
-
"version": "0.9.0-beta.
|
|
4
|
-
"description": "青梧 UI 悬浮滚动栏 —— 描边绕圈翻转「滚到底/回顶部」、Lenis 可插拔(未装回退 rAF
|
|
3
|
+
"version": "0.9.0-beta.3",
|
|
4
|
+
"description": "青梧 UI 悬浮滚动栏 —— 描边绕圈翻转「滚到底/回顶部」、Lenis 可插拔(未装回退 rAF)、异形形状(银杏叶等进度完全包裹)、单模式/阈值显隐/onScroll 进度回调、零强依赖",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "Apache-2.0",
|
|
7
7
|
"sideEffects": [
|