clickgo 3.1.1-dev10 → 3.1.3-dev12

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.
Files changed (106) hide show
  1. package/dist/app/demo/app.js +0 -72
  2. package/dist/app/demo/config.json +109 -0
  3. package/dist/app/demo/form/control/button/button.js +12 -11
  4. package/dist/app/demo/form/control/button/button.xml +6 -6
  5. package/dist/app/demo/form/control/check/check.js +14 -10
  6. package/dist/app/demo/form/control/file/file.js +15 -13
  7. package/dist/app/demo/form/control/{overflow/overflow.css → flow/flow.css} +0 -0
  8. package/dist/app/demo/form/control/flow/flow.js +64 -0
  9. package/dist/app/demo/form/control/{overflow/overflow.scss → flow/flow.scss} +0 -0
  10. package/dist/app/demo/form/control/flow/flow.xml +101 -0
  11. package/dist/app/demo/form/control/form/form.js +1 -1
  12. package/dist/app/demo/form/control/form/form.xml +3 -3
  13. package/dist/app/demo/form/control/img/img.xml +2 -2
  14. package/dist/app/demo/form/control/list/list.js +95 -75
  15. package/dist/app/demo/form/control/list/list.xml +15 -11
  16. package/dist/app/demo/form/control/marquee/marquee.js +12 -10
  17. package/dist/app/demo/form/control/menu/menu.js +10 -6
  18. package/dist/app/demo/form/control/monaco/monaco.js +50 -60
  19. package/dist/app/demo/form/control/monaco/monaco.xml +6 -5
  20. package/dist/app/demo/form/control/property/property.js +131 -127
  21. package/dist/app/demo/form/control/radio/radio.js +9 -5
  22. package/dist/app/demo/form/control/scroll/scroll.js +16 -12
  23. package/dist/app/demo/form/control/scroll/scroll.xml +10 -10
  24. package/dist/app/demo/form/control/select/select.js +132 -71
  25. package/dist/app/demo/form/control/select/select.xml +69 -67
  26. package/dist/app/demo/form/control/tab/tab.js +21 -20
  27. package/dist/app/demo/form/control/tab/tab.xml +2 -2
  28. package/dist/app/demo/form/control/text/text.js +53 -45
  29. package/dist/app/demo/form/control/text/text.xml +3 -3
  30. package/dist/app/demo/form/control/{greatview/greatview.css → vflow/vflow.css} +0 -0
  31. package/dist/app/demo/form/control/vflow/vflow.js +79 -0
  32. package/dist/app/demo/form/control/{greatview/greatview.scss → vflow/vflow.scss} +0 -0
  33. package/dist/app/demo/form/control/{greatview/greatview.xml → vflow/vflow.xml} +25 -25
  34. package/dist/app/demo/form/event/form/form.js +58 -56
  35. package/dist/app/demo/form/event/form/form.xml +3 -3
  36. package/dist/app/demo/form/event/screen/screen.js +30 -28
  37. package/dist/app/demo/form/event/screen/screen.xml +2 -2
  38. package/dist/app/demo/form/event/task/task.js +31 -31
  39. package/dist/app/demo/form/event/task/task.xml +3 -3
  40. package/dist/app/demo/form/main.js +166 -5
  41. package/dist/app/demo/form/main.xml +37 -35
  42. package/dist/app/demo/form/method/aform/aform.js +57 -0
  43. package/dist/app/demo/form/method/aform/aform.xml +35 -0
  44. package/dist/app/demo/form/method/aform/test.xml +6 -0
  45. package/dist/app/demo/form/method/core/core.js +11 -8
  46. package/dist/app/demo/form/method/core/core.xml +2 -1
  47. package/dist/app/demo/form/method/dom/dom.js +91 -99
  48. package/dist/app/demo/form/method/dom/dom.xml +6 -7
  49. package/dist/app/demo/form/method/form/form.js +10 -28
  50. package/dist/app/demo/form/method/form/form.xml +8 -15
  51. package/dist/app/demo/form/method/fs/fs.js +34 -33
  52. package/dist/app/demo/form/method/fs/fs.xml +1 -1
  53. package/dist/app/demo/form/method/fs/text.js +12 -12
  54. package/dist/app/demo/form/method/native/native.js +50 -0
  55. package/dist/app/demo/form/method/native/native.xml +12 -0
  56. package/dist/app/demo/form/method/system/system.js +50 -0
  57. package/dist/app/demo/form/method/system/system.xml +11 -0
  58. package/dist/app/demo/form/method/task/task.js +59 -61
  59. package/dist/app/demo/form/method/task/task.xml +4 -6
  60. package/dist/app/demo/form/method/theme/theme.js +14 -14
  61. package/dist/app/demo/form/method/tool/tool.js +29 -28
  62. package/dist/app/demo/form/method/tool/tool.xml +3 -3
  63. package/dist/app/demo/form/method/zip/zip.js +46 -41
  64. package/dist/app/demo/form/method/zip/zip.xml +1 -1
  65. package/dist/app/task/app.js +0 -25
  66. package/dist/app/task/config.json +29 -0
  67. package/dist/app/task/form/bar/bar.js +2 -2
  68. package/dist/app/task/form/bar/bar.xml +1 -1
  69. package/dist/clickgo.js +17 -5
  70. package/dist/clickgo.ts +22 -3
  71. package/dist/control/common.cgc +0 -0
  72. package/dist/control/form.cgc +0 -0
  73. package/dist/control/monaco.cgc +0 -0
  74. package/dist/control/property.cgc +0 -0
  75. package/dist/control/task.cgc +0 -0
  76. package/dist/global.css +1 -1
  77. package/dist/index.js +28 -8
  78. package/dist/index.ts +33 -7
  79. package/dist/lib/control.js +75 -105
  80. package/dist/lib/control.ts +102 -124
  81. package/dist/lib/core.js +108 -252
  82. package/dist/lib/core.ts +122 -268
  83. package/dist/lib/dom.js +564 -483
  84. package/dist/lib/dom.ts +703 -546
  85. package/dist/lib/form.js +170 -153
  86. package/dist/lib/form.ts +132 -99
  87. package/dist/lib/fs.js +1 -1
  88. package/dist/lib/fs.ts +1 -1
  89. package/dist/lib/native.js +135 -8
  90. package/dist/lib/native.ts +176 -12
  91. package/dist/lib/task.js +301 -175
  92. package/dist/lib/task.ts +330 -207
  93. package/dist/lib/tool.js +48 -1
  94. package/dist/lib/tool.ts +61 -0
  95. package/dist/lib/zip.ts +2 -0
  96. package/dist/theme/familiar.cgt +0 -0
  97. package/package.json +1 -1
  98. package/types/index.d.ts +26 -29
  99. package/dist/app/demo/form/control/greatview/greatview.js +0 -92
  100. package/dist/app/demo/form/control/overflow/overflow.js +0 -70
  101. package/dist/app/demo/form/control/overflow/overflow.xml +0 -98
  102. package/dist/app/demo/form/control/view/view.css +0 -1
  103. package/dist/app/demo/form/control/view/view.js +0 -73
  104. package/dist/app/demo/form/control/view/view.scss +0 -18
  105. package/dist/app/demo/form/control/view/view.xml +0 -94
  106. package/dist/app/demo/form/method/form/test.xml +0 -5
package/dist/lib/dom.ts CHANGED
@@ -17,6 +17,7 @@ import * as types from '../../types';
17
17
  import * as clickgo from '../clickgo';
18
18
  import * as form from './form';
19
19
  import * as core from './core';
20
+ import * as tool from './tool';
20
21
 
21
22
  /** --- style list 的 div --- */
22
23
  const topClass: string[] = ['#cg-form-list', '#cg-pop-list', '#cg-system', '#cg-simpletask', '#cg-launcher'];
@@ -31,17 +32,13 @@ function classUnfold(after?: string, out: string[] = []): string {
31
32
  return arr.join(', ');
32
33
  }
33
34
 
34
- /*
35
- #cg-gesture {box-sizing: border-box; position: fixed; z-index: 20020004; border: solid 3px #ff976a; border-radius: 50%; filter: drop-shadow(0 0 3px #ff976a); pointer-events: none; opacity: 0; transform: scale(0); width: 20px; height: 20px;}
36
- #cg-gesture.done {background: #ff976a;}
37
- */
38
35
  const styleList: HTMLDivElement = document.createElement('div');
39
36
  styleList.style.display = 'none';
40
37
  document.getElementsByTagName('body')[0].appendChild(styleList);
41
38
  styleList.insertAdjacentHTML('beforeend', '<style id=\'cg-global-cursor\'></style>');
