nhanh-pure-function 1.0.0 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/Index.d.ts ADDED
@@ -0,0 +1,3 @@
1
+ export * from "./Utility";
2
+ export * from "./User";
3
+
package/lib/Index.js CHANGED
@@ -1,5 +1,3 @@
1
- import * as Utility from "./Utility.js";
2
- import * as User from "./User.js";
3
-
4
- const _PureFunction = { Utility, User };
5
- export default _PureFunction;
1
+ export * from "./Utility";
2
+ export * from "./User";
3
+
package/lib/User.d.ts CHANGED
@@ -3,7 +3,10 @@
3
3
  * @param {滚动标签} element
4
4
  * @param {触底事件} callback
5
5
  */
6
- export function _AddScrollBottomListener(element: HTMLElement, callback: Function): void;
6
+ export function _AddScrollBottomListener(
7
+ element: HTMLElement,
8
+ callback: Function
9
+ ): void;
7
10
 
8
11
  /**
9
12
  * 自动处理 currentPage * pageSize > total
@@ -183,3 +186,71 @@ export class _LoadingController {
183
186
  // stopLoading方法的类型定义
184
187
  stopLoading(invoker: any, key?: string): void;
185
188
  }
189
+
190
+ type UiLibrary = "naiveUI" | "ElementPlus" | "Element";
191
+ /**
192
+ * 点击非指定dom(包含子级dom)时执行 callback
193
+ * @param clickableSelector 允许点击的 dom 选择器
194
+ * @param callback 满足条件时执行的回调
195
+ * @param uiLibrary 项目使用的 ui库 , 用于排除 ui库 创建的元素 , 避免点击 ui库 创建的元素时意外的执行 callback
196
+ */
197
+ export function _CloseOnOutsideClick(
198
+ clickableSelector: string[],
199
+ callback: Function,
200
+ uiLibrary?: UiLibrary[]
201
+ ): void;
202
+
203
+ /** 拖拽配置 */
204
+ type DragOption = {
205
+ /** 拖拽范围限制 */
206
+ limit?: {
207
+ max: {
208
+ top: number;
209
+ left: number;
210
+ };
211
+ min: {
212
+ top: number;
213
+ left: number;
214
+ };
215
+ };
216
+ /** 指定的拖拽元素 */
217
+ dragDom?: HTMLElement;
218
+ };
219
+ /** 拖拽 */
220
+ export class Drag {
221
+ /**
222
+ * 初始化拖拽
223
+ * @param dom 被拖拽的元素
224
+ * @param option 拖拽配置
225
+ */
226
+ init(dom: HTMLElement, option?: DragOption): void;
227
+ /** 结束拖拽 */
228
+ finish(): void;
229
+ }
230
+
231
+ /** 更新后的位置信息 */
232
+ type UpdateValue = {
233
+ top: number;
234
+ left: number;
235
+ percentage?: {
236
+ top: number;
237
+ left: number;
238
+ };
239
+ };
240
+ /** 局部拖拽配置 */
241
+ type LocalDragOptions = {
242
+ limit?: DragOption["limit"];
243
+ update_move?: (value: UpdateValue) => void | undefined;
244
+ update_up?: (value: UpdateValue) => void | undefined;
245
+ };
246
+ /** 局部拖拽 计算位置距离/百分比 */
247
+ export class LocalDrag {
248
+ /**
249
+ * 初始化拖拽
250
+ * @param parentDom 被拖拽元素的祖先元素
251
+ * @param option 局部拖拽配置
252
+ */
253
+ init(parentDom: HTMLElement, options: LocalDragOptions = {}): void;
254
+ /** 结束拖拽 */
255
+ finish(): void;
256
+ }
package/lib/User.js CHANGED
@@ -1,4 +1,4 @@
1
- import { _IsObject, _IsWithinErrorMargin, _NotNull } from './Utility';
1
+ import { _IsObject, _IsWithinErrorMargin, _NotNull } from "./Utility";
2
2
 
3
3
  /**
4
4
  * 添加滚动触底事件
@@ -6,8 +6,14 @@ import { _IsObject, _IsWithinErrorMargin, _NotNull } from './Utility';
6
6
  * @param {触底事件} callback
7
7
  */
