lyb-js 1.6.31 → 1.6.33

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 CHANGED
@@ -1,806 +1,964 @@
1
- # Lib自用JS工具方法
2
-
3
- ## 介绍
4
-
5
- > 该库为作者在写项目时收集的常用方法,代码简陋,没有严格的边缘处理
6
- >
7
- > 在通过`import`引入使用时,鼠标悬浮在每一个方法上都有较为完整的`Jsdoc`提示
8
-
9
- ## 起步
10
-
11
- > 完整使用
12
-
13
- ```ts
14
- import { LibJs } from "lyb-js";
15
-
16
- const t = LibJs.Base.libJsGetDataType("Hellow World!");
17
- console.log(t); //"string"
18
- ```
19
-
20
- > 按需引入,打包时就不会把整个库打进去
21
-
22
- ```ts
23
- import { libJsGetDataType } from "lyb-js/Base/LibJsGetDataType";
24
-
25
- const t = libJsGetDataType("Hellow World!");
26
- console.log(t); //"string"
27
- ```
28
-
29
- > 如果在多个文件使用到同一个方法,建议采用按需引入聚合导出
30
-
31
- ```ts
32
- //你的公共工具函数文件 utils.ts
33
- export * from "lyb-js/Base/LibJsGetDataType";
34
- export * from "lyb-js/Math/LibJsCalculateExpression";
35
-
36
- //你的项目文件 index.ts
37
- import { libJsGetDataType,libJsCalculateExpression } from "utils";
38
-
39
- const t = libJsGetDataType("Hellow World!");
40
- console.log(t); //"string"
41
-
42
- const v = libJsCalculateExpression("(1+2)-(3*4)/5");
43
- conosle.log(v); //0.6
44
- ```
45
-
46
- ## 目录
47
-
48
- ### 基础
49
-
50
- \- [LibJsGetDataType-数据类型](#LibJsGetDataType-数据类型)
51
-
52
- \- [LibJsPromiseTimeout-延时执行](#LibJsPromiseTimeout-延时执行)
53
-
54
- \- [LibJsResizeWatcher-窗口监听](#LibJsResizeWatcher-窗口监听)
55
-
56
- \- [LibIsNull-是否为空值](#LibIsNull-是否为空值)
57
-
58
-
59
- ### Browser-浏览器
60
-
61
- \- [LibJsColorConsole-有色打印](#LibJsColorConsole-有色打印)
62
-
63
- \- [LibJsIsMobile-判断手机](#LibJsIsMobile-判断手机)
64
-
65
- \- [LibJsIsPad-判断平板](#LibJsIsPad-判断平板)
66
-
67
- \- [LibJsPathParams-地址栏参数](#LibJsPathParams-地址栏参数)
68
-
69
- \- [LibJsParseQueryString-URL参数转对象](#LibJsParseQueryString-URL参数转对象)
70
-
71
- \- [LibJsSetTitleIcon-网站标题图标](#LibJsSetTitleIcon-网站标题图标)
72
-
73
- \- [LibJsTagTitleTip-网站标题交互](#LibJsTagTitleTip-网站标题交互)
74
-
75
- \- [LibJsObjToUrlParams-对象转Url参数](#LibJsObjToUrlParams-对象转Url参数)
76
-
77
- \- [LibJsCopy-复制文本到剪贴板](#LibJsCopy-复制文本到剪贴板)
78
-
79
-
80
- ### Data-数据
81
-
82
- \- [LibJsChunkArray-数组拆分](#LibJsChunkArray-数组拆分)
83
-
84
- \- [LibJsDeepJSONParse-深度解析JSON](#LibJsDeepJSONParse-深度解析JSON)
85
-
86
- \- [LibJsGroupArrayByKey-分类汇总](#LibJsGroupArrayByKey-分类汇总)
87
-
88
- \- [LibJsMatchEmail-匹配E-Mail](#LibJsMatchEmail-匹配E-Mail)
89
-
90
- \- [LibJsShuffleArray-数组乱序](#LibJsShuffleArray-数组乱序)
91
-
92
- \- [LibJsStepArray-数组偏移](#LibJsStepArray-数组偏移)
93
-
94
- \- [LibReverseArrayFromIndex-数组定位翻转](#LibReverseArrayFromIndex-数组定位翻转)
95
-
96
-
97
- ### File-文件
98
-
99
- \- [LibJsDownloadImageLink-图片下载](#LibJsDownloadImageLink-图片下载)
100
-
101
- \- [LibJsImageOptimizer-图片压缩](#LibJsImageOptimizer-图片压缩)
102
-
103
- \- [LibJsSaveJson-保存文件](#LibJsSaveJson-保存文件)
104
-
105
-
106
- ### Formatter-格式化
107
-
108
- \- [LibJsFormatterByte-字节格式化](#LibJsFormatterByte-字节格式化)
109
-
110
- \- [LibJsMaskPhoneNumber-隐藏手机号码](#LibJsMaskPhoneNumber-隐藏手机号码)
111
-
112
- \- [LibJsNumberUnit-数字单位](#LibJsNumberUnit-数字单位)
113
-
114
- \- [LibJsNumComma-数字逗号](#LibJsNumComma-数字逗号)
115
-
116
- \- [LibJsSecondsFormatterChinese-中文时间](#LibJsSecondsFormatterChinese-中文时间)
117
-
118
-
119
- ### Math-数学
120
-
121
- \- [LibJsCalculateExpression-表达式字符串](#LibJsCalculateExpression-表达式字符串)
122
-
123
- \- [LibJsConvertAngle-角弧度互转](#LibJsConvertAngle-角弧度互转)
124
-
125
- \- [LibJsCoordsAngle-两点角度](#LibJsCoordsAngle-两点角度)
126
-
127
- \- [LibJsCoordsDistance-两点距离](#LibJsCoordsDistance-两点距离)
128
-
129
- \- [LibJsDecimal-高精度计算](#LibJsDecimal-高精度计算)
130
-
131
- \- [LibJsLerp-线性插值](#LibJsLerp-线性插值)
132
-
133
- \- [LibJsNormalizeInRange-范围归一化](#LibJsNormalizeInRange-范围归一化)
134
-
135
- ### Misc-杂项
136
-
137
- \- [LibJsRegFormValidate-表单验证](#LibJsRegFormValidate-表单验证)
138
-
139
- \- [LibJsRetryRequest-请求重连](#LibJsRetryRequest-请求重连)
140
-
141
- \- [LibJsNumberStepper-数字步进器](#LibJsNumberStepper-数字步进器)
142
-
143
- \- [LibJsEmitter-事件发射器](#LibJsEmitter-事件发射器)
144
-
145
- \- [LibJsClassObservable-类属性监听器](#LibJsClassObservable-类属性监听器)
146
-
147
- \- [LibJsHorizontal-游戏横版状态](#LibJsHorizontal-游戏横版状态)
148
-
149
-
150
- ### Random-随机
151
-
152
- \- [LibJsProbabilityResult-概率触发](#LibJsProbabilityResult-概率触发)
153
-
154
- \- [LibJsRandom-随机数](#LibJsRandom-随机数)
155
-
156
- \- [LibJsRandomColor-随机色](#LibJsRandomColor-随机色)
157
-
158
- \- [LibJsUniqueRandomNumbers-随机数数组](#LibJsUniqueRandomNumbers-随机数数组)
159
-
160
-
161
- ### Time-时间
162
-
163
- \- [LibJsSameTimeCheck-时间比对](#LibJsSameTimeCheck-时间比对)
164
-
165
- \- [LibJsTimeAgo-中文时间差](#LibJsTimeAgo-中文时间差)
166
-
167
- \- [LibJsTimeGreeting-时间问候](#LibJsTimeGreeting-时间问候)
168
-
169
- \- [LibJsCountdown-倒计时](#LibJsCountdown-倒计时)
170
-
171
-
172
- ## Base-基础
173
-
174
- ### LibJsGetDataType-数据类型
175
-
176
- > 返回数据类型
177
-
178
- ```ts
179
-
180
- const result1 = libJsGetDataType(123);
181
- console.log(result1); //"number"
182
-
183
- const result2 = libJsGetDataType("hello");
184
- console.log(result2); //"string"
185
-
186
- const result3 = libJsGetDataType([1, 2, 3]);
187
- console.log(result3); //"array"
188
- ```
189
-
190
- ### LibJsPromiseTimeout-延时执行
191
-
192
- > 延时执行,切换到其他页面会暂停
193
-
194
- ```ts
195
- libJsPromiseTimeout(3000, () => {
196
- console.log("执行延时函数");
197
- });
198
- ```
199
-
200
- ### LibJsResizeWatcher-窗口监听
201
-
202
- > 监听窗口变化,内部只注册一次`resize`事件,调用监听自身可取消监听
203
-
204
- ```ts
205
- const libJsResizeWatcher = new LibJsResizeWatcher();
206
-
207
- const off = libJsResizeWatcher.on((w,h)=>{})
208
-
209
- off()
210
- ```
211
-
212
- ### LibIsNull-是否为空值
213
-
214
- > 判断是否为空值
215
-
216
- ## Browser-浏览器
217
-
218
- ### LibJsColorConsole-有色打印
219
-
220
- > `console`有色打印
221
-
222
- ```ts
223
- //使用红色打印日志
224
- libJsColorConsole("错误提示", "red", [{ label: "错误代码", value: 500 }]);
225
-
226
- //使用蓝色打印简单日志
227
- libJsColorConsole("信息", "blue", "操作成功");
228
- ```
229
-
230
- ### LibJsIsMobile-判断手机
231
-
232
- > 判断是否为移动设备
233
-
234
- ```ts
235
- const isMobile = libJsIsMobile();
236
- console.log(isMobile); //true 或 false
237
- ```
238
-
239
- ### LibJsIsPad-判断平板
240
-
241
- > 判断是否为平板
242
-
243
- ```ts
244
- const isPad = libJsIsPad();
245
- console.log(isPad); //true 或 false
246
- ```
247
-
248
- ### LibJsPathParams-地址栏参数
249
-
250
- > 获取浏览器地址栏参数
251
-
252
- ```ts
253
- const params = libJsPathParams();
254
- console.log(params); //{ param1: "value1", param2: "value2" }
255
- ```
256
-
257
- ### LibJsParseQueryString-URL参数转对象
258
-
259
- > 将`URL`参数转为对象
260
-
261
- ```ts
262
- libJsParseQueryString("name=lengyibai&age=18"); //{name: "lengyibai", age: "18"}
263
- ```
264
-
265
- ### LibJsSetTitleIcon-网站标题图标
266
-
267
- > 动态设置网站标题及图标,涉及到不同平台的打包,可以根据不同环境来设置网站标题和图标
268
-
269
- ```ts
270
- libJsSetTitleIcon("我的网站", "https://example.com/favicon.ico");
271
- ```
272
-
273
- ### LibJsTagTitleTip-网站标题交互
274
-
275
- > 网站标题交互,当从当前网页切换到其他网页,网站标题自动切换
276
-
277
- ```ts
278
- libJsTagTitleTip("欢迎回来", "来和妲己玩耍吧!");
279
- ```
280
-
281
- ### LibJsObjToUrlParams-对象转Url参数
282
-
283
- > 将对象转为地址栏参数
284
-
285
- ```js
286
- libJsObjToParams({ name: "John", age: 30, active: true });
287
- // "name=John&age=30&active=true"
288
- ```
289
-
290
- ### LibJsCopy-复制文本到剪贴板
291
-
292
- > 内部做了兼容
293
-
294
- ```ts
295
- libJsCopy("Hello World!")
296
- ```
297
-
298
- ## Data-数据
299
-
300
- ### LibJsChunkArray-数组拆分
301
-
302
- > 将数组拆分成指定数组元素数量的多个数组
303
-
304
- ```ts
305
- const chunks = libJsChunkArray([1, 2, 3, 4, 5, 6], 2);
306
- console.log(chunks); //[[1, 2], [3, 4], [5, 6]]
307
- ```
308
-
309
- ### LibJsDeepJSONParse-深度解析JSON
310
-
311
- > 递归将JSON字符串深度解析为对象
312
-
313
- ```ts
314
- const obj = libJsDeepJSONParse('{"a": 1, "b": "{\"c\": 2}"}');
315
- console.log(obj); //{ a: 1, b: { c: 2 } }
316
- ```
317
-
318
- ### LibJsGroupArrayByKey-分类汇总
319
-
320
- > 将数组对象按照指定键值整理成一个以键值为键名的对象
321
-
322
- ```ts
323
- const grouped = libJsGroupArrayByKey([{ id: 1, name: 'A' }, { id: 2, name: 'B' }, { id: 1, name: 'C' }], 'id');
324
- console.log(grouped); //{ 1: [{ id: 1, name: 'A' }, { id: 1, name: 'C' }], 2: [{ id: 2, name: 'B' }] }
325
- ```
326
-
327
- ### LibJsMatchEmail-匹配E-Mail
328
-
329
- > 可用于实时输入时,自动补全常用邮箱后缀
330
-
331
- ```ts
332
- const emails = libJsMatchEmail("user", ["@gmail.com", "@yahoo.com"]);
333
- console.log(emails); //["user@gmail.com", "user@yahoo.com"]
334
- ```
335
-
336
- ### LibJsShuffleArray-数组乱序
337
-
338
- > 将数组打乱顺序
339
-
340
- ```ts
341
- const shuffled = libJsShuffleArray([1, 2, 3, 4, 5]);
342
- console.log(shuffled); //[3, 5, 2, 1, 4] (结果每次不同)
343
- ```
344
-
345
- ### LibJsStepArray-数组偏移
346
-
347
- > 数组元素整体步数移动
348
-
349
- ```ts
350
- const moved = libJsStepArray([1, 2, 3, 4, 5], 2);
351
- console.log(moved); //[4, 5, 1, 2, 3]
352
- ```
353
-
354
- ### LibReverseArrayFromIndex-数组定位翻转
355
-
356
- > 翻转指定索引后面的数组
357
-
358
- ```ts
359
- const newArr = libReverseArrayFromIndex([1, 2, 3, 4, 5], 1);
360
- console.log(newArr); // [1, 2, 5, 4, 3]
361
- ```
362
-
363
- ### libJsPickUnique-随机选择未使用元素
364
-
365
- > 从候选数组中随机取一个未使用的元素
366
-
367
- ## File-文件
368
-
369
- ### LibJsDownloadImageLink-图片下载
370
-
371
- > 将链接图片下载到本地
372
-
373
- ```ts
374
- libJsDownloadImageLink("https://example.com/image.jpg", "图片.jpg");
375
- ```
376
-
377
- ### LibJsImageOptimizer-图片压缩
378
-
379
- > 支持`png`压缩,保留透明背景
380
-
381
- ```ts
382
- //图片压缩使用示例
383
- libJsImageOptimizerOptionsParams({
384
- file: myFile,
385
- ratio: 0.8,
386
- width: 800,
387
- maxSize: 1024,
388
- success: (formData, file, url) => {
389
- console.log('压缩成功', formData, file, url);
390
- },
391
- fail: (error) => {
392
- console.error('压缩失败', error);
393
- }
394
- });
395
- ```
396
-
397
- ### LibJsSaveJson-保存文件
398
-
399
- > 保存`JSON`文件到本地,也支持保存纯文本的`txt`文件
400
-
401
- ```ts
402
- libJsSaveJson("example.json", JSON.stringify({ key: "value" }));
403
- libJsSaveJson("example.txt", "Hellow World!");
404
- ```
405
-
406
- ## Formatter-格式化
407
-
408
- ### LibJsFormatterByte-字节格式化
409
-
410
- > 将字节单位的数字格式化
411
-
412
- ```ts
413
- const result = libJsFormatterByte(2048);
414
- console.log(result); //[2.00, KB, 2.00 KB]
415
- ```
416
-
417
- ### LibJsMaskPhoneNumber-隐藏手机号码
418
-
419
- > 隐藏手机号码中间的四位数字
420
-
421
- ```ts
422
- const masked = libJsMaskPhoneNumber("13812345678");
423
- console.log(masked); //138****5678
424
- ```
425
-
426
- ### LibJsNumberUnit-数字单位
427
-
428
- > 将大于或等于单位组的属性值,则使用它的属性名作为单位,你甚至可以用中文键名
429
-
430
- ```ts
431
- const result1 = libJsNumberUnit(1500, { K: 1000, M: 1000000 });
432
- console.log(result1); //1.5K
433
-
434
- const result2 = libJsNumberUnit(0.05, { 分: 0.01, 角: 0.1, 元: 1 });
435
- console.log(result2); //0.05分
436
- ```
437
-
438
- ### LibJsNumComma-数字逗号
439
-
440
- > 数字每三位添加逗号
441
-
442
- ```ts
443
- const formatted = libJsNumComma(1234567.89);
444
- console.log(formatted); //1,234,567.89
445
- ```
446
-
447
- ### LibJsSecondsFormatterChinese-中文时间
448
-
449
- > 将秒数格式化为中文时间描述,支持扩展到年
450
-
451
- ```ts
452
- const result1 = libJsSecondsFormatterChinese(100000);
453
- console.log(result1); //"1天3小时46分40秒"
454
-
455
- const result2 = libJsSecondsFormatterChinese(31536000);
456
- console.log(result2); //"1年"
457
-
458
- const result3 = libJsSecondsFormatterChinese(3600);
459
- console.log(result3); //"1小时"
460
-
461
- const result4 = libJsSecondsFormatterChinese(90);
462
- console.log(result4); //"1分30秒"
463
- ```
464
-
465
- ## Math-数学
466
-
467
- ### LibJsCalculateExpression-表达式字符串
468
-
469
- > 计算表达式字符串
470
-
471
- ```ts
472
- const result = libJsCalculateExpression("(1+2)-(3*4)/5");
473
- console.log(result); //0.6
474
- ```
475
-
476
- ### LibJsConvertAngle-角弧度互转
477
-
478
- > 角度和弧度互相转换
479
-
480
- ```ts
481
- //角度转弧度
482
- const rad = libJsConvertAngle(90, "rad");
483
- console.log(rad); //1.5708... (π/2)
484
-
485
- //弧度转角度
486
- const deg = libJsConvertAngle(Math.PI, "deg");
487
- console.log(deg); //180
488
- ```
489
-
490
- ### LibJsCoordsAngle-两点角度
491
-
492
- > 计算两点角度
493
-
494
- ```ts
495
- const result1 = libJsCoordsAngle({ x: 0, y: 0 }, { x: 1, y: 0 });
496
- console.log(result1); //0
497
-
498
- const result2 = libJsCoordsAngle({ x: 0, y: 0 }, { x: 1, y: 1 });
499
- console.log(result2); //45
500
-
501
- const result3 = libJsCoordsAngle({ x: 0, y: 0 }, { x: 0, y: 1 });
502
- console.log(result3); //90
503
- ```
504
-
505
- ### LibJsCoordsDistance-两点距离
506
-
507
- > 计算两点距离
508
-
509
- ```ts
510
- const result1 = libJsCoordsDistance({ x: 0, y: 0 }, { x: 3, y: 4 });
511
- console.log(result1); //5
512
-
513
- const result2 = libJsCoordsDistance({ x: 1, y: 1 }, { x: 4, y: 5 });
514
- console.log(result2); //5
515
-
516
- const result3 = libJsCoordsDistance({ x: 0, y: 0 }, { x: 0, y: 0 });
517
- console.log(result3); //0
518
- ```
519
-
520
- ### LibJsDecimal-高精度计算
521
-
522
- > 计算两个数的运算结果,并保留指定位数的小数
523
-
524
- ```ts
525
- const result1 = libJsDecimal(10, 3, "+");
526
- console.log(result1); //13
527
-
528
- const result2 = libJsDecimal(10, 3, "-");
529
- console.log(result2); //7
530
-
531
- const result3 = libJsDecimal(10, 3, "/", 2);
532
- console.log(result3); //3.33
533
- ```
534
-
535
- ### LibJsLerp-线性插值
536
-
537
- > 线性插值
538
-
539
- ```ts
540
- console.log(LibJsLerp(0, 100, 0.25)); //25
541
- console.log(LibJsLerp(100, 0, 0.75)); //25
542
- ```
543
-
544
- ### LibJsNormalizeInRange-范围归一化
545
-
546
- > 值介于起点与终点之间时返回一个介于0与1之间的数
547
-
548
- ```ts
549
- console.log(LibJsNormalizeInRange(0, 100, 75)); //0.75
550
- console.log(LibJsNormalizeInRange(100, 0, 75)); //0.25
551
- ```
552
-
553
- ## Misc-杂项
554
-
555
- ### LibJsRegFormValidate-表单验证
556
-
557
- > 通过传递对象数字的方式进行正则或自定义函数进行验证
558
-
559
- ```ts
560
- const form = { username: "john", email: "john@example.com" };
561
- const rules = [
562
- { key: "username", verify: /^[a-zA-Z0-9]{3,}$/, msg: "用户名不合法", name: "用户名" },
563
- { key: "email", verify: /^\S+@\S+\.\S+$/, msg: "邮箱格式不正确", name: "邮箱" },
564
- ];
565
- const result1 = libJsRegFormValidate(form, rules);
566
- console.log(result1); //返回结果: []
567
-
568
- const invalidForm = { username: "jo", email: "invalid-email" };
569
- const result2 = libJsRegFormValidate(invalidForm, rules);
570
- console.log(result2);
571
- //返回结果: [
572
- // { key: "username", msg: "用户名不合法", name: "用户名" },
573
- // { key: "email", msg: "邮箱格式不正确", name: "邮箱" }
574
- //]
575
- ```
576
-
577
- ### LibJsRetryRequest-请求重连
578
-
579
- > 请求失败重连
580
-
581
- ```ts
582
- const requestFn = (params: { url: string }) => fetch(params.url).then(res => res.json());
583
- const params = { url: "https://api.example.com/data" };
584
- libJsRetryRequest({
585
- promiseFn: requestFn,
586
- params,
587
- maxRetries: 5,
588
- retryDelay: 1000
589
- })
590
- .then(data => console.log(data))
591
- .catch(err => console.error(err));
592
- ```
593
-
594
- ### LibJsNumberStepper-数字步进器
595
-
596
- > 通过调用方法来增加和减少数字索引
597
-
598
- ```ts
599
- const stepper = new LibJsNumberStepper(10, (index) => console.log(index));
600
- stepper.down("add"); // 索引加1
601
- stepper.updateIndex(5); // 更新索引为5
602
- stepper.down("sub"); // 索引减1
603
- ```
604
-
605
- ### LibJsEmitter-事件发射器
606
-
607
- > 发布-订阅模式
608
-
609
- ```ts
610
- type EventType = {
611
- play: [a: number, b: string];
612
- stop: string;
613
- };
614
-
615
- //使用方式
616
- const $bus = LibJsEmitter<EventType>();
617
-
618
- $bus.on("play", (c, d) => {
619
- console.log(c, d);
620
- });
621
- $bus.on("stop", (v) => {
622
- console.log(v);
623
- });
624
-
625
- $bus.emit("play", 1, "hello");
626
- $bus.emit("stop", "4");
627
-
628
- $bus.off("play");
629
- $bus.off("stop");
630
- ```
631
-
632
- ### LibJsClassObservable-类属性监听器
633
-
634
- > 监听`class`的属性值更改
635
-
636
- ```ts
637
- import { LibJsClassObservable } from "@/utils/LibJsClassObservable";
638
-
639
- interface Static {
640
- /** 用户ID */
641
- userID: number;
642
- /** 是否为学生 */
643
- sdutent: boolean;
644
- /** 年龄 */
645
- age: number;
646
- }
647
-
648
- /** @description 静态数据 */
649
- export class DataStore extends LibJsClassObservable<Static> {
650
- constructor() {
651
- super({
652
- userID: 0,
653
- sdutent: true,
654
- age: 18,
655
- });
656
- }
657
- }
658
-
659
- const dataStore = new DataStore()
660
-
661
- //获取
662
- const userID = dataStore.getValue("userID"); //0
663
-
664
- //更改
665
- const userID = dataStore.setValue("userID", 1); //1
666
-
667
- //Boolean 类型取反
668
- const stdutent = dataStore.setBooleanValue("sdutent"); //false
669
-
670
- //数字类型累加
671
- const stdutent = dataStore.setNumberValue("age"); //19
672
-
673
- //手动触发指定属性监听的所有回调,配合 setValue 第三参数为false,即不自动触发监听的时候,可手动触发
674
- dataStore.setValue("userID", 3, false);
675
- setTimeout(()=>{
676
- dataStore.updateFake("userID");
677
- }, 1000)
678
- ```
679
-
680
- ### LibJsHorizontal-游戏横版状态
681
-
682
- > 需要传递当前游戏的适配模式
683
-
684
- ### LibJsPruneEmpty-对象属性去空值
685
-
686
- > 递归对象并去掉对象内的 `undefined`、`null`、`""`
687
-
688
- ## Random-随机
689
-
690
- ### LibJsProbabilityResult-概率触发
691
-
692
- > 百分比概率结果
693
-
694
- ```ts
695
- const result1 = libJsProbabilityResult(50);
696
- console.log(result1); //50% 概率为 true
697
-
698
- const result2 = libJsProbabilityResult(80); //80% 概率为 true
699
- console.log(result2); //50% 概率为 true
700
-
701
- const result3 = libJsProbabilityResult(100); //100% 概率为 true
702
- console.log(result3); //50% 概率为 true
703
- ```
704
-
705
- ### LibJsRandom-随机数
706
-
707
- > 随机获取两个数之间的值,包含两数自身
708
-
709
- ```ts
710
- const result1 = libJsRandom(1, 10); //1 到 10 之间的随机整数
711
- console.log(result1); //50% 概率为 true
712
-
713
- const result2 = libJsRandom(1, 10, 2); //1 到 10 之间保留两位小数的随机数
714
- console.log(result2); //50% 概率为 true
715
- ```
716
-
717
- ### LibJsRandomColor-随机色
718
-
719
- > 随机 RGBA 颜色
720
-
721
- ```ts
722
- const result1 = libJsRandomColor(); //生成随机的 RGBA 颜色,默认透明度 1
723
- console.log(result1); //50% 概率为 true
724
-
725
- const result2 = libJsRandomColor(0.5); //生成随机的 RGBA 颜色,透明度为 0.5
726
- console.log(result2); //50% 概率为 true
727
- ```
728
-
729
- ### LibJsUniqueRandomNumbers-随机数数组
730
-
731
- > 随机生成指定个数、指定范围不重复的随机数数组
732
-
733
- ```ts
734
- const result1 = libJsUniqueRandomNumbers(1, 10, 5); //从 1 到 10 中随机生成 5 个唯一数字
735
- console.log(result1); //50% 概率为 true
736
-
737
- const result2 = libJsUniqueRandomNumbers(1, 100, 10); //从 1 到 100 中随机生成 10 个唯一数字
738
- console.log(result2); //50% 概率为 true
739
- ```
740
-
741
- ## Time-时间
742
-
743
- ### LibJsSameTimeCheck-时间比对
744
-
745
- > 传入时间戳与当前时间判断是否为同一分、同一时、同一天、同一周、同一月、同一年
746
-
747
- ```ts
748
- const timestamp = 1679872800000; //时间戳
749
- const result = libJsSameTimeCheck(timestamp, 'day'); //判断是否为同一天
750
- console.log(result); //0: 同一天, 1: 新的一天, -1: 时间戳大于当前时间
751
- ```
752
-
753
- ### LibJsTimeAgo-中文时间差
754
-
755
- > 时间差计算
756
-
757
- ```ts
758
- const result1 = libJsTimeAgotamp(Date.now() - 3600000); //"1 小时前"
759
- console.log(result1); //50% 概率为 true
760
-
761
- const result2 = libJsTimeAgotamp(Date.now() - 86400000); //"1 天前"
762
- console.log(result2); //50% 概率为 true
763
-
764
- const result3 = libJsTimeAgotamp(Date.now() - 31536000000); //"1 年前"
765
- console.log(result3); //50% 概率为 true
766
-
767
- const result4 = libJsTimeAgotamp(Date.now() - 10000); //"刚刚"
768
- console.log(result4); //50% 概率为 true
769
- ```
770
-
771
- ### LibJsTimeGreeting-时间问候
772
-
773
- > 根据当前时间返回问候语
774
-
775
- ```ts
776
- const result1 = libJsTimeGreeting(); //根据当前时间返回默认问候语
777
- console.log(result1); //50% 概率为 true
778
-
779
- const result2 = libJsTimeGreeting({ morning: "早安" }); //自定义早上问候语
780
- console.log(result2); //50% 概率为 true
781
-
782
- const result3 = libJsTimeGreeting({ afternoon: "午后好" }); //自定义下午问候语
783
- console.log(result3); //50% 概率为 true
784
- ```
785
-
786
- ### LibJsCountdown-倒计时
787
-
788
- > 以当前时间为开始时间,传递结束时间,返回年到秒的数值,以及是否结束
789
-
790
- ```ts
791
- const result = libJsCountdown("2025-12-01 12:00:00");
792
-
793
- console.log(result);
794
- /*
795
- {
796
- years: "00",
797
- months: "01",
798
- days: "20",
799
- hours: "05",
800
- minutes: "34",
801
- seconds: "12",
802
- ended: false
803
- }
804
- */
805
- ```
806
-
1
+ # Lib自用JS工具方法
2
+
3
+ ## 介绍
4
+
5
+ `lyb-js` 是一个偏项目实用型的工具集合,覆盖浏览器交互、数据处理、数学计算、时间格式化、随机工具等常见场景。
6
+
7
+ 文档以“快速上手”为目标:
8
+
9
+ - 先告诉你怎么安装、怎么导入
10
+ - 再给每个工具最常用的调用方式
11
+ - 示例尽量贴近实际项目,不把 README 写成冗长 API 手册
12
+
13
+ ## 安装
14
+
15
+ ```bash
16
+ npm install lyb-js
17
+ ```
18
+
19
+ ```bash
20
+ pnpm add lyb-js
21
+ ```
22
+
23
+ ```bash
24
+ yarn add lyb-js
25
+ ```
26
+
27
+ ## 起步
28
+
29
+ ### 1. 整库引入
30
+
31
+ 适合快速试用,或者你已经统一通过 `LibJs` 管理工具访问。
32
+
33
+ ```ts
34
+ import { LibJs } from "lyb-js";
35
+
36
+ const type = LibJs.Base.libJsGetDataType("Hello World");
37
+ console.log(type); // "string"
38
+ ```
39
+
40
+ ### 2. 按需引入
41
+
42
+ 更推荐。只引入当前用到的方法,路径按分类和文件名区分。
43
+
44
+ ```ts
45
+ import { libJsGetDataType } from "lyb-js/Base/LibJsGetDataType";
46
+ import { libJsCalculateExpression } from "lyb-js/Math/LibJsCalculateExpression";
47
+
48
+ console.log(libJsGetDataType([1, 2, 3])); // "array"
49
+ console.log(libJsCalculateExpression("(1+2)-(3*4)/5")); // 0.6
50
+ ```
51
+
52
+ ### 3. 项目内二次封装
53
+
54
+ 如果同一批工具会在多个文件重复使用,建议在你自己的 `utils.ts` 中统一导出。
55
+
56
+ ```ts
57
+ // utils.ts
58
+ export * from "lyb-js/Base/LibJsGetDataType";
59
+ export * from "lyb-js/Math/LibJsCalculateExpression";
60
+ export * from "lyb-js/Browser/LibJsGetRowValue";
61
+ ```
62
+
63
+ ```ts
64
+ // page.ts
65
+ import {
66
+ libJsGetDataType,
67
+ libJsCalculateExpression,
68
+ libJsGetRowValue,
69
+ } from "./utils";
70
+
71
+ console.log(libJsGetDataType({ a: 1 })); // "object"
72
+ console.log(libJsCalculateExpression("10/4", 2)); // 2.5
73
+ console.log(libJsGetRowValue({ user: { name: "Tom" } }, "user.name")); // "Tom"
74
+ ```
75
+
76
+ ## 使用说明
77
+
78
+ ### 环境说明
79
+
80
+ - `Browser`、`File` 中的大多数工具依赖 `window`、`document`、`navigator`,适合浏览器环境。
81
+ - `Data`、`Formatter`、`Math`、`Random` 中的大多数工具既可在浏览器使用,也可在 Node 环境使用。
82
+ - `Time` 分类依赖 `dayjs` 的少量能力,但作为库使用时你只需要正常安装 `lyb-js` 即可。
83
+
84
+ ### 阅读方式
85
+
86
+ 每个工具章节都遵循同一结构:
87
+
88
+ 1. 作用说明
89
+ 2. 按需导入
90
+ 3. 1 到 2 个最常见示例
91
+
92
+ 如果你只想找某个工具的导入方式,可以直接看每个章节的第一段代码。
93
+
94
+ ## 目录
95
+
96
+ ### Base-基础
97
+
98
+ - [LibJsGetDataType-数据类型](#libjsgetdatatype-数据类型)
99
+ - [LibJsIsNull-是否为空值](#libjsisnull-是否为空值)
100
+ - [LibJsPromiseTimeout-延时执行](#libjspromisetimeout-延时执行)
101
+ - [LibJsResizeWatcher-窗口监听](#libjsresizewatcher-窗口监听)
102
+
103
+ ### Browser-浏览器
104
+
105
+ - [LibJsColorConsole-有色打印](#libjscolorconsole-有色打印)
106
+ - [LibJsCopy-复制文本到剪贴板](#libjscopy-复制文本到剪贴板)
107
+ - [LibJsGetRowValue-按点路径获取对象值](#libjsgetrowvalue-按点路径获取对象值)
108
+ - [LibJsIsMobile-判断手机](#libjsismobile-判断手机)
109
+ - [LibJsIsPad-判断平板](#libjsispad-判断平板)
110
+ - [LibJsObjToUrlParams-对象转Url参数](#libjsobjtourlparams-对象转url参数)
111
+ - [LibJsParseQueryString-URL参数转对象](#libjsparsequerystring-url参数转对象)
112
+ - [LibJsPathParams-地址栏参数](#libjspathparams-地址栏参数)
113
+ - [LibJsSetTitleIcon-网站标题图标](#libjssettitleicon-网站标题图标)
114
+ - [LibJsTagTitleTip-网站标题交互](#libjstagtitletip-网站标题交互)
115
+
116
+ ### Data-数据
117
+
118
+ - [LibJsChunkArray-数组拆分](#libjschunkarray-数组拆分)
119
+ - [LibJsDeepJSONParse-深度解析JSON](#libjsdeepjsonparse-深度解析json)
120
+ - [LibJsGroupArrayByKey-分类汇总](#libjsgrouparraybykey-分类汇总)
121
+ - [LibJsMatchEmail-匹配E-Mail](#libjsmatchemail-匹配e-mail)
122
+ - [libJsPickUnique-随机选择未使用元素](#libjspickunique-随机选择未使用元素)
123
+ - [LibJsShuffleArray-数组乱序](#libjsshufflearray-数组乱序)
124
+ - [LibJsStepArray-数组偏移](#libjssteparray-数组偏移)
125
+ - [LibReverseArrayFromIndex-数组定位翻转](#libreversearrayfromindex-数组定位翻转)
126
+
127
+ ### File-文件
128
+
129
+ - [LibJsDownloadImageLink-图片下载](#libjsdownloadimagelink-图片下载)
130
+ - [LibJsImageOptimizer-图片压缩](#libjsimageoptimizer-图片压缩)
131
+ - [LibJsSaveJson-保存文件](#libjssavejson-保存文件)
132
+
133
+ ### Formatter-格式化
134
+
135
+ - [LibJsFormatterByte-字节格式化](#libjsformatterbyte-字节格式化)
136
+ - [LibJsMaskPhoneNumber-隐藏手机号码](#libjsmaskphonenumber-隐藏手机号码)
137
+ - [LibJsNumberUnit-数字单位](#libjsnumberunit-数字单位)
138
+ - [LibJsNumComma-数字逗号](#libjsnumcomma-数字逗号)
139
+ - [LibJsSecondsFormatterChinese-中文时间](#libjssecondsformatterchinese-中文时间)
140
+
141
+ ### Math-数学
142
+
143
+ - [LibJsCalculateExpression-表达式字符串](#libjscalculateexpression-表达式字符串)
144
+ - [LibJsConvertAngle-角弧度互转](#libjsconvertangle-角弧度互转)
145
+ - [LibJsCoordsAngle-两点角度](#libjscoordsangle-两点角度)
146
+ - [LibJsCoordsDistance-两点距离](#libjscoordsdistance-两点距离)
147
+ - [LibJsDecimal-高精度计算](#libjsdecimal-高精度计算)
148
+ - [LibJsLerp-线性插值](#libjslerp-线性插值)
149
+ - [LibJsNormalizeInRange-范围归一化](#libjsnormalizeinrange-范围归一化)
150
+
151
+ ### Misc-杂项
152
+
153
+ - [LibJsClassObservable-类属性监听器](#libjsclassobservable-类属性监听器)
154
+ - [LibJsEmitter-事件发射器](#libjsemitter-事件发射器)
155
+ - [LibJsEmitterClose-一次性关闭监听](#libjsemitterclose-一次性关闭监听)
156
+ - [LibJsHorizontal-游戏横版状态](#libjshorizontal-游戏横版状态)
157
+ - [LibJsNumberStepper-数字步进器](#libjsnumberstepper-数字步进器)
158
+ - [LibJsPruneEmpty-对象属性去空值](#libjspruneempty-对象属性去空值)
159
+ - [LibJsPullUpLoad-上拉加载](#libjspullupload-上拉加载)
160
+ - [LibJsRegFormValidate-表单验证](#libjsregformvalidate-表单验证)
161
+ - [LibJsRetryRequest-请求重连](#libjsretryrequest-请求重连)
162
+
163
+ ### Random-随机
164
+
165
+ - [LibJsProbabilityResult-概率触发](#libjsprobabilityresult-概率触发)
166
+ - [LibJsRandom-随机数](#libjsrandom-随机数)
167
+ - [LibJsRandomColor-随机色](#libjsrandomcolor-随机色)
168
+ - [LibJsUniqueRandomNumbers-随机数数组](#libjsuniquerandomnumbers-随机数数组)
169
+
170
+ ### Time-时间
171
+
172
+ - [LibJsCountdown-倒计时](#libjscountdown-倒计时)
173
+ - [LibJsSameTimeCheck-时间比对](#libjssametimecheck-时间比对)
174
+ - [LibJsTimeAgo-中文时间差](#libjstimeago-中文时间差)
175
+ - [LibJsTimeGreeting-时间问候](#libjstimegreeting-时间问候)
176
+
177
+ ## Base-基础
178
+
179
+ ### LibJsGetDataType-数据类型
180
+
181
+ 返回标准化后的数据类型字符串。
182
+
183
+ ```ts
184
+ import { libJsGetDataType } from "lyb-js/Base/LibJsGetDataType";
185
+
186
+ console.log(libJsGetDataType(123)); // "number"
187
+ console.log(libJsGetDataType("hello")); // "string"
188
+ console.log(libJsGetDataType([1, 2, 3])); // "array"
189
+ console.log(libJsGetDataType(() => {})); // "function"
190
+ ```
191
+
192
+ ### LibJsIsNull-是否为空值
193
+
194
+ 判断值是否为 `null`、`undefined` 或空字符串 `""`。
195
+
196
+ ```ts
197
+ import { libJsIsNull } from "lyb-js/Base/LibJsIsNull";
198
+
199
+ console.log(libJsIsNull(null)); // true
200
+ console.log(libJsIsNull(undefined)); // true
201
+ console.log(libJsIsNull("")); // true
202
+ console.log(libJsIsNull(0)); // false
203
+ ```
204
+
205
+ ### LibJsPromiseTimeout-延时执行
206
+
207
+ 延时执行回调;页面切到后台时会暂停计时,回到当前页后继续。
208
+
209
+ ```ts
210
+ import { libJsPromiseTimeout } from "lyb-js/Base/LibJsPromiseTimeout";
211
+
212
+ await libJsPromiseTimeout(1000, () => {
213
+ console.log("1 秒后执行");
214
+ });
215
+
216
+ console.log("延时结束");
217
+ ```
218
+
219
+ ### LibJsResizeWatcher-窗口监听
220
+
221
+ 监听窗口尺寸变化,适合做画布缩放、布局适配或横竖屏比例计算。
222
+
223
+ ```ts
224
+ import { LibJsResizeWatcher } from "lyb-js/Base/LibJsResizeWatcher";
225
+
226
+ const watcher = new LibJsResizeWatcher();
227
+
228
+ const off = watcher.on((w, h, scale) => {
229
+ console.log("窗口宽高", w, h);
230
+ console.log("缩放比例", scale);
231
+ });
232
+
233
+ off();
234
+ ```
235
+
236
+ ```ts
237
+ const fixedHorizontal = new LibJsResizeWatcher("h");
238
+ fixedHorizontal.on((w, h, scale) => {
239
+ console.log(w, h, scale); // 1920 1080 ...
240
+ });
241
+ ```
242
+
243
+ ## Browser-浏览器
244
+
245
+ ### LibJsColorConsole-有色打印
246
+
247
+ 给 `console.log` 增加标题色块和结构化信息,适合调试接口和业务流程。
248
+
249
+ ```ts
250
+ import { libJsColorConsole } from "lyb-js/Browser/LibJsColorConsole";
251
+
252
+ libJsColorConsole("请求成功", "green", [
253
+ { label: "接口", value: "/api/user" },
254
+ { label: "状态码", value: 200 },
255
+ ]);
256
+
257
+ libJsColorConsole("调试信息", "blue", { page: "home", loaded: true });
258
+ ```
259
+
260
+ ### LibJsCopy-复制文本到剪贴板
261
+
262
+ 复制字符串到剪贴板,内部兼容 `navigator.clipboard` 和降级方案。
263
+
264
+ ```ts
265
+ import { libJsCopy } from "lyb-js/Browser/LibJsCopy";
266
+
267
+ button.addEventListener("click", () => {
268
+ libJsCopy("Hello World!");
269
+ });
270
+ ```
271
+
272
+ ### LibJsGetRowValue-按点路径获取对象值
273
+
274
+ 按点路径读取对象中的嵌套值,路径不存在时返回 `undefined`。
275
+
276
+ ```ts
277
+ import { libJsGetRowValue } from "lyb-js/Browser/LibJsGetRowValue";
278
+
279
+ const row = {
280
+ user: {
281
+ profile: {
282
+ name: "Tom",
283
+ },
284
+ },
285
+ };
286
+
287
+ console.log(libJsGetRowValue(row, "user.profile.name")); // "Tom"
288
+ console.log(libJsGetRowValue(row, "user.profile.age")); // undefined
289
+ ```
290
+
291
+ ### LibJsIsMobile-判断手机
292
+
293
+ 通过 `userAgent` 判断当前设备是否为手机端。
294
+
295
+ ```ts
296
+ import { libJsIsMobile } from "lyb-js/Browser/LibJsIsMobile";
297
+
298
+ if (libJsIsMobile()) {
299
+ console.log("当前是手机设备");
300
+ }
301
+ ```
302
+
303
+ ### LibJsIsPad-判断平板
304
+
305
+ 结合 `userAgent`、屏幕尺寸、宽高比判断是否为平板设备。
306
+
307
+ ```ts
308
+ import { libJsIsPad } from "lyb-js/Browser/LibJsIsPad";
309
+
310
+ console.log(libJsIsPad()); // true 或 false
311
+ ```
312
+
313
+ ### LibJsObjToUrlParams-对象转Url参数
314
+
315
+ 把普通对象拼接成查询字符串。
316
+
317
+ ```ts
318
+ import { libJsObjToUrlParams } from "lyb-js/Browser/LibJsObjToUrlParams";
319
+
320
+ const query = libJsObjToUrlParams({
321
+ name: "John",
322
+ age: 30,
323
+ active: true,
324
+ });
325
+
326
+ console.log(query); // "name=John&age=30&active=true"
327
+ ```
328
+
329
+ ### LibJsParseQueryString-URL参数转对象
330
+
331
+ 把查询字符串转回对象。注意导入路径文件名是小写 `libJsParseQueryString`。
332
+
333
+ ```ts
334
+ import { libJsParseQueryString } from "lyb-js/Browser/libJsParseQueryString";
335
+
336
+ console.log(libJsParseQueryString("name=lengyibai&age=18"));
337
+ // { name: "lengyibai", age: "18" }
338
+ ```
339
+
340
+ ### LibJsPathParams-地址栏参数
341
+
342
+ 读取当前地址栏参数,也可以传入一段完整 URL 进行解析。
343
+
344
+ ```ts
345
+ import { libJsPathParams } from "lyb-js/Browser/LibJsPathParams";
346
+
347
+ console.log(libJsPathParams("https://example.com?a=1&b=hello"));
348
+ // { a: "1", b: "hello" }
349
+ ```
350
+
351
+ ### LibJsSetTitleIcon-网站标题图标
352
+
353
+ 动态设置网页标题和 favicon。
354
+
355
+ ```ts
356
+ import { libJsSetTitleIcon } from "lyb-js/Browser/LibJsSetTitleIcon";
357
+
358
+ libJsSetTitleIcon("我的网站", "/favicon.ico");
359
+ ```
360
+
361
+ ### LibJsTagTitleTip-网站标题交互
362
+
363
+ 页面隐藏和返回时切换标题,适合网页消息提醒。
364
+
365
+ ```ts
366
+ import { libJsTagTitleTip } from "lyb-js/Browser/LibJsTagTitleTip";
367
+
368
+ libJsTagTitleTip("欢迎回来", "你有新的消息");
369
+ ```
370
+
371
+ ## Data-数据
372
+
373
+ ### LibJsChunkArray-数组拆分
374
+
375
+ 把数组按固定长度拆成多个小数组。
376
+
377
+ ```ts
378
+ import { libJsChunkArray } from "lyb-js/Data/LibJsChunkArray";
379
+
380
+ console.log(libJsChunkArray([1, 2, 3, 4, 5, 6], 2));
381
+ // [[1, 2], [3, 4], [5, 6]]
382
+ ```
383
+
384
+ ### LibJsDeepJSONParse-深度解析JSON
385
+
386
+ 递归解析对象或数组中嵌套的 JSON 字符串。
387
+
388
+ ```ts
389
+ import { libJsDeepJSONParse } from "lyb-js/Data/LibJsDeepJSONParse";
390
+
391
+ const data = '{"user":"{\\"name\\":\\"Tom\\",\\"age\\":18}"}';
392
+ console.log(libJsDeepJSONParse(data));
393
+ // { user: { name: "Tom", age: 18 } }
394
+ ```
395
+
396
+ ### LibJsGroupArrayByKey-分类汇总
397
+
398
+ 按对象中的某个字段分组。
399
+
400
+ ```ts
401
+ import { libJsGroupArrayByKey } from "lyb-js/Data/LibJsGroupArrayByKey";
402
+
403
+ const list = [
404
+ { type: "fruit", name: "apple" },
405
+ { type: "fruit", name: "banana" },
406
+ { type: "drink", name: "tea" },
407
+ ];
408
+
409
+ console.log(libJsGroupArrayByKey(list, "type"));
410
+ // {
411
+ // fruit: [{...}, {...}],
412
+ // drink: [{...}]
413
+ // }
414
+ ```
415
+
416
+ ### LibJsMatchEmail-匹配E-Mail
417
+
418
+ 根据当前输入自动补全邮箱后缀。
419
+
420
+ ```ts
421
+ import { libJsMatchEmail } from "lyb-js/Data/LibJsMatchEmail";
422
+
423
+ const result = libJsMatchEmail("user@g", ["@gmail.com", "@github.com"]);
424
+ console.log(result); // ["user@gmail.com", "user@github.com"]
425
+ ```
426
+
427
+ ### libJsPickUnique-随机选择未使用元素
428
+
429
+ 从候选数组中随机选一个还没有被使用过的元素。
430
+
431
+ ```ts
432
+ import { libJsPickUnique } from "lyb-js/Data/libJsPickUnique";
433
+
434
+ const pool = ["A", "B", "C", "D"];
435
+ const used = ["A", "B"];
436
+
437
+ console.log(libJsPickUnique(pool, used)); // "C" 或 "D"
438
+ console.log(libJsPickUnique(["A"], ["A"])); // undefined
439
+ ```
440
+
441
+ ### LibJsShuffleArray-数组乱序
442
+
443
+ 返回一个打乱顺序的新数组,不会直接修改原数组。
444
+
445
+ ```ts
446
+ import { libJsShuffleArray } from "lyb-js/Data/LibJsShuffleArray";
447
+
448
+ console.log(libJsShuffleArray([1, 2, 3, 4, 5]));
449
+ // [3, 5, 1, 4, 2] 结果每次不同
450
+ ```
451
+
452
+ ### LibJsStepArray-数组偏移
453
+
454
+ 让数组整体循环移动;正数向右移,负数向左移。
455
+
456
+ ```ts
457
+ import { libJsStepArray } from "lyb-js/Data/LibJsStepArray";
458
+
459
+ console.log(libJsStepArray([1, 2, 3, 4, 5], 2));
460
+ // [4, 5, 1, 2, 3]
461
+
462
+ console.log(libJsStepArray([1, 2, 3, 4, 5], -1));
463
+ // [2, 3, 4, 5, 1]
464
+ ```
465
+
466
+ ### LibReverseArrayFromIndex-数组定位翻转
467
+
468
+ 保留某个索引之前的内容,把该索引后面的数组反转。
469
+
470
+ ```ts
471
+ import { libReverseArrayFromIndex } from "lyb-js/Data/LibReverseArrayFromIndex";
472
+
473
+ console.log(libReverseArrayFromIndex([1, 2, 3, 4, 5], 1));
474
+ // [1, 2, 5, 4, 3]
475
+ ```
476
+
477
+ ## File-文件
478
+
479
+ ### LibJsDownloadImageLink-图片下载
480
+
481
+ 把图片链接下载到本地。
482
+
483
+ ```ts
484
+ import { libJsDownloadImageLink } from "lyb-js/File/LibJsDownloadImageLink";
485
+
486
+ libJsDownloadImageLink("https://example.com/banner.jpg", "banner.jpg");
487
+ ```
488
+
489
+ ### LibJsImageOptimizer-图片压缩
490
+
491
+ 在浏览器里压缩图片,并通过回调拿到压缩后的 `File`、`FormData` 和预览地址。
492
+
493
+ ```ts
494
+ import { libJsImageOptimizer } from "lyb-js/File/LibJsImageOptimizer";
495
+
496
+ input.addEventListener("change", () => {
497
+ const file = input.files?.[0];
498
+ if (!file) return;
499
+
500
+ libJsImageOptimizer({
501
+ file,
502
+ width: 1200,
503
+ ratio: 0.8,
504
+ maxSize: 1024,
505
+ success(formData, newFile, url) {
506
+ console.log(formData, newFile);
507
+ preview.src = url;
508
+ },
509
+ fail(error) {
510
+ console.error("压缩失败", error);
511
+ },
512
+ });
513
+ });
514
+ ```
515
+
516
+ ### LibJsSaveJson-保存文件
517
+
518
+ 把字符串或 JSON 内容保存成浏览器下载文件。
519
+
520
+ ```ts
521
+ import { libJsSaveJson } from "lyb-js/File/LibJsSaveJson";
522
+
523
+ const content = JSON.stringify({ name: "Tom", age: 18 }, null, 2);
524
+ libJsSaveJson("user.json", content);
525
+ ```
526
+
527
+ ## Formatter-格式化
528
+
529
+ ### LibJsFormatterByte-字节格式化
530
+
531
+ 把字节数格式化为更易读的大小信息。
532
+
533
+ ```ts
534
+ import { libJsFormatterByte } from "lyb-js/Formatter/LibJsFormatterByte";
535
+
536
+ console.log(libJsFormatterByte(1536));
537
+ // ["1.50", "KB", "1.50 KB"]
538
+ ```
539
+
540
+ ### LibJsMaskPhoneNumber-隐藏手机号码
541
+
542
+ 文件名是 `LibJsMaskPhoneNumber`,导出的方法名是 `libJsMaskCenterFour`。
543
+
544
+ ```ts
545
+ import { libJsMaskCenterFour } from "lyb-js/Formatter/LibJsMaskPhoneNumber";
546
+
547
+ console.log(libJsMaskCenterFour("13800138000"));
548
+ // "138****8000"
549
+ ```
550
+
551
+ ### LibJsNumberUnit-数字单位
552
+
553
+ 根据阈值自动追加单位,适合展示“万、亿、K、M”等短格式数字。
554
+
555
+ ```ts
556
+ import { libJsNumberUnit } from "lyb-js/Formatter/LibJsNumberUnit";
557
+
558
+ console.log(libJsNumberUnit(12600, { 万: 10000, 亿: 100000000 }, 1));
559
+ // "1.2万"
560
+
561
+ console.log(libJsNumberUnit(2500000, { K: 1000, M: 1000000 }, 2));
562
+ // "2.50M"
563
+ ```
564
+
565
+ ### LibJsNumComma-数字逗号
566
+
567
+ 给数字加千分位,并按指定小数位截断,不做四舍五入。
568
+
569
+ ```ts
570
+ import { libJsNumComma } from "lyb-js/Formatter/LibJsNumComma";
571
+
572
+ console.log(libJsNumComma(1234567.8912, 2));
573
+ // "1,234,567.89"
574
+ ```
575
+
576
+ ### LibJsSecondsFormatterChinese-中文时间
577
+
578
+ 把秒数格式化成中文时间描述。
579
+
580
+ ```ts
581
+ import { libJsSecondsFormatterChinese } from "lyb-js/Formatter/LibJsSecondsFormatterChinese";
582
+
583
+ console.log(libJsSecondsFormatterChinese(90));
584
+ // "1分钟30秒"
585
+
586
+ console.log(libJsSecondsFormatterChinese(3661));
587
+ // "1小时1分钟1秒"
588
+ ```
589
+
590
+ ## Math-数学
591
+
592
+ ### LibJsCalculateExpression-表达式字符串
593
+
594
+ 计算只包含加减乘除和括号的表达式字符串。
595
+
596
+ ```ts
597
+ import { libJsCalculateExpression } from "lyb-js/Math/LibJsCalculateExpression";
598
+
599
+ console.log(libJsCalculateExpression("(1+2)-(3*4)/5"));
600
+ // 0.6
601
+
602
+ console.log(libJsCalculateExpression("10/3", 2));
603
+ // 3.33
604
+ ```
605
+
606
+ ### LibJsConvertAngle-角弧度互转
607
+
608
+ 在角度和弧度之间转换。
609
+
610
+ ```ts
611
+ import { libJsConvertAngle } from "lyb-js/Math/LibJsConvertAngle";
612
+
613
+ console.log(libJsConvertAngle(180, "rad")); // 3.141592653589793
614
+ console.log(libJsConvertAngle(Math.PI, "deg")); // 180
615
+ ```
616
+
617
+ ### LibJsCoordsAngle-两点角度
618
+
619
+ 计算两点连线角度,默认返回角度制。
620
+
621
+ ```ts
622
+ import { libJsCoordsAngle } from "lyb-js/Math/LibJsCoordsAngle";
623
+
624
+ console.log(libJsCoordsAngle({ x: 0, y: 0 }, { x: 1, y: 0 }));
625
+ console.log(libJsCoordsAngle({ x: 0, y: 0 }, { x: 1, y: 1 }, "rad"));
626
+ ```
627
+
628
+ ### LibJsCoordsDistance-两点距离
629
+
630
+ 计算两点之间的直线距离。
631
+
632
+ ```ts
633
+ import { libJsCoordsDistance } from "lyb-js/Math/LibJsCoordsDistance";
634
+
635
+ console.log(libJsCoordsDistance({ x: 0, y: 0 }, { x: 3, y: 4 }));
636
+ // 5
637
+ ```
638
+
639
+ ### LibJsDecimal-高精度计算
640
+
641
+ 使用 `decimal.js` 处理高精度加减乘除。
642
+
643
+ ```ts
644
+ import { libJsDecimal } from "lyb-js/Math/LibJsDecimal";
645
+
646
+ console.log(libJsDecimal(0.1, 0.2, "+", 2)); // 0.3
647
+ console.log(libJsDecimal(1.5, 0.3, "*", 2)); // 0.45
648
+ ```
649
+
650
+ ### LibJsLerp-线性插值
651
+
652
+ 文件名是 `LibJsLerp`,导出的方法名也是 `LibJsLerp`。
653
+
654
+ ```ts
655
+ import { LibJsLerp } from "lyb-js/Math/LibJsLerp";
656
+
657
+ console.log(LibJsLerp(0, 100, 0.25)); // 25
658
+ console.log(LibJsLerp(10, 20, 0.5)); // 15
659
+ ```
660
+
661
+ ### LibJsNormalizeInRange-范围归一化
662
+
663
+ 把一个值映射到 `0 ~ 1` 区间。
664
+
665
+ ```ts
666
+ import { LibJsNormalizeInRange } from "lyb-js/Math/LibJsNormalizeInRange";
667
+
668
+ console.log(LibJsNormalizeInRange(0, 100, 50)); // 0.5
669
+ console.log(LibJsNormalizeInRange(0, 100, 120)); // 1
670
+ ```
671
+
672
+ ## Misc-杂项
673
+
674
+ ### LibJsClassObservable-类属性监听器
675
+
676
+ 给普通对象状态加上监听、按键更新、批量订阅等能力。
677
+
678
+ ```ts
679
+ import { LibJsClassObservable } from "lyb-js/Misc/LibJsClassObservable";
680
+
681
+ const store = new LibJsClassObservable({
682
+ count: 0,
683
+ visible: false,
684
+ });
685
+
686
+ const off = store.onValue("count", (value) => {
687
+ console.log("count changed:", value);
688
+ });
689
+
690
+ store.setValue("count", 1);
691
+ store.setNumberValue("count", "add", 2);
692
+ store.setBooleanValue("visible");
693
+ off();
694
+ ```
695
+
696
+ ### LibJsEmitter-事件发射器
697
+
698
+ 创建一个带类型约束的轻量事件中心。
699
+
700
+ ```ts
701
+ import { LibJsEmitter } from "lyb-js/Misc/LibJsEmitter";
702
+
703
+ type Events = {
704
+ loaded: { page: string };
705
+ resize: [number, number];
706
+ };
707
+
708
+ const emitter = LibJsEmitter<Events>();
709
+
710
+ emitter.on("loaded", (payload) => {
711
+ console.log(payload.page);
712
+ });
713
+
714
+ emitter.on("resize", (w, h) => {
715
+ console.log(w, h);
716
+ });
717
+
718
+ emitter.emit("loaded", { page: "home" });
719
+ emitter.emit("resize", 1920, 1080);
720
+ emitter.clear();
721
+ ```
722
+
723
+ ### LibJsEmitterClose-一次性关闭监听
724
+
725
+ 注册一个事件名对应的回调,触发后会执行并自动清空这一组监听。
726
+
727
+ ```ts
728
+ import { LibJsEmitterClose } from "lyb-js/Misc/LibJsEmitterClose";
729
+
730
+ LibJsEmitterClose.on("dialog-close", () => {
731
+ console.log("弹窗关闭后执行一次");
732
+ });
733
+
734
+ LibJsEmitterClose.emit("dialog-close");
735
+ LibJsEmitterClose.emit("dialog-close"); // 第二次不会再次触发上面的回调
736
+ ```
737
+
738
+ ### LibJsHorizontal-游戏横版状态
739
+
740
+ 根据当前窗口或指定模式返回横竖屏状态和参考宽高。
741
+
742
+ ```ts
743
+ import { libJsHorizontal } from "lyb-js/Misc/LibJsHorizontal";
744
+
745
+ console.log(libJsHorizontal("auto"));
746
+ console.log(libJsHorizontal("h")); // 强制横版参考尺寸
747
+ console.log(libJsHorizontal("v")); // 强制竖版参考尺寸
748
+ ```
749
+
750
+ ### LibJsNumberStepper-数字步进器
751
+
752
+ 用于长按按钮连续加减数字。
753
+
754
+ ```ts
755
+ import { LibJsNumberStepper } from "lyb-js/Misc/LibJsNumberStepper";
756
+
757
+ let count = 0;
758
+ const stepper = new LibJsNumberStepper((type) => {
759
+ count = type === "add" ? count + 1 : count - 1;
760
+ console.log(count);
761
+ });
762
+
763
+ addButton.addEventListener("pointerdown", () => stepper.down("add"));
764
+ subButton.addEventListener("pointerdown", () => stepper.down("sub"));
765
+ ```
766
+
767
+ ### LibJsPruneEmpty-对象属性去空值
768
+
769
+ 递归移除对象中的空字符串、`null`、`undefined` 和空对象;空数组也会被去掉。
770
+
771
+ ```ts
772
+ import { libJsPruneEmpty } from "lyb-js/Misc/LibJsPruneEmpty";
773
+
774
+ const result = libJsPruneEmpty({
775
+ name: "Tom",
776
+ empty: "",
777
+ profile: {
778
+ age: undefined,
779
+ city: "Shanghai",
780
+ },
781
+ tags: [],
782
+ });
783
+
784
+ console.log(result);
785
+ // { name: "Tom", profile: { city: "Shanghai" } }
786
+ ```
787
+
788
+ ### LibJsPullUpLoad-上拉加载
789
+
790
+ 给滚动容器添加“滚到底部自动触发加载”的能力,并可观察当前加载状态。
791
+
792
+ ```ts
793
+ import { LibJsPullUpLoad } from "lyb-js/Misc/LibJsPullUpLoad";
794
+
795
+ const pull = new LibJsPullUpLoad({
796
+ scrollEl: document.querySelector(".list") as HTMLElement,
797
+ loadStatusEl: document.querySelector(".load-status") as HTMLElement,
798
+ onLoad() {
799
+ console.log("触发加载更多");
800
+ },
801
+ });
802
+
803
+ pull.onValue("statusText", (text) => {
804
+ console.log("状态文案:", text);
805
+ });
806
+
807
+ pull.setValue("loadStatus", "idle");
808
+ ```
809
+
810
+ ### LibJsRegFormValidate-表单验证
811
+
812
+ 通过正则或自定义函数批量校验表单字段。
813
+
814
+ ```ts
815
+ import { libJsRegFormValidate } from "lyb-js/Misc/LibJsRegFormValidate";
816
+
817
+ const result = libJsRegFormValidate(
818
+ { phone: "13800138000", code: "12" },
819
+ [
820
+ {
821
+ key: "phone",
822
+ name: "手机号",
823
+ verify: /^1\\d{10}$/,
824
+ msg: "手机号格式错误",
825
+ },
826
+ {
827
+ key: "code",
828
+ name: "验证码",
829
+ verify: (value) => String(value).length === 6,
830
+ msg: "验证码长度错误",
831
+ },
832
+ ]
833
+ );
834
+
835
+ console.log(result);
836
+ ```
837
+
838
+ ### LibJsRetryRequest-请求重连
839
+
840
+ 请求失败后自动按次数重试。
841
+
842
+ ```ts
843
+ import { libJsRetryRequest } from "lyb-js/Misc/LibJsRetryRequest";
844
+
845
+ const data = await libJsRetryRequest({
846
+ promiseFn: (id?: number) => fetch(`/api/user/${id}`).then((res) => res.json()),
847
+ params: 1,
848
+ maxRetries: 3,
849
+ retryDelay: 1000,
850
+ });
851
+
852
+ console.log(data);
853
+ ```
854
+
855
+ ## Random-随机
856
+
857
+ ### LibJsProbabilityResult-概率触发
858
+
859
+ 按百分比返回是否触发成功。
860
+
861
+ ```ts
862
+ import { libJsProbabilityResult } from "lyb-js/Random/LibJsProbabilityResult";
863
+
864
+ console.log(libJsProbabilityResult(30)); // 30% 概率返回 true
865
+ ```
866
+
867
+ ### LibJsRandom-随机数
868
+
869
+ 获取指定范围内的随机数,可设置保留小数位。
870
+
871
+ ```ts
872
+ import { libJsRandom } from "lyb-js/Random/LibJsRandom";
873
+
874
+ console.log(libJsRandom(1, 10)); // 1 ~ 10
875
+ console.log(libJsRandom(1, 10, 2)); // 例如 6.38
876
+ ```
877
+
878
+ ### LibJsRandomColor-随机色
879
+
880
+ 生成随机 RGBA 颜色字符串。
881
+
882
+ ```ts
883
+ import { libJsRandomColor } from "lyb-js/Random/LibJsRandomColor";
884
+
885
+ console.log(libJsRandomColor()); // "rgba(...)"
886
+ console.log(libJsRandomColor(0.5)); // 半透明
887
+ ```
888
+
889
+ ### LibJsUniqueRandomNumbers-随机数数组
890
+
891
+ 在指定区间中生成不重复的随机数数组。
892
+
893
+ ```ts
894
+ import { libJsUniqueRandomNumbers } from "lyb-js/Random/LibJsUniqueRandomNumbers";
895
+
896
+ console.log(libJsUniqueRandomNumbers(1, 10, 4));
897
+ // 例如 [3, 7, 1, 9]
898
+ ```
899
+
900
+ ## Time-时间
901
+
902
+ ### LibJsCountdown-倒计时
903
+
904
+ 根据结束时间返回倒计时对象,可指定保留的最小单位。
905
+
906
+ ```ts
907
+ import { libJsCountdown } from "lyb-js/Time/LibJsCountdown";
908
+
909
+ const endTime = Date.now() + 5 * 60 * 1000;
910
+ console.log(libJsCountdown(endTime, "minute"));
911
+ // { years, months, days, hours, minutes, seconds, ended }
912
+ ```
913
+
914
+ ### LibJsSameTimeCheck-时间比对
915
+
916
+ 比较传入时间和当前时间是否处于同一个时间单位。
917
+
918
+ ```ts
919
+ import { libJsSameTimeCheck } from "lyb-js/Time/LibJsSameTimeCheck";
920
+
921
+ console.log(libJsSameTimeCheck(Date.now(), "day")); // 0
922
+ console.log(libJsSameTimeCheck(Date.now() - 86400000, "day")); // 1
923
+ ```
924
+
925
+ 返回值说明:
926
+
927
+ - `0`:同一时间单位
928
+ - `1`:比当前更早
929
+ - `-1`:比当前更晚
930
+
931
+ ### LibJsTimeAgo-中文时间差
932
+
933
+ 把时间戳转成“多久前 / 多久后”形式的中文文案。
934
+
935
+ ```ts
936
+ import { libJsTimeAgo } from "lyb-js/Time/LibJsTimeAgo";
937
+
938
+ console.log(libJsTimeAgo(Date.now() - 60 * 60 * 1000));
939
+ console.log(libJsTimeAgo(Date.now() + 2 * 24 * 60 * 60 * 1000));
940
+ ```
941
+
942
+ ### LibJsTimeGreeting-时间问候
943
+
944
+ 根据当前时间返回默认问候语,也可以传入自定义文案。
945
+
946
+ ```ts
947
+ import { libJsTimeGreeting } from "lyb-js/Time/LibJsTimeGreeting";
948
+
949
+ console.log(libJsTimeGreeting());
950
+
951
+ console.log(
952
+ libJsTimeGreeting({
953
+ morning: "早安",
954
+ afternoon: "下午好",
955
+ evening: "晚上好",
956
+ })
957
+ );
958
+ ```
959
+
960
+ ## 补充说明
961
+
962
+ - 文档里的导入路径以 `npm` 目录实际产物为准。
963
+ - 个别工具存在“文件名和导出名不同”的情况,README 已在对应章节中显式说明。
964
+ - 如果你想快速查某个工具支持什么参数,除了 README,也可以直接查看导入时的 TypeScript / JSDoc 提示。