42
39
  styleList.insertAdjacentHTML('beforeend', `<style id='cg-global'>
43
- ${classUnfold()} {-webkit-user-select: none; user-select: none; position: fixed; cursor: default; box-sizing: border-box;}
44
- ${topClass.slice(0, 3).join(', ')} {left: 0; top: 0; width: 0; height: 0;}
40
+ ${classUnfold()} {-webkit-user-select: none; user-select: none; cursor: default; box-sizing: border-box;}
41
+ ${topClass.slice(0, 3).join(', ')} {left: 0; top: 0; width: 0; height: 0; position: absolute;}
45
42
  ${classUnfold('img')} {vertical-align: bottom;}
46
43
  ${classUnfold('::selection', ['#cg-launcher'])} {background-color: rgba(0, 0, 0, .1);}
47
44
  ${classUnfold('*')}, ${classUnfold('*::after')}, ${classUnfold('*::before')} {box-sizing: border-box; -webkit-tap-highlight-color: rgba(0, 0, 0, 0); flex-shrink: 0;}
@@ -184,45 +181,41 @@ export function getStyleCount(taskId: number, type: 'theme' | 'control' | 'form'
184
181
  return document.querySelectorAll(`#cg-style-task${taskId} > .cg-style-${type} > style`).length;
185
182
  }
186
183
 
187
- /**
188
- * --- 获取实时的 DOM SIZE ---
189
- * @param el 要获取的 dom
190
- */
191
- export function getSize(el: HTMLElement): types.IDomSize {
192
- const rect = el.getBoundingClientRect();
193
- const cs = getComputedStyle(el);
194
- const border = {
195
- 'top': parseFloat(cs.borderTopWidth),
196
- 'right': parseFloat(cs.borderRightWidth),
197
- 'bottom': parseFloat(cs.borderBottomWidth),
198
- 'left': parseFloat(cs.borderLeftWidth)
199
- };
200
- const padding = {
201
- 'top': parseFloat(cs.paddingTop),
202
- 'right': parseFloat(cs.paddingRight),
203
- 'bottom': parseFloat(cs.paddingBottom),
204
- 'left': parseFloat(cs.paddingLeft)
205
- };
206
- return {
207
- 'top': rect.top,
208
- 'right': rect.right,
209
- 'bottom': rect.bottom,
210
- 'left': rect.left,
211
- 'width': rect.width,
212
- 'height': rect.height,
213
- 'padding': padding,
214
- 'border': border,
215
- 'clientWidth': rect.width - border.left - border.right,
216
- 'clientHeight': rect.height - border.top - border.bottom,
217
- 'innerWidth': rect.width - border.left - border.right - padding.left - padding.right,
218
- 'innerHeight': rect.height - border.top - border.bottom - padding.top - padding.bottom,
219
- 'scrollWidth': el.scrollWidth,
220
- 'scrollHeight': el.scrollHeight
221
- };
222
- }
184
+ // -----------------
185
+ // --- watchSize ---
186
+ // -----------------
223
187
 
224
188
  /** --- 被监视中的元素 --- */
225
- const watchSizeList: types.IWatchSizeItem[] = [];
189
+ const watchSizeList: Record<string, types.IWatchSizeItem> = {};
190
+
191
+ /** --- 监视元素的 data-cg-roindex --- */
192
+ let watchSizeIndex: number = 0;
193
+
194
+ // --- 创建 ro 对象 ---
195
+ const resizeObserver = new ResizeObserver(function(entries): void {
196
+ for (const entrie of entries) {
197
+ const el = entrie.target as HTMLElement;
198
+ if (!el.offsetParent) {
199
+ resizeObserver.unobserve(el);
200
+ if (watchSizeList[el.dataset.cgRoindex!]) {
201
+ delete watchSizeList[el.dataset.cgRoindex!];
202
+ }
203
+ continue;
204
+ }
205
+ const item = watchSizeList[el.dataset.cgRoindex!];
206
+ try {
207
+ const r = item.handler();
208
+ if (r instanceof Promise) {
209
+ r.catch(function(e) {
210
+ console.log('ResizeObserver', e);
211
+ });
212
+ }
213
+ }
214
+ catch (e) {
215
+ console.log('ResizeObserver', e);
216
+ }
217
+ }
218
+ });
226
219
 
227
220
  /**
228
221
  * --- 添加监视 Element 对象大小,元素移除后自动停止监视(浏览器原生效果) ---
@@ -233,108 +226,89 @@ const watchSizeList: types.IWatchSizeItem[] = [];
233
226
  */