8
8
  export function _AddScrollBottomListener(element, callback) {
9
- element.addEventListener('scroll', function () {
10
- if (_IsWithinErrorMargin(element.scrollTop + element.clientHeight, element.scrollHeight, 2)) {
9
+ element.addEventListener("scroll", function () {
10
+ if (
11
+ _IsWithinErrorMargin(
12
+ element.scrollTop + element.clientHeight,
13
+ element.scrollHeight,
14
+ 2
15
+ )
16
+ ) {
11
17
  callback();
12
18
  }
13
19
  });
@@ -53,25 +59,25 @@ export function _FormatNumberWithUnit(number, config = {}) {
53
59
  const { join, suffix, integer } = Object.assign(
54
60
  {
55
61
  join: true,
56
- suffix: '',
57
- integer: false
62
+ suffix: "",
63
+ integer: false,
58
64
  },
59
65
  config
60
66
  );
61
67
 
62
68
  function _join(value, suffix, plus = true) {
63
- value = (plus ? '' : '-') + value;
69
+ value = (plus ? "" : "-") + value;
64
70
  if (join) return value + suffix;
65
71
  else return [value, suffix];
66
72
  }
67
73
 
68
- if (typeof number == 'string') {
74
+ if (typeof number == "string") {
69
75
  if (!/^\d+$/.test(number.trim())) {
70
- console.error('错误输入:', number);
76
+ console.error("错误输入:", number);
71
77
  return _join(0, suffix);
72
78
  }
73
- } else if (typeof number != 'number') {
74
- console.error('错误输入:', number);
79
+ } else if (typeof number != "number") {
80
+ console.error("错误输入:", number);
75
81
  return _join(0, suffix);
76
82
  }
77
83
 
@@ -83,7 +89,21 @@ export function _FormatNumberWithUnit(number, config = {}) {
83
89
  const plus = number >= 0;
84
90
  number = Math.abs(number);
85
91
 
86
- const units = ['', '万', '亿', '兆', '京', '垓', '秭', '穰', '沟', '涧', '正', '载', '极'];
92
+ const units = [
93
+ "",
94
+ "万",
95
+ "亿",
96
+ "兆",
97
+ "京",
98
+ "垓",
99
+ "秭",
100
+ "穰",
101
+ "沟",
102
+ "涧",
103
+ "正",
104
+ "载",
105
+ "极",
106
+ ];
87
107
  const digits = Math.floor(Math.log10(number) / 4); // 计算位数
88
108
 
89
109
  // 不超过万位的数字直接返回
@@ -109,16 +129,17 @@ export function _FormatNumberWithUnit(number, config = {}) {
109
129
  */
110
130
  export function _SetQuantifierAttribute(data, options = []) {
111
131
  if (!_IsObject(data)) {
112
- console.error('异常输入:', data);
132
+ console.error("异常输入:", data);
113
133
  return data;
114
134
  }
115
135
 
116
136
  options.forEach((item) => {
117
- if (typeof item === 'string') {
137
+ if (typeof item === "string") {
118
138
  data[item] = _FormatNumberWithUnit(data[item]);
119
139
  } else if (Array.isArray(item)) {
120
140
  const [label, config] = data[item];
121
- if (_NotNull(label) && _IsObject(config)) data[label] = _FormatNumberWithUnit(label, config);
141
+ if (_NotNull(label) && _IsObject(config))
142
+ data[label] = _FormatNumberWithUnit(label, config);
122
143
  }
123
144
  });
124
145
  return data;
@@ -132,11 +153,11 @@ export function _SetQuantifierAttribute(data, options = []) {
132
153
  */
133
154
  export function _SetDefaultValue(data, options = {}) {
134
155
  if (!_IsObject(data)) {
135
- console.error('异常输入:', data);
156
+ console.error("异常输入:", data);
136
157
  return data;
137
158
  }
138
159
 
139
- const { defaultValue = '--', fieldsNotRequiringAction } = options;
160
+ const { defaultValue = "--", fieldsNotRequiringAction } = options;
140
161
 
141
162
  for (const key in data) {
142
163
  if (Object.prototype.hasOwnProperty.call(data, key)) {
@@ -162,7 +183,7 @@ export function _SetDefaultValue(data, options = {}) {
162
183
  */
163
184
  export function _SetDictionary(data, options = {}) {
164
185
  if (!_IsObject(data)) {
165
- console.error('异常输入:', data);
186
+ console.error("异常输入:", data);
166
187
  return data;
167
188
  }
168
189
 
@@ -170,7 +191,7 @@ export function _SetDictionary(data, options = {}) {
170
191
  dictionaryLabel = [],
171
192
  dictionaryLabelJoin = [],
172
193
  dictionaryOptions,
173
- defaultValue = '--'
194
+ defaultValue = "--",
174
195
  } = options;
175
196
 
176
197
  if (dictionaryOptions) {
@@ -188,11 +209,11 @@ export function _SetDictionary(data, options = {}) {
188
209
  }
189
210
  });
190
211
  dictionaryLabelJoin.forEach((label) => {
191
- if (_NotNull(data[label]) && data[label] != '') {
212
+ if (_NotNull(data[label]) && data[label] != "") {
192
213
  const options = dictionaryOptions[label];
193
214
  if (options) {
194
- const oldvalue = data[label].split(',');
195
- data[label] = '';
215
+ const oldvalue = data[label].split(",");
216
+ data[label] = "";
196
217
  oldvalue.forEach((_label) => {
197
218
  data[label] += options[_label];
198
219
  });
@@ -216,7 +237,7 @@ export function _SetDictionary(data, options = {}) {
216
237
  */
217
238
  export function _SetPhoto(data, options = {}) {
218
239
  if (!_IsObject(data)) {
219
- console.error('异常输入:', data);
240
+ console.error("异常输入:", data);
220
241
  return data;
221
242
  }
222
243
 
@@ -226,8 +247,8 @@ export function _SetPhoto(data, options = {}) {
226
247
  label.forEach((label) => {
227
248
  const defaultValue = (defaultUrl && defaultUrl[label]) || [];
228
249
  const value = data[label];
229
- if (typeof value === 'string') {
230
- data[label] = value.split(',').filter(Boolean);
250
+ if (typeof value === "string") {
251
+ data[label] = value.split(",").filter(Boolean);
231
252
  } else {
232
253
  data[label] = defaultValue;
233
254
  }
@@ -245,7 +266,7 @@ export function _SetPhoto(data, options = {}) {
245
266
  */
246
267
  export function _Exhibit_details(data, options = {}) {
247
268
  if (!_IsObject(data)) {
248
- console.error('异常输入:', data);
269
+ console.error("异常输入:", data);
249
270
  return {};
250
271
  }
251
272
 
@@ -263,19 +284,19 @@ export function _Exhibit_details(data, options = {}) {
263
284
 
264
285
  filterLabel = [],
265
286
 
266
- defaultValue = '--'
287
+ defaultValue = "--",
267
288
  } = options;
268
289
 
269
290
  _SetDictionary(data, {
270
291
  dictionaryLabel,
271
292
  dictionaryLabelJoin,
272
293
  dictionaryOptions,
273
- defaultValue
294
+ defaultValue,
274
295
  });
275
296
 
276
297
  _SetPhoto(data, {
277
298
  label: photoLabel,
278
- defaultUrl: photoDefaultUrl
299
+ defaultUrl: photoDefaultUrl,
279
300
  });
280
301
 
281
302
  _SetQuantifierAttribute(data, quantifierLabel);
@@ -288,12 +309,12 @@ export function _Exhibit_details(data, options = {}) {
288
309
  .concat(
289
310
  quantifierLabel
290
311
  .map((item) => {
291
- if (typeof item == 'string') return item;
312
+ if (typeof item == "string") return item;
292
313
  if (Array.isArray(item)) return item[0];
293
314
  })
294
315
  .filter(Boolean)
295
316
  )
296
- .concat(filterLabel)
317
+ .concat(filterLabel),
297
318
  });
298
319
 
299
320
  return data;
@@ -304,14 +325,14 @@ export class _LoadingController {
304
325
  #controllersCollection = new Map();
305
326
  constructor() {}
306
327
 
307
- addController(key = 'default', config) {
328
+ addController(key = "default", config) {
308
329
  if (this.#controllersCollection.has(key))
309
- throw new Error('key为: ' + key + ' 的loading控制器已存在, 请重命名。');
330
+ throw new Error("key为: " + key + " 的loading控制器已存在, 请重命名。");
310
331
 
311
332
  const {
312
333
  loadingState /** 更新/获取 loading 状态的方法 */,
313
334
  delayTime = 200 /** 延迟时间 */,
314
- minDisplayTime = 400 /** 最少显示时间 */
335
+ minDisplayTime = 400 /** 最少显示时间 */,
315
336
  } = config;
316
337
  this.#controllersCollection.set(key, {
317
338
  invokers: new Set(),
@@ -319,7 +340,7 @@ export class _LoadingController {
319
340
  startTime: 0,
320
341
  loadingState,
321
342
  delayTime,
322
- minDisplayTime
343
+ minDisplayTime,
323
344
  });
324
345
  }
325
346
 
@@ -327,9 +348,10 @@ export class _LoadingController {
327
348
  this.#controllersCollection.delete(key);
328
349
  }
329
350
 
330
- getController(key = 'default') {
351
+ getController(key = "default") {
331
352
  const controller = this.#controllersCollection.get(key);
332
- if (!controller) throw new Error('还未添加key为: ' + key + ' 的loading控制器');
353
+ if (!controller)
354
+ throw new Error("还未添加key为: " + key + " 的loading控制器");
333
355
  return controller;
334
356
  }
335
357
 
@@ -378,10 +400,235 @@ export class _LoadingController {
378
400
  if (displayTime >= minDisplayTime) {
379
401
  this.resetController(key);
380
402
  } else {
381
- setTimeout(() => this.stopLoading(invoker, key), displayTime - minDisplayTime);
403
+ setTimeout(
404
+ () => this.stopLoading(invoker, key),
405
+ displayTime - minDisplayTime
406
+ );
382
407
  }
383
408
  } else {
384
409
  invokers.delete(invoker);
385
410
  }
386
411
  }
387
412
  }
413
+
414
+ /**
415
+ * 点击非指定dom(包含子级dom)时执行 callback
416
+ * @param clickableSelector 允许点击的 dom 选择器
417
+ * @param callback 满足条件时执行的回调
418
+ * @param uiLibrary 项目使用的 ui库 , 用于排除 ui库 创建的元素 , 避免点击 ui库 创建的元素时意外的执行 callback
419
+ */
420
+ export function _CloseOnOutsideClick(
421
+ clickableSelector,
422
+ callback,
423
+ uiLibrary = ["naiveUI", "ElementPlus", "Element"]
424
+ ) {
425
+ function mousedown(event) {
426
+ const target = event.target;
427
+ const UI = (function (obj) {
428
+ const arr = [];
429
+ for (const key in obj) {
430
+ if (Object.hasOwnProperty.call(obj, key)) {
431
+ if (uiLibrary.includes(key)) arr.concat(obj[key]);
432
+ }
433
+ }
434
+ return arr;
435
+ })({
436
+ naiveUI: [".v-binder-follower-container"],
437
+ ElementPlus: ["el-popper"],
438
+ Element: ["el-popper"],
439
+ });
440
+ const isClickable = clickableSelector
441
+ .concat(UI)
442
+ .some((className) => Boolean(target?.closest(className)));
443
+
444
+ if (!isClickable) {
445
+ callback();
446
+ document.removeEventListener("mousedown", mousedown);
447
+ }
448
+ }
449
+ requestAnimationFrame(() =>
450
+ document.addEventListener("mousedown", mousedown)
451
+ );
452
+ }
453
+
454
+ /** 拖拽dom */
455
+ export class Drag {
456
+ #dom = null;
457
+ #isAllowed = false;
458
+ #eventFunction = {};
459
+ #pageX = 0;
460
+ #pageY = 0;
461
+ #top = 0;
462
+ #left = 0;
463
+ #limit;
464
+ #dragDom;
465
+
466
+ init(dom, option) {
467
+ this.#dom = dom;
468
+ this.#limit = option?.limit;
469
+ this.#dragDom = option?.dragDom;
470
+ this.#eventFunction = {
471
+ mousedown: this.mousedown.bind(this),
472
+ mousemove: this.mousemove.bind(this),
473
+ mouseup: this.mouseup.bind(this),
474
+ };
475
+
476
+ this.bindOrUnbindEvent("bind");
477
+ }
478
+ finish() {
479
+ this.bindOrUnbindEvent("unbind");
480
+ }
481
+ bindOrUnbindEvent(type) {
482
+ const EventType =
483
+ type === "bind" ? "addEventListener" : "removeEventListener";
484
+ if (!this.#dom) return console.error("No DOM");
485
+
486
+ this.#dom[EventType]("mousedown", this.#eventFunction.mousedown);
487
+ document[EventType]("mousemove", this.#eventFunction.mousemove);
488
+ document[EventType]("mouseup", this.#eventFunction.mouseup);
489
+ }
490
+ alterLocation() {
491
+ if (!this.#dom) return console.error("No DOM");
492
+ if (this.#limit) {
493
+ this.#top = Math.min(this.#top, this.#limit.max.top);
494
+ this.#top = Math.max(this.#top, this.#limit.min.top);
495
+ this.#left = Math.min(this.#left, this.#limit.max.left);
496
+ this.#left = Math.max(this.#left, this.#limit.min.left);
497
+ }
498
+ this.#dom.style.setProperty("--top", this.#top + "px");
499
+ this.#dom.style.setProperty("--left", this.#left + "px");
500
+ }
501
+ mousedown(event) {
502
+ if (!this.#dom) return console.error("No DOM");
503
+ if (this.#dragDom && event.target != this.#dragDom) return;
504
+ document.body.classList.add("no-select");
505
+
506
+ this.#isAllowed = true;
507
+ const clientRect = this.#dom.getBoundingClientRect();
508
+
509
+ const { pageX, pageY } = event;
510
+ this.#pageX = pageX;
511
+ this.#pageY = pageY;
512
+ this.#top = clientRect.y;
513
+ this.#left = clientRect.x;
514
+ }
515
+ mousemove(event) {
516
+ const { pageX, pageY } = event;
517
+ if (this.#isAllowed) {
518
+ this.#top += pageY - this.#pageY;
519
+ this.#left += pageX - this.#pageX;
520
+ this.#pageX = pageX;
521
+ this.#pageY = pageY;
522
+
523
+ this.alterLocation();
524
+ }
525
+ }
526
+ mouseup() {
527
+ if (this.#isAllowed) {
528
+ this.#isAllowed = false;
529
+ document.body.classList.remove("no-select");
530
+ }
531
+ }
532
+ }
533
+
534
+ /** 局部拖拽 计算位置距离/百分比 */
535
+ export class LocalDrag {
536
+ #parentDom = null;
537
+ #isAllowed = false;
538
+ #eventFunction = {};
539
+ #clientRectX = 0;
540
+ #clientRectY = 0;
541
+ #top = 0;
542
+ #left = 0;
543
+ #limit;
544
+ #update_move;
545
+ #update_up;
546
+
547
+ init(parentDom, options = {}) {
548
+ this.#parentDom = parentDom;
549
+ this.#limit = options.limit;
550
+ this.#update_move = options.update_move;
551
+ this.#update_up = options.update_up;
552
+ this.#eventFunction = {
553
+ mousedown: this.mousedown.bind(this),
554
+ mousemove: this.mousemove.bind(this),
555
+ mouseup: this.mouseup.bind(this),
556
+ };
557
+
558
+ this.bindOrUnbindEvent("bind");
559
+ }
560
+ finish() {
561
+ this.bindOrUnbindEvent("unbind");
562
+ }
563
+ bindOrUnbindEvent(type) {
564
+ const EventType =
565
+ type === "bind" ? "addEventListener" : "removeEventListener";
566
+ if (!this.#parentDom) return window.customize_error("No DOM");
567
+
568
+ this.#parentDom[EventType]("mousedown", this.#eventFunction.mousedown);
569
+ document[EventType]("mousemove", this.#eventFunction.mousemove);
570
+ document[EventType]("mouseup", this.#eventFunction.mouseup);
571
+ }
572
+ updateValue() {
573
+ const value = {
574
+ top: this.#top,
575
+ left: this.#left,
576
+ };
577
+ if (this.#limit) {
578
+ const v = (type) =>
579
+ this.#limit
580
+ ? (value[type] - this.#limit.min[type]) /
581
+ (this.#limit.max[type] - this.#limit.min[type])
582
+ : 0;
583
+
584
+ value.percentage = {
585
+ top: v("top") || 0,
586
+ left: v("left") || 0,
587
+ };
588
+ }
589
+ return value;
590
+ }
591
+ alterLocation() {
592
+ if (!this.#parentDom) return window.customize_error("No DOM");
593
+ if (this.#limit) {
594
+ this.#top = Math.min(this.#top, this.#limit.max.top);
595
+ this.#top = Math.max(this.#top, this.#limit.min.top);
596
+ this.#left = Math.min(this.#left, this.#limit.max.left);
597
+ this.#left = Math.max(this.#left, this.#limit.min.left);
598
+ }
599
+ if (this.#update_move) this.#update_move(this.updateValue());
600
+
601
+ this.#parentDom.style.setProperty("--top", this.#top + "px");
602
+ this.#parentDom.style.setProperty("--left", this.#left + "px");
603
+ }
604
+ mousedown(event) {
605
+ if (!this.#parentDom) return window.customize_error("No DOM");
606
+ document.body.classList.add("no-select");
607
+
608
+ this.#isAllowed = true;
609
+ const clientRect = this.#parentDom.getBoundingClientRect();
610
+ this.#clientRectY = clientRect.y;
611
+ this.#clientRectX = clientRect.x;
612
+
613
+ const { pageX, pageY } = event;
614
+ this.#top = pageY - this.#clientRectY;
615
+ this.#left = pageX - this.#clientRectX;
616
+
617
+ this.alterLocation();
618
+ }
619
+ mousemove(event) {
620
+ const { pageX, pageY } = event;
621
+ if (this.#isAllowed) {
622
+ this.#top = pageY - this.#clientRectY;
623
+ this.#left = pageX - this.#clientRectX;
624
+ this.alterLocation();
625
+ }
626
+ }
627
+ mouseup() {
628
+ if (this.#isAllowed) {
629
+ this.#isAllowed = false;
630
+ document.body.classList.remove("no-select");
631
+ if (this.#update_up) this.#update_up(this.updateValue());
632
+ }
633
+ }
634
+ }
package/lib/Utility.d.ts CHANGED
@@ -104,27 +104,30 @@ export function _ReadFile(src: string): Promise<string>;
104
104
 
105
105
  /**
106
106
  * 下载文件
107
- * @param {文件路径} href
108
- * @param {导出文件名} fileName
107
+ * @param {string} href 文件路径
108
+ * @param {string} fileName 导出文件名
109
109
  */
110
110
  export function _DownloadFile(href: string, fileName?: string): void;
111
111
 
112
112
  /**
113
113
  * 获取帧率
114
- * @param {Function} callback callback( 帧率 , 每帧时间 )
114
+ * @param {(fps , frameTime)=>void} callback callback( 帧率 , 每帧时间 )
115
115
  * @param {Number} referenceNode 参考节点数量
116
116
  */
117
117
  export function _GetFrameRate(
118
- callback: Function,
118
+ callback: (fps: number, frameTime: number) => void,
119
119
  referenceNode: number = 10
120
120
  ): void;
121
121
 
122
122
  /**
123
123
  * 进度
124
- * @param {Function} callback callback( 进度百分比 )
124
+ * @param {(schedule)=>void} callback callback( 进度百分比 )
125
125
  * @param {Number} TIME 总时长
126
126
  */
127
- export function _Schedule(callback: Function, TIME: number = 500): void;
127
+ export function _Schedule(
128
+ callback: (schedule: number) => void,
129
+ TIME: number = 500
130
+ ): void;
128
131
 
129
132
  /**
130
133
  * 格式化数字,给数字加上千位分隔符。
@@ -150,3 +153,15 @@ export function _ConvertToCamelCase(
150
153
  str: string,
151
154
  isRemoveDelimiter?: boolean
152
155
  ): string;
156
+
157
+ /**
158
+ * 创建文件并下载
159
+ * @param {BlobPart[]} content 文件内容
160
+ * @param {string} fileName 文件名称
161
+ * @param {BlobPropertyBag} options Blob 配置
162
+ */
163
+ export function _CreateAndDownloadFile(
164
+ content: BlobPart[],
165
+ fileName: string,
166
+ options?: BlobPropertyBag
167
+ ): void;
package/lib/Utility.js CHANGED
@@ -228,32 +228,32 @@ export function _DownloadFile(href, fileName) {
228
228
 
229
229
  /**
230
230
  * 获取帧率
231
- * @param {Function} callback callback( 帧率 , 每帧时间 )
231
+ * @param {(fps , frameTime)=>void} callback callback( 帧率 , 每帧时间 )
232
232
  * @param {Number} referenceNode 参考节点数量
233
233
  */
234
234
  export function _GetFrameRate(callback, referenceNode = 10) {
235
235
  let t,
236
- arr = [];
237
- function loop(time) {
238
- if (t) {
239
- arr.push(time - t);
240
- let l = arr.length;
241
- if (l >= referenceNode) {
242
- let num = arr.reduce((a, b) => a + b, 0);
243
- num /= l;
244
- callback(1000 / num, num);
245
- return;
246
- }
236
+ l = referenceNode;
237
+ function loop() {
238
+ if (l > 0) {
239
+ l--;
240
+ requestAnimationFrame(loop);
241
+ } else {
242
+ const time = new Date() - t;
243
+ const frameTime = time / referenceNode;
244
+ const fps = 1000 / frameTime;
245
+ callback(Number(fps.toFixed(2)), Number(frameTime.toFixed(2)));
247
246
  }
248
- t = time;
249
- requestAnimationFrame(loop);
250
247
  }
251
- requestAnimationFrame(loop);
248
+ requestAnimationFrame(() => {
249
+ t = new Date() - 0;
250
+ loop();
251
+ });
252
252
  }
253
253
 
254
254
  /**
255
255
  * 进度
256
- * @param {Function} callback callback( 进度百分比 )
256
+ * @param {(schedule)=>void} callback callback( 进度百分比 )
257
257
  * @param {Number} TIME 总时长
258
258
  */
259
259
  export function _Schedule(callback, TIME = 500) {
@@ -302,3 +302,29 @@ export function _ConvertToCamelCase(str, isRemoveDelimiter) {
302
302
  if (isRemoveDelimiter) return str.replace(/[^a-zA-Z]+/g, "");
303
303
  return str;
304
304
  }
305
+
306
+ /**
307
+ * 创建文件并下载
308
+ * @param {BlobPart[]} content 文件内容
309
+ * @param {string} fileName 文件名称
310
+ * @param {BlobPropertyBag} options Blob 配置
311
+ */
312
+ export function _CreateAndDownloadFile(content, fileName, options) {
313
+ if (!options) {
314
+ let type = fileName.replace(/^[^.]+./, "");
315
+ type = type == fileName ? "text/plain" : "application/" + type;
316
+ options = { type };
317
+ }
318
+ const bolb = new Blob(content, options);
319
+ // 创建一个 URL,该 URL 可以用于在浏览器中引用 Blob 对象(例如,在 <a> 标签的 href 属性中)
320
+ const url = URL.createObjectURL(bolb);
321
+ // 你可以创建一个链接来下载这个 Blob 对象
322
+ const downloadLink = document.createElement("a");
323
+ downloadLink.href = url;
324
+ downloadLink.download = fileName; // 设置下载文件的名称
325
+ document.body.appendChild(downloadLink); // 添加到文档中
326
+ downloadLink.click(); // 模拟点击以开始下载
327
+ document.body.removeChild(downloadLink); // 然后从文档中移除
328
+ // 最后,别忘了撤销 Blob 对象的 URL,以释放资源
329
+ URL.revokeObjectURL(url);
330
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nhanh-pure-function",
3
- "version": "1.0.0",
3
+ "version": "1.1.0",
4
4
  "description": "纯函数工具",
5
5
  "main": "lib/Index.js",
6
6
  "scripts": {