234
227
  export function watchSize(
235
228
  el: HTMLElement,
236
- cb: (size: types.IDomSize) => Promise<void> | void,
229
+ cb: () => void | Promise<void>,
237
230
  immediate: boolean = false,
238
231
  taskId?: number
239
- ): types.IDomSize {
240
- const fsize = getSize(el);
241
- for (const item of watchSizeList) {
242
- if (item.el === el) {
243
- return fsize;
244
- }
232
+ ): boolean {
233
+ if (isWatchSize(el)) {
234
+ return false;
245
235
  }
246
236
  if (immediate) {
247
- const r = cb(fsize);
248
- if (r instanceof Promise) {
249
- r.catch(function(e) {
250
- console.log(e);
251
- });
252
- }
253
- }
254
- const resizeObserver = new (window as any).ResizeObserver(function(): void {
255
- const size = getSize(el);
256
- if (Number.isNaN(size.clientWidth)) {
257
- return;
237
+ try {
238
+ const r = cb();
239
+ if (r instanceof Promise) {
240
+ r.catch(function(e) {
241
+ console.log('dom.watchSize', e);
242
+ });
243
+ }
258
244
  }
259
- const r = cb(size);
260
- if (r instanceof Promise) {
261
- r.catch(function(e) {
262
- console.log(e);
263
- });
245
+ catch (e) {
246
+ console.log('dom.watchSize', e);
264
247
  }
265
- });
248
+ }
266
249
  resizeObserver.observe(el);
267
- watchSizeList.push({
250
+ watchSizeList[watchSizeIndex] = {
268
251
  'el': el,
269
- 'ro': resizeObserver,
252
+ 'handler': cb,
270
253
  'taskId': taskId
271
- });
272
- return fsize;
254
+ };
255
+ el.dataset.cgRoindex = watchSizeIndex.toString();
256
+ ++watchSizeIndex;
257
+ return true;
273
258
  }
274
259
 
275
260
  /**
276
- * --- 移除监视 Element 对象大小
261
+ * --- 移除监视 Element 对象大小 ---
277
262
  * @param el 要移除监视
278
263
  * @param taskId 校验任务 id,App 模式下无效
279
264
  */
280
265
  export function unwatchSize(el: HTMLElement, taskId?: number): void {
281
- for (let i = 0; i < watchSizeList.length; ++i) {
282
- const item = watchSizeList[i];
283
- if (item.el !== el) {
284
- continue;
285
- }
286
- if (taskId && taskId !== item.taskId) {
287
- // --- taskId 校验失败 ---
288
- return;
289
- }
290
- // --- 要移除 ---
291
- if (item.el.offsetParent) {
292
- item.ro.unobserve(item.el);
293
- }
294
- watchSizeList.splice(i, 1);
295
- --i;
266
+ const index = el.dataset.cgRoindex;
267
+ if (index === undefined) {
296
268
  return;
297
269
  }
270
+ const item = watchSizeList[index];
271
+ if (taskId && item.taskId !== taskId) {
272
+ return;
273
+ }
274
+ resizeObserver.unobserve(el);
275
+ el.removeAttribute('data-cg-roindex');
276
+ delete watchSizeList[index];
298
277
  }
299
278
 
300
279
  /**
301
- * --- 清除某个任务下面的所有监视 ---
302
- * @param taskId 任务 id,App 模式下无效
280
+ * --- 检测一个标签是否正在被 watchSize ---
281
+ * @param el 要检测的标签
303
282
  */
304
- export function clearWatchSize(taskId?: number): void {
305
- if (!taskId) {
306
- return;
307
- }
308
- for (let i = 0; i < watchSizeList.length; ++i) {
309
- const item = watchSizeList[i];
310
- if (taskId !== item.taskId) {
311
- continue;
312
- }
313
- if (item.el.offsetParent) {
314
- item.ro.unobserve(item.el);
315
- }
316
- watchSizeList.splice(i, 1);
317
- --i;
318
- }
283
+ export function isWatchSize(el: HTMLElement): boolean {
284
+ return el.dataset.cgRoindex ? true : false;
319
285
  }
320
286
 
321
287
  /**
322
- * --- 每隔一段时间清除一次无效的 el ---
288
+ * --- 清除某个任务的所有 watch size 监视,App 模式下无效 ---
289
+ * @param taskId 任务 id
323
290
  */
324
- function cgClearWatchSize(): void {
325
- for (let i = 0; i < watchSizeList.length; ++i) {
326
- const item = watchSizeList[i];
327
- if (item.el.offsetParent) {
291
+ export function clearWatchSize(taskId: number): void {
292
+ for (const index in watchSizeList) {
293
+ const item = watchSizeList[index];
294
+ if (taskId !== item.taskId) {
328
295
  continue;
329
296
  }
330
- watchSizeList.splice(i, 1);
331
- --i;
297
+ resizeObserver.unobserve(item.el);
298
+ item.el.removeAttribute('data-cg-roindex');
299
+ delete watchSizeList[index];
332
300
  }
333
301
  }
334
- setInterval(cgClearWatchSize, 1000 * 60 * 5);
302
+
303
+ // -------------
304
+ // --- watch ---
305
+ // -------------
335
306
 
336
307
  /** --- 监视 dom 变动中的元素 */
337
- const watchList: types.IWatchItem[] = [];
308
+ const watchList: Record<string, types.IWatchItem> = {};
309
+
310
+ /** --- 监视元素的 data-cg-moindex --- */
311
+ let watchIndex: number = 0;
338
312
 
339
313
  /**
340
314
  * --- 添加 DOM 内容变化监视 ---
@@ -343,10 +317,24 @@ const watchList: types.IWatchItem[] = [];
343
317
  * @param mode 监听模式
344
318
  * @param taskId 归属到一个任务里可留空,App 模式下无效
345
319
  */
346
- export function watch(el: HTMLElement, cb: () => void, mode: 'child' | 'childsub' | 'style' | 'default' = 'default', immediate: boolean = false, taskId?: number): void {
320
+ export function watch(el: HTMLElement, cb: (mutations: MutationRecord[]) => void | Promise<void>, mode: 'child' | 'childsub' | 'style' | 'default' = 'default', immediate: boolean = false, taskId?: number): boolean {
321
+ if (isWatch(el)) {
322
+ return false;
323
+ }
347
324
  if (immediate) {
348
- cb();
325
+ try {
326
+ const r = cb([]);
327
+ if (r instanceof Promise) {
328
+ r.catch(function(e) {
329
+ console.log('dom.watch', e);
330
+ });
331
+ }
332
+ }
333
+ catch (e) {
334
+ console.log('dom.watch', e);
335
+ }
349
336
  }
337
+ const index = watchIndex;
350
338
  let moi: MutationObserverInit;
351
339
  switch (mode) {
352
340
  case 'child': {
@@ -385,13 +373,35 @@ export function watch(el: HTMLElement, cb: () => void, mode: 'child' | 'childsub
385
373
  moi = mode;
386
374
  }
387
375
  }
388
- const mo = new MutationObserver(cb);
376
+ const mo = new MutationObserver((mutations) => {
377
+ if (!el.offsetParent) {
378
+ mo.disconnect();
379
+ if (watchList[index]) {
380
+ delete watchList[index];
381
+ }
382
+ return;
383
+ }
384
+ try {
385
+ const r = cb(mutations);
386
+ if (r instanceof Promise) {
387
+ r.catch(function(e) {
388
+ console.log('dom.watch', e);
389
+ });
390
+ }
391
+ }
392
+ catch (e) {
393
+ console.log('dom.watch', e);
394
+ }
395
+ });
389
396
  mo.observe(el, moi);
390
- watchList.push({
397
+ watchList[index] = {
391
398
  'el': el,
392
399
  'mo': mo,
393
400
  'taskId': taskId
394
- });
401
+ };
402
+ el.dataset.cgMoindex = index.toString();
403
+ ++watchIndex;
404
+ return true;
395
405
  /*
396
406
  {
397
407
  'attributeFilter': ['style', 'class'],
@@ -404,92 +414,97 @@ export function watch(el: HTMLElement, cb: () => void, mode: 'child' | 'childsub
404
414
  }
405
415
 
406
416
  /**
407
- * --- 移除监视 Element 对象大小
417
+ * --- 移除监视 Element 对象变动 ---
408
418
  * @param el 要移除监视
409
419
  * @param taskId 校验任务 id 可留空,App 模式下无效
410
420
  */
411
421
  export function unwatch(el: HTMLElement, taskId?: number): void {
412
- for (let i = 0; i < watchList.length; ++i) {
413
- const item = watchList[i];
414
- if (item.el !== el) {
415
- continue;
416
- }
417
- if (taskId && taskId !== item.taskId) {
418
- // --- taskId 校验失败 ---
419
- return;
420
- }
421
- // --- 要移除 ---
422
- if (item.el.offsetParent) {
423
- item.mo.disconnect();
424
- }
425
- watchList.splice(i, 1);
426
- --i;
422
+ const index = el.dataset.cgMoindex;
423
+ if (index === undefined) {
424
+ return;
425
+ }
426
+ const item = watchList[index];
427
+ if (taskId && item.taskId !== taskId) {
427
428
  return;
428
429
  }
430
+ el.removeAttribute('data-cg-moindex');
431
+ watchList[index].mo.disconnect();
432
+ delete watchList[index];
429
433
  }
430
434
 
431
435
  /**
432
- * --- 清除某个任务下面的所有 watch 监视 ---
433
- * @param taskId 任务 id,App 模式下无效
436
+ * --- 检测一个标签是否正在被 watchSize ---
437
+ * @param el 要检测的标签
434
438
  */
435
- export function clearWatch(taskId?: number): void {
436
- if (!taskId) {
437
- return;
438
- }
439
- for (let i = 0; i < watchList.length; ++i) {
440
- const item = watchList[i];
441
- if (taskId !== item.taskId) {
442
- continue;
443
- }
444
- if (item.el.offsetParent) {
445
- item.mo.disconnect();
446
- }
447
- watchList.splice(i, 1);
448
- --i;
449
- }
439
+ export function isWatch(el: HTMLElement): boolean {
440
+ return el.dataset.cgMoindex ? true : false;
450
441
  }
451
442
 
452
443
  /**
453
- * --- 每隔一段时间清除一次无效的 watch el ---
444
+ * --- 清除某个任务下面的所有 watch 监视,App 模式下无效 ---
445
+ * @param taskId 任务 id,App 模式下无效
454
446
  */
455
- function cgClearWatch(): void {
456
- for (let i = 0; i < watchList.length; ++i) {
457
- const item = watchList[i];
458
- if (item.el.offsetParent) {
447
+ export function clearWatch(taskId: number): void {
448
+ for (const index in watchList) {
449
+ const item = watchList[index];
450
+ if (taskId !== item.taskId) {
459
451
  continue;
460
452
  }
461
- watchList.splice(i, 1);
462
- --i;
453
+ item.el.removeAttribute('data-cg-moindex');
454
+ item.mo.disconnect();
455
+ delete watchList[index];
463
456
  }
464
457
  }
465
- setInterval(cgClearWatch, 1000 * 60 * 5);
466
458
 
467
- const watchStyleObjects: Array<{
459
+ // ------------------
460
+ // --- watchStyle ---
461
+ // ------------------
462
+
463
+ const watchStyleList: Record<string, Record<string, {
468
464
  'el': HTMLElement;
469
465
  'sd': CSSStyleDeclaration;
470
466
  'names': Record<string, {
471
- 'old': string;
472
- 'cb': Array<(name: string, value: string) => void>;
467
+ 'val': string;
468
+ 'cb': Array<(name: string, value: string, old: string) => void>;
473
469
  }>;
474
- }> = [];
470
+ }>> = {};
471
+
472
+ /** --- 监视元素的 data-cg-styleindex --- */
473
+ let watchStyleIndex: number = 0;
474
+
475
+ /**
476
+ * --- 监听一个标签的计算后样式的变化 ---
477
+ * @param el 对象
478
+ * @param name 样式名
479
+ * @param cb 变更回调
480
+ * @param immediate 是否立刻执行一次
481
+ */
475
482
  export function watchStyle(
476
483
  el: HTMLElement,
477
484
  name: string | string[],
478
- cb: (name: string, value: string) => void,
485
+ cb: (name: string, value: string, old: string) => void,
479
486
  immediate: boolean = false
480
487
  ): void {
481
488
  if (typeof name === 'string') {
482
489
  name = [name];
483
490
  }
484
- for (const item of watchStyleObjects) {
485
- if (item.el !== el) {
486
- continue;
487
- }
491
+ // --- 获取监视标签的所属 wrap ---
492
+ const formWrap = findParentByData(el, 'form-id');
493
+ if (!formWrap) {
494
+ return;
495
+ }
496
+ const formId = formWrap.dataset.formId!;
497
+ if (!watchStyleList[formId]) {
498
+ watchStyleList[formId] = {};
499
+ }
500
+ const index = el.dataset.cgStyleindex;
501
+ if (index) {
502
+ const item = watchStyleList[formId][index];
488
503
  // --- 已经有监听了 ---
489
504
  for (const n of name) {
490
505
  if (!item.names[n]) {
491
506
  item.names[n] = {
492
- 'old': (item.sd as any)[n],
507
+ 'val': (item.sd as any)[n],
493
508
  'cb': [cb]
494
509
  };
495
510
  }
@@ -497,69 +512,251 @@ export function watchStyle(
497
512
  item.names[n].cb.push(cb);
498
513
  }
499
514
  if (immediate) {
500
- cb(n, (item.sd as any)[n]);
515
+ cb(n, (item.sd as any)[n], '');
501
516
  }
502
517
  }
503
518
  return;
504
519
  }
505
520
  // --- 创建监听 ---
506
521
  const sd = getComputedStyle(el);
507
- watchStyleObjects.push({
522
+ watchStyleList[formId][watchStyleIndex] = {
508
523
  'el': el,
509
524
  'sd': sd,
510
525
  'names': {}
511
- });
512
- const item = watchStyleObjects[watchStyleObjects.length - 1];
526
+ };
527
+ const item = watchStyleList[formId][watchStyleIndex];
513
528
  for (const n of name) {
514
529
  item.names[n] = {
515
- 'old': (item.sd as any)[n],
530
+ 'val': (item.sd as any)[n],
516
531
  'cb': [cb]
517
532
  };
518
533
  if (immediate) {
519
- cb(n, (item.sd as any)[n]);
534
+ cb(n, (item.sd as any)[n], '');
520
535
  }
521
536
  }
537
+ el.dataset.cgStyleindex = watchStyleIndex.toString();
538
+ ++watchStyleIndex;
522
539
  }
523
- const watchStyleRAF = function(): void {
524
- for (let i = 0; i < watchStyleObjects.length; ++i) {
525
- const item = watchStyleObjects[i];
526
- if (watchStyleObjects[i].sd.flex === '') {
527
- watchStyleObjects.splice(i, 1);
528
- --i;
529
- continue;
530
- }
531
- // --- 执行 cb ---
532
- for (const name in item.names) {
533
- if ((item.sd as any)[name] === item.names[name].old) {
534
- continue;
535
- }
536
- item.names[name].old = (item.sd as any)[name];
537
- for (const cb of item.names[name].cb) {
538
- cb(name, (item.sd as any)[name]);
540
+
541
+ /** --- watch style timer --- */
542
+ let watchStyleTimer = 0;
543
+ const watchStyleHandler = function(): void {
544
+ // --- 为什么要判断 form.getFocus 存在否,因为 form 类可能还没加载出来,这个函数就已经开始执行了 ---
545
+ if (form.getFocus) {
546
+ // --- 只判断和执行活跃中的窗体的监听事件 ---
547
+ const formId: number | null = form.getFocus();
548
+ if (formId && watchStyleList[formId]) {
549
+ for (const index in watchStyleList[formId]) {
550
+ const item = watchStyleList[formId][index];
551
+ if (!item.el.offsetParent) {
552
+ delete watchStyleList[formId][index];
553
+ if (!Object.keys(watchStyleList[formId]).length) {
554
+ delete watchStyleList[formId];
555
+ }
556
+ continue;
557
+ }
558
+ // --- 执行 cb ---
559
+ for (const name in item.names) {
560
+ if ((item.sd as any)[name] === item.names[name].val) {
561
+ continue;
562
+ }
563
+ const old = item.names[name].val;
564
+ item.names[name].val = (item.sd as any)[name];
565
+ for (const cb of item.names[name].cb) {
566
+ cb(name, (item.sd as any)[name], old);
567
+ }
568
+ }
539
569
  }
540
570
  }
541
571
  }
542
- requestAnimationFrame(watchStyleRAF);
572
+ watchStyleTimer = requestAnimationFrame(watchStyleHandler);
543
573
  };
544
- watchStyleRAF();
574
+ watchStyleHandler();
545
575
 
546
576
  /**
547
577
  * --- 检测一个标签是否正在被 watchStyle ---
548
578
  * @param el 要检测的标签
549
579
  */
550
580
  export function isWatchStyle(el: HTMLElement): boolean {
551
- for (const item of watchStyleObjects) {
552
- if (item.el !== el) {
553
- continue;
581
+ return el.dataset.cgStyleindex ? true : false;
582
+ }
583
+
584
+ /**
585
+ * --- 清除某个窗体的所有 watch style 监视,App 模式下无效 ---
586
+ * @param formId 窗体 id
587
+ */
588
+ export function clearWatchStyle(formId: number | string): void {
589
+ if (!watchStyleList[formId]) {
590
+ return;
591
+ }
592
+ for (const index in watchStyleList[formId]) {
593
+ const item = watchStyleList[formId][index];
594
+ item.el.removeAttribute('data-cg-styleindex');
595
+ }
596
+ delete watchStyleList[formId];
597
+ }
598
+
599
+ // ---------------------
600
+ // --- watchProperty ---
601
+ // ---------------------
602
+
603
+ /**
604
+ * --- 监听中的标签对象,对应 formId -> 数组列表 ---
605
+ */
606
+ const watchPropertyObjects: Record<string, Record<string, {
607
+ 'el': HTMLElement;
608
+ 'names': Record<string, {
609
+ 'val': string;
610
+ 'cb': Array<(name: string, value: string) => void>;
611
+ }>;
612
+ }>> = {};
613
+
614
+ /** --- 监视元素的 data-cg-propertyindex --- */
615
+ let watchPropertyIndex: number = 0;
616
+
617
+ /**
618
+ * --- 监听一个对象的属性变化 ---
619
+ * @param el 对象
620
+ * @param name 属性名
621
+ * @param cb 回调函数
622
+ * @param immediate 是否立即执行一次
623
+ */
624
+ export function watchProperty(
625
+ el: HTMLElement,
626
+ name: string | string[],
627
+ cb: (name: string, value: string) => void,
628
+ immediate: boolean = false
629
+ ): void {
630
+ if (typeof name === 'string') {
631
+ name = [name];
632
+ }
633
+ // --- 获取监视标签的所属 wrap ---
634
+ const formWrap = findParentByData(el, 'form-id');
635
+ if (!formWrap) {
636
+ return;
637
+ }
638
+ const formId = formWrap.dataset.formId!;
639
+ if (!watchPropertyObjects[formId]) {
640
+ watchPropertyObjects[formId] = {};
641
+ }
642
+ const index = el.dataset.cgPropertyindex;
643
+ if (index) {
644
+ const item = watchPropertyObjects[formId][index];
645
+ // --- 已经有监听了 ---
646
+ for (const n of name) {
647
+ if (!item.names[n]) {
648
+ item.names[n] = {
649
+ 'val': (item.el as any)[n],
650
+ 'cb': [cb]
651
+ };
652
+ }
653
+ else {
654
+ item.names[n].cb.push(cb);
655
+ }
656
+ if (immediate) {
657
+ cb(n, (item.el as any)[n]);
658
+ }
554
659
  }
555
- return true;
660
+ return;
556
661
  }
557
- return false;
662
+ // --- 创建监听 ---
663
+ watchPropertyObjects[formId][watchPropertyIndex] = {
664
+ 'el': el,
665
+ 'names': {}
666
+ };
667
+ const item = watchPropertyObjects[formId][watchPropertyIndex];
668
+ for (const n of name) {
669
+ item.names[n] = {
670
+ 'val': (item.el as any)[n],
671
+ 'cb': [cb]
672
+ };
673
+ if (immediate) {
674
+ cb(n, (item.el as any)[n]);
675
+ }
676
+ }
677
+ el.dataset.cgPropertyindex = watchPropertyIndex.toString();
678
+ ++watchPropertyIndex;
679
+ }
680
+
681
+ /** --- watch property 的 timer --- */
682
+ let watchPropertyTimer = 0;
683
+ const watchPropertyHandler = function(): void {
684
+ // --- 为什么要判断 form.getFocus 存在否,因为 form 类可能还没加载出来,这个函数就已经开始执行了 ---
685
+ if (form.getFocus) {
686
+ // --- 只判断和执行活跃中的窗体的监听事件 ---
687
+ const formId: number | null = form.getFocus();
688
+ if (formId && watchPropertyObjects[formId]) {
689
+ for (const index in watchPropertyObjects[formId]) {
690
+ const item = watchPropertyObjects[formId][index];
691
+ if (!item.el.offsetParent) {
692
+ delete watchPropertyObjects[formId][index];
693
+ if (!Object.keys(watchPropertyObjects[formId]).length) {
694
+ delete watchPropertyObjects[formId];
695
+ }
696
+ continue;
697
+ }
698
+ // --- 执行 cb ---
699
+ for (const name in item.names) {
700
+ if ((item.el as any)[name] === item.names[name].val) {
701
+ continue;
702
+ }
703
+ item.names[name].val = (item.el as any)[name];
704
+ for (const cb of item.names[name].cb) {
705
+ cb(name, (item.el as any)[name]);
706
+ }
707
+ }
708
+ }
709
+ }
710
+ }
711
+ watchPropertyTimer = requestAnimationFrame(watchPropertyHandler);
712
+ };
713
+ watchPropertyHandler();
714
+
715
+ /**
716
+ * --- 检测一个标签是否正在被 watchProperty ---
717
+ * @param el 要检测的标签
718
+ */
719
+ export function isWatchProperty(el: HTMLElement): boolean {
720
+ return el.dataset.cgPropertyindex ? true : false;
721
+ }
722
+
723
+ /**
724
+ * --- 清除某个窗体的所有 watch property 监视,虽然窗体结束后相关监视永远不会再被执行,但是会形成冗余,App 模式下无效 ---
725
+ * @param formId 窗体 id
726
+ */
727
+ export function clearWatchProperty(formId: number | string): void {
728
+ if (!watchPropertyObjects[formId]) {
729
+ return;
730
+ }
731
+ for (const index in watchPropertyObjects[formId]) {
732
+ const item = watchPropertyObjects[formId][index];
733
+ item.el.removeAttribute('data-cg-propertyindex');
734
+ }
735
+ delete watchPropertyObjects[formId];
736
+ }
737
+
738
+ /**
739
+ * --- 鼠标/手指没移动时,click 才生效 ---
740
+ * @param e 事件对象
741
+ * @param handler 回调
742
+ */
743
+ export function bindClick(e: MouseEvent | TouchEvent, handler: () => void): void {
744
+ const x = e instanceof MouseEvent ? e.clientX : e.touches[0].clientX;
745
+ const y = e instanceof MouseEvent ? e.clientY : e.touches[0].clientY;
746
+ bindDown(e, {
747
+ up: (ne) => {
748
+ const nx = ne instanceof MouseEvent ? ne.clientX : ne.touches[0].clientX;
749
+ const ny = ne instanceof MouseEvent ? ne.clientY : ne.touches[0].clientY;
750
+ if (nx === x && ny === y) {
751
+ handler();
752
+ }
753
+ }
754
+ });
558
755
  }
559
756
 
560
757
  /**
561
- * --- 绑定按下以及弹起事件 ---
562
- * @param e MouseEvent | TouchEvent
758
+ * --- 绑定按下以及弹起事件,touch 和 mouse 只会绑定一个 ---
759
+ * @param oe MouseEvent | TouchEvent
563
760
  * @param opt 回调选项
564
761
  */
565
762
  export function bindDown(oe: MouseEvent | TouchEvent, opt: types.IBindDownOptions): void {
@@ -583,7 +780,7 @@ export function bindDown(oe: MouseEvent | TouchEvent, opt: types.IBindDownOption
583
780
  let end: ((e: MouseEvent | TouchEvent) => void) | undefined = undefined;
584
781
  const move = function(e: MouseEvent | TouchEvent): void {
585
782
  // --- 虽然上层已经有 preventDefault 了,但是有可能 e.target 会被注销,这样就响应不到上层的 preventDefault 事件,所以要在这里再加一个 ---
586
- if (!e.target || !(e.target as HTMLElement).offsetParent) {
783
+ if ((!e.target || !(e.target as HTMLElement).offsetParent) && e.cancelable) {
587
784
  e.preventDefault();
588
785
  }
589
786
  /** --- 本次的移动方向 --- */
@@ -683,380 +880,314 @@ export function bindDown(oe: MouseEvent | TouchEvent, opt: types.IBindDownOption
683
880
  opt.down?.(oe);
684
881
  }
685
882
 
686
- const bindGestureData: {
687
- 'el': HTMLElement | null;
688
- 'xx': number;
689
- 'xy': number;
690
- 'tx': number;
691
- 'ty': number;
692
- 'dir': 'top' | 'right' | 'bottom' | 'left' | null;
693
- 'timers': {
694
- 'ani': number;
695
- 'sleep': number;
696
- };
697
- } = {
698
- 'el': null,
699
- 'xx': 0, // 当前 x
700
- 'xy': 0,
701
- 'tx': 0, // 最终 x
702
- 'ty': 0,
703
- 'dir': null,
704
- 'timers': {
705
- 'ani': 0,
706
- 'sleep': 0
707
- }
883
+ /** --- 绑定拖拉响应操作的 wheel 数据对象 --- */
884
+ const gestureWheel = {
885
+ /** --- 最后一次响应事件时间 --- */
886
+ 'last': 0,
887
+ /** --- 本轮 offset 滚动距离 --- */
888
+ 'offset': 0,
889
+ /** --- 本轮是否已经完成了滚动 --- */
890
+ 'done': false,
891
+ /** --- 未执行等待隐藏的 timer --- */
892
+ 'timer': 0,
893
+ /** --- 是否等待首次执行中 --- */
894
+ 'firstTimer': false,
895
+ /** --- 本轮滚动的方向 --- */
896
+ 'dir': ''
708
897
  };
709
898
 
710
- function clearGestureData(): void {
711
- bindGestureData.xx = 0;
712
- bindGestureData.xy = 0;
713
- bindGestureData.tx = 0;
714
- bindGestureData.ty = 0;
715
- }
716
-
717
- function bindGestureAnimation(opt: { 'rect': DOMRect; 'dirs'?: Array<('top' | 'right' | 'bottom' | 'left')>; handler: (dir: 'top' | 'right' | 'bottom' | 'left') => void; }): void {
718
- if (!bindGestureData.el) {
719
- return;
720
- }
721
- const speed: number = 6;
722
- const gestureElement = document.getElementById('cg-gesture')!;
723
- const dirs = opt.dirs ?? ['top', 'bottom'];
724
-
725
- if (bindGestureData.tx > bindGestureData.xx) {
726
- bindGestureData.xx += speed;
727
- if (bindGestureData.xx > bindGestureData.tx) {
728
- bindGestureData.xx = bindGestureData.tx;
729
- }
730
- }
731
- else {
732
- bindGestureData.xx -= speed;
733
- if (bindGestureData.xx < bindGestureData.tx) {
734
- bindGestureData.xx = bindGestureData.tx;
735
- }
736
- }
737
- if (bindGestureData.ty > bindGestureData.xy) {
738
- bindGestureData.xy += speed;
739
- if (bindGestureData.xy > bindGestureData.ty) {
740
- bindGestureData.xy = bindGestureData.ty;
741
- }
742
- }
743
- else {
744
- bindGestureData.xy -= speed;
745
- if (bindGestureData.xy < bindGestureData.ty) {
746
- bindGestureData.xy = bindGestureData.ty;
747
- }
748
- }
749
- const xxAbs = Math.abs(bindGestureData.xx);
750
- const xyAbs = Math.abs(bindGestureData.xy);
751
- if ((!dirs.includes('top') && !dirs.includes('bottom')) || ((xxAbs > xyAbs) && (dirs.includes('left') || dirs.includes('right')))) {
752
- // --- 水平 ---
753
- if (bindGestureData.xx < 0) {
754
- // --- left ---
755
- if (!dirs.includes('left')) {
756
- gestureElement.style.opacity = '0';
757
- clearGestureData();
758
- return;
759
- }
760
- gestureElement.style.opacity = '1';
761
- if (xxAbs === 90) {
762
- bindGestureData.dir = 'left';
763
- gestureElement.classList.add('done');
764
- }
765
- else {
766
- bindGestureData.dir = null;
767
- gestureElement.classList.remove('done');
768
- }
769
- gestureElement.style.top = (opt.rect.top + ((opt.rect.height - 20) / 2)).toString() + 'px';
770
- gestureElement.style.left = (opt.rect.left - 10 + (xxAbs / 1.5)).toString() + 'px';
771
- gestureElement.style.transform = 'scale(' + (xxAbs / 90).toString() + ')';
772
- }
773
- else {
774
- // --- right ---
775
- if (!dirs.includes('right')) {
776
- gestureElement.style.opacity = '0';
777
- clearGestureData();
778
- return;
779
- }
780
- gestureElement.style.opacity = '1';
781
- if (xxAbs === 90) {
782
- bindGestureData.dir = 'right';
783
- gestureElement.classList.add('done');
784
- }
785
- else {
786
- bindGestureData.dir = null;
787
- gestureElement.classList.remove('done');
788
- }
789
- gestureElement.style.top = (opt.rect.top + ((opt.rect.height - 20) / 2)).toString() + 'px';
790
- gestureElement.style.left = (opt.rect.left + opt.rect.width - 10 - (xxAbs / 1.5)).toString() + 'px';
791
- gestureElement.style.transform = 'scale(' + (xxAbs / 90).toString() + ')';
792
- }
793
- }
794
- else {
795
- if (bindGestureData.xy < 0) {
796
- // --- top ---
797
- if (!dirs.includes('top')) {
798
- gestureElement.style.opacity = '0';
799
- clearGestureData();
800
- return;
801
- }
802
- gestureElement.style.opacity = '1';
803
- if (xyAbs === 90) {
804
- bindGestureData.dir = 'top';
805
- gestureElement.classList.add('done');
806
- }
807
- else {
808
- bindGestureData.dir = null;
809
- gestureElement.classList.remove('done');
810
- }
811
- gestureElement.style.left = (opt.rect.left + ((opt.rect.width - 20) / 2)).toString() + 'px';
812
- gestureElement.style.top = (opt.rect.top - 10 + (xyAbs / 1.5)).toString() + 'px';
813
- gestureElement.style.transform = 'scale(' + (xyAbs / 90).toString() + ')';
814
- }
815
- else {
816
- // --- bottom ---
817
- if (!dirs.includes('bottom')) {
818
- gestureElement.style.opacity = '0';
819
- clearGestureData();
820
- return;
821
- }
822
- gestureElement.style.opacity = '1';
823
- if (xyAbs === 90) {
824
- bindGestureData.dir = 'bottom';
825
- gestureElement.classList.add('done');
826
- }
827
- else {
828
- bindGestureData.dir = null;
829
- gestureElement.classList.remove('done');
830
- }
831
- gestureElement.style.left = (opt.rect.left + ((opt.rect.width - 20) / 2)).toString() + 'px';
832
- gestureElement.style.top = (opt.rect.top + opt.rect.height - 10 - (xyAbs / 1.5)).toString() + 'px';
833
- gestureElement.style.transform = 'scale(' + (xyAbs / 90).toString() + ')';
834
- }
835
- }
836
-
837
- if (bindGestureData.xx === bindGestureData.tx && bindGestureData.xy === bindGestureData.ty) {
838
- // --- 中途终止 ---
839
- bindGestureData.timers.ani = 0;
840
- bindGestureData.timers.sleep = window.setTimeout(() => {
841
- // --- 触摸板 wheel 可能会多次执行,导致圆圈一直在,有缓动 ---
842
- clearGestureData();
843
- bindGestureData.timers.sleep = 0;
844
- gestureElement.style.opacity = '0';
845
- if (!bindGestureData.dir) {
846
- return;
847
- }
848
- opt.handler(bindGestureData.dir);
849
- }, 500);
850
- return;
851
- }
852
- bindGestureData.timers.ani = requestAnimationFrame(() => {
853
- bindGestureAnimation(opt);
854
- });
855
- }
856
-
857
899
  /**
858
900
  * --- 绑定上拉、下拉、左拉、右拉 ---
859
- * @param e 响应事件
860
- * @param opt 选项
901
+ * @param oe 响应事件
902
+ * @param before before 事件,返回 true 则显示 gesture
903
+ * @param handler 执行完毕的话才会回调
861
904
  */
862
- export function bindGesture(e: MouseEvent | TouchEvent | WheelEvent | { 'x'?: number; 'y'?: number; }, opt: types.IBindGestureOptions): void {
863
- const gestureElement = document.getElementById('cg-gesture')!;
864
- const el: HTMLElement | undefined = ((e as any).currentTarget as HTMLElement | undefined)
865
- ?? ((e as any).target as HTMLElement | undefined) ?? opt.el;
905
+ export function bindGesture(oe: MouseEvent | TouchEvent | WheelEvent, before: (e: MouseEvent | TouchEvent | WheelEvent, dir: 'top' | 'right' | 'bottom' | 'left') => boolean, handler: (dir: 'top' | 'right' | 'bottom' | 'left') => void | Promise<void>): void {
906
+ const el = oe.currentTarget as HTMLElement | null;
866
907
  if (!el) {
867
908
  return;
868
909
  }
869
- let rect: DOMRect;
870
- if ((e as any).rect) {
871
- rect = (e as any).rect as DOMRect;
872
- }
873
- else if (opt.rect) {
874
- rect = opt.rect;
875
- }
876
- else {
877
- if (!(el.getBoundingClientRect)) {
878
- return;
879
- }
880
- rect = el.getBoundingClientRect();
881
- }
882
- const dirs = opt.dirs ?? ['top', 'bottom'];
883
- if ((e instanceof MouseEvent || e instanceof TouchEvent) && !(e instanceof WheelEvent)) {
884
- // --- touch / mouse 触发的 ---
885
- const x: number = e instanceof MouseEvent ? e.clientX : e.touches[0].clientX;
886
- const y: number = e instanceof MouseEvent ? e.clientY : e.touches[0].clientY;
887
- let dir: 'top' | 'right' | 'bottom' | 'left' | null = null;
910
+ const rect = el.getBoundingClientRect();
911
+ if ((oe instanceof MouseEvent || oe instanceof TouchEvent) && !(oe instanceof WheelEvent)) {
912
+ // --- touch / mouse 触发的,dir 会和鼠标的 dir 相反,向下拖动是上方加载 ---
913
+ let offset: number = 0;
914
+ let origin: number = 0;
915
+ /** --- 是否第一次执行 move 事件,1-是,0-不是且继续,-1-终止 --- */
916
+ let first = 1;
917
+ /** --- 哪个方向要加载 --- */
918
+ let dir: 'top' | 'right' | 'bottom' | 'left' = 'top';
888
919
  /** --- 当前手势方向 --- */
889
- bindDown(e, {
890
- move: (e) => {
891
- e.preventDefault();
892
- if (bindGestureData.timers.ani !== 0) {
893
- cancelAnimationFrame(bindGestureData.timers.ani);
894
- bindGestureData.timers.ani = 0;
895
- }
896
- if (bindGestureData.timers.sleep !== 0) {
897
- clearTimeout(bindGestureData.timers.sleep);
898
- bindGestureData.timers.sleep = 0;
920
+ bindDown(oe, {
921
+ move: (e, d) => {
922
+ if (first < 0) {
923
+ if (first > -30) {
924
+ before(e, dir);
925
+ --first;
926
+ }
927
+ return;
899
928
  }
900
-
901
- const nx: number = e instanceof MouseEvent ? e.clientX : e.touches[0].clientX;
902
- const ny: number = e instanceof MouseEvent ? e.clientY : e.touches[0].clientY;
903
- /** --- 相对于按下时 x 的差值 --- */
904
- const xx = nx - x;
905
- /** --- 相对于按下时 y 的差值 --- */
906
- const xy = ny - y;
907
- let xxAbs = Math.abs(xx);
908
- let xyAbs = Math.abs(xy);
909
- if ((!dirs.includes('top') && !dirs.includes('bottom')) || ((xxAbs > xyAbs) && (dirs.includes('left') || dirs.includes('right')))) {
910
- // --- 水平 ---
911
- if (xx > 0) {
912
- // --- left ---
913
- if (!dirs.includes('left')) {
914
- gestureElement.style.opacity = '0';
915
- return;
929
+ if (first === 1) {
930
+ // --- 第一次执行,响应 before 判断是否继续执行 ---
931
+ first = 0;
932
+ switch (d) {
933
+ case 'top': {
934
+ dir = 'bottom';
935
+ break;
916
936
  }
917
- gestureElement.style.opacity = '1';
918
- if (xxAbs > 90) {
919
- xxAbs = 90;
937
+ case 'bottom': {
938
+ dir = 'top';
939
+ break;
940
+ }
941
+ case 'left': {
942
+ dir = 'right';
943
+ break;
944
+ }
945
+ default: {
920
946
  dir = 'left';
921
- gestureElement.classList.add('done');
922
947
  }
923
- else {
924
- dir = null;
925
- gestureElement.classList.remove('done');
948
+ }
949
+ if (!before(e, dir)) {
950
+ first = -1;
951
+ return;
952
+ }
953
+ // --- 初始化原点 ---
954
+ switch (dir) {
955
+ case 'top':
956
+ case 'bottom': {
957
+ origin = oe instanceof MouseEvent ? oe.clientY : oe.touches[0].clientY;
958
+ break;
959
+ }
960
+ default: {
961
+ origin = oe instanceof MouseEvent ? oe.clientX : oe.touches[0].clientX;
926
962
  }
927
- gestureElement.style.top = (rect.top + ((rect.height - 20) / 2)).toString() + 'px';
928
- gestureElement.style.left = (rect.left - 10 + (xxAbs / 1.5)).toString() + 'px';
929
- gestureElement.style.transform = 'scale(' + (xxAbs / 90).toString() + ')';
930
963
  }
931
- else {
932
- // --- right ---
933
- if (!dirs.includes('right')) {
934
- gestureElement.style.opacity = '0';
935
- return;
964
+ }
965
+ /** --- 当前坐标 --- */
966
+ let pos: number = 0;
967
+ switch (dir) {
968
+ case 'top':
969
+ case 'bottom': {
970
+ pos = e instanceof MouseEvent ? e.clientY : e.touches[0].clientY;
971
+ if (dir === 'top') {
972
+ offset = pos - origin;
936
973
  }
937
- gestureElement.style.opacity = '1';
938
- if (xxAbs > 90) {
939
- xxAbs = 90;
940
- dir = 'right';
941
- gestureElement.classList.add('done');
974
+ else {
975
+ offset = origin - pos;
976
+ }
977
+ break;
978
+ }
979
+ default: {
980
+ pos = e instanceof MouseEvent ? e.clientX : e.touches[0].clientX;
981
+ if (dir === 'left') {
982
+ offset = pos - origin;
942
983
  }
943
984
  else {
944
- dir = null;
945
- gestureElement.classList.remove('done');
985
+ offset = origin - pos;
946
986
  }
947
- gestureElement.style.top = (rect.top + ((rect.height - 20) / 2)).toString() + 'px';
948
- gestureElement.style.left = (rect.left + rect.width - 10 - (xxAbs / 1.5)).toString() + 'px';
949
- gestureElement.style.transform = 'scale(' + (xxAbs / 90).toString() + ')';
950
987
  }
951
988
  }
989
+
990
+ // --- 处理差值 ---
991
+ if (offset >= 90) {
992
+ offset = 90;
993
+ form.elements.gesture.style.opacity = '1';
994
+ form.elements.gesture.classList.add('done');
995
+ }
952
996
  else {
953
- if (xy > 0) {
954
- // --- top ---
955
- if (!dirs.includes('top')) {
956
- gestureElement.style.opacity = '0';
957
- return;
958
- }
959
- gestureElement.style.opacity = '1';
960
- if (xyAbs > 90) {
961
- xyAbs = 90;
962
- dir = 'top';
963
- gestureElement.classList.add('done');
997
+ form.elements.gesture.classList.remove('done');
998
+ if (offset < 0) {
999
+ offset = 0;
1000
+ form.elements.gesture.style.opacity = '0';
1001
+ }
1002
+ else {
1003
+ form.elements.gesture.style.opacity = '1';
1004
+ }
1005
+ }
1006
+ form.elements.gesture.style.transform = 'scale(' + (offset / 90).toString() + ')';
1007
+
1008
+ // --- 处理标签位置 ---
1009
+ switch (dir) {
1010
+ case 'top':
1011
+ case 'bottom': {
1012
+ form.elements.gesture.style.left = (rect.left + ((rect.width - 20) / 2)).toString() + 'px';
1013
+ if (dir === 'top') {
1014
+ form.elements.gesture.style.top = (rect.top + (offset / 1.5)).toString() + 'px';
964
1015
  }
965
1016
  else {
966
- dir = null;
967
- gestureElement.classList.remove('done');
1017
+ form.elements.gesture.style.top = (rect.bottom - 20 - (offset / 1.5)).toString() + 'px';
968
1018
  }
969
- gestureElement.style.left = (rect.left + ((rect.width - 20) / 2)).toString() + 'px';
970
- gestureElement.style.top = (rect.top - 10 + (xyAbs / 1.5)).toString() + 'px';
971
- gestureElement.style.transform = 'scale(' + (xyAbs / 90).toString() + ')';
1019
+ break;
972
1020
  }
973
- else {
974
- // --- bottom ---
975
- if (!dirs.includes('bottom')) {
976
- gestureElement.style.opacity = '0';
977
- return;
978
- }
979
- gestureElement.style.opacity = '1';
980
- if (xyAbs > 90) {
981
- xyAbs = 90;
982
- dir = 'bottom';
983
- gestureElement.classList.add('done');
1021
+ default: {
1022
+ form.elements.gesture.style.top = (rect.top + ((rect.height - 20) / 2)).toString() + 'px';
1023
+ if (dir === 'left') {
1024
+ form.elements.gesture.style.left = (rect.left + (offset / 1.5)).toString() + 'px';
984
1025
  }
985
1026
  else {
986
- dir = null;
987
- gestureElement.classList.remove('done');
1027
+ form.elements.gesture.style.left = (rect.right - 20 - (offset / 1.5)).toString() + 'px';
988
1028
  }
989
- gestureElement.style.left = (rect.left + ((rect.width - 20) / 2)).toString() + 'px';
990
- gestureElement.style.top = (rect.top + rect.height - 10 - (xyAbs / 1.5)).toString() + 'px';
991
- gestureElement.style.transform = 'scale(' + (xyAbs / 90).toString() + ')';
992
1029
  }
993
1030
  }
994
1031
  },
995
1032
  end: (): void => {
996
- gestureElement.style.opacity = '0';
997
- if (!dir) {
1033
+ form.elements.gesture.style.opacity = '0';
1034
+ if (offset < 90) {
998
1035
  return;
999
1036
  }
1000
- opt.handler(dir);
1037
+ handler(dir) as any;
1001
1038
  }
1002
1039
  });
1003
1040
  }
1004
1041
  else {
1005
- // --- wheel 触发、自定义触发 ---
1006
- if (bindGestureData.el !== el) {
1007
- bindGestureData.el = el;
1008
- bindGestureData.xx = 0;
1009
- bindGestureData.xy = 0;
1010
- }
1011
- let x: number = 0, y: number = 0;
1012
- if (e instanceof WheelEvent) {
1013
- e.preventDefault();
1014
- if (Math.abs(e.deltaX) < 5 && Math.abs(e.deltaY) < 5) {
1042
+ // --- wheel 触发 ---
1043
+ (async () => {
1044
+ const now = Date.now();
1045
+ if (now - gestureWheel.last > 250) {
1046
+ // --- 超过 250 毫秒,已经进入下一轮 ---
1047
+ gestureWheel.offset = 0;
1048
+ gestureWheel.done = false;
1049
+ gestureWheel.timer = 0;
1050
+ gestureWheel.firstTimer = false;
1051
+ gestureWheel.dir = '';
1052
+ }
1053
+ // --- 赋值最后执行时间 ---
1054
+ gestureWheel.last = now;
1055
+ if (gestureWheel.firstTimer) {
1015
1056
  return;
1016
1057
  }
1017
- x = Math.round(e.deltaX / 3);
1018
- y = Math.round(e.deltaY / 3);
1019
- if ((e as any).direction === 'h') {
1020
- x = y;
1021
- y = 0;
1058
+ // --- 判断本轮是否已经处理结束 ---
1059
+ if (gestureWheel.done) {
1060
+ return;
1022
1061
  }
1023
- else if ((e as any).direction === 'v') {
1024
- y = x;
1025
- x = 0;
1062
+ // --- 获取滚动 delta 坐标 ---
1063
+ let deltaY = oe.deltaY;
1064
+ let deltaX = oe.deltaX;
1065
+ if (clickgo.dom.is.shift) {
1066
+ deltaY = oe.deltaX;
1067
+ deltaX = oe.deltaY;
1026
1068
  }
1027
- }
1028
- else {
1029
- x = e.x ?? 0;
1030
- y = e.y ?? 0;
1031
- }
1032
- let tx = bindGestureData.tx + x;
1033
- if (tx > 90) {
1034
- tx = 90;
1035
- }
1036
- else if (tx < -90) {
1037
- tx = -90;
1038
- }
1039
- let ty = bindGestureData.ty + y;
1040
- if (ty > 90) {
1041
- ty = 90;
1042
- }
1043
- else if (ty < -90) {
1044
- ty = -90;
1045
- }
1046
- bindGestureData.tx = tx;
1047
- bindGestureData.ty = ty;
1048
- if (bindGestureData.timers.ani !== 0) {
1049
- cancelAnimationFrame(bindGestureData.timers.ani);
1050
- bindGestureData.timers.ani = 0;
1051
- }
1052
- if (bindGestureData.timers.sleep !== 0) {
1053
- clearTimeout(bindGestureData.timers.sleep);
1054
- bindGestureData.timers.sleep = 0;
1055
- }
1056
- bindGestureAnimation({
1057
- 'rect': rect,
1058
- 'dirs': opt.dirs,
1059
- 'handler': opt.handler
1069
+ // --- 判断是不是本轮首次滚动 ---
1070
+ if (gestureWheel.dir === '') {
1071
+ // --- 判断滚动方向 ---
1072
+ if (Math.abs(deltaY) > Math.abs(deltaX)) {
1073
+ // --- 竖向滚动 ---
1074
+ if (deltaY < 0) {
1075
+ // --- 向上滚 ---
1076
+ gestureWheel.dir = 'top';
1077
+ }
1078
+ else {
1079
+ // --- 向下滚 ---
1080
+ gestureWheel.dir = 'bottom';
1081
+ }
1082
+ }
1083
+ else {
1084
+ // --- 横向滚动 ---
1085
+ if (deltaX < 0) {
1086
+ // --- 向左滚 ---
1087
+ gestureWheel.dir = 'left';
1088
+ }
1089
+ else {
1090
+ // --- 向下滚 ---
1091
+ gestureWheel.dir = 'right';
1092
+ }
1093
+ }
1094
+ // --- 判断是否要显示滚动条 ---
1095
+ if (!before(oe, gestureWheel.dir as any)) {
1096
+ // --- 不显示 ---
1097
+ gestureWheel.done = true;
1098
+ return;
1099
+ }
1100
+ // --- 重置位置 ---
1101
+ form.elements.gesture.style.transform = 'scale(0)';
1102
+ switch (gestureWheel.dir) {
1103
+ case 'top':
1104
+ case 'bottom': {
1105
+ form.elements.gesture.style.left = (rect.left + ((rect.width - 20) / 2)).toString() + 'px';
1106
+ if (gestureWheel.dir === 'top') {
1107
+ form.elements.gesture.style.top = (rect.top + 10).toString() + 'px';
1108
+ }
1109
+ else {
1110
+ form.elements.gesture.style.top = (rect.bottom - 10).toString() + 'px';
1111
+ }
1112
+ break;
1113
+ }
1114
+ default: {
1115
+ form.elements.gesture.style.top = (rect.top + ((rect.height - 20) / 2)).toString() + 'px';
1116
+ if (gestureWheel.dir === 'left') {
1117
+ form.elements.gesture.style.left = (rect.left + 10).toString() + 'px';
1118
+ }
1119
+ else {
1120
+ form.elements.gesture.style.left = (rect.right - 10).toString() + 'px';
1121
+ }
1122
+ }
1123
+ }
1124
+ gestureWheel.firstTimer = true;
1125
+ await tool.sleep(30);
1126
+ gestureWheel.firstTimer = false;
1127
+ form.elements.gesture.classList.add('ani');
1128
+ }
1129
+ // --- 滚动 ---
1130
+ switch (gestureWheel.dir) {
1131
+ case 'top':
1132
+ case 'bottom': {
1133
+ gestureWheel.offset += (gestureWheel.dir === 'top') ? -deltaY : deltaY;
1134
+ break;
1135
+ }
1136
+ default: {
1137
+ gestureWheel.offset += (gestureWheel.dir === 'left') ? -deltaX : deltaX;
1138
+ }
1139
+ }
1140
+ if (gestureWheel.offset < 0) {
1141
+ // --- 隐藏了,直接 return ---
1142
+ gestureWheel.offset = 0;
1143
+ form.elements.gesture.style.opacity = '0';
1144
+ return;
1145
+ }
1146
+ // --- 显示 gesture 对象 ---
1147
+ form.elements.gesture.style.opacity = '1';
1148
+ let offset = gestureWheel.offset / 1.38;
1149
+ if (offset > 90) {
1150
+ offset = 90;
1151
+ form.elements.gesture.classList.add('done');
1152
+ }
1153
+ else {
1154
+ form.elements.gesture.classList.remove('done');
1155
+ }
1156
+ // --- 处理标签位置(20 像素是 gesture 的宽高) ---
1157
+ form.elements.gesture.style.transform = 'scale(' + (offset / 90).toString() + ')';
1158
+ switch (gestureWheel.dir) {
1159
+ case 'top': {
1160
+ form.elements.gesture.style.top = (rect.top + (offset / 1.5)).toString() + 'px';
1161
+ break;
1162
+ }
1163
+ case 'bottom': {
1164
+ form.elements.gesture.style.top = (rect.bottom - 20 - (offset / 1.5)).toString() + 'px';
1165
+ break;
1166
+ }
1167
+ case 'left': {
1168
+ form.elements.gesture.style.left = (rect.left + (offset / 1.5)).toString() + 'px';
1169
+ break;
1170
+ }
1171
+ default: {
1172
+ form.elements.gesture.style.left = (rect.right - 20 - (offset / 1.5)).toString() + 'px';
1173
+ }
1174
+ }
1175
+ // --- 看是否要响应 ---
1176
+ clearTimeout(gestureWheel.timer);
1177
+ if (offset < 90) {
1178
+ gestureWheel.timer = window.setTimeout(() => {
1179
+ form.elements.gesture.style.opacity = '0';
1180
+ form.elements.gesture.classList.remove('ani');
1181
+ }, 250);
1182
+ return;
1183
+ }
1184
+ gestureWheel.done = true;
1185
+ handler(gestureWheel.dir as any) as any;
1186
+ await tool.sleep(500);
1187
+ form.elements.gesture.style.opacity = '0';
1188
+ form.elements.gesture.classList.remove('ani');
1189
+ })().catch((e) => {
1190
+ console.log('error', 'dom.bindGesture', e);
1060
1191
  });
1061
1192
  }
1062
1193
  }
@@ -1151,9 +1282,9 @@ export function bindDrag(e: MouseEvent | TouchEvent, opt: { 'el': HTMLElement; '
1151
1282
  otop = rect.top;
1152
1283
  oleft = rect.left;
1153
1284
  },
1154
- 'move': function(ox, oy, x, y) {
1155
- const ntop = otop + oy;
1156
- const nleft = oleft + ox;
1285
+ 'move': function(e, o) {
1286
+ const ntop = otop + o.oy;
1287
+ const nleft = oleft + o.ox;
1157
1288
  form.moveDrag({
1158
1289
  'top': ntop,
1159
1290
  'left': nleft,
@@ -1162,7 +1293,7 @@ export function bindDrag(e: MouseEvent | TouchEvent, opt: { 'el': HTMLElement; '
1162
1293
  otop = ntop;
1163
1294
  oleft = nleft;
1164
1295
  // --- 获取当前 element ---
1165
- const els = document.elementsFromPoint(x, y) as HTMLElement[];
1296
+ const els = document.elementsFromPoint(o.x, o.y) as HTMLElement[];
1166
1297
  for (const el of els) {
1167
1298
  if (el.dataset.cgDrop === undefined) {
1168
1299
  continue;
@@ -1549,8 +1680,20 @@ export function bindMove(e: MouseEvent | TouchEvent, opt: types.IBindMoveOptions
1549
1680
  'ox': ox,
1550
1681
  'oy': oy
1551
1682
  });
1552
-
1553
- opt.move?.(ox, oy, x, y, border, dir, e);
1683
+ opt.move?.(e, {
1684
+ 'ox': ox,
1685
+ 'oy': oy,
1686
+ 'x': x,
1687
+ 'y': y,
1688
+ 'border': border,
1689
+ 'inBorder': {
1690
+ 'top': inBorderTop,
1691
+ 'right': inBorderRight,
1692
+ 'bottom': inBorderBottom,
1693
+ 'left': inBorderLeft
1694
+ },
1695
+ 'dir': dir
1696
+ });
1554
1697
  tx = x;
1555
1698
  ty = y;
1556
1699
  },
@@ -1668,22 +1811,22 @@ export function bindResize(e: MouseEvent | TouchEvent, opt: types.IBindResizeOpt
1668
1811
  'offsetRight': offsetRight,
1669
1812
  'offsetBottom': offsetBottom,
1670
1813
  'start': opt.start,
1671
- 'move': function(ox, oy, x, y, border) {
1814
+ 'move': function(e, o) {
1672
1815
  if (opt.border === 'tr' || opt.border === 'r' || opt.border === 'rb') {
1673
- opt.objectWidth! += ox;
1816
+ opt.objectWidth! += o.ox;
1674
1817
  }
1675
1818
  else if (opt.border === 'bl' || opt.border === 'l' || opt.border === 'lt') {
1676
- opt.objectWidth! -= ox;
1677
- opt.objectLeft! += ox;
1819
+ opt.objectWidth! -= o.ox;
1820
+ opt.objectLeft! += o.ox;
1678
1821
  }
1679
1822
  if (opt.border === 'rb' || opt.border === 'b' || opt.border === 'bl') {
1680
- opt.objectHeight! += oy;
1823
+ opt.objectHeight! += o.oy;
1681
1824
  }
1682
1825
  else if (opt.border === 'lt' || opt.border === 't' || opt.border === 'tr') {
1683
- opt.objectHeight! -= oy;
1684
- opt.objectTop! += oy;
1826
+ opt.objectHeight! -= o.oy;
1827
+ opt.objectTop! += o.oy;
1685
1828
  }
1686
- opt.move?.(opt.objectLeft!, opt.objectTop!, opt.objectWidth!, opt.objectHeight!, x, y, border);
1829
+ opt.move?.(opt.objectLeft!, opt.objectTop!, opt.objectWidth!, opt.objectHeight!, x, y, o.border);
1687
1830
  },
1688
1831
  'end': opt.end
1689
1832
  });
@@ -1786,3 +1929,17 @@ export function fullscreen(): boolean {
1786
1929
  return false;
1787
1930
  }
1788
1931
  }
1932
+
1933
+ // --- 处理 timer 类,窗体消失时不进行监听 ---
1934
+ document.addEventListener('visibilitychange', function() {
1935
+ if (document.hidden) {
1936
+ // --- 隐藏 ---
1937
+ cancelAnimationFrame(watchStyleTimer);
1938
+ cancelAnimationFrame(watchPropertyTimer);
1939
+ }
1940
+ else {
1941
+ // --- 显示 ---
1942
+ watchStyleTimer = requestAnimationFrame(watchStyleHandler);
1943
+ watchPropertyTimer = requestAnimationFrame(watchPropertyHandler);
1944
+ }
1945
+ });