reborn-ui 0.1.74 → 0.1.77

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 (37) hide show
  1. package/dist/index.js +229 -220
  2. package/dist/index.js.map +1 -1
  3. package/package.json +1 -1
  4. package/registry/components/reborn-affix.json +8 -3
  5. package/registry/components/reborn-back-top.json +11 -5
  6. package/registry/components/reborn-badge.json +13 -6
  7. package/registry/components/reborn-button.json +8 -6
  8. package/registry/components/reborn-card.json +18 -0
  9. package/registry/components/reborn-checkbox.json +8 -6
  10. package/registry/components/reborn-chip.json +13 -6
  11. package/registry/components/reborn-collapse.json +13 -6
  12. package/registry/components/reborn-dropdown-select.json +18 -0
  13. package/registry/components/reborn-footer.json +40 -0
  14. package/registry/components/reborn-form.json +15 -8
  15. package/registry/components/reborn-image.json +11 -5
  16. package/registry/components/reborn-input-number.json +14 -7
  17. package/registry/components/reborn-input-otp.json +40 -0
  18. package/registry/components/reborn-input.json +8 -6
  19. package/registry/components/reborn-page.json +13 -0
  20. package/registry/components/reborn-picker-view.json +26 -0
  21. package/registry/components/reborn-popup.json +25 -0
  22. package/registry/components/reborn-qrcode.json +23 -0
  23. package/registry/components/reborn-radio.json +45 -0
  24. package/registry/components/reborn-rate.json +40 -0
  25. package/registry/components/reborn-select-date.json +40 -0
  26. package/registry/components/reborn-select-trigger.json +25 -0
  27. package/registry/components/reborn-select.json +41 -0
  28. package/registry/components/reborn-slider.json +40 -0
  29. package/registry/components/reborn-sticky.json +13 -6
  30. package/registry/components/reborn-switch.json +13 -6
  31. package/registry/components/reborn-tabs copy.json +46 -0
  32. package/registry/components/reborn-tabs-test.json +46 -0
  33. package/registry/components/reborn-tabs.json +14 -7
  34. package/registry/components/reborn-text.json +34 -0
  35. package/registry/components/reborn-textarea.json +6 -5
  36. package/registry/components/scroll-island.json +2 -2
  37. package/registry/registry.json +670 -64
package/dist/index.js CHANGED
@@ -93,6 +93,72 @@ async function detectPackageManager(cwd) {
93
93
  if (await pathExists(path2.join(cwd, "bun.lockb"))) return "bun";
94
94
  return "pnpm";
95
95
  }
96
+ async function readPackageJson(cwd) {
97
+ return await readJsonFile(path2.join(cwd, "package.json"));
98
+ }
99
+ function getMissingDeps(pkg, deps) {
100
+ const existing = /* @__PURE__ */ new Set([
101
+ ...Object.keys(pkg.dependencies ?? {}),
102
+ ...Object.keys(pkg.devDependencies ?? {})
103
+ ]);
104
+ return deps.filter((d) => !existing.has(d));
105
+ }
106
+ async function installDeps(params) {
107
+ const { cwd, pm, deps, dev } = params;
108
+ if (!deps.length) return;
109
+ const args = [];
110
+ if (pm === "pnpm") args.push("add");
111
+ else if (pm === "npm") args.push("install");
112
+ else if (pm === "yarn") args.push("add");
113
+ else if (pm === "bun") args.push("add");
114
+ if (dev) {
115
+ if (pm === "npm") args.push("--save-dev");
116
+ else args.push("-D");
117
+ }
118
+ args.push(...deps);
119
+ await execa(pm, args, { cwd, stdio: "inherit" });
120
+ }
121
+
122
+ // src/utils/dependencies.ts
123
+ var DEPENDENCY_MAP = {
124
+ // UniApp 版本的 Reborn Select 需要特定的前置组件和 lodash-es
125
+ "reborn-select/uniapp": {
126
+ components: ["reborn-button", "reborn-picker-view", "reborn-popup", "reborn-select-trigger"],
127
+ npmDependencies: ["lodash-es", "@types/lodash-es"]
128
+ },
129
+ "reborn-select-date/uniapp": {
130
+ components: ["reborn-button", "reborn-picker-view", "reborn-popup", "reborn-select-trigger"],
131
+ npmDependencies: ["lodash-es", "@types/lodash-es"]
132
+ }
133
+ // 在此处添加其他已知的依赖映射
134
+ };
135
+ function getDependencies(componentName, platform) {
136
+ const result = { components: [], npmDependencies: [] };
137
+ const visited = /* @__PURE__ */ new Set();
138
+ function resolve(name) {
139
+ const keyWithPlatform = platform ? `${name}/${platform}` : name;
140
+ const deps = DEPENDENCY_MAP[keyWithPlatform] || DEPENDENCY_MAP[name];
141
+ if (!deps) return;
142
+ if (deps.components) {
143
+ for (const comp of deps.components) {
144
+ if (!visited.has(comp)) {
145
+ visited.add(comp);
146
+ result.components.push(comp);
147
+ resolve(comp);
148
+ }
149
+ }
150
+ }
151
+ if (deps.npmDependencies) {
152
+ for (const npmDep of deps.npmDependencies) {
153
+ if (!result.npmDependencies.includes(npmDep)) {
154
+ result.npmDependencies.push(npmDep);
155
+ }
156
+ }
157
+ }
158
+ }
159
+ resolve(componentName);
160
+ return result;
161
+ }
96
162
 
97
163
  // src/utils/registry.ts
98
164
  import path3 from "path";
@@ -289,7 +355,86 @@ function addCommand() {
289
355
  if (!platform) {
290
356
  throw new Error("\u5DF2\u53D6\u6D88");
291
357
  }
292
- const totalFiles = targets.reduce((acc, name) => {
358
+ const allComponentsToInstall = new Set(targets);
359
+ const allNpmDependencies = /* @__PURE__ */ new Set();
360
+ for (const target of targets) {
361
+ const deps = getDependencies(target, platform);
362
+ if (deps.components) {
363
+ for (const c of deps.components) allComponentsToInstall.add(c);
364
+ }
365
+ if (deps.npmDependencies) {
366
+ for (const d of deps.npmDependencies) allNpmDependencies.add(d);
367
+ }
368
+ }
369
+ const additionalComponents = Array.from(allComponentsToInstall).filter((c) => !targets.includes(c));
370
+ const npmDependenciesArray = Array.from(allNpmDependencies);
371
+ if (npmDependenciesArray.length > 0) {
372
+ const pkg = await readPackageJson(cwd);
373
+ const missingDeps = getMissingDeps(pkg, npmDependenciesArray);
374
+ if (missingDeps.length > 0) {
375
+ console.log(chalk2.blue(`
376
+ \u68C0\u6D4B\u5230\u5F53\u524D\u7EC4\u4EF6\u9700\u8981\u4EE5\u4E0B\u672A\u5B89\u88C5\u7684 npm \u4F9D\u8D56\uFF1A${missingDeps.join(", ")}`));
377
+ const { installNpm } = await prompts({
378
+ type: "confirm",
379
+ name: "installNpm",
380
+ message: `\u662F\u5426\u9700\u8981\u4E3A\u8FD9\u4E9B\u7EC4\u4EF6\u5B89\u88C5\u4EE5\u4E0A npm \u4F9D\u8D56?`,
381
+ initial: true
382
+ });
383
+ if (installNpm) {
384
+ console.log(chalk2.cyan("\u6B63\u5728\u5B89\u88C5 npm \u4F9D\u8D56..."));
385
+ await installDeps({ cwd, pm, deps: missingDeps });
386
+ successLog(`npm \u4F9D\u8D56\u5B89\u88C5\u5B8C\u6210`);
387
+ }
388
+ }
389
+ }
390
+ let finalTargets = [...targets];
391
+ if (additionalComponents.length > 0) {
392
+ const missingComponents = [];
393
+ for (const c of additionalComponents) {
394
+ const baseDir = path4.join(cwd, cfg.componentsDir, c);
395
+ if (!await pathExists(baseDir)) {
396
+ missingComponents.push(c);
397
+ }
398
+ }
399
+ if (missingComponents.length > 0) {
400
+ console.log(chalk2.blue(`
401
+ \u68C0\u6D4B\u5230\u9700\u8981\u524D\u7F6E\u6216\u5173\u8054\u7EC4\u4EF6\uFF1A${missingComponents.join(", ")}`));
402
+ const { installComponents } = await prompts({
403
+ type: "confirm",
404
+ name: "installComponents",
405
+ message: `\u662F\u5426\u81EA\u52A8\u5B89\u88C5\u7F3A\u5931\u7684\u524D\u7F6E\u7EC4\u4EF6?`,
406
+ initial: true
407
+ });
408
+ if (installComponents) {
409
+ finalTargets = [...finalTargets, ...missingComponents];
410
+ }
411
+ }
412
+ }
413
+ if (!opts.overwrite) {
414
+ const existingComponents = [];
415
+ for (const name of finalTargets) {
416
+ const baseDir = path4.join(cwd, cfg.componentsDir, name);
417
+ if (await pathExists(baseDir)) {
418
+ existingComponents.push(name);
419
+ }
420
+ }
421
+ if (existingComponents.length > 0) {
422
+ console.log(chalk2.yellow(`
423
+ \u9047\u5230\u5DF2\u5B58\u5728\u7684\u7EC4\u4EF6\uFF1A${existingComponents.join(", ")}`));
424
+ const { overwrite } = await prompts({
425
+ type: "confirm",
426
+ name: "overwrite",
427
+ message: `\u662F\u5426\u8986\u76D6\u5E76\u7EE7\u7EED\u5B89\u88C5\uFF1F`,
428
+ initial: false
429
+ });
430
+ if (!overwrite) {
431
+ console.log(chalk2.red("\u5DF2\u7EC8\u6B62\u5B89\u88C5"));
432
+ return;
433
+ }
434
+ opts.overwrite = true;
435
+ }
436
+ }
437
+ const totalFiles = finalTargets.reduce((acc, name) => {
293
438
  const c = registry.components.find((x) => x.name === name);
294
439
  return acc + (c?.files.length ?? 0);
295
440
  }, 0);
@@ -300,7 +445,7 @@ function addCommand() {
300
445
  hideCursor: true
301
446
  });
302
447
  bar.start(totalFiles, 0);
303
- for (const name of targets) {
448
+ for (const name of finalTargets) {
304
449
  const c = registry.components.find((x) => x.name === name);
305
450
  if (!c) {
306
451
  bar.stop();
@@ -318,11 +463,11 @@ function addCommand() {
318
463
  }
319
464
  bar.stop();
320
465
  console.log("");
321
- for (const name of targets) {
466
+ for (const name of finalTargets) {
322
467
  successLog(`\u7EC4\u4EF6 ${chalk2.bold(name)} \u5DF2\u6210\u529F\u6DFB\u52A0\u5230\u9879\u76EE`);
323
468
  }
324
469
  console.log(`
325
- ${chalk2.bold.green("DONE")} \u5DF2\u5B8C\u6210 ${targets.length} \u4E2A\u7EC4\u4EF6\u7684\u6DFB\u52A0\uFF08pm=${pm}\uFF09`);
470
+ ${chalk2.bold.green("DONE")} \u5DF2\u5B8C\u6210 ${finalTargets.length} \u4E2A\u7EC4\u4EF6\u7684\u6DFB\u52A0\uFF08pm=${pm}\uFF09`);
326
471
  });
327
472
  return cmd;
328
473
  }
@@ -435,13 +580,25 @@ function buildCommand() {
435
580
  (p) => path5.join(rootDir, p)
436
581
  );
437
582
  const dirents = await fs4.readdir(sourceDir, { withFileTypes: true });
438
- const componentDirs = dirents.filter((d) => d.isDirectory())?.map((d) => path5.join(sourceDir, d.name)).sort((a, b) => a.localeCompare(b));
583
+ let componentDirs = dirents.filter((d) => d.isDirectory())?.map((d) => d.name);
584
+ if (uniappSourceDir && fssync.existsSync(uniappSourceDir)) {
585
+ const uniappDirents = await fs4.readdir(uniappSourceDir, { withFileTypes: true });
586
+ const uniappDirs = uniappDirents.filter((d) => d.isDirectory())?.map((d) => d.name);
587
+ for (const dir of uniappDirs) {
588
+ if (!componentDirs.includes(dir)) {
589
+ componentDirs.push(dir);
590
+ }
591
+ }
592
+ }
593
+ componentDirs = componentDirs.sort((a, b) => a.localeCompare(b));
439
594
  const components = [];
440
- for (const absComponentDir of componentDirs) {
441
- const name = path5.basename(absComponentDir);
442
- const absFiles = (await listFilesRecursive(absComponentDir)).filter(
443
- isAllowedFile
444
- );
595
+ for (const componentName of componentDirs) {
596
+ const name = componentName;
597
+ const absComponentDir = path5.join(sourceDir, componentName);
598
+ let absFiles = [];
599
+ if (fssync.existsSync(absComponentDir)) {
600
+ absFiles = (await listFilesRecursive(absComponentDir)).filter(isAllowedFile);
601
+ }
445
602
  const files = [];
446
603
  const depSet = /* @__PURE__ */ new Set();
447
604
  for (const absFile of absFiles) {
@@ -451,7 +608,19 @@ function buildCommand() {
451
608
  if (ext === ".vue") {
452
609
  files.push({ path: rel, content, target: "web" });
453
610
  } else {
454
- files.push({ path: rel, content });
611
+ let target;
612
+ if (uniappSourceDir) {
613
+ const parts = rel.split("/");
614
+ const uniappFile = path5.join(uniappSourceDir, name, ...parts);
615
+ if (fssync.existsSync(uniappFile)) {
616
+ target = "web";
617
+ }
618
+ }
619
+ if (target) {
620
+ files.push({ path: rel, content, target });
621
+ } else {
622
+ files.push({ path: rel, content });
623
+ }
455
624
  }
456
625
  if (ext === ".ts" || ext === ".js" || ext === ".vue") {
457
626
  for (const dep of extractNpmDependenciesFromText(content)) {
@@ -611,235 +780,75 @@ export function useNavigation(navigation?: Ref<ContentNavigationItem[]>) {\r
611
780
  `
612
781
  },
613
782
  "uniapp": {
614
- "lib/tv.ts": `// @ts-ignore\r
615
- import type { TV } from "tailwind-variants";\r
616
- import { create } from '@weapp-tailwindcss/variants-v3'\r
783
+ "lib/animation.ts": "import type FrameCallback from 'android.view.Choreographer.FrameCallback' // \u5E27\u56DE\u8C03\u63A5\u53E3\r\nimport type Long from 'kotlin.Long' // Kotlin Long \u7C7B\u578B\r\n// #ifdef APP-ANDROID\r\nimport Choreographer from 'android.view.Choreographer' // Android \u5E27\u540C\u6B65\u5668\uFF0C\u63D0\u4F9B\u5782\u76F4\u540C\u6B65\u4FE1\u53F7\r\n// #endif\r\n\r\n/**\r\n * \u7F13\u52A8\u51FD\u6570\u7C7B\u578B\u5B9A\u4E49\r\n */\r\nexport type EasingFunction = (progress: number) => number\r\n\r\n/**\r\n * \u52A8\u753B\u5C5E\u6027\u914D\u7F6E\r\n */\r\nexport interface AnimationAttribute {\r\n /** \u8D77\u59CB\u503C */\r\n fromValue: string\r\n /** \u7ED3\u675F\u503C */\r\n toValue: string\r\n /** \u5355\u4F4D (px, %, deg\u7B49) */\r\n unit: string\r\n /** \u5F53\u524D\u503C */\r\n currentValue: string\r\n /** \u5F53\u524D\u8FDB\u5EA6 (0-1) */\r\n progress: number\r\n /** \u5C5E\u6027\u540D\u79F0 */\r\n propertyName: string\r\n}\r\n\r\n/**\r\n * \u52A8\u753B\u914D\u7F6E\u9009\u9879\r\n */\r\nexport interface AnimationOptions {\r\n /** \u52A8\u753B\u6301\u7EED\u65F6\u95F4(\u6BEB\u79D2) */\r\n duration?: number\r\n /** \u5FAA\u73AF\u6B21\u6570 (-1\u4E3A\u65E0\u9650\u5FAA\u73AF) */\r\n loop?: number\r\n /** \u662F\u5426\u5F80\u8FD4\u64AD\u653E */\r\n alternate?: boolean\r\n /** \u662F\u5426\u6309\u5C5E\u6027\u987A\u5E8F\u4F9D\u6B21\u6267\u884C\u52A8\u753B */\r\n sequential?: boolean\r\n /** \u7F13\u52A8\u51FD\u6570\u540D\u79F0 */\r\n timingFunction?: string\r\n /** \u81EA\u5B9A\u4E49\u8D1D\u585E\u5C14\u66F2\u7EBF\u53C2\u6570 */\r\n bezier?: number[]\r\n /** \u52A8\u753B\u5B8C\u6210\u56DE\u8C03 */\r\n complete?: () => void\r\n /** \u52A8\u753B\u5F00\u59CB\u56DE\u8C03 */\r\n start?: () => void\r\n /** \u6BCF\u5E27\u56DE\u8C03 */\r\n frame?: (progress: number) => void\r\n}\r\n\r\n// \u8D1D\u585E\u5C14\u66F2\u7EBF\u8BA1\u7B97\u5E38\u91CF\r\nconst BEZIER_SPLINE_SIZE = 11 // \u6837\u672C\u70B9\u6570\u91CF\uFF0C\u7528\u4E8E\u9884\u8BA1\u7B97\u4F18\u5316\r\nconst BEZIER_SAMPLE_STEP = 1.0 / (BEZIER_SPLINE_SIZE - 1.0) // \u6837\u672C\u6B65\u957F\r\n\r\n/**\r\n * \u8D1D\u585E\u5C14\u66F2\u7EBF\u7CFB\u6570A\r\n * \u4E09\u6B21\u8D1D\u585E\u5C14\u66F2\u7EBF\u7684\u4E09\u6B21\u9879\u7CFB\u6570\r\n */\r\nfunction getBezierCoefficientA(x1: number, x2: number): number {\r\n return 1.0 - 3.0 * x2 + 3.0 * x1 // B(t) = (1-t)\xB3P\u2080 + 3(1-t)\xB2tP\u2081 + 3(1-t)t\xB2P\u2082 + t\xB3P\u2083 \u4E2D\u7684 t\xB3 \u7CFB\u6570\r\n}\r\n\r\n/**\r\n * \u8D1D\u585E\u5C14\u66F2\u7EBF\u7CFB\u6570B\r\n * \u4E09\u6B21\u8D1D\u585E\u5C14\u66F2\u7EBF\u7684\u4E8C\u6B21\u9879\u7CFB\u6570\r\n */\r\nfunction getBezierCoefficientB(x1: number, x2: number): number {\r\n return 3.0 * x2 - 6.0 * x1 // \u4E8C\u6B21\u9879\u7CFB\u6570\r\n}\r\n\r\n/**\r\n * \u8D1D\u585E\u5C14\u66F2\u7EBF\u7CFB\u6570C\r\n * \u4E09\u6B21\u8D1D\u585E\u5C14\u66F2\u7EBF\u7684\u4E00\u6B21\u9879\u7CFB\u6570\r\n */\r\nfunction getBezierCoefficientC(x1: number): number {\r\n return 3.0 * x1 // \u4E00\u6B21\u9879\u7CFB\u6570\r\n}\r\n\r\n/**\r\n * \u8BA1\u7B97\u8D1D\u585E\u5C14\u66F2\u7EBF\u503C\r\n * \u4F7F\u7528\u970D\u7EB3\u6CD5\u5219\u63D0\u9AD8\u8BA1\u7B97\u6548\u7387\r\n * @param t \u65F6\u95F4\u53C2\u6570 (0-1)\r\n * @param x1 \u63A7\u5236\u70B91\u7684x\u5750\u6807\r\n * @param x2 \u63A7\u5236\u70B92\u7684x\u5750\u6807\r\n */\r\nfunction calculateBezierValue(t: number, x1: number, x2: number): number {\r\n const a = getBezierCoefficientA(x1, x2) // \u83B7\u53D6\u4E09\u6B21\u9879\u7CFB\u6570\r\n const b = getBezierCoefficientB(x1, x2) // \u83B7\u53D6\u4E8C\u6B21\u9879\u7CFB\u6570\r\n const c = getBezierCoefficientC(x1) // \u83B7\u53D6\u4E00\u6B21\u9879\u7CFB\u6570\r\n return ((a * t + b) * t + c) * t // \u970D\u7EB3\u6CD5\u5219\uFF1A((at + b)t + c)t\uFF0C\u51CF\u5C11\u4E58\u6CD5\u8FD0\u7B97\r\n}\r\n\r\n/**\r\n * \u8BA1\u7B97\u8D1D\u585E\u5C14\u66F2\u7EBF\u659C\u7387\r\n * \u5BF9\u8D1D\u585E\u5C14\u66F2\u7EBF\u6C42\u5BFC\u5F97\u5230\u659C\u7387\u51FD\u6570\r\n * @param t \u65F6\u95F4\u53C2\u6570 (0-1)\r\n * @param x1 \u63A7\u5236\u70B91\u7684x\u5750\u6807\r\n * @param x2 \u63A7\u5236\u70B92\u7684x\u5750\u6807\r\n */\r\nfunction getBezierSlope(t: number, x1: number, x2: number): number {\r\n const a = getBezierCoefficientA(x1, x2) // \u4E09\u6B21\u9879\u7CFB\u6570\r\n const b = getBezierCoefficientB(x1, x2) // \u4E8C\u6B21\u9879\u7CFB\u6570\r\n const c = getBezierCoefficientC(x1) // \u4E00\u6B21\u9879\u7CFB\u6570\r\n return 3.0 * a * t * t + 2.0 * b * t + c // \u5BFC\u6570\uFF1A3at\xB2 + 2bt + c\r\n}\r\n\r\n/**\r\n * \u4E8C\u5206\u6CD5\u6C42\u89E3\u8D1D\u585E\u5C14\u66F2\u7EBF\u53C2\u6570\r\n * \u7528\u4E8E\u6839\u636Ex\u503C\u53CD\u63A8t\u53C2\u6570\uFF0C\u9002\u7528\u4E8E\u659C\u7387\u8F83\u5C0F\u7684\u60C5\u51B5\r\n * @param targetX \u76EE\u6807x\u503C\r\n * @param startT \u8D77\u59CBt\u503C\r\n * @param endT \u7ED3\u675Ft\u503C\r\n * @param x1 \u63A7\u5236\u70B91\u7684x\u5750\u6807\r\n * @param x2 \u63A7\u5236\u70B92\u7684x\u5750\u6807\r\n */\r\nfunction binarySearchBezierT(\r\n targetX: number,\r\n startT: number,\r\n endT: number,\r\n x1: number,\r\n x2: number,\r\n): number {\r\n let currentX: number // \u5F53\u524D\u8BA1\u7B97\u7684x\u503C\r\n let currentT: number // \u5F53\u524D\u7684t\u53C2\u6570\r\n let iterations = 0 // \u8FED\u4EE3\u6B21\u6570\u8BA1\u6570\u5668\r\n const maxIterations = 10 // \u6700\u5927\u8FED\u4EE3\u6B21\u6570\uFF0C\u907F\u514D\u65E0\u9650\u5FAA\u73AF\r\n const precision = 0.0000001 // \u7CBE\u5EA6\u8981\u6C42\r\n\r\n do {\r\n currentT = startT + (endT - startT) / 2.0 // \u53D6\u4E2D\u70B9\r\n currentX = calculateBezierValue(currentT, x1, x2) - targetX // \u8BA1\u7B97\u8BEF\u5DEE\r\n if (currentX > 0.0) {\r\n // \u5982\u679C\u5F53\u524Dx\u503C\u5927\u4E8E\u76EE\u6807\u503C\r\n endT = currentT // \u7F29\u5C0F\u53F3\u8FB9\u754C\r\n }\r\n else {\r\n // \u5982\u679C\u5F53\u524Dx\u503C\u5C0F\u4E8E\u76EE\u6807\u503C\r\n startT = currentT // \u7F29\u5C0F\u5DE6\u8FB9\u754C\r\n }\r\n iterations++ // \u589E\u52A0\u8FED\u4EE3\u8BA1\u6570\r\n } while (Math.abs(currentX) > precision && iterations < maxIterations) // \u76F4\u5230\u7CBE\u5EA6\u6EE1\u8DB3\u6216\u8FBE\u5230\u6700\u5927\u8FED\u4EE3\u6B21\u6570\r\n\r\n return currentT // \u8FD4\u56DE\u627E\u5230\u7684t\u53C2\u6570\r\n}\r\n\r\n/**\r\n * \u725B\u987F-\u62C9\u592B\u900A\u6CD5\u6C42\u89E3\u8D1D\u585E\u5C14\u66F2\u7EBF\u53C2\u6570\r\n * \u9002\u7528\u4E8E\u659C\u7387\u8F83\u5927\u7684\u60C5\u51B5\uFF0C\u6536\u655B\u901F\u5EA6\u5FEB\r\n * @param targetX \u76EE\u6807x\u503C\r\n * @param initialGuess \u521D\u59CB\u731C\u6D4B\u503C\r\n * @param x1 \u63A7\u5236\u70B91\u7684x\u5750\u6807\r\n * @param x2 \u63A7\u5236\u70B92\u7684x\u5750\u6807\r\n */\r\nfunction newtonRaphsonBezierT(\r\n targetX: number,\r\n initialGuess: number,\r\n x1: number,\r\n x2: number,\r\n): number {\r\n let t = initialGuess // \u5F53\u524Dt\u503C\uFF0C\u4ECE\u521D\u59CB\u731C\u6D4B\u5F00\u59CB\r\n const maxIterations = 4 // \u6700\u5927\u8FED\u4EE3\u6B21\u6570\uFF0C\u725B\u987F\u6CD5\u6536\u655B\u5FEB\r\n\r\n for (let i = 0; i < maxIterations; i++) {\r\n const slope = getBezierSlope(t, x1, x2) // \u8BA1\u7B97\u5F53\u524D\u70B9\u7684\u659C\u7387\r\n if (slope == 0.0) {\r\n // \u5982\u679C\u659C\u7387\u4E3A0\uFF0C\u907F\u514D\u9664\u96F6\u9519\u8BEF\r\n return t\r\n }\r\n const currentX = calculateBezierValue(t, x1, x2) - targetX // \u8BA1\u7B97\u5F53\u524D\u8BEF\u5DEE\r\n t = t - currentX / slope // \u725B\u987F\u6CD5\u8FED\u4EE3\u516C\u5F0F\uFF1At_new = t - f(t)/f'(t)\r\n }\r\n return t // \u8FD4\u56DE\u6536\u655B\u540E\u7684t\u503C\r\n}\r\n\r\n/**\r\n * \u521B\u5EFA\u8D1D\u585E\u5C14\u7F13\u52A8\u51FD\u6570\r\n * \u6839\u636E\u56DB\u4E2A\u63A7\u5236\u70B9\u5750\u6807\u751F\u6210\u7F13\u52A8\u51FD\u6570\uFF0C\u7C7B\u4F3CCSS\u7684cubic-bezier\r\n * @param x1 \u63A7\u5236\u70B91\u7684x\u5750\u6807 (0-1)\r\n * @param y1 \u63A7\u5236\u70B91\u7684y\u5750\u6807 (0-1)\r\n * @param x2 \u63A7\u5236\u70B92\u7684x\u5750\u6807 (0-1)\r\n * @param y2 \u63A7\u5236\u70B92\u7684y\u5750\u6807 (0-1)\r\n */\r\nfunction createBezierEasing(x1: number, y1: number, x2: number, y2: number): EasingFunction | null {\r\n // \u9A8C\u8BC1\u63A7\u5236\u70B9\u5750\u6807\u8303\u56F4\uFF0Cx\u5750\u6807\u5FC5\u987B\u57280-1\u4E4B\u95F4\r\n if (!(x1 >= 0 && x1 <= 1 && x2 >= 0 && x2 <= 1)) {\r\n return null // \u53C2\u6570\u65E0\u6548\u65F6\u8FD4\u56DEnull\r\n }\r\n\r\n const sampleValues: number[] = [] // \u9884\u8BA1\u7B97\u7684\u6837\u672C\u503C\u6570\u7EC4\r\n\r\n // \u9884\u8BA1\u7B97\u6837\u672C\u503C\u4EE5\u63D0\u9AD8\u6027\u80FD\uFF0C\u4EC5\u5BF9\u975E\u7EBF\u6027\u66F2\u7EBF\u8FDB\u884C\u9884\u8BA1\u7B97\r\n if (x1 != y1 || x2 != y2) {\r\n // \u5982\u679C\u4E0D\u662F\u7EBF\u6027\u51FD\u6570\r\n for (let i = 0; i < BEZIER_SPLINE_SIZE; i++) {\r\n // \u8BA1\u7B97\u7B49\u95F4\u8DDD\u7684\u6837\u672C\u70B9\uFF0C\u7528\u4E8E\u5FEB\u901F\u67E5\u627E\r\n sampleValues.push(calculateBezierValue(i * BEZIER_SAMPLE_STEP, x1, x2))\r\n }\r\n }\r\n\r\n /**\r\n * \u6839\u636Ex\u503C\u83B7\u53D6\u5BF9\u5E94\u7684t\u53C2\u6570\r\n * \u4F7F\u7528\u9884\u8BA1\u7B97\u6837\u672C\u8FDB\u884C\u5FEB\u901F\u67E5\u627E\u548C\u63D2\u503C\r\n * @param x \u8F93\u5165\u7684x\u503C (0-1)\r\n */\r\n function getTParameterForX(x: number): number {\r\n let intervalStart = 0.0 // \u533A\u95F4\u8D77\u59CB\u4F4D\u7F6E\r\n let currentSample = 1 // \u5F53\u524D\u6837\u672C\u7D22\u5F15\r\n const lastSample = BEZIER_SPLINE_SIZE - 1 // \u6700\u540E\u4E00\u4E2A\u6837\u672C\u7D22\u5F15\r\n\r\n // \u627E\u5230x\u503C\u6240\u5728\u7684\u533A\u95F4\uFF0C\u7EBF\u6027\u641C\u7D22\u9884\u8BA1\u7B97\u7684\u6837\u672C\r\n for (; currentSample != lastSample && sampleValues[currentSample] <= x; currentSample++) {\r\n intervalStart += BEZIER_SAMPLE_STEP // \u79FB\u52A8\u533A\u95F4\u8D77\u59CB\u4F4D\u7F6E\r\n }\r\n currentSample-- // \u56DE\u9000\u5230\u6B63\u786E\u7684\u533A\u95F4\r\n\r\n // \u7EBF\u6027\u63D2\u503C\u83B7\u5F97\u521D\u59CB\u731C\u6D4B\u503C\uFF0C\u63D0\u9AD8\u540E\u7EED\u6C42\u89E3\u7CBE\u5EA6\r\n const dist\r\n = (x - sampleValues[currentSample])\r\n / (sampleValues[currentSample + 1] - sampleValues[currentSample]) // \u8BA1\u7B97\u5728\u533A\u95F4\u5185\u7684\u76F8\u5BF9\u4F4D\u7F6E\r\n const initialGuess = intervalStart + dist * BEZIER_SAMPLE_STEP // \u8BA1\u7B97\u521D\u59CB\u731C\u6D4B\u7684t\u503C\r\n const initialSlope = getBezierSlope(initialGuess, x1, x2) // \u8BA1\u7B97\u521D\u59CB\u70B9\u7684\u659C\u7387\r\n\r\n // \u6839\u636E\u659C\u7387\u9009\u62E9\u5408\u9002\u7684\u6C42\u89E3\u65B9\u6CD5\r\n if (initialSlope >= 0.001) {\r\n // \u659C\u7387\u8DB3\u591F\u5927\u65F6\u4F7F\u7528\u725B\u987F\u6CD5\r\n return newtonRaphsonBezierT(x, initialGuess, x1, x2)\r\n }\r\n else if (initialSlope == 0.0) {\r\n // \u659C\u7387\u4E3A0\u65F6\u76F4\u63A5\u8FD4\u56DE\r\n return initialGuess\r\n }\r\n // \u659C\u7387\u592A\u5C0F\u65F6\u4F7F\u7528\u4E8C\u5206\u6CD5\uFF0C\u66F4\u7A33\u5B9A\r\n return binarySearchBezierT(x, intervalStart, intervalStart + BEZIER_SAMPLE_STEP, x1, x2)\r\n }\r\n\r\n // \u8FD4\u56DE\u7F13\u52A8\u51FD\u6570\uFF0C\u8FD9\u662F\u6700\u7EC8\u7684\u7F13\u52A8\u51FD\u6570\u63A5\u53E3\r\n return function (progress: number): number {\r\n // \u7EBF\u6027\u60C5\u51B5\u76F4\u63A5\u8FD4\u56DE\uFF0C\u4F18\u5316\u6027\u80FD\r\n if (x1 == y1 && x2 == y2) {\r\n return progress\r\n }\r\n // \u8FB9\u754C\u60C5\u51B5\u5904\u7406\uFF0C\u907F\u514D\u8BA1\u7B97\u8BEF\u5DEE\r\n if (progress == 0.0 || progress == 1.0) {\r\n return progress\r\n }\r\n // \u8BA1\u7B97\u8D1D\u585E\u5C14\u66F2\u7EBF\u503C\uFF1A\u5148\u6839\u636Eprogress(x)\u627E\u5230\u5BF9\u5E94\u7684t\uFF0C\u518D\u8BA1\u7B97y\u503C\r\n return calculateBezierValue(getTParameterForX(progress), y1, y2)\r\n }\r\n}\r\n\r\n/**\r\n * \u989C\u8272\u5DE5\u5177\u51FD\u6570\uFF1A\u6807\u51C6\u5316\u989C\u8272\u503C\u683C\u5F0F\r\n * \u5904\u7406\u4E0D\u540C\u683C\u5F0F\u7684\u989C\u8272\u8F93\u5165\uFF0C\u786E\u4FDD\u8FD4\u56DE\u6709\u6548\u7684\u989C\u8272\u503C\r\n */\r\nfunction getDefaultColor(colorValue: string): string {\r\n // \u7B80\u5316\u7684\u989C\u8272\u5904\u7406\uFF0C\u5B9E\u9645\u9879\u76EE\u4E2D\u53EF\u80FD\u9700\u8981\u66F4\u5B8C\u6574\u7684\u989C\u8272\u8F6C\u6362\r\n if (colorValue.startsWith('#')) {\r\n // \u5341\u516D\u8FDB\u5236\u989C\u8272\u683C\u5F0F\r\n return colorValue\r\n }\r\n if (colorValue.startsWith('rgb')) {\r\n // RGB\u6216RGBA\u989C\u8272\u683C\u5F0F\r\n return colorValue\r\n }\r\n // \u9ED8\u8BA4\u8FD4\u56DE\u9ED1\u8272\uFF0C\u4F5C\u4E3A\u515C\u5E95\u5904\u7406\r\n return '#000000'\r\n}\r\n\r\n/**\r\n * \u5341\u516D\u8FDB\u5236\u989C\u8272\u8F6CRGB\u5BF9\u8C61\r\n * \u5C06#RRGGBB\u683C\u5F0F\u7684\u989C\u8272\u8F6C\u6362\u4E3A{r,g,b,a}\u5BF9\u8C61\uFF0C\u7528\u4E8E\u989C\u8272\u52A8\u753B\u63D2\u503C\r\n * @param hex \u5341\u516D\u8FDB\u5236\u989C\u8272\u503C\uFF0C\u5982\"#FF0000\"\r\n * @returns \u5305\u542Br,g,b,a\u5C5E\u6027\u7684\u989C\u8272\u5BF9\u8C61\r\n */\r\nfunction hexToRgb(hex: string): any {\r\n // \u4F7F\u7528\u6B63\u5219\u8868\u8FBE\u5F0F\u89E3\u6790\u5341\u516D\u8FDB\u5236\u989C\u8272\uFF0C\u652F\u6301\u5E26#\u548C\u4E0D\u5E26#\u7684\u683C\u5F0F\r\n const result = /^#?([a-f\\d]{2})([a-f\\d]{2})([a-f\\d]{2})$/i.exec(hex)\r\n if (result != null) {\r\n // \u89E3\u6790\u6210\u529F\r\n return {\r\n r: Number.parseInt(result[1] ?? '0', 16), // \u7EA2\u8272\u5206\u91CF\uFF0C16\u8FDB\u5236\u8F6C10\u8FDB\u5236\r\n g: Number.parseInt(result[2] ?? '0', 16), // \u7EFF\u8272\u5206\u91CF\r\n b: Number.parseInt(result[3] ?? '0', 16), // \u84DD\u8272\u5206\u91CF\r\n a: 1.0, // \u900F\u660E\u5EA6\uFF0C\u9ED8\u8BA4\u4E0D\u900F\u660E\r\n }\r\n }\r\n // \u89E3\u6790\u5931\u8D25\u65F6\u8FD4\u56DE\u9ED1\u8272\r\n return {\r\n r: 0,\r\n g: 0,\r\n b: 0,\r\n a: 1.0,\r\n }\r\n}\r\n\r\n/**\r\n * \u9AD8\u6027\u80FD\u52A8\u753B\u5F15\u64CE\u7C7B\r\n * \u652F\u6301\u591A\u5E73\u53F0\u7684\u6D41\u7545\u52A8\u753B\u6548\u679C\uFF0C\u63D0\u4F9B\u4E30\u5BCC\u7684\u7F13\u52A8\u51FD\u6570\u548C\u52A8\u753B\u63A7\u5236\r\n */\r\nexport class AnimationEngine {\r\n /** \u9884\u5B9A\u4E49\u7F13\u52A8\u51FD\u6570\u6620\u5C04\uFF0C\u5B58\u50A8\u5E38\u7528\u7684\u8D1D\u585E\u5C14\u66F2\u7EBF\u53C2\u6570 */\r\n private readonly easingPresets = new Map<string, number[]>([\r\n ['linear', [0.0, 0.0, 1.0, 1.0]], // \u7EBF\u6027\u7F13\u52A8\r\n ['ease', [0.25, 0.1, 0.25, 1.0]], // \u9ED8\u8BA4\u7F13\u52A8\r\n ['easeIn', [0.42, 0.0, 1.0, 1.0]], // \u52A0\u901F\u8FDB\u5165\r\n ['easeOut', [0.0, 0.0, 0.58, 1.0]], // \u51CF\u901F\u9000\u51FA\r\n ['easeInOut', [0.42, 0.0, 0.58, 1.0]], // \u5148\u52A0\u901F\u540E\u51CF\u901F\r\n ['easeInQuad', [0.55, 0.085, 0.68, 0.53]], // \u4E8C\u6B21\u65B9\u52A0\u901F\r\n ['easeOutQuad', [0.25, 0.46, 0.45, 0.94]], // \u4E8C\u6B21\u65B9\u51CF\u901F\r\n ['easeInOutQuad', [0.455, 0.03, 0.515, 0.955]], // \u4E8C\u6B21\u65B9\u5148\u52A0\u901F\u540E\u51CF\u901F\r\n ['easeInCubic', [0.55, 0.055, 0.675, 0.19]], // \u4E09\u6B21\u65B9\u52A0\u901F\r\n ['easeOutCubic', [0.215, 0.61, 0.355, 1.0]], // \u4E09\u6B21\u65B9\u51CF\u901F\r\n ['easeInOutCubic', [0.645, 0.045, 0.355, 1.0]], // \u4E09\u6B21\u65B9\u5148\u52A0\u901F\u540E\u51CF\u901F\r\n ['easeInQuart', [0.895, 0.03, 0.685, 0.22]], // \u56DB\u6B21\u65B9\u52A0\u901F\r\n ['easeOutQuart', [0.165, 0.84, 0.44, 1.0]], // \u56DB\u6B21\u65B9\u51CF\u901F\r\n ['easeInOutQuart', [0.77, 0.0, 0.175, 1.0]], // \u56DB\u6B21\u65B9\u5148\u52A0\u901F\u540E\u51CF\u901F\r\n ['easeInQuint', [0.755, 0.05, 0.855, 0.06]], // \u4E94\u6B21\u65B9\u52A0\u901F\r\n ['easeOutQuint', [0.23, 1.0, 0.32, 1.0]], // \u4E94\u6B21\u65B9\u51CF\u901F\r\n ['easeInOutQuint', [0.86, 0.0, 0.07, 1.0]], // \u4E94\u6B21\u65B9\u5148\u52A0\u901F\u540E\u51CF\u901F\r\n ['easeInSine', [0.47, 0.0, 0.745, 0.715]], // \u6B63\u5F26\u52A0\u901F\r\n ['easeOutSine', [0.39, 0.575, 0.565, 1.0]], // \u6B63\u5F26\u51CF\u901F\r\n ['easeInOutSine', [0.445, 0.05, 0.55, 0.95]], // \u6B63\u5F26\u5148\u52A0\u901F\u540E\u51CF\u901F\r\n ['easeInExpo', [0.95, 0.05, 0.795, 0.035]], // \u6307\u6570\u52A0\u901F\r\n ['easeOutExpo', [0.19, 1.0, 0.22, 1.0]], // \u6307\u6570\u51CF\u901F\r\n ['easeInOutExpo', [1.0, 0.0, 0.0, 1.0]], // \u6307\u6570\u5148\u52A0\u901F\u540E\u51CF\u901F\r\n ['easeInCirc', [0.6, 0.04, 0.98, 0.335]], // \u5706\u5F62\u52A0\u901F\r\n ['easeOutCirc', [0.075, 0.82, 0.165, 1.0]], // \u5706\u5F62\u51CF\u901F\r\n ['easeInOutBack', [0.68, -0.55, 0.265, 1.55]], // \u56DE\u5F39\u6548\u679C\r\n ])\r\n\r\n /** \u76EE\u6807DOM\u5143\u7D20\uFF0C\u52A8\u753B\u4F5C\u7528\u7684\u5BF9\u8C61 */\r\n private targetElement: any | null = null\r\n\r\n /** \u52A8\u753B\u6301\u7EED\u65F6\u95F4(\u6BEB\u79D2)\uFF0C\u9ED8\u8BA4500ms */\r\n private animationDuration: number = 500\r\n\r\n /** \u52A8\u753B\u662F\u5426\u6B63\u5728\u8FD0\u884C\uFF0C\u7528\u4E8E\u63A7\u5236\u52A8\u753B\u5FAA\u73AF */\r\n private isRunning: boolean = false\r\n\r\n /** \u52A8\u753B\u662F\u5426\u6682\u505C\uFF0C\u6682\u505C\u65F6\u4FDD\u7559\u5F53\u524D\u8FDB\u5EA6 */\r\n private isPaused: boolean = false\r\n\r\n /** \u5F53\u524D\u52A8\u753B\u8FDB\u5EA6 (0-1)\uFF0C\u7528\u4E8E\u6062\u590D\u6682\u505C\u7684\u52A8\u753B */\r\n private currentProgress: number = 0\r\n\r\n /** \u662F\u5426\u53CD\u5411\u64AD\u653E\uFF0C\u5F71\u54CD\u52A8\u753B\u65B9\u5411 */\r\n private isReversed: boolean = false\r\n\r\n /** \u662F\u5426\u5F80\u8FD4\u64AD\u653E\u6A21\u5F0F\uFF0C\u63A7\u5236\u52A8\u753B\u662F\u5426\u6765\u56DE\u64AD\u653E */\r\n private isAlternate: boolean = false\r\n /** \u5F80\u8FD4\u64AD\u653E\u65F6\u662F\u5426\u5904\u4E8E\u53CD\u5411\u72B6\u6001 */\r\n private isAlternateReversed: boolean = false\r\n\r\n /** \u5FAA\u73AF\u64AD\u653E\u6B21\u6570 (-1\u4E3A\u65E0\u9650\u5FAA\u73AF) */\r\n private loopCount: number = 1\r\n /** \u5F53\u524D\u5DF2\u5B8C\u6210\u7684\u5FAA\u73AF\u6B21\u6570 */\r\n private currentLoop: number = 0\r\n\r\n /** \u52A8\u753B\u662F\u5426\u6B63\u5728\u505C\u6B62\uFF0C\u7528\u4E8E\u63D0\u524D\u7EC8\u6B62\u52A8\u753B */\r\n private isStopping: boolean = true\r\n\r\n /** \u5F53\u524D\u6267\u884C\u7684\u5C5E\u6027\u7D22\u5F15(\u987A\u5E8F\u6267\u884C\u6A21\u5F0F)\uFF0C\u7528\u4E8E\u63A7\u5236\u5C5E\u6027\u4F9D\u6B21\u52A8\u753B */\r\n private currentAttributeIndex: number = 0\r\n\r\n /** \u56DE\u8C03\u51FD\u6570\uFF0C\u63D0\u4F9B\u52A8\u753B\u751F\u547D\u5468\u671F\u94A9\u5B50 */\r\n private onComplete: () => void = () => { } // \u52A8\u753B\u5B8C\u6210\u56DE\u8C03\r\n private onStart: () => void = () => { } // \u52A8\u753B\u5F00\u59CB\u56DE\u8C03\r\n private onFrame: (progress: number) => void = () => { } // \u6BCF\u5E27\u56DE\u8C03\r\n\r\n /** \u52A8\u753B\u5C5E\u6027\u5217\u8868\uFF0C\u5B58\u50A8\u6240\u6709\u8981\u52A8\u753B\u7684CSS\u5C5E\u6027 */\r\n private animationAttributes: AnimationAttribute[] = []\r\n\r\n /** \u52A8\u753B\u5F00\u59CB\u65F6\u95F4\u6233\uFF0C\u7528\u4E8E\u8BA1\u7B97\u52A8\u753B\u8FDB\u5EA6 */\r\n private startTimestamp: number = 0\r\n\r\n /** \u5F53\u524D\u4F7F\u7528\u7684\u7F13\u52A8\u51FD\u6570\uFF0C\u5C06\u7EBF\u6027\u8FDB\u5EA6\u8F6C\u6362\u4E3A\u7F13\u52A8\u8FDB\u5EA6 */\r\n private currentEasingFunction: EasingFunction | null = null\r\n\r\n /** \u662F\u5426\u6309\u5C5E\u6027\u987A\u5E8F\u4F9D\u6B21\u6267\u884C\u52A8\u753B\uFF0C\u800C\u975E\u5E76\u884C\u6267\u884C */\r\n private isSequentialMode: boolean = false\r\n\r\n // \u5E73\u53F0\u76F8\u5173\u7684\u52A8\u753B\u63A7\u5236\u5668\r\n // Android\u5E73\u53F0\u4F7F\u7528Choreographer\u63D0\u4F9B\u9AD8\u6027\u80FD\u52A8\u753B\r\n // #ifdef APP-ANDROID\r\n private choreographer: Choreographer | null = null // Android\u7CFB\u7EDF\u5E27\u540C\u6B65\u5668\r\n private frameCallback: FrameCallback | null = null // \u5E27\u56DE\u8C03\u5904\u7406\u5668\r\n // #endif\r\n\r\n // iOS/\u5C0F\u7A0B\u5E8F\u5E73\u53F0\u4F7F\u7528\u5B9A\u65F6\u5668\r\n // #ifdef APP-IOS\r\n private displayLinkTimer: number = 0 // iOS\u5B9A\u65F6\u5668ID\r\n // #endif\r\n\r\n // Web\u5E73\u53F0\u4F7F\u7528requestAnimationFrame\r\n private animationFrameId: number | null = null // \u52A8\u753B\u5E27ID\r\n\r\n /**\r\n * \u521B\u5EFA\u52A8\u753B\u5F15\u64CE\u5B9E\u4F8B\r\n * \u521D\u59CB\u5316\u52A8\u753B\u5F15\u64CE\uFF0C\u8BBE\u7F6E\u76EE\u6807\u5143\u7D20\u548C\u52A8\u753B\u914D\u7F6E\r\n * @param element \u76EE\u6807DOM\u5143\u7D20\uFF0Cnull\u65F6\u4EC5\u505A\u8BA1\u7B97\u4E0D\u5E94\u7528\u6837\u5F0F\r\n * @param options \u52A8\u753B\u914D\u7F6E\u9009\u9879\uFF0C\u5305\u542B\u6301\u7EED\u65F6\u95F4\u3001\u7F13\u52A8\u51FD\u6570\u7B49\r\n */\r\n constructor(element: any | null, options: AnimationOptions) {\r\n this.targetElement = element // \u4FDD\u5B58\u76EE\u6807\u5143\u7D20\u5F15\u7528\r\n\r\n // \u8BBE\u7F6E\u52A8\u753B\u53C2\u6570\uFF0C\u4F7F\u7528\u9009\u9879\u503C\u6216\u9ED8\u8BA4\u503C\r\n this.animationDuration\r\n = options.duration != null ? options.duration : this.animationDuration // \u8BBE\u7F6E\u52A8\u753B\u6301\u7EED\u65F6\u95F4\r\n this.loopCount = options.loop != null ? options.loop : this.loopCount // \u8BBE\u7F6E\u5FAA\u73AF\u6B21\u6570\r\n this.isAlternate = options.alternate != null ? options.alternate : this.isAlternate // \u8BBE\u7F6E\u5F80\u8FD4\u64AD\u653E\r\n this.isSequentialMode\r\n = options.sequential != null ? options.sequential : this.isSequentialMode // \u8BBE\u7F6E\u987A\u5E8F\u6267\u884C\u6A21\u5F0F\r\n\r\n // \u8BBE\u7F6E\u7F13\u52A8\u51FD\u6570\uFF0C\u4F18\u5148\u4F7F\u7528\u9884\u5B9A\u4E49\u51FD\u6570\r\n if (options.timingFunction != null) {\r\n const easingParams = this.easingPresets.get(options.timingFunction) // \u67E5\u627E\u9884\u5B9A\u4E49\u7F13\u52A8\u53C2\u6570\r\n if (easingParams != null) {\r\n // \u6839\u636E\u8D1D\u585E\u5C14\u53C2\u6570\u521B\u5EFA\u7F13\u52A8\u51FD\u6570\r\n this.currentEasingFunction = createBezierEasing(\r\n easingParams[0], // x1\u5750\u6807\r\n easingParams[1], // y1\u5750\u6807\r\n easingParams[2], // x2\u5750\u6807\r\n easingParams[3], // y2\u5750\u6807\r\n )\r\n }\r\n }\r\n\r\n // \u81EA\u5B9A\u4E49\u8D1D\u585E\u5C14\u66F2\u7EBF\uFF0C\u4F1A\u8986\u76D6\u9884\u5B9A\u4E49\u51FD\u6570\r\n if (options.bezier != null && options.bezier.length == 4) {\r\n this.currentEasingFunction = createBezierEasing(\r\n options.bezier[0], // \u81EA\u5B9A\u4E49x1\u5750\u6807\r\n options.bezier[1], // \u81EA\u5B9A\u4E49y1\u5750\u6807\r\n options.bezier[2], // \u81EA\u5B9A\u4E49x2\u5750\u6807\r\n options.bezier[3], // \u81EA\u5B9A\u4E49y2\u5750\u6807\r\n )\r\n }\r\n\r\n // \u8BBE\u7F6E\u56DE\u8C03\u51FD\u6570\uFF0C\u63D0\u4F9B\u52A8\u753B\u751F\u547D\u5468\u671F\u94A9\u5B50\r\n if (options.complete != null) {\r\n this.onComplete = options.complete // \u52A8\u753B\u5B8C\u6210\u56DE\u8C03\r\n }\r\n if (options.start != null) {\r\n this.onStart = options.start // \u52A8\u753B\u5F00\u59CB\u56DE\u8C03\r\n }\r\n if (options.frame != null) {\r\n this.onFrame = options.frame // \u6BCF\u5E27\u66F4\u65B0\u56DE\u8C03\r\n }\r\n }\r\n\r\n /**\r\n * \u4ECE\u6837\u5F0F\u503C\u4E2D\u63D0\u53D6\u5355\u4F4D\r\n * \u89E3\u6790CSS\u503C\u4E2D\u7684\u5355\u4F4D\u90E8\u5206\uFF0C\u7528\u4E8E\u52A8\u753B\u8BA1\u7B97\r\n * @param value \u6837\u5F0F\u503C\uFF0C\u5982 \"100px\", \"50%\"\r\n * @param propertyName CSS\u5C5E\u6027\u540D\u79F0\uFF0C\u7528\u4E8E\u5224\u65AD\u662F\u5426\u9700\u8981\u9ED8\u8BA4\u5355\u4F4D\r\n * @returns \u5355\u4F4D\u5B57\u7B26\u4E32\r\n */\r\n private extractUnit(value?: string, propertyName?: string): string {\r\n if (value == null) { return 'px' } // \u9ED8\u8BA4\u5355\u4F4D\u4E3Apx\r\n const unit = value.replace(/[\\d|\\-+.]/g, '') // \u79FB\u9664\u6570\u5B57\u3001\u8D1F\u53F7\u3001\u6B63\u53F7\u3001\u5C0F\u6570\u70B9\uFF0C\u4FDD\u7559\u5355\u4F4D\r\n\r\n // opacity\u3001z-index\u7B49\u5C5E\u6027\u65E0\u9700\u5355\u4F4D\r\n if (propertyName == 'opacity' || propertyName == 'z-index') {\r\n return '' // \u8FD4\u56DE\u7A7A\u5B57\u7B26\u4E32\u8868\u793A\u65E0\u5355\u4F4D\r\n }\r\n\r\n return unit == '' ? 'px' : unit // \u5982\u679C\u6CA1\u6709\u5355\u4F4D\u5219\u9ED8\u8BA4\u4E3Apx\r\n }\r\n\r\n /**\r\n * \u6DFB\u52A0\u81EA\u5B9A\u4E49\u7F13\u52A8\u51FD\u6570\r\n * \u5411\u5F15\u64CE\u6CE8\u518C\u65B0\u7684\u7F13\u52A8\u51FD\u6570\uFF0C\u53EF\u5728\u540E\u7EED\u52A8\u753B\u4E2D\u4F7F\u7528\r\n * @param name \u7F13\u52A8\u51FD\u6570\u540D\u79F0\r\n * @param bezierParams \u8D1D\u585E\u5C14\u66F2\u7EBF\u53C2\u6570 [x1, y1, x2, y2]\r\n */\r\n addCustomEasing(name: string, bezierParams: number[]): AnimationEngine {\r\n if (bezierParams.length == 4) {\r\n // \u9A8C\u8BC1\u53C2\u6570\u6570\u91CF\r\n this.easingPresets.set(name, bezierParams) // \u6DFB\u52A0\u5230\u9884\u8BBE\u6620\u5C04\u4E2D\r\n }\r\n return this // \u8FD4\u56DE\u81EA\u8EAB\u652F\u6301\u94FE\u5F0F\u8C03\u7528\r\n }\r\n\r\n /**\r\n * \u8BBE\u7F6E\u52A8\u753B\u53CD\u5411\u64AD\u653E\r\n * \u63A7\u5236\u52A8\u753B\u4ECE\u7ED3\u675F\u503C\u5411\u8D77\u59CB\u503C\u64AD\u653E\r\n * @param reverse \u662F\u5426\u53CD\u5411\u64AD\u653E\uFF0Cnull\u8868\u793A\u5207\u6362\u5F53\u524D\u72B6\u6001\r\n */\r\n setReverse(reverse: boolean | null = null): AnimationEngine {\r\n if (reverse != null) {\r\n this.isReversed = reverse // \u8BBE\u7F6E\u6307\u5B9A\u72B6\u6001\r\n }\r\n else {\r\n this.isReversed = !this.isReversed // \u5207\u6362\u5F53\u524D\u72B6\u6001\r\n }\r\n return this // \u652F\u6301\u94FE\u5F0F\u8C03\u7528\r\n }\r\n\r\n /**\r\n * \u8BBE\u7F6E\u5FAA\u73AF\u64AD\u653E\u6B21\u6570\r\n * \u63A7\u5236\u52A8\u753B\u91CD\u590D\u6267\u884C\u7684\u6B21\u6570\r\n * @param count \u5FAA\u73AF\u6B21\u6570\uFF0C-1\u8868\u793A\u65E0\u9650\u5FAA\u73AF\r\n */\r\n setLoopCount(count: number): AnimationEngine {\r\n this.loopCount = count // \u8BBE\u7F6E\u5FAA\u73AF\u6B21\u6570\r\n return this // \u652F\u6301\u94FE\u5F0F\u8C03\u7528\r\n }\r\n\r\n /**\r\n * \u8BBE\u7F6E\u52A8\u753B\u6301\u7EED\u65F6\u95F4\r\n * \u63A7\u5236\u52A8\u753B\u4ECE\u5F00\u59CB\u5230\u7ED3\u675F\u7684\u603B\u65F6\u957F\r\n * @param duration \u6301\u7EED\u65F6\u95F4(\u6BEB\u79D2)\r\n */\r\n setDuration(duration: number): AnimationEngine {\r\n this.animationDuration = duration // \u8BBE\u7F6E\u52A8\u753B\u6301\u7EED\u65F6\u95F4\r\n return this // \u652F\u6301\u94FE\u5F0F\u8C03\u7528\r\n }\r\n\r\n /**\r\n * \u8BBE\u7F6E\u5F80\u8FD4\u64AD\u653E\u6A21\u5F0F\r\n * \u63A7\u5236\u52A8\u753B\u662F\u5426\u5728\u6BCF\u6B21\u5FAA\u73AF\u65F6\u53CD\u5411\u64AD\u653E\r\n * @param alternate \u662F\u5426\u5F80\u8FD4\u64AD\u653E\r\n */\r\n setAlternate(alternate: boolean): AnimationEngine {\r\n this.isAlternate = alternate // \u8BBE\u7F6E\u5F80\u8FD4\u64AD\u653E\u6807\u5FD7\r\n return this // \u652F\u6301\u94FE\u5F0F\u8C03\u7528\r\n }\r\n\r\n /**\r\n * \u8BBE\u7F6E\u987A\u5E8F\u6267\u884C\u6A21\u5F0F\r\n * \u63A7\u5236\u591A\u4E2A\u5C5E\u6027\u662F\u540C\u65F6\u52A8\u753B\u8FD8\u662F\u4F9D\u6B21\u52A8\u753B\r\n * @param sequential \u662F\u5426\u6309\u5C5E\u6027\u987A\u5E8F\u4F9D\u6B21\u6267\u884C\r\n */\r\n setSequential(sequential: boolean): AnimationEngine {\r\n this.isSequentialMode = sequential // \u8BBE\u7F6E\u6267\u884C\u6A21\u5F0F\r\n return this // \u652F\u6301\u94FE\u5F0F\u8C03\u7528\r\n }\r\n\r\n /**\r\n * \u6DFB\u52A0\u52A8\u753B\u5C5E\u6027\r\n * \u5411\u52A8\u753B\u5F15\u64CE\u6DFB\u52A0\u4E00\u4E2ACSS\u5C5E\u6027\u7684\u52A8\u753B\u914D\u7F6E\r\n * @param propertyName CSS\u5C5E\u6027\u540D\u79F0\r\n * @param fromValue \u8D77\u59CB\u503C(\u652F\u6301\u6570\u5B57+\u5355\u4F4D\uFF0C\u5982\"100px\"\u3001\"50%\")\r\n * @param toValue \u7ED3\u675F\u503C(\u5355\u4F4D\u5FC5\u987B\u4E0E\u8D77\u59CB\u503C\u4E00\u81F4)\r\n * @param unique \u662F\u5426\u552F\u4E00\uFF0Ctrue\u65F6\u540C\u540D\u5C5E\u6027\u4F1A\u88AB\u66FF\u6362\r\n */\r\n addAttribute(\r\n propertyName: string,\r\n fromValue: string,\r\n toValue: string,\r\n unique: boolean = true,\r\n ): AnimationEngine {\r\n const isColor = this.isColorProperty(propertyName) // \u68C0\u6D4B\u662F\u5426\u4E3A\u989C\u8272\u5C5E\u6027\r\n const unit = isColor ? '' : this.extractUnit(fromValue, propertyName) // \u63D0\u53D6\u5355\u4F4D\r\n\r\n // \u6839\u636E\u5C5E\u6027\u7C7B\u578B\u5904\u7406\u503C\r\n const processedFromValue = isColor\r\n ? getDefaultColor(fromValue) // \u989C\u8272\u5C5E\u6027\u6807\u51C6\u5316\r\n : Number.parseFloat(fromValue).toString() // \u6570\u503C\u5C5E\u6027\u63D0\u53D6\u6570\u5B57\r\n const processedToValue = isColor\r\n ? getDefaultColor(toValue) // \u989C\u8272\u5C5E\u6027\u6807\u51C6\u5316\r\n : Number.parseFloat(toValue).toString() // \u6570\u503C\u5C5E\u6027\u63D0\u53D6\u6570\u5B57\r\n\r\n // \u67E5\u627E\u662F\u5426\u5DF2\u5B58\u5728\u540C\u540D\u5C5E\u6027\uFF0C\u7528\u4E8E\u51B3\u5B9A\u662F\u66FF\u6362\u8FD8\u662F\u65B0\u589E\r\n let existingIndex = this.animationAttributes.findIndex(\r\n (attr: AnimationAttribute): boolean => attr.propertyName == propertyName,\r\n )\r\n\r\n if (!unique) {\r\n existingIndex = -1 // \u5F3A\u5236\u6DFB\u52A0\u65B0\u5C5E\u6027\uFF0C\u4E0D\u66FF\u6362\r\n }\r\n\r\n // \u521B\u5EFA\u65B0\u7684\u52A8\u753B\u5C5E\u6027\u5BF9\u8C61\r\n const newAttribute: AnimationAttribute = {\r\n fromValue: processedFromValue, // \u5904\u7406\u540E\u7684\u8D77\u59CB\u503C\r\n toValue: processedToValue, // \u5904\u7406\u540E\u7684\u7ED3\u675F\u503C\r\n unit, // \u5355\u4F4D\r\n progress: 0, // \u521D\u59CB\u8FDB\u5EA6\u4E3A0\r\n currentValue: processedFromValue, // \u5F53\u524D\u503C\u521D\u59CB\u5316\u4E3A\u8D77\u59CB\u503C\r\n propertyName, // \u5C5E\u6027\u540D\u79F0\r\n }\r\n\r\n if (existingIndex == -1) {\r\n this.animationAttributes.push(newAttribute) // \u6DFB\u52A0\u65B0\u5C5E\u6027\r\n }\r\n else {\r\n this.animationAttributes[existingIndex] = newAttribute // \u66FF\u6362\u73B0\u6709\u5C5E\u6027\r\n }\r\n\r\n return this // \u652F\u6301\u94FE\u5F0F\u8C03\u7528\r\n }\r\n\r\n /**\r\n * \u5FEB\u6377\u65B9\u6CD5\uFF1A\u6DFB\u52A0\u53D8\u6362\u5C5E\u6027\r\n */\r\n transform(property: string, fromValue: string, toValue: string): AnimationEngine {\r\n return this.addAttribute(property, fromValue, toValue)\r\n }\r\n\r\n /**\r\n * \u5FEB\u6377\u65B9\u6CD5\uFF1A\u6DFB\u52A0\u4F4D\u79FB\u52A8\u753B\r\n */\r\n translate(fromX: string, fromY: string, toX: string, toY: string): AnimationEngine {\r\n this.addAttribute('translateX', fromX, toX)\r\n this.addAttribute('translateY', fromY, toY)\r\n return this\r\n }\r\n\r\n /**\r\n * \u6DFB\u52A0X\u8F74\u4F4D\u79FB\u52A8\u753B\r\n * @param fromX \u8D77\u59CBX\u4F4D\u7F6E\uFF0C\u53EF\u4EE5\u4F7F\u7528\"current\"\u8868\u793A\u5F53\u524D\u4F4D\u7F6E\r\n * @param toX \u7ED3\u675FX\u4F4D\u7F6E\r\n * @returns\r\n */\r\n translateX(fromX: string, toX: string): AnimationEngine {\r\n return this.addAttribute('translateX', fromX, toX)\r\n }\r\n\r\n /**\r\n * \u6DFB\u52A0Y\u8F74\u4F4D\u79FB\u52A8\u753B\r\n * @param fromY \u8D77\u59CBY\u4F4D\u7F6E\uFF0C\u53EF\u4EE5\u4F7F\u7528\"current\"\u8868\u793A\u5F53\u524D\u4F4D\u7F6E\r\n * @param toY \u7ED3\u675FY\u4F4D\u7F6E\r\n * @returns\r\n */\r\n translateY(fromY: string, toY: string): AnimationEngine {\r\n return this.addAttribute('translateY', fromY, toY)\r\n }\r\n\r\n /**\r\n * \u5FEB\u6377\u65B9\u6CD5\uFF1A\u6DFB\u52A0\u7F29\u653E\u52A8\u753B\r\n */\r\n scale(fromScale: string, toScale: string): AnimationEngine {\r\n return this.addAttribute('scale', fromScale, toScale)\r\n }\r\n\r\n /**\r\n * \u5FEB\u6377\u65B9\u6CD5\uFF1A\u6DFB\u52A0\u65CB\u8F6C\u52A8\u753B\r\n */\r\n rotate(fromDegree: string, toDegree: string): AnimationEngine {\r\n return this.addAttribute('rotate', fromDegree, toDegree)\r\n }\r\n\r\n /**\r\n * \u5FEB\u6377\u65B9\u6CD5\uFF1A\u6DFB\u52A0\u900F\u660E\u5EA6\u52A8\u753B\r\n */\r\n opacity(fromOpacity: string, toOpacity: string): AnimationEngine {\r\n return this.addAttribute('opacity', fromOpacity, toOpacity)\r\n }\r\n\r\n /**\r\n * \u7EBF\u6027\u63D2\u503C\u8BA1\u7B97\r\n * \u6839\u636E\u8FDB\u5EA6\u5728\u4E24\u4E2A\u6570\u503C\u4E4B\u95F4\u8FDB\u884C\u63D2\u503C\uFF0C\u7528\u4E8E\u8BA1\u7B97\u52A8\u753B\u4E2D\u95F4\u503C\r\n * @param startValue \u8D77\u59CB\u503C\r\n * @param endValue \u7ED3\u675F\u503C\r\n * @param progress \u8FDB\u5EA6 (0-1)\r\n */\r\n private interpolateValue(startValue: number, endValue: number, progress: number): number {\r\n return startValue + (endValue - startValue) * progress // \u7EBF\u6027\u63D2\u503C\u516C\u5F0F\uFF1Astart + (end - start) * progress\r\n }\r\n\r\n /**\r\n * \u5224\u65AD\u662F\u5426\u4E3A\u989C\u8272\u76F8\u5173\u5C5E\u6027\r\n * \u68C0\u6D4BCSS\u5C5E\u6027\u540D\u662F\u5426\u4E0E\u989C\u8272\u76F8\u5173\uFF0C\u7528\u4E8E\u7279\u6B8A\u7684\u989C\u8272\u52A8\u753B\u5904\u7406\r\n * @param propertyName \u5C5E\u6027\u540D\u79F0\r\n */\r\n private isColorProperty(propertyName: string): boolean {\r\n return (\r\n propertyName.includes('background') // \u80CC\u666F\u989C\u8272\u76F8\u5173\r\n || propertyName.includes('color') // \u6587\u5B57\u989C\u8272\u76F8\u5173\r\n || propertyName.includes('border-color') // \u8FB9\u6846\u989C\u8272\u76F8\u5173\r\n || propertyName.includes('shadow') // \u9634\u5F71\u989C\u8272\u76F8\u5173\r\n )\r\n }\r\n\r\n /**\r\n * \u5224\u65AD\u662F\u5426\u4E3ATransform\u76F8\u5173\u5C5E\u6027\r\n * \u68C0\u6D4B\u5C5E\u6027\u540D\u662F\u5426\u4E3Atransform\u76F8\u5173\u7684CSS\u5C5E\u6027\r\n * @param propertyName CSS\u5C5E\u6027\u540D\u79F0\r\n * @returns \u662F\u5426\u4E3Atransform\u5C5E\u6027\r\n */\r\n private isTransformProperty(propertyName: string): boolean {\r\n return (\r\n propertyName == 'scaleX' // X\u8F74\u7F29\u653E\r\n || propertyName == 'scaleY' // Y\u8F74\u7F29\u653E\r\n || propertyName == 'scale' // \u7B49\u6BD4\u7F29\u653E\r\n || propertyName == 'rotateX' // X\u8F74\u65CB\u8F6C\r\n || propertyName == 'rotateY' // Y\u8F74\u65CB\u8F6C\r\n || propertyName == 'rotate' // Z\u8F74\u65CB\u8F6C\r\n || propertyName == 'translateX' // X\u8F74\u4F4D\u79FB\r\n || propertyName == 'translateY' // Y\u8F74\u4F4D\u79FB\r\n || propertyName == 'translate' // \u53CC\u8F74\u4F4D\u79FB\r\n )\r\n }\r\n\r\n /**\r\n * \u8BBE\u7F6E\u5143\u7D20\u6837\u5F0F\u5C5E\u6027\r\n * \u6839\u636E\u5C5E\u6027\u7C7B\u578B\u5E94\u7528\u76F8\u5E94\u7684\u6837\u5F0F\u503C\uFF0C\u652F\u6301transform\u3001\u989C\u8272\u3001\u666E\u901A\u6570\u503C\u5C5E\u6027\r\n * @param propertyName \u5C5E\u6027\u540D\u79F0\r\n * @param currentValue \u5F53\u524D\u503C\r\n * @param unit \u5355\u4F4D\r\n * @param progress \u52A8\u753B\u8FDB\u5EA6\r\n * @param attribute \u52A8\u753B\u5C5E\u6027\u5BF9\u8C61\r\n */\r\n private setElementProperty(\r\n propertyName: string,\r\n currentValue: number,\r\n unit: string,\r\n progress: number,\r\n attribute: AnimationAttribute,\r\n ): void {\r\n if (this.targetElement == null) { return } // \u6CA1\u6709\u76EE\u6807\u5143\u7D20\u65F6\u76F4\u63A5\u8FD4\u56DE\r\n\r\n const element = this.targetElement // \u83B7\u53D6\u76EE\u6807\u5143\u7D20\u5F15\u7528\r\n const valueStr = currentValue.toFixed(2) // \u6570\u503C\u4FDD\u7559\u4E24\u4F4D\u5C0F\u6570\r\n\r\n // #ifdef MP\r\n if (element.style == null) {\r\n return\r\n }\r\n // #endif\r\n\r\n // Transform \u76F8\u5173\u5C5E\u6027\u5904\u7406\uFF0C\u4F7F\u7528CSS transform\u5C5E\u6027\r\n switch (propertyName) {\r\n case 'scaleX': // X\u8F74\u7F29\u653E\r\n element.style!.setProperty('transform', `scaleX(${currentValue})`)\r\n break\r\n case 'scaleY': // Y\u8F74\u7F29\u653E\r\n element.style!.setProperty('transform', `scaleY(${currentValue})`)\r\n break\r\n case 'scale': // \u7B49\u6BD4\u7F29\u653E\r\n element.style!.setProperty('transform', `scale(${currentValue})`)\r\n break\r\n case 'rotateX': // X\u8F74\u65CB\u8F6C\r\n element.style!.setProperty('transform', `rotateX(${valueStr + unit})`)\r\n break\r\n case 'rotateY': // Y\u8F74\u65CB\u8F6C\r\n element.style!.setProperty('transform', `rotateY(${valueStr + unit})`)\r\n break\r\n case 'rotate': // Z\u8F74\u65CB\u8F6C\r\n element.style!.setProperty('transform', `rotate(${valueStr + unit})`)\r\n break\r\n case 'translateX': // X\u8F74\u4F4D\u79FB\r\n element.style!.setProperty('transform', `translateX(${valueStr + unit})`)\r\n break\r\n case 'translateY': // Y\u8F74\u4F4D\u79FB\r\n element.style!.setProperty('transform', `translateY(${valueStr + unit})`)\r\n break\r\n case 'translate': // \u53CC\u8F74\u4F4D\u79FB\r\n element.style!.setProperty(\r\n 'transform',\r\n `translate(${valueStr + unit},${valueStr + unit})`,\r\n )\r\n break\r\n default:\r\n // \u989C\u8272\u5C5E\u6027\u5904\u7406\uFF0C\u9700\u8981\u8FDB\u884CRGBA\u63D2\u503C\r\n if (this.isColorProperty(propertyName)) {\r\n const startColor = hexToRgb(attribute.fromValue) // \u89E3\u6790\u8D77\u59CB\u989C\u8272\r\n const endColor = hexToRgb(attribute.toValue) // \u89E3\u6790\u7ED3\u675F\u989C\u8272\r\n\r\n // \u63D0\u53D6\u8D77\u59CB\u989C\u8272\u7684RGBA\u5206\u91CF\uFF0C\u517C\u5BB9\u4E0D\u540C\u7684JSON\u5BF9\u8C61\u8BBF\u95EE\u65B9\u5F0F\r\n const startR\r\n = startColor.getNumber != null\r\n ? startColor.getNumber('r')\r\n : (startColor.r as number)\r\n const startG\r\n = startColor.getNumber != null\r\n ? startColor.getNumber('g')\r\n : (startColor.g as number)\r\n const startB\r\n = startColor.getNumber != null\r\n ? startColor.getNumber('b')\r\n : (startColor.b as number)\r\n const startA\r\n = startColor.getNumber != null\r\n ? startColor.getNumber('a')\r\n : (startColor.a as number)\r\n\r\n // \u63D0\u53D6\u7ED3\u675F\u989C\u8272\u7684RGBA\u5206\u91CF\r\n const endR\r\n = endColor.getNumber != null\r\n ? endColor.getNumber('r')\r\n : (endColor.r as number)\r\n const endG\r\n = endColor.getNumber != null\r\n ? endColor.getNumber('g')\r\n : (endColor.g as number)\r\n const endB\r\n = endColor.getNumber != null\r\n ? endColor.getNumber('b')\r\n : (endColor.b as number)\r\n const endA\r\n = endColor.getNumber != null\r\n ? endColor.getNumber('a')\r\n : (endColor.a as number)\r\n\r\n // \u5BF9\u6BCF\u4E2A\u989C\u8272\u5206\u91CF\u8FDB\u884C\u63D2\u503C\u8BA1\u7B97\r\n const r = this.interpolateValue(\r\n startR != null ? startR : 0,\r\n endR != null ? endR : 0,\r\n progress,\r\n )\r\n const g = this.interpolateValue(\r\n startG != null ? startG : 0,\r\n endG != null ? endG : 0,\r\n progress,\r\n )\r\n const b = this.interpolateValue(\r\n startB != null ? startB : 0,\r\n endB != null ? endB : 0,\r\n progress,\r\n )\r\n const a = this.interpolateValue(\r\n startA != null ? startA : 1,\r\n endA != null ? endA : 1,\r\n progress,\r\n )\r\n\r\n // \u8BBE\u7F6ERGBA\u989C\u8272\u503C\r\n element.style!.setProperty(\r\n propertyName,\r\n `rgba(${r.toFixed(0)},${g.toFixed(0)},${b.toFixed(0)},${a.toFixed(1)})`,\r\n )\r\n }\r\n else {\r\n // \u666E\u901A\u6570\u503C\u5C5E\u6027\u5904\u7406\uFF0C\u76F4\u63A5\u8BBE\u7F6E\u6570\u503C\u548C\u5355\u4F4D\r\n element.style!.setProperty(propertyName, valueStr + unit)\r\n }\r\n break\r\n }\r\n }\r\n\r\n /**\r\n * Web\u5E73\u53F0\u52A8\u753B\u8FD0\u884C\u65B9\u6CD5 (H5/iOS/Harmony)\r\n * \u4F7F\u7528requestAnimationFrame\u5B9E\u73B0\u6D41\u7545\u7684\u52A8\u753B\u5FAA\u73AF\r\n */\r\n private runWebAnimation(): void {\r\n // #ifdef H5 || APP-IOS || APP-HARMONY\r\n const self = this // \u4FDD\u5B58this\u5F15\u7528\uFF0C\u907F\u514D\u5728\u5185\u90E8\u51FD\u6570\u4E2Dthis\u6307\u5411\u6539\u53D8\r\n self.startTimestamp = 0 // \u91CD\u7F6E\u5F00\u59CB\u65F6\u95F4\u6233\r\n\r\n // \u53D6\u6D88\u4E4B\u524D\u7684\u52A8\u753B\u5E27\uFF0C\u907F\u514D\u91CD\u590D\u6267\u884C\r\n if (self.animationFrameId != null) {\r\n cancelAnimationFrame(self.animationFrameId)\r\n }\r\n\r\n function animationLoop(): void {\r\n // \u521D\u59CB\u5316\u5F00\u59CB\u65F6\u95F4\uFF0C\u9996\u6B21\u6267\u884C\u65F6\u8BB0\u5F55\u65F6\u95F4\u6233\r\n if (self.startTimestamp <= 0) {\r\n self.startTimestamp = Date.now()\r\n }\r\n\r\n // \u8BA1\u7B97\u5F53\u524D\u8FDB\u5EA6\uFF1A(\u5DF2\u7528\u65F6\u95F4 / \u603B\u65F6\u95F4) + \u6682\u505C\u524D\u7684\u8FDB\u5EA6\r\n const elapsed = Date.now() - self.startTimestamp // \u5DF2\u7ECF\u8FC7\u7684\u65F6\u95F4\r\n const progress = Math.min(elapsed / self.animationDuration + self.currentProgress, 1.0) // \u9650\u5236\u8FDB\u5EA6\u4E0D\u8D85\u8FC71\r\n\r\n // \u6267\u884C\u52A8\u753B\u66F4\u65B0\uFF0C\u5E94\u7528\u5F53\u524D\u8FDB\u5EA6\u5230\u6240\u6709\u5C5E\u6027\r\n self.updateAnimationFrame(progress)\r\n\r\n // \u68C0\u67E5\u6682\u505C\u72B6\u6001\r\n if (self.isPaused) {\r\n self.isRunning = false // \u505C\u6B62\u8FD0\u884C\u6807\u5FD7\r\n self.currentProgress = progress // \u4FDD\u5B58\u5F53\u524D\u8FDB\u5EA6\uFF0C\u7528\u4E8E\u6062\u590D\r\n console.log('\u52A8\u753B\u5DF2\u6682\u505C')\r\n return // \u9000\u51FA\u52A8\u753B\u5FAA\u73AF\r\n }\r\n\r\n // \u68C0\u67E5\u52A8\u753B\u5B8C\u6210\u6216\u505C\u6B62\r\n if (progress >= 1.0 || self.isStopping) {\r\n self.handleAnimationComplete() // \u5904\u7406\u52A8\u753B\u5B8C\u6210\u903B\u8F91\r\n return // \u9000\u51FA\u52A8\u753B\u5FAA\u73AF\r\n }\r\n\r\n // \u7EE7\u7EED\u4E0B\u4E00\u5E27\uFF0C\u52A8\u753B\u672A\u5B8C\u6210\u4E14\u4ECD\u5728\u8FD0\u884C\r\n if (progress < 1.0 && self.isRunning) {\r\n self.onFrame(progress) // \u89E6\u53D1\u6BCF\u5E27\u56DE\u8C03\r\n self.animationFrameId = requestAnimationFrame(animationLoop) // \u8BF7\u6C42\u4E0B\u4E00\u5E27\r\n }\r\n }\r\n\r\n // \u5F00\u59CB\u52A8\u753B\uFF0C\u89E6\u53D1\u5F00\u59CB\u56DE\u8C03\u5E76\u542F\u52A8\u52A8\u753B\u5FAA\u73AF\r\n self.onStart()\r\n animationLoop()\r\n // #endif\r\n }\r\n\r\n /**\r\n * \u66F4\u65B0\u52A8\u753B\u5E27\r\n * \u6839\u636E\u6267\u884C\u6A21\u5F0F\u66F4\u65B0\u6240\u6709\u6216\u5F53\u524D\u5C5E\u6027\u7684\u52A8\u753B\u503C\r\n * @param progress \u5F53\u524D\u8FDB\u5EA6 (0-1)\r\n */\r\n private updateAnimationFrame(progress: number): void {\r\n if (this.targetElement == null) { return } // \u6CA1\u6709\u76EE\u6807\u5143\u7D20\u65F6\u76F4\u63A5\u8FD4\u56DE\r\n\r\n if (!this.isSequentialMode) {\r\n // \u5E76\u884C\u6267\u884C\u6240\u6709\u5C5E\u6027\u52A8\u753B\uFF0C\u6240\u6709\u5C5E\u6027\u540C\u65F6\u8FDB\u884C\u52A8\u753B\r\n for (let i = 0; i < this.animationAttributes.length; i++) {\r\n this.updateSingleAttribute(this.animationAttributes[i], progress)\r\n }\r\n }\r\n else {\r\n // \u987A\u5E8F\u6267\u884C\u5C5E\u6027\u52A8\u753B\uFF0C\u4E00\u4E2A\u63A5\u4E00\u4E2A\u5730\u6267\u884C\u5C5E\u6027\u52A8\u753B\r\n if (this.currentAttributeIndex < this.animationAttributes.length) {\r\n this.updateSingleAttribute(\r\n this.animationAttributes[this.currentAttributeIndex],\r\n progress,\r\n )\r\n }\r\n }\r\n }\r\n\r\n /**\r\n * \u66F4\u65B0\u5355\u4E2A\u5C5E\u6027\u7684\u52A8\u753B\r\n * \u8BA1\u7B97\u5C5E\u6027\u7684\u5F53\u524D\u503C\u5E76\u5E94\u7528\u5230\u5143\u7D20\u4E0A\r\n * @param attribute \u52A8\u753B\u5C5E\u6027\r\n * @param progress \u8FDB\u5EA6\r\n */\r\n private updateSingleAttribute(attribute: AnimationAttribute, progress: number): void {\r\n attribute.progress = progress // \u66F4\u65B0\u5C5E\u6027\u7684\u8FDB\u5EA6\u8BB0\u5F55\r\n\r\n if (!this.isColorProperty(attribute.propertyName)) {\r\n // \u6570\u503C\u5C5E\u6027\u5904\u7406\r\n const fromValue = Number.parseFloat(attribute.fromValue) // \u8D77\u59CB\u6570\u503C\r\n const toValue = Number.parseFloat(attribute.toValue) // \u7ED3\u675F\u6570\u503C\r\n\r\n // \u5E94\u7528\u7F13\u52A8\u51FD\u6570\uFF0C\u5C06\u7EBF\u6027\u8FDB\u5EA6\u8F6C\u6362\u4E3A\u7F13\u52A8\u8FDB\u5EA6\r\n let easedProgress = progress\r\n if (this.currentEasingFunction != null) {\r\n easedProgress = this.currentEasingFunction(progress)\r\n }\r\n\r\n // \u8BA1\u7B97\u5F53\u524D\u503C\uFF0C\u4F7F\u7528\u7F13\u52A8\u8FDB\u5EA6\u8FDB\u884C\u63D2\u503C\r\n let currentValue = this.interpolateValue(fromValue, toValue, easedProgress)\r\n\r\n // \u5904\u7406\u53CD\u5411\u548C\u5F80\u8FD4\u64AD\u653E\uFF0C\u4EA4\u6362\u8D77\u59CB\u548C\u7ED3\u675F\u503C\r\n if (this.isReversed || this.isAlternateReversed) {\r\n currentValue = this.interpolateValue(toValue, fromValue, easedProgress)\r\n }\r\n\r\n // \u5E94\u7528\u8BA1\u7B97\u51FA\u7684\u503C\u5230\u5143\u7D20\u5C5E\u6027\r\n this.setElementProperty(\r\n attribute.propertyName,\r\n currentValue,\r\n attribute.unit,\r\n progress,\r\n attribute,\r\n )\r\n }\r\n else {\r\n // \u989C\u8272\u5C5E\u6027\u5904\u7406\uFF0Cprogress\u53C2\u6570\u4F1A\u5728setElementProperty\u4E2D\u7528\u4E8E\u989C\u8272\u63D2\u503C\r\n this.setElementProperty(attribute.propertyName, 0, attribute.unit, progress, attribute)\r\n }\r\n }\r\n\r\n /**\r\n * \u5904\u7406\u52A8\u753B\u5B8C\u6210\r\n */\r\n private handleAnimationComplete(): void {\r\n // \u987A\u5E8F\u6A21\u5F0F\u4E0B\u68C0\u67E5\u662F\u5426\u8FD8\u6709\u672A\u6267\u884C\u7684\u5C5E\u6027\r\n if (\r\n this.isSequentialMode\r\n && this.currentAttributeIndex < this.animationAttributes.length - 1\r\n ) {\r\n this.currentAttributeIndex++\r\n this.currentProgress = 0\r\n this.restartAnimation()\r\n return\r\n }\r\n\r\n // \u91CD\u7F6E\u72B6\u6001\r\n // #ifdef H5 || APP-IOS || APP-HARMONY\r\n if (this.animationFrameId != null) {\r\n cancelAnimationFrame(this.animationFrameId)\r\n }\r\n // #endif\r\n\r\n this.currentAttributeIndex = 0\r\n this.currentProgress = 0\r\n\r\n // \u5904\u7406\u5F80\u8FD4\u64AD\u653E\r\n if (this.isAlternate) {\r\n this.isAlternateReversed = !this.isAlternateReversed\r\n }\r\n\r\n // \u5904\u7406\u5FAA\u73AF\u64AD\u653E\r\n if (this.loopCount == -1) {\r\n // \u65E0\u9650\u5FAA\u73AF\r\n this.restartAnimation()\r\n return\r\n }\r\n else {\r\n this.currentLoop++\r\n if (this.currentLoop < this.loopCount) {\r\n this.restartAnimation()\r\n return\r\n }\r\n }\r\n\r\n // \u52A8\u753B\u5B8C\u6210\r\n this.isRunning = false\r\n this.onComplete()\r\n }\r\n\r\n /**\r\n * \u6839\u636E\u5E73\u53F0\u91CD\u65B0\u542F\u52A8\u52A8\u753B\r\n */\r\n private restartAnimation(): void {\r\n // \u91CD\u7F6E\u5F00\u59CB\u65F6\u95F4\u6233\uFF0C\u786E\u4FDD\u5FAA\u73AF\u52A8\u753B\u6B63\u786E\u8BA1\u65F6\r\n this.startTimestamp = 0\r\n\r\n // \u6839\u636E\u5E73\u53F0\u9009\u62E9\u5408\u9002\u7684\u52A8\u753B\u5F15\u64CE\r\n // #ifdef H5 || APP-IOS || APP-HARMONY\r\n this.runWebAnimation()\r\n // #endif\r\n // #ifdef APP-ANDROID\r\n this.runAndroidAnimation()\r\n // #endif\r\n // #ifdef MP\r\n this.runMPAnimation()\r\n // #endif\r\n }\r\n\r\n /**\r\n * Android\u5E73\u53F0\u52A8\u753B\u8FD0\u884C\u65B9\u6CD5\r\n */\r\n private runAndroidAnimation(): void {\r\n // #ifdef APP-ANDROID\r\n const self = this\r\n self.startTimestamp = 0\r\n\r\n // \u521D\u59CB\u5316Choreographer\r\n if (self.choreographer == null) {\r\n self.choreographer = Choreographer.getInstance()\r\n }\r\n else {\r\n // \u6E05\u9664\u4E4B\u524D\u7684\u56DE\u8C03\r\n if (self.frameCallback != null) {\r\n self.choreographer.removeFrameCallback(self.frameCallback)\r\n }\r\n }\r\n\r\n /**\r\n * Android\u539F\u751F\u5E27\u56DE\u8C03\u7C7B\r\n */\r\n class frameCallback extends Choreographer.FrameCallback {\r\n // @ts-ignore\r\n override doFrame(frameTimeNanos: Long) {\r\n // \u68C0\u67E5\u52A8\u753B\u662F\u5426\u5E94\u8BE5\u505C\u6B62\r\n if (!self.isRunning || self.isStopping) {\r\n return\r\n }\r\n\r\n // \u521D\u59CB\u5316\u5F00\u59CB\u65F6\u95F4\r\n if (self.startTimestamp <= 0) {\r\n self.startTimestamp = Date.now()\r\n }\r\n\r\n // \u8BA1\u7B97\u5F53\u524D\u8FDB\u5EA6\r\n const elapsed = Date.now() - self.startTimestamp\r\n const progress = Math.min(\r\n elapsed / self.animationDuration + self.currentProgress,\r\n 1.0,\r\n )\r\n\r\n // \u6267\u884C\u52A8\u753B\u66F4\u65B0\r\n self.updateAnimationFrame(progress)\r\n\r\n // \u68C0\u67E5\u6682\u505C\u72B6\u6001\r\n if (self.isPaused) {\r\n self.isRunning = false\r\n self.currentProgress = progress\r\n return\r\n }\r\n\r\n // \u68C0\u67E5\u52A8\u753B\u5B8C\u6210\u6216\u505C\u6B62\r\n if (progress >= 1.0 || self.isStopping) {\r\n self.handleAnimationComplete()\r\n return\r\n }\r\n\r\n // \u7EE7\u7EED\u4E0B\u4E00\u5E27\r\n if (progress < 1.0 && self.isRunning && !self.isStopping) {\r\n self.onFrame(progress)\r\n if (self.choreographer != null) {\r\n self.choreographer.postFrameCallback(this)\r\n }\r\n }\r\n }\r\n }\r\n\r\n // \u542F\u52A8\u52A8\u753B\r\n self.onStart()\r\n self.frameCallback = new frameCallback()\r\n self.choreographer!.postFrameCallback(self.frameCallback)\r\n // #endif\r\n }\r\n\r\n /**\r\n * \u5C0F\u7A0B\u5E8F\u5E73\u53F0\u52A8\u753B\u8FD0\u884C\u65B9\u6CD5\r\n */\r\n private runMPAnimation(): void {\r\n // #ifdef MP\r\n const self = this\r\n self.startTimestamp = 0\r\n\r\n // \u6E05\u9664\u4E4B\u524D\u7684\u5B9A\u65F6\u5668\r\n if (self.displayLinkTimer != 0) {\r\n clearTimeout(self.displayLinkTimer)\r\n }\r\n\r\n function animationLoop(): void {\r\n // \u521D\u59CB\u5316\u5F00\u59CB\u65F6\u95F4\r\n if (self.startTimestamp <= 0) {\r\n self.startTimestamp = Date.now()\r\n }\r\n\r\n // \u8BA1\u7B97\u5F53\u524D\u8FDB\u5EA6\r\n const elapsed = Date.now() - self.startTimestamp\r\n const progress = Math.min(elapsed / self.animationDuration + self.currentProgress, 1.0)\r\n\r\n // \u6267\u884C\u52A8\u753B\u66F4\u65B0\r\n self.updateAnimationFrame(progress)\r\n\r\n // \u68C0\u67E5\u6682\u505C\u72B6\u6001\r\n if (self.isPaused) {\r\n self.isRunning = false\r\n self.currentProgress = progress\r\n return\r\n }\r\n\r\n // \u68C0\u67E5\u52A8\u753B\u5B8C\u6210\u6216\u505C\u6B62\r\n if (progress >= 1.0 || self.isStopping) {\r\n self.handleAnimationComplete()\r\n return\r\n }\r\n\r\n // \u7EE7\u7EED\u4E0B\u4E00\u5E27\r\n if (progress < 1.0 && self.isRunning) {\r\n self.onFrame(progress)\r\n self.displayLinkTimer = setTimeout(animationLoop, 16) as any // \u7EA660fps\r\n }\r\n }\r\n\r\n // \u5F00\u59CB\u52A8\u753B\r\n self.onStart()\r\n animationLoop()\r\n // #endif\r\n }\r\n\r\n /**\r\n * \u5F00\u59CB\u64AD\u653E\u52A8\u753B\r\n */\r\n play(): AnimationEngine {\r\n if (this.isRunning) { return this }\r\n\r\n // \u521D\u59CB\u5316\u52A8\u753B\u72B6\u6001\r\n this.isRunning = true\r\n this.isStopping = false\r\n this.isPaused = false\r\n this.currentLoop = 0\r\n this.currentAttributeIndex = 0\r\n\r\n // \u6839\u636E\u5E73\u53F0\u9009\u62E9\u5408\u9002\u7684\u52A8\u753B\u5F15\u64CE\r\n // #ifdef H5 || APP-IOS || APP-HARMONY\r\n this.runWebAnimation()\r\n // #endif\r\n // #ifdef APP-ANDROID\r\n this.runAndroidAnimation()\r\n // #endif\r\n // #ifdef MP\r\n this.runMPAnimation()\r\n // #endif\r\n\r\n return this\r\n }\r\n\r\n /**\r\n * \u5F02\u6B65\u64AD\u653E\u52A8\u753B\uFF0C\u652F\u6301await\r\n * @returns Promise\uFF0C\u52A8\u753B\u5B8C\u6210\u65F6resolve\r\n */\r\n playAsync(): Promise<void> {\r\n return new Promise<void>((resolve) => {\r\n const originalComplete = this.onComplete\r\n this.onComplete = () => {\r\n originalComplete()\r\n resolve()\r\n }\r\n this.play()\r\n })\r\n }\r\n\r\n /**\r\n * \u505C\u6B62\u52A8\u753B\r\n * \u4F1A\u7ACB\u5373\u505C\u6B62\u52A8\u753B\u5E76\u8DF3\u8F6C\u5230\u7ED3\u675F\u72B6\u6001\r\n */\r\n stop(): AnimationEngine {\r\n this.isStopping = true\r\n this.currentProgress = 0\r\n this.currentAttributeIndex = this.animationAttributes.length\r\n\r\n // \u6E05\u7406\u5E73\u53F0\u76F8\u5173\u7684\u52A8\u753B\u63A7\u5236\u5668\r\n // #ifdef WEB || APP-IOS || APP-HARMONY\r\n if (this.animationFrameId != null) {\r\n cancelAnimationFrame(this.animationFrameId)\r\n this.animationFrameId = null\r\n }\r\n // #endif\r\n\r\n // #ifdef APP-ANDROID\r\n if (this.choreographer != null && this.frameCallback != null) {\r\n this.choreographer.removeFrameCallback(this.frameCallback)\r\n }\r\n // #endif\r\n\r\n // #ifdef MP\r\n if (this.displayLinkTimer != 0) {\r\n clearTimeout(this.displayLinkTimer)\r\n this.displayLinkTimer = 0\r\n }\r\n // #endif\r\n\r\n this.isRunning = false\r\n return this\r\n }\r\n\r\n /**\r\n * \u6682\u505C\u52A8\u753B\r\n * \u4FDD\u7559\u5F53\u524D\u72B6\u6001\uFF0C\u53EF\u4EE5\u901A\u8FC7play()\u6062\u590D\r\n */\r\n pause(): AnimationEngine {\r\n this.isPaused = true\r\n return this\r\n }\r\n\r\n /**\r\n * \u6062\u590D\u6682\u505C\u7684\u52A8\u753B\r\n */\r\n resume(): AnimationEngine {\r\n if (this.isPaused) {\r\n this.isPaused = false\r\n this.play()\r\n }\r\n return this\r\n }\r\n\r\n /**\r\n * \u6E05\u7A7A\u5E94\u7528\u5230\u5143\u7D20\u4E0A\u7684\u52A8\u753B\u6837\u5F0F\r\n * \u53EA\u6E05\u7A7A\u5B9E\u9645\u88AB\u52A8\u753B\u5F15\u64CE\u8BBE\u7F6E\u8FC7\u7684CSS\u5C5E\u6027\r\n */\r\n private clearElementStyles(): void {\r\n if (this.targetElement == null) { return }\r\n\r\n const element = this.targetElement\r\n\r\n // \u6E05\u7A7A\u6240\u6709\u52A8\u753B\u5C5E\u6027\u5217\u8868\u4E2D\u8BB0\u5F55\u7684\u5C5E\u6027\r\n for (const attr of this.animationAttributes) {\r\n const propertyName = attr.propertyName\r\n\r\n // Transform \u76F8\u5173\u5C5E\u6027\u9700\u8981\u6E05\u7A7Atransform\r\n if (this.isTransformProperty(propertyName)) {\r\n element.style!.setProperty('transform', '')\r\n }\r\n else {\r\n // \u5176\u4ED6\u5C5E\u6027\u76F4\u63A5\u6E05\u7A7A\r\n element.style!.setProperty(propertyName, '')\r\n }\r\n }\r\n }\r\n\r\n /**\r\n * \u91CD\u7F6E\u52A8\u753B\u5230\u521D\u59CB\u72B6\u6001\uFF0C\u6E05\u7A7A\u6240\u6709\u5185\u5BB9\r\n */\r\n reset(): AnimationEngine {\r\n // \u505C\u6B62\u5F53\u524D\u52A8\u753B\r\n this.stop()\r\n\r\n // \u6E05\u7A7A\u5E94\u7528\u5230\u5143\u7D20\u4E0A\u7684\u6240\u6709\u6837\u5F0F\r\n this.clearElementStyles()\r\n\r\n // \u91CD\u7F6E\u6240\u6709\u52A8\u753B\u72B6\u6001\r\n this.currentProgress = 0\r\n this.currentLoop = 0\r\n this.currentAttributeIndex = 0\r\n this.isAlternateReversed = false\r\n this.isReversed = false\r\n this.isPaused = false\r\n this.isStopping = true\r\n this.startTimestamp = 0\r\n\r\n // \u6E05\u7A7A\u52A8\u753B\u5C5E\u6027\u5217\u8868\r\n this.animationAttributes = []\r\n\r\n // \u91CD\u7F6E\u7F13\u52A8\u51FD\u6570\r\n this.currentEasingFunction = null\r\n\r\n // \u91CD\u7F6E\u56DE\u8C03\u51FD\u6570\r\n this.onComplete = () => { }\r\n this.onStart = () => { }\r\n this.onFrame = () => { }\r\n\r\n // \u6E05\u7406\u5E73\u53F0\u76F8\u5173\u7684\u52A8\u753B\u63A7\u5236\u5668\r\n // #ifdef WEB || APP-IOS || APP-HARMONY\r\n if (this.animationFrameId != null) {\r\n cancelAnimationFrame(this.animationFrameId)\r\n this.animationFrameId = null\r\n }\r\n // #endif\r\n\r\n // #ifdef APP-ANDROID\r\n if (this.choreographer != null && this.frameCallback != null) {\r\n this.choreographer.removeFrameCallback(this.frameCallback)\r\n this.frameCallback = null\r\n }\r\n this.choreographer = null\r\n // #endif\r\n\r\n // #ifdef MP\r\n if (this.displayLinkTimer != 0) {\r\n clearTimeout(this.displayLinkTimer)\r\n this.displayLinkTimer = 0\r\n }\r\n // #endif\r\n\r\n return this\r\n }\r\n\r\n /**\r\n * \u83B7\u53D6\u5F53\u524D\u52A8\u753B\u8FDB\u5EA6\r\n */\r\n getProgress(): number {\r\n return this.currentProgress\r\n }\r\n\r\n /**\r\n * \u83B7\u53D6\u52A8\u753B\u662F\u5426\u6B63\u5728\u8FD0\u884C\r\n */\r\n isAnimating(): boolean {\r\n return this.isRunning\r\n }\r\n\r\n /**\r\n * \u83B7\u53D6\u5F53\u524D\u5FAA\u73AF\u6B21\u6570\r\n */\r\n getCurrentLoop(): number {\r\n return this.currentLoop\r\n }\r\n\r\n /**\r\n * \u6E05\u9664\u6240\u6709\u52A8\u753B\u5C5E\u6027\r\n */\r\n clearAttributes(): AnimationEngine {\r\n this.animationAttributes = []\r\n return this\r\n }\r\n\r\n /**\r\n * \u83B7\u53D6\u52A8\u753B\u5C5E\u6027\u6570\u91CF\r\n */\r\n getAttributeCount(): number {\r\n return this.animationAttributes.length\r\n }\r\n\r\n /**\r\n * \u6DE1\u5165\u52A8\u753B\r\n * @param duration \u6301\u7EED\u65F6\u95F4\r\n */\r\n fadeIn(duration: number = 300): AnimationEngine {\r\n return this.setDuration(duration).opacity('0', '1')\r\n }\r\n\r\n /**\r\n * \u6DE1\u51FA\u52A8\u753B\r\n * @param duration \u6301\u7EED\u65F6\u95F4\r\n */\r\n fadeOut(duration: number = 300): AnimationEngine {\r\n return this.setDuration(duration).opacity('1', '0')\r\n }\r\n\r\n /**\r\n * \u6ED1\u5165\u52A8\u753B(\u4ECE\u5DE6)\r\n * @param duration \u6301\u7EED\u65F6\u95F4\r\n */\r\n slideInLeft(duration: number = 300): AnimationEngine {\r\n return this.setDuration(duration).translateX('-100%', '0%').opacity('0', '1')\r\n }\r\n\r\n /**\r\n * \u6ED1\u5165\u52A8\u753B(\u4ECE\u53F3)\r\n * @param duration \u6301\u7EED\u65F6\u95F4\r\n */\r\n slideInRight(duration: number = 300): AnimationEngine {\r\n return this.setDuration(duration).translateX('100%', '0%').opacity('0', '1')\r\n }\r\n\r\n /**\r\n * \u6ED1\u5165\u52A8\u753B(\u4ECE\u4E0A)\r\n * @param duration \u6301\u7EED\u65F6\u95F4\r\n */\r\n slideInUp(duration: number = 300): AnimationEngine {\r\n return this.setDuration(duration)\r\n .addAttribute('translateY', '-100%', '0%')\r\n .opacity('0', '1')\r\n }\r\n\r\n /**\r\n * \u6ED1\u5165\u52A8\u753B(\u4ECE\u4E0B)\r\n * @param duration \u6301\u7EED\u65F6\u95F4\r\n */\r\n slideInDown(duration: number = 300): AnimationEngine {\r\n return this.setDuration(duration)\r\n .addAttribute('translateY', '100%', '0%')\r\n .opacity('0', '1')\r\n }\r\n\r\n /**\r\n * \u7F29\u653E\u52A8\u753B(\u653E\u5927)\r\n * @param duration \u6301\u7EED\u65F6\u95F4\r\n */\r\n zoomIn(duration: number = 300): AnimationEngine {\r\n return this.setDuration(duration).scale('0', '1').opacity('0', '1')\r\n }\r\n\r\n /**\r\n * \u7F29\u653E\u52A8\u753B(\u7F29\u5C0F)\r\n * @param duration \u6301\u7EED\u65F6\u95F4\r\n */\r\n zoomOut(duration: number = 300): AnimationEngine {\r\n return this.setDuration(duration).scale('1', '0').opacity('1', '0')\r\n }\r\n\r\n /**\r\n * \u65CB\u8F6C\u52A8\u753B\r\n * @param duration \u6301\u7EED\u65F6\u95F4\r\n * @param degrees \u65CB\u8F6C\u89D2\u5EA6\r\n */\r\n rotateIn(duration: number = 500, degrees: number = 360): AnimationEngine {\r\n return this.setDuration(duration).rotate('0deg', `${degrees}deg`).opacity('0', '1')\r\n }\r\n\r\n /**\r\n * \u65CB\u8F6C\u9000\u51FA\u52A8\u753B\r\n * @param duration \u6301\u7EED\u65F6\u95F4\r\n * @param degrees \u65CB\u8F6C\u89D2\u5EA6\r\n */\r\n rotateOut(duration: number = 500, degrees: number = 360): AnimationEngine {\r\n return this.setDuration(duration).rotate('0deg', `${degrees}deg`).opacity('1', '0')\r\n }\r\n\r\n /**\r\n * \u5F39\u8DF3\u52A8\u753B\r\n * @param duration \u6301\u7EED\u65F6\u95F4\r\n */\r\n bounce(duration: number = 600): AnimationEngine {\r\n return this.setDuration(duration)\r\n .addCustomEasing('bounce', [0.68, -0.55, 0.265, 1.55])\r\n .scale('1', '1.1')\r\n .setAlternate(true)\r\n .setLoopCount(2)\r\n }\r\n\r\n /**\r\n * \u6447\u6446\u52A8\u753B\r\n * @param duration \u6301\u7EED\u65F6\u95F4\r\n */\r\n shake(duration: number = 500): AnimationEngine {\r\n return this.setDuration(duration)\r\n .addAttribute('translateX', '0px', '10px')\r\n .setAlternate(true)\r\n .setLoopCount(6)\r\n }\r\n\r\n /**\r\n * \u94FE\u5F0F\u52A8\u753B\uFF1A\u652F\u6301\u591A\u4E2A\u52A8\u753B\u4F9D\u6B21\u6267\u884C\r\n * @param animations \u52A8\u753B\u914D\u7F6E\u51FD\u6570\u6570\u7EC4\r\n */\r\n sequence(animations: ((engine: AnimationEngine) => AnimationEngine)[]): AnimationEngine {\r\n const self = this\r\n\r\n if (animations.length == 0) {\r\n return this\r\n }\r\n\r\n // \u6267\u884C\u7B2C\u4E00\u4E2A\u52A8\u753B\r\n const firstEngine = animations[0](new AnimationEngine(this.targetElement, {}))\r\n\r\n // \u5982\u679C\u53EA\u6709\u4E00\u4E2A\u52A8\u753B\uFF0C\u76F4\u63A5\u8FD4\u56DE\r\n if (animations.length == 1) {\r\n return firstEngine\r\n }\r\n\r\n // \u9012\u5F52\u8BBE\u7F6E\u540E\u7EED\u52A8\u753B\r\n function setNextAnimation(\r\n currentEngine: AnimationEngine,\r\n remainingAnimations: ((engine: AnimationEngine) => AnimationEngine)[],\r\n ): void {\r\n if (remainingAnimations.length == 0) {\r\n return\r\n }\r\n\r\n const originalComplete = currentEngine.onComplete\r\n currentEngine.onComplete = () => {\r\n originalComplete()\r\n\r\n // \u6267\u884C\u4E0B\u4E00\u4E2A\u52A8\u753B\r\n const nextEngine = remainingAnimations[0](\r\n new AnimationEngine(self.targetElement, {}),\r\n )\r\n\r\n // \u5982\u679C\u8FD8\u6709\u66F4\u591A\u52A8\u753B\uFF0C\u7EE7\u7EED\u8BBE\u7F6E\u94FE\u5F0F\r\n if (remainingAnimations.length > 1) {\r\n setNextAnimation(nextEngine, remainingAnimations.slice(1))\r\n }\r\n\r\n nextEngine.play()\r\n }\r\n }\r\n\r\n // \u8BBE\u7F6E\u52A8\u753B\u94FE\r\n setNextAnimation(firstEngine, animations.slice(1))\r\n\r\n return firstEngine\r\n }\r\n\r\n /**\r\n * \u6ED1\u51FA\u52A8\u753B(\u5411\u5DE6)\r\n * @param duration \u6301\u7EED\u65F6\u95F4\r\n */\r\n slideOutLeft(duration: number = 300): AnimationEngine {\r\n return this.setDuration(duration).translateX('0%', '-100%').opacity('1', '0')\r\n }\r\n\r\n /**\r\n * \u6ED1\u51FA\u52A8\u753B(\u5411\u53F3)\r\n * @param duration \u6301\u7EED\u65F6\u95F4\r\n */\r\n slideOutRight(duration: number = 300): AnimationEngine {\r\n return this.setDuration(duration).translateX('0%', '100%').opacity('1', '0')\r\n }\r\n\r\n /**\r\n * \u6ED1\u51FA\u52A8\u753B(\u5411\u4E0A)\r\n * @param duration \u6301\u7EED\u65F6\u95F4\r\n */\r\n slideOutUp(duration: number = 300): AnimationEngine {\r\n return this.setDuration(duration)\r\n .addAttribute('translateY', '0%', '-100%')\r\n .opacity('1', '0')\r\n }\r\n\r\n /**\r\n * \u6ED1\u51FA\u52A8\u753B(\u5411\u4E0B)\r\n * @param duration \u6301\u7EED\u65F6\u95F4\r\n */\r\n slideOutDown(duration: number = 300): AnimationEngine {\r\n return this.setDuration(duration)\r\n .addAttribute('translateY', '0%', '100%')\r\n .opacity('1', '0')\r\n }\r\n\r\n /**\r\n * \u7FFB\u8F6C\u52A8\u753B(\u6C34\u5E73)\r\n * @param duration \u6301\u7EED\u65F6\u95F4\r\n */\r\n flipX(duration: number = 600): AnimationEngine {\r\n return this.setDuration(duration)\r\n .addAttribute('rotateX', '0deg', '180deg')\r\n .addCustomEasing('ease-in-out', [0.25, 0.1, 0.25, 1.0])\r\n }\r\n\r\n /**\r\n * \u7FFB\u8F6C\u52A8\u753B(\u5782\u76F4)\r\n * @param duration \u6301\u7EED\u65F6\u95F4\r\n */\r\n flipY(duration: number = 600): AnimationEngine {\r\n return this.setDuration(duration)\r\n .addAttribute('rotateY', '0deg', '180deg')\r\n .addCustomEasing('ease-in-out', [0.25, 0.1, 0.25, 1.0])\r\n }\r\n\r\n /**\r\n * \u5F39\u6027\u8FDB\u5165\u52A8\u753B\r\n * @param duration \u6301\u7EED\u65F6\u95F4\r\n */\r\n elasticIn(duration: number = 600): AnimationEngine {\r\n return this.setDuration(duration)\r\n .scale('0', '1')\r\n .opacity('0', '1')\r\n .addCustomEasing('elastic', [0.175, 0.885, 0.32, 1.275])\r\n }\r\n\r\n /**\r\n * \u5F39\u6027\u9000\u51FA\u52A8\u753B\r\n * @param duration \u6301\u7EED\u65F6\u95F4\r\n */\r\n elasticOut(duration: number = 600): AnimationEngine {\r\n return this.setDuration(duration)\r\n .scale('1', '0')\r\n .opacity('1', '0')\r\n .addCustomEasing('elastic', [0.68, -0.55, 0.265, 1.55])\r\n }\r\n\r\n /**\r\n * \u56DE\u5F39\u52A8\u753B\r\n * @param duration \u6301\u7EED\u65F6\u95F4\r\n */\r\n rubberBand(duration: number = 1000): AnimationEngine {\r\n return this.setDuration(duration)\r\n .addAttribute('scaleX', '1', '1.25')\r\n .addAttribute('scaleY', '1', '0.75')\r\n .setAlternate(true)\r\n .setLoopCount(2)\r\n .addCustomEasing('ease-in-out', [0.25, 0.1, 0.25, 1.0])\r\n }\r\n\r\n /**\r\n * \u6446\u52A8\u52A8\u753B\r\n * @param duration \u6301\u7EED\u65F6\u95F4\r\n */\r\n swing(duration: number = 1000): AnimationEngine {\r\n return this.setDuration(duration)\r\n .addAttribute('rotate', '0deg', '15deg')\r\n .setAlternate(true)\r\n .setLoopCount(4)\r\n .addCustomEasing('ease-in-out', [0.25, 0.1, 0.25, 1.0])\r\n }\r\n\r\n /**\r\n * \u6296\u52A8\u52A8\u753B\r\n * @param duration \u6301\u7EED\u65F6\u95F4\r\n */\r\n wobble(duration: number = 1000): AnimationEngine {\r\n return this.setDuration(duration)\r\n .addAttribute('translateX', '0px', '25px')\r\n .addAttribute('rotate', '0deg', '5deg')\r\n .setAlternate(true)\r\n .setLoopCount(4)\r\n }\r\n\r\n /**\r\n * \u6EDA\u52A8\u8FDB\u5165\u52A8\u753B\r\n * @param duration \u6301\u7EED\u65F6\u95F4\r\n */\r\n rollIn(duration: number = 600): AnimationEngine {\r\n return this.setDuration(duration)\r\n .translateX('-100%', '0%')\r\n .rotate('-120deg', '0deg')\r\n .opacity('0', '1')\r\n }\r\n\r\n /**\r\n * \u6EDA\u52A8\u9000\u51FA\u52A8\u753B\r\n * @param duration \u6301\u7EED\u65F6\u95F4\r\n */\r\n rollOut(duration: number = 600): AnimationEngine {\r\n return this.setDuration(duration)\r\n .translateX('0%', '100%')\r\n .rotate('0deg', '120deg')\r\n .opacity('1', '0')\r\n }\r\n\r\n /**\r\n * \u706F\u5149\u6548\u679C\u52A8\u753B\r\n * @param duration \u6301\u7EED\u65F6\u95F4\r\n */\r\n lightSpeed(duration: number = 500): AnimationEngine {\r\n return this.setDuration(duration)\r\n .translateX('-100%', '0%')\r\n .addAttribute('skewX', '-30deg', '0deg')\r\n .opacity('0', '1')\r\n .addCustomEasing('ease-out', [0.25, 0.46, 0.45, 0.94])\r\n }\r\n\r\n /**\r\n * \u6D6E\u52A8\u52A8\u753B\r\n * @param duration \u6301\u7EED\u65F6\u95F4\r\n */\r\n float(duration: number = 3000): AnimationEngine {\r\n return this.setDuration(duration)\r\n .translateY('0px', '-10px')\r\n .setAlternate(true)\r\n .setLoopCount(-1)\r\n .addCustomEasing('ease-in-out', [0.25, 0.1, 0.25, 1.0])\r\n }\r\n\r\n /**\r\n * \u547C\u5438\u52A8\u753B\r\n * @param duration \u6301\u7EED\u65F6\u95F4\r\n */\r\n breathe(duration: number = 2000): AnimationEngine {\r\n return this.setDuration(duration)\r\n .scale('1', '1.1')\r\n .setAlternate(true)\r\n .setLoopCount(-1)\r\n .addCustomEasing('ease-in-out', [0.25, 0.1, 0.25, 1.0])\r\n }\r\n\r\n /**\r\n * \u53D1\u5149\u52A8\u753B\r\n * @param duration \u6301\u7EED\u65F6\u95F4\r\n */\r\n glow(duration: number = 1500): AnimationEngine {\r\n return this.setDuration(duration)\r\n .addAttribute(\r\n 'boxShadow',\r\n '0 0 5px rgba(255,255,255,0.5)',\r\n '0 0 20px rgba(255,255,255,1)',\r\n )\r\n .setAlternate(true)\r\n .setLoopCount(-1)\r\n .addCustomEasing('ease-in-out', [0.25, 0.1, 0.25, 1.0])\r\n }\r\n\r\n /**\r\n * \u8FDB\u5EA6\u6761\u52A8\u753B\r\n * @param duration \u6301\u7EED\u65F6\u95F4\r\n * @param progress \u8FDB\u5EA6\u767E\u5206\u6BD4 (0-100)\r\n */\r\n progressBar(duration: number = 1000, progress: number = 100): AnimationEngine {\r\n return this.setDuration(duration)\r\n .addAttribute('width', '0%', `${progress}%`)\r\n .addCustomEasing('ease-out', [0.25, 0.46, 0.45, 0.94])\r\n }\r\n\r\n /**\r\n * \u6A21\u6001\u6846\u8FDB\u5165\u52A8\u753B\r\n * @param duration \u6301\u7EED\u65F6\u95F4\r\n */\r\n modalIn(duration: number = 300): AnimationEngine {\r\n return this.setDuration(duration)\r\n .scale('0.7', '1')\r\n .opacity('0', '1')\r\n .addCustomEasing('ease-out', [0.25, 0.46, 0.45, 0.94])\r\n }\r\n\r\n /**\r\n * \u6A21\u6001\u6846\u9000\u51FA\u52A8\u753B\r\n * @param duration \u6301\u7EED\u65F6\u95F4\r\n */\r\n modalOut(duration: number = 300): AnimationEngine {\r\n return this.setDuration(duration)\r\n .scale('1', '0.7')\r\n .opacity('1', '0')\r\n .addCustomEasing('ease-in', [0.42, 0.0, 1.0, 1.0])\r\n }\r\n\r\n /**\r\n * \u5361\u7247\u7FFB\u8F6C\u52A8\u753B\r\n * @param duration \u6301\u7EED\u65F6\u95F4\r\n */\r\n cardFlip(duration: number = 600): AnimationEngine {\r\n return this.setDuration(duration)\r\n .addAttribute('rotateY', '0deg', '180deg')\r\n .addCustomEasing('ease-in-out', [0.25, 0.1, 0.25, 1.0])\r\n }\r\n\r\n /**\r\n * \u6CE2\u7EB9\u6269\u6563\u52A8\u753B\r\n * @param duration \u6301\u7EED\u65F6\u95F4\r\n */\r\n ripple(duration: number = 600): AnimationEngine {\r\n return this.setDuration(duration)\r\n .scale('0', '4')\r\n .opacity('0.7', '0')\r\n .addCustomEasing('ease-out', [0.25, 0.46, 0.45, 0.94])\r\n }\r\n}\r\n\r\n/**\r\n * \u521B\u5EFA\u52A8\u753B\u5B9E\u4F8B\r\n * @param element \u76EE\u6807\u5143\u7D20\r\n * @param options \u52A8\u753B\u9009\u9879\r\n */\r\nexport function createAnimation(\r\n element: any | null,\r\n options: AnimationOptions = {},\r\n): AnimationEngine {\r\n return new AnimationEngine(element, options)\r\n}\r\n",
784
+ "lib/dayUts.ts": "export class DayUts {\r\n private _date: Date\r\n\r\n constructor(date?: Date | string | number | null) {\r\n if (date == null || date == '') {\r\n this._date = new Date()\r\n }\r\n else if (typeof date == 'string') {\r\n // \u4FEE\u590D iOS/Safari \u7684 'YYYY-MM-DD HH:mm:ss' \u683C\u5F0F\u517C\u5BB9\u6027\u95EE\u9898\r\n // iOS \u53EA\u652F\u6301 'YYYY/MM/DD' \u7B49\u4F7F\u7528\u659C\u6760\u7684\u683C\u5F0F\uFF0C\u6240\u4EE5\u5982\u679C\u5305\u542B '-' \u5219\u66FF\u6362\u4E3A '/' (\u4EC5\u65E5\u671F\u90E8\u5206\u7684 '-')\r\n const safeDateStr = date.includes('-') && date.includes(':')\r\n ? date.replace(/-/g, '/')\r\n : date\r\n this._date = new Date(safeDateStr)\r\n }\r\n else if (typeof date == 'number') {\r\n this._date = new Date(date)\r\n }\r\n else if (date instanceof Date) {\r\n this._date = new Date(date.getTime())\r\n }\r\n else {\r\n this._date = new Date()\r\n }\r\n }\r\n\r\n /**\r\n * \u683C\u5F0F\u5316\u65E5\u671F\r\n * @param template \u683C\u5F0F\u6A21\u677F\uFF0C\u652F\u6301 YYYY-MM-DD HH:mm:ss \u7B49\r\n */\r\n format(template: string): string {\r\n // \u4F7F\u7528\u4F20\u5165\u7684\u6A21\u677F\r\n const actualTemplate: string = template\r\n\r\n const year = this._date.getFullYear()\r\n const month = this._date.getMonth() + 1\r\n const date = this._date.getDate()\r\n const hours = this._date.getHours()\r\n const minutes = this._date.getMinutes()\r\n const seconds = this._date.getSeconds()\r\n const milliseconds = this._date.getMilliseconds()\r\n\r\n // \u4F7F\u7528\u6570\u7EC4\u6765\u4F9D\u6B21\u66FF\u6362\uFF0C\u907F\u514DUTS\u7684replace\u65B9\u6CD5\u517C\u5BB9\u6027\u95EE\u9898\r\n let result: string = actualTemplate\r\n\r\n // \u6309\u7167\u957F\u5EA6\u4ECE\u957F\u5230\u77ED\u66FF\u6362\uFF0C\u907F\u514D\u51B2\u7A81\r\n // \u66FF\u6362\u5E74\u4EFD (YYYY \u5FC5\u987B\u5728 YY \u4E4B\u524D)\r\n result = result.replace('YYYY', year.toString())\r\n result = result.replace('YY', year.toString().slice(-2))\r\n\r\n // \u66FF\u6362\u6708\u4EFD (MM \u5FC5\u987B\u5728 M \u4E4B\u524D)\r\n result = result.replace('MM', month.toString().padStart(2, '0'))\r\n result = result.replace('M', month.toString())\r\n\r\n // \u66FF\u6362\u65E5\u671F (DD \u5FC5\u987B\u5728 D \u4E4B\u524D)\r\n result = result.replace('DD', date.toString().padStart(2, '0'))\r\n result = result.replace('D', date.toString())\r\n\r\n // \u66FF\u6362\u5C0F\u65F6 (HH \u5FC5\u987B\u5728 H \u4E4B\u524D)\r\n result = result.replace('HH', hours.toString().padStart(2, '0'))\r\n result = result.replace('H', hours.toString())\r\n\r\n // \u66FF\u6362\u5206\u949F (mm \u5FC5\u987B\u5728 m \u4E4B\u524D)\r\n result = result.replace('mm', minutes.toString().padStart(2, '0'))\r\n result = result.replace('m', minutes.toString())\r\n\r\n // \u66FF\u6362\u79D2\u6570 (ss \u5FC5\u987B\u5728 s \u4E4B\u524D)\r\n result = result.replace('ss', seconds.toString().padStart(2, '0'))\r\n result = result.replace('s', seconds.toString())\r\n\r\n // \u66FF\u6362\u6BEB\u79D2\r\n result = result.replace('SSS', milliseconds.toString().padStart(3, '0'))\r\n\r\n return result\r\n }\r\n\r\n /**\r\n * \u672C\u6708\u591A\u5C11\u5929\r\n */\r\n getDays(): number {\r\n return new Date(this._date.getFullYear(), this._date.getMonth() + 1, 0).getDate()\r\n }\r\n\r\n /**\r\n * \u662F\u5426\u4E3A\u95F0\u5E74\r\n */\r\n isLeapYear(): boolean {\r\n return this._date.getFullYear() % 4 == 0 && this._date.getFullYear() % 100 != 0\r\n }\r\n\r\n /**\r\n * \u661F\u671F\u51E0\r\n */\r\n getDay(): number {\r\n return this._date.getDay()\r\n }\r\n\r\n /**\r\n * \u83B7\u53D6\u67D0\u4E2A\u5355\u4F4D\u7684\u5F00\u59CB\u65F6\u95F4\r\n */\r\n startOf(unit: 'month' | 'day' | 'year' | 'week'): DayUts {\r\n const newDate = new Date(this._date.getTime())\r\n\r\n switch (unit) {\r\n case 'year':\r\n newDate.setMonth(0)\r\n newDate.setDate(1)\r\n newDate.setHours(0)\r\n newDate.setMinutes(0)\r\n newDate.setSeconds(0)\r\n newDate.setMilliseconds(0)\r\n break\r\n case 'month':\r\n newDate.setDate(1)\r\n newDate.setHours(0)\r\n newDate.setMinutes(0)\r\n newDate.setSeconds(0)\r\n newDate.setMilliseconds(0)\r\n break\r\n case 'week':\r\n newDate.setDate(newDate.getDate() - newDate.getDay())\r\n newDate.setHours(0)\r\n newDate.setMinutes(0)\r\n newDate.setSeconds(0)\r\n newDate.setMilliseconds(0)\r\n break\r\n case 'day':\r\n newDate.setHours(0)\r\n newDate.setMinutes(0)\r\n newDate.setSeconds(0)\r\n newDate.setMilliseconds(0)\r\n break\r\n }\r\n\r\n return new DayUts(newDate)\r\n }\r\n\r\n /**\r\n * \u83B7\u53D6\u67D0\u4E2A\u5355\u4F4D\u7684\u7ED3\u675F\u65F6\u95F4\r\n */\r\n endOf(unit: 'month' | 'day' | 'year' | 'week'): DayUts {\r\n const newDate = new Date(this._date.getTime())\r\n\r\n switch (unit) {\r\n case 'year':\r\n newDate.setMonth(11)\r\n newDate.setDate(31)\r\n newDate.setHours(23)\r\n newDate.setMinutes(59)\r\n newDate.setSeconds(59)\r\n newDate.setMilliseconds(999)\r\n break\r\n case 'month':\r\n newDate.setMonth(newDate.getMonth() + 1)\r\n newDate.setDate(0)\r\n newDate.setHours(23)\r\n newDate.setMinutes(59)\r\n newDate.setSeconds(59)\r\n newDate.setMilliseconds(999)\r\n break\r\n case 'week':\r\n const day = newDate.getDay()\r\n const diff = 6 - day\r\n newDate.setDate(newDate.getDate() + diff)\r\n newDate.setHours(23)\r\n newDate.setMinutes(59)\r\n newDate.setSeconds(59)\r\n newDate.setMilliseconds(999)\r\n break\r\n case 'day':\r\n newDate.setHours(23)\r\n newDate.setMinutes(59)\r\n newDate.setSeconds(59)\r\n newDate.setMilliseconds(999)\r\n break\r\n }\r\n\r\n return new DayUts(newDate)\r\n }\r\n\r\n /**\r\n * \u5224\u65AD\u662F\u5426\u65E9\u4E8E\u53E6\u4E00\u4E2A\u65E5\u671F\r\n */\r\n isBefore(date: DayUts | Date | string | number): boolean {\r\n const compareDate = this._parseDate(date)\r\n return this._date.getTime() < compareDate.getTime()\r\n }\r\n\r\n /**\r\n * \u5224\u65AD\u662F\u5426\u665A\u4E8E\u53E6\u4E00\u4E2A\u65E5\u671F\r\n */\r\n isAfter(date: DayUts | Date | string | number): boolean {\r\n const compareDate = this._parseDate(date)\r\n return this._date.getTime() > compareDate.getTime()\r\n }\r\n\r\n /**\r\n * \u5224\u65AD\u662F\u5426\u4E0E\u53E6\u4E00\u4E2A\u65E5\u671F\u76F8\u540C\r\n */\r\n isSame(date: DayUts | Date | string | number): boolean {\r\n const compareDate = this._parseDate(date)\r\n return this._date.getTime() == compareDate.getTime()\r\n }\r\n\r\n /**\r\n * \u8BA1\u7B97\u4E0E\u53E6\u4E00\u4E2A\u65E5\u671F\u7684\u5DEE\u503C\uFF08\u6BEB\u79D2\uFF09\r\n */\r\n diff(date: DayUts | Date | string | number): number {\r\n const compareDate = this._parseDate(date)\r\n return this._date.getTime() - compareDate.getTime()\r\n }\r\n\r\n /**\r\n * \u8BA1\u7B97\u4E0E\u53E6\u4E00\u4E2A\u65E5\u671F\u7684\u5DEE\u503C\uFF08\u6307\u5B9A\u5355\u4F4D\uFF09\r\n */\r\n diffUnit(\r\n date: DayUts | Date | string | number,\r\n unit: 'day' | 'hour' | 'minute' | 'second' | 'millisecond',\r\n ): number {\r\n const compareDate = this._parseDate(date)\r\n const diffMs = this._date.getTime() - compareDate.getTime()\r\n\r\n switch (unit) {\r\n case 'day':\r\n return Math.floor(diffMs / (1000 * 60 * 60 * 24))\r\n case 'hour':\r\n return Math.floor(diffMs / (1000 * 60 * 60))\r\n case 'minute':\r\n return Math.floor(diffMs / (1000 * 60))\r\n case 'second':\r\n return Math.floor(diffMs / 1000)\r\n case 'millisecond':\r\n default:\r\n return diffMs\r\n }\r\n }\r\n\r\n /**\r\n * \u6DFB\u52A0\u65F6\u95F4\r\n */\r\n add(value: number, unit: 'day' | 'month' | 'year' | 'hour' | 'minute' | 'second'): DayUts {\r\n const newDate = new Date(this._date.getTime())\r\n\r\n switch (unit) {\r\n case 'year':\r\n newDate.setFullYear(newDate.getFullYear() + value)\r\n break\r\n case 'month':\r\n newDate.setMonth(newDate.getMonth() + value)\r\n break\r\n case 'day':\r\n newDate.setDate(newDate.getDate() + value)\r\n break\r\n case 'hour':\r\n newDate.setHours(newDate.getHours() + value)\r\n break\r\n case 'minute':\r\n newDate.setMinutes(newDate.getMinutes() + value)\r\n break\r\n case 'second':\r\n newDate.setSeconds(newDate.getSeconds() + value)\r\n break\r\n }\r\n\r\n return new DayUts(newDate)\r\n }\r\n\r\n /**\r\n * \u51CF\u5C11\u65F6\u95F4\r\n */\r\n subtract(value: number, unit: 'day' | 'month' | 'year' | 'hour' | 'minute' | 'second'): DayUts {\r\n return this.add(-value, unit)\r\n }\r\n\r\n /**\r\n * \u83B7\u53D6\u65F6\u95F4\u6233\r\n */\r\n valueOf(): number {\r\n return this._date.getTime()\r\n }\r\n\r\n /**\r\n * \u83B7\u53D6\u539F\u751FDate\u5BF9\u8C61\r\n */\r\n toDate(): Date {\r\n return new Date(this._date.getTime())\r\n }\r\n\r\n /**\r\n * \u83B7\u53D6\u65E5\u671F\u6570\u7EC4\r\n */\r\n toArray(): number[] {\r\n return [\r\n this._date.getFullYear(),\r\n this._date.getMonth() + 1,\r\n this._date.getDate(),\r\n this._date.getHours(),\r\n this._date.getMinutes(),\r\n this._date.getSeconds(),\r\n ]\r\n }\r\n\r\n /**\r\n * \u79C1\u6709\u65B9\u6CD5\uFF1A\u89E3\u6790\u4E0D\u540C\u7C7B\u578B\u7684\u65E5\u671F\u53C2\u6570\r\n */\r\n private _parseDate(date: DayUts | Date | string | number): Date {\r\n if (date instanceof DayUts) {\r\n return date.toDate()\r\n }\r\n else if (date instanceof Date) {\r\n return date\r\n }\r\n else if (typeof date == 'string') {\r\n const safeDateStr = date.includes('-') && date.includes(':')\r\n ? date.replace(/-/g, '/')\r\n : date\r\n return new Date(safeDateStr)\r\n }\r\n else if (typeof date == 'number') {\r\n return new Date(date)\r\n }\r\n else {\r\n // \u5982\u679C\u90FD\u4E0D\u5339\u914D\uFF0C\u8FD4\u56DE\u5F53\u524D\u65F6\u95F4\r\n return new Date()\r\n }\r\n }\r\n}\r\n\r\n/**\r\n * \u521B\u5EFA DayUts \u5B9E\u4F8B\r\n */\r\nexport function dayUts(date: Date | string | number | null = new Date()): DayUts {\r\n return new DayUts(date)\r\n}\r\n",
785
+ "lib/device.ts": "/**\r\n * \u68C0\u67E5\u662F\u5426\u4E3A\u5C0F\u7A0B\u5E8F\u73AF\u5883\r\n * @returns \u662F\u5426\u4E3A\u5C0F\u7A0B\u5E8F\u73AF\u5883\r\n */\r\nexport function isMp(): boolean {\r\n // #ifdef MP\r\n return true\r\n // #endif\r\n\r\n return false\r\n}\r\n\r\n/**\r\n * \u68C0\u67E5\u662F\u5426\u4E3AApp\u73AF\u5883\r\n * @returns \u662F\u5426\u4E3AApp\u73AF\u5883\r\n */\r\nexport function isApp(): boolean {\r\n // #ifdef APP\r\n return true\r\n // #endif\r\n\r\n return false\r\n}\r\n\r\n/**\r\n * \u68C0\u67E5\u662F\u5426\u4E3AApp-IOS\u73AF\u5883\r\n * @returns \u662F\u5426\u4E3AApp-IOS\u73AF\u5883\r\n */\r\nexport function isAppIOS(): boolean {\r\n // #ifdef APP-IOS\r\n return true\r\n // #endif\r\n return false\r\n}\r\n\r\n/**\r\n * \u68C0\u67E5\u662F\u5426\u4E3AApp-Android\u73AF\u5883\r\n * @returns \u662F\u5426\u4E3AApp-Android\u73AF\u5883\r\n */\r\nexport function isAppAndroid(): boolean {\r\n // #ifdef APP-ANDROID\r\n return true\r\n // #endif\r\n return false\r\n}\r\n\r\n/**\r\n * \u68C0\u67E5\u662F\u5426\u4E3AH5\u73AF\u5883\r\n * @returns \u662F\u5426\u4E3AH5\u73AF\u5883\r\n */\r\nexport function isH5(): boolean {\r\n // #ifdef H5\r\n return true\r\n // #endif\r\n\r\n return false\r\n}\r\n\r\n/**\r\n * \u68C0\u67E5\u662F\u5426\u4E3A\u9E3F\u8499\u73AF\u5883\r\n * @returns \u662F\u5426\u4E3A\u9E3F\u8499\u73AF\u5883\r\n */\r\nexport function isHarmony(): boolean {\r\n // #ifdef APP-HARMONY\r\n return true\r\n // #endif\r\n\r\n return false\r\n}\r\n\r\nexport function initTheme() {\r\n let value: string | null\r\n\r\n // #ifdef APP\r\n const appInfo = uni.getAppBaseInfo()\r\n // @ts-ignore\r\n const appTheme = appInfo.appTheme as string\r\n const osTheme = uni.getSystemInfoSync().osTheme!\r\n\r\n // \u5982\u679C appTheme \u4E3A auto\uFF0C\u5219\u8DDF\u968F\u7CFB\u7EDF\u4E3B\u9898\uFF0C\u5426\u5219\u4F7F\u7528 appTheme\r\n value = appTheme == 'auto' ? osTheme : appTheme\r\n // #endif\r\n\r\n // #ifdef H5 || MP\r\n const hostTheme = uni.getAppBaseInfo().hostTheme\r\n if (hostTheme) {\r\n // \u5982\u679C\u6709 hostTheme\uFF0C\u5219\u4F7F\u7528 hostTheme\r\n value = hostTheme\r\n }\r\n else {\r\n // \u9ED8\u8BA4\u4F7F\u7528 light \u4E3B\u9898\r\n value = 'light'\r\n }\r\n // #endif\r\n return value\r\n}\r\n\r\n/**\r\n * \u83B7\u53D6\u5B89\u5168\u533A\u57DF\u9AD8\u5EA6\r\n * @param type \u7C7B\u578B\r\n * @returns \u5B89\u5168\u533A\u57DF\u9AD8\u5EA6\r\n */\r\nexport function getSafeAreaHeight(type: 'top' | 'bottom') {\r\n const { safeAreaInsets } = uni.getWindowInfo()\r\n\r\n let h: number\r\n\r\n if (type == 'top') {\r\n h = safeAreaInsets.top\r\n }\r\n else {\r\n h = safeAreaInsets.bottom\r\n\r\n // #ifdef APP-ANDROID\r\n if (h == 0) {\r\n h = 16\r\n }\r\n // #endif\r\n }\r\n\r\n return h\r\n}",
786
+ "lib/file.ts": 'export function uuid(): string {\r\n let uuid = "";\r\n let i: number;\r\n let random: number;\r\n\r\n for (i = 0; i < 36; i++) {\r\n if (i == 8 || i == 13 || i == 18 || i == 23) {\r\n uuid += "-";\r\n } else if (i == 14) {\r\n uuid += "4";\r\n } else if (i == 19) {\r\n random = (Math.random() * 16) | 0;\r\n uuid += ((random & 0x3) | 0x8).toString(16);\r\n } else {\r\n random = (Math.random() * 16) | 0;\r\n uuid += random.toString(16);\r\n }\r\n }\r\n return uuid;\r\n}\r\n\r\n\r\nexport function base64ToBlob(data: string, type: string = "image/jpeg"): Blob {\r\n // #ifdef H5\r\n let bytes = window.atob(data.split(",")[1]);\r\n let ab = new ArrayBuffer(bytes.length);\r\n let ia = new Uint8Array(ab);\r\n for (let i = 0; i < bytes.length; i++) {\r\n ia[i] = bytes.charCodeAt(i);\r\n }\r\n return new Blob([ab], { type });\r\n // #endif\r\n}\r\n\r\n/**\r\n * \u5C06canvas\u8F6C\u6362\u4E3Apng\u56FE\u7247\r\n * @param options \u8F6C\u6362\u53C2\u6570\r\n * @returns \u56FE\u7247\u8DEF\u5F84\r\n */\r\nexport function canvasToPng(canvasRef: any): Promise<string> {\r\n return new Promise((resolve) => {\r\n // #ifdef APP\r\n canvasRef.parentElement!.takeSnapshot({\r\n success(res: any) {\r\n resolve(res.tempFilePath);\r\n },\r\n fail(err: any) {\r\n console.error(err);\r\n resolve("");\r\n }\r\n });\r\n // #endif\r\n\r\n // #ifdef H5\r\n const url = URL.createObjectURL(\r\n base64ToBlob(\r\n (canvasRef as unknown as HTMLCanvasElement)?.toDataURL("image/png", 1) ?? ""\r\n )\r\n );\r\n\r\n resolve(url);\r\n // #endif\r\n\r\n // #ifdef MP\r\n uni.createCanvasContextAsync({\r\n id: canvasRef.id,\r\n component: canvasRef.$vm,\r\n success(context: any) {\r\n // \u83B7\u53D62D\u7ED8\u56FE\u4E0A\u4E0B\u6587\r\n const ctx = context.getContext("2d")!;\r\n\r\n // \u83B7\u53D6canvas\u5BF9\u8C61\r\n const canvas = ctx.canvas;\r\n\r\n // \u5C06canvas\u8F6C\u6362\u4E3Abase64\u683C\u5F0F\u7684PNG\u56FE\u7247\u6570\u636E\r\n const data = canvas.toDataURL("image/png", 1);\r\n\r\n // \u83B7\u53D6\u6587\u4EF6\u7CFB\u7EDF\u7BA1\u7406\u5668\r\n const fileMg = uni.getFileSystemManager();\r\n\r\n // \u751F\u6210\u4E34\u65F6\u6587\u4EF6\u8DEF\u5F84\r\n // @ts-ignore\r\n const filepath = `${wx.env.USER_DATA_PATH}/${uuid()}.png`;\r\n\r\n // \u5C06base64\u6570\u636E\u5199\u5165\u6587\u4EF6\r\n fileMg.writeFile({\r\n filePath: filepath,\r\n data: data.split(",")[1],\r\n encoding: "base64",\r\n success() {\r\n resolve(filepath);\r\n },\r\n fail(err) {\r\n console.error(err);\r\n resolve("");\r\n }\r\n });\r\n },\r\n fail(err: any) {\r\n console.error(err);\r\n resolve("");\r\n }\r\n });\r\n // #endif\r\n });\r\n}\r\n',
787
+ "lib/tv.ts": "import type { TV } from 'tailwind-variants'\r\n\r\n// #ifdef MP-WEIXIN\r\n// @ts-ignore\r\nimport { create } from '@weapp-tailwindcss/variants-v3'\r\n// #endif\r\n\r\n// #ifndef MP-WEIXIN\r\nimport { tv as originTv } from 'tailwind-variants'\r\n// #endif\r\n\r\nconst textSizeKeys = ['20', '22', '24', '26', '28', '30', '32', '36', '40', '48', '52']\r\n\r\nconst twMergeConfig = {\r\n extend: {\r\n classGroups: {\r\n 'font-size': [{ text: textSizeKeys }],\r\n },\r\n },\r\n}\r\n\r\n// #ifdef MP-WEIXIN\r\n\r\n// @ts-ignore\r\nconst isH5 = process.env.UNI_PLATFORM === 'h5'\r\n\r\nconst { tv: createTV } = create({\r\n // @ts-ignore\r\n escape: !isH5,\r\n // @ts-ignore\r\n unescape: !isH5,\r\n})\r\n\r\n// @ts-ignore\r\nexport const tv: TV = (options, config) =>\r\n // @ts-ignore\r\n createTV(options, {\r\n ...config,\r\n twMerge: config?.twMerge ?? true,\r\n twMergeConfig: {\r\n ...config?.twMergeConfig,\r\n ...twMergeConfig,\r\n },\r\n })\r\n\r\n// #endif\r\n\r\n// #ifndef MP-WEIXIN\r\n// @ts-ignore\r\nexport const tv: TV = (options, config) =>\r\n originTv(options, {\r\n ...config,\r\n twMerge: config?.twMerge ?? true,\r\n twMergeConfig: {\r\n ...config?.twMergeConfig,\r\n ...twMergeConfig,\r\n },\r\n })\r\n\r\n// #endif\r\n",
788
+ "lib/utils.ts": `\r
789
+ import { type ClassValue, clsx } from 'clsx'\r
617
790
  \r
791
+ // #ifndef MP-WEIXIN\r
792
+ import { extendTailwindMerge } from 'tailwind-merge'\r
618
793
  // @ts-ignore\r
619
- const isH5 = process.env.UNI_PLATFORM === 'h5'\r
620
- \r
621
- const { tv: createTV } = create({\r
622
- // @ts-ignore\r
623
- escape: !isH5,\r
624
- // @ts-ignore\r
625
- unescape: !isH5,\r
794
+ const twMerge = extendTailwindMerge({\r
795
+ extend: {\r
796
+ classGroups: {\r
797
+ 'font-size': [{ text: ['20', '22', '24', '26', '28', '30', '32', '36', '40', '48', '52'] }],\r
798
+ },\r
799
+ },\r
626
800
  })\r
627
801
  \r
628
- const textSizeKeys = ['20', '22', '24', '26', '28', '30', '32', '36', '40', '48', '52']\r
802
+ // #endif\r
629
803
  \r
630
- const twMergeConfig = {\r
804
+ // #ifdef MP-WEIXIN\r
805
+ import { create } from '@weapp-tailwindcss/merge-v3'\r
806
+ \r
807
+ // @ts-ignore\r
808
+ const isH5 = process.env.UNI_PLATFORM === 'h5'\r
809
+ // @ts-ignore\r
810
+ const { twMerge } = create({\r
811
+ escape: !isH5,\r
812
+ unescape: !isH5,\r
813
+ // @ts-ignore\r
631
814
  extend: {\r
632
815
  classGroups: {\r
633
- 'font-size': [{ text: textSizeKeys }],\r
816
+ 'font-size': [{ text: ['20', '22', '24', '26', '28', '30', '32', '36', '40', '48', '52'] }],\r
634
817
  },\r
635
818
  },\r
636
- } as const\r
637
- \r
638
- export const tv: TV = (options, config) =>\r
639
- createTV(options, {\r
640
- ...config,\r
641
- twMerge: config?.twMerge ?? true,\r
642
- twMergeConfig: {\r
643
- ...config?.twMergeConfig,\r
644
- ...twMergeConfig,\r
645
- },\r
646
- });\r
647
- `,
648
- "lib/utils.ts": "import { type ClassValue, clsx } from 'clsx'\r\nimport { create } from '@weapp-tailwindcss/merge-v3'\r\n\r\n// @ts-ignore\r\nconst isH5 = process.env.UNI_PLATFORM === 'h5'\r\nconst { twMerge } = create({\r\n escape: !isH5,\r\n unescape: !isH5,\r\n // @ts-ignore\r\n extend: {\r\n classGroups: {\r\n 'font-size': [{ text: ['20', '22', '24', '26', '28', '30', '32', '36', '40', '48', '52'] }],\r\n },\r\n },\r\n})\r\n\r\nexport function cn(...inputs: ClassValue[]) {\r\n return twMerge(clsx(inputs))\r\n}\r\n",
649
- "composables/useDarkMode.ts": "export const useDarkMode = () => {\r\n const isDark = ref(false)\r\n\r\n const toggleDarkMode = () => {\r\n isDark.value = !isDark.value\r\n updateTheme()\r\n }\r\n\r\n const updateTheme = () => {\r\n const value = isDark.value\r\n uni.setStorageSync('darkMode', value)\r\n\r\n // #ifdef H5\r\n if (value) {\r\n document.documentElement.classList.add('dark')\r\n } else {\r\n document.documentElement.classList.remove('dark')\r\n }\r\n // #endif\r\n\r\n }\r\n\r\n const initTheme = () => {\r\n const saved = uni.getStorageSync('darkMode')\r\n if (saved !== '') {\r\n isDark.value = !!saved\r\n } else {\r\n isDark.value = false\r\n }\r\n updateTheme()\r\n }\r\n\r\n initTheme()\r\n\r\n return {\r\n isDark,\r\n toggleDarkMode\r\n }\r\n}\r\n",
650
- "composables/useFieldGroup.ts": `import { ref, provide, inject, computed, watch, onMounted, onUnmounted, getCurrentInstance } from 'vue';\r
651
- import * as z from 'zod';\r
652
- \r
653
- export type FormValidateError = {\r
654
- field: string;\r
655
- message: string;\r
656
- };\r
819
+ })\r
820
+ // #endif\r
657
821
  \r
658
- export interface UseFieldGroupProps {\r
659
- modelValue?: any;\r
822
+ export function cn(...inputs: ClassValue[]) {\r
823
+ return twMerge(clsx(inputs))\r
660
824
  }\r
661
825
  \r
662
- export function useFieldGroup() {\r
663
- \r
664
- // Data & Context\r
665
- const errors = ref(new Map<string, string>());\r
666
- const fields = ref(new Set<string>([]));\r
667
- const fieldInstances = ref<any[]>([]);\r
668
- \r
669
- // --- Field Management ---\r
670
- const addField = (field: any) => {\r
671
- fieldInstances.value.push(field);\r
672
- if (field.prop) {\r
673
- fields.value.add(field.prop);\r
674
- }\r
675
- };\r
676
- \r
677
- const removeField = (field: any) => {\r
678
- const index = fieldInstances.value.indexOf(field);\r
679
- if (index > -1) {\r
680
- fieldInstances.value.splice(index, 1);\r
681
- }\r
682
- if (field.prop) {\r
683
- fields.value.delete(field.prop);\r
684
- }\r
685
- };\r
686
- \r
687
- // --- Error Management ---\r
688
- const errorLock = ref(false);\r
689
- \r
690
- function setError(prop: string, error: string) {\r
691
- if (errorLock.value) return;\r
692
- if (prop !== "") errors.value.set(prop, error);\r
693
- }\r
694
- \r
695
- function removeError(prop: string) {\r
696
- if (prop !== "") errors.value.delete(prop);\r
697
- }\r
698
- \r
699
- function getError(prop: string): string {\r
700
- if (prop !== "") return errors.value.get(prop) ?? "";\r
701
- return "";\r
826
+ export function isArray(value: any): boolean {\r
827
+ if (typeof Array.isArray === "function") {\r
828
+ return Array.isArray(value);\r
829
+ } else {\r
830
+ return Object.prototype.toString.call(value) === "[object Array]";\r
702
831
  }\r
832
+ }\r
703
833
  \r
704
- async function getErrors(): Promise<FormValidateError[]> {\r
705
- const errs = [] as FormValidateError[];\r
706
- errors.value.forEach((msg, field) => {\r
707
- errs.push({ field, message: msg });\r
708
- });\r
709
- return errs;\r
834
+ export function isEmpty(value: any): boolean {\r
835
+ if (isArray(value)) {\r
836
+ return value.length === 0;\r
710
837
  }\r
711
838
  \r
712
- function clearValidate(fieldsToClear?: string | string[]) {\r
713
- if (fieldsToClear) {\r
714
- const propsArray = Array.isArray(fieldsToClear) ? fieldsToClear : [fieldsToClear];\r
715
- propsArray.forEach(prop => removeError(prop));\r
716
- } else {\r
717
- errors.value.clear();\r
718
- }\r
839
+ if (value instanceof Object) {\r
840
+ return Object.keys(value).length === 0;\r
719
841
  }\r
720
842
  \r
721
- return {\r
722
- errors,\r
723
- fields,\r
724
- fieldInstances,\r
725
- addField,\r
726
- removeField,\r
727
- setError,\r
728
- removeError,\r
729
- getError, // Used by children\r
730
- getErrors, // Used by validate\r
731
- clearValidate\r
732
- };\r
733
- }\r
734
- \r
735
- \r
736
- export interface UseFieldGroupItemProps {\r
737
- prop?: string;\r
738
- label?: string;\r
739
- labelPosition?: string;\r
740
- labelWidth?: string | number;\r
741
- ui?: any;\r
742
- }\r
743
- \r
744
- export function useFieldGroupItem(props: UseFieldGroupItemProps) {\r
745
- const form = inject<any>('rebornForm', undefined);\r
746
- const instance = getCurrentInstance();\r
747
- \r
748
- \r
749
- const error = computed(() => {\r
750
- if (!form || !props.prop) return '';\r
751
- return form.getError ? form.getError(props.prop) : '';\r
752
- });\r
753
- \r
754
- const labelPosition = computed(() => {\r
755
- return props.labelPosition || form?.props?.labelPosition || 'left';\r
756
- });\r
757
- \r
758
- const labelWidth = computed(() => {\r
759
- if (labelPosition.value === 'top') return 'auto';\r
760
- return props.labelWidth || form?.props?.labelWidth || 'auto';\r
761
- });\r
762
- \r
763
- const size = computed(() => {\r
764
- const s = form?.props?.size;\r
765
- if (s && ['sm', 'md', 'lg'].includes(s)) {\r
766
- return s as "sm" | "md" | "lg";\r
767
- }\r
768
- return 'sm';\r
769
- });\r
770
- \r
771
- const getBoundingClientRect = (callback: (res: any) => void) => {\r
772
- uni.createSelectorQuery()\r
773
- .in(instance?.proxy)\r
774
- .select('.re-form-item')\r
775
- .boundingClientRect(callback)\r
776
- .exec();\r
777
- };\r
778
- \r
779
- // Watch for prop changes to update registration\r
780
- watch(() => props.prop, (newProp, oldProp) => {\r
781
- if (form) {\r
782
- if (oldProp) {\r
783
- form.removeField({ uid: instance?.uid, prop: oldProp });\r
784
- }\r
785
- if (newProp) {\r
786
- form.addField({ uid: instance?.uid, prop: newProp, getBoundingClientRect });\r
787
- }\r
788
- }\r
789
- });\r
790
- \r
791
- onMounted(() => {\r
792
- if (form && props.prop) {\r
793
- form.addField({ uid: instance?.uid, prop: props.prop, getBoundingClientRect });\r
794
- }\r
795
- });\r
796
- \r
797
- onUnmounted(() => {\r
798
- if (form && props.prop) {\r
799
- form.removeField({ uid: instance?.uid, prop: props.prop });\r
800
- }\r
801
- });\r
802
- \r
803
- return {\r
804
- form,\r
805
- error,\r
806
- labelPosition,\r
807
- labelWidth,\r
808
- size,\r
809
- getBoundingClientRect\r
810
- };\r
843
+ return value === "" || value === undefined || value === null;\r
811
844
  }\r
812
845
  \r
813
- // Consumer for inner components (Input, Select, etc.)\r
814
- export function useFormInject(props: any) {\r
815
- const form = inject<any>('rebornForm', null);\r
816
- const formItem = inject<any>('rebornFormItem', null);\r
817
- \r
818
- const size = computed(() => {\r
819
- return form?.props?.size || props.size;\r
820
- });\r
821
- \r
822
- const disabled = computed(() => {\r
823
- return form?.props?.disabled || props.disabled;\r
824
- });\r
825
- \r
826
- const orientation = computed(() => {\r
827
- return form?.props?.orientation || props.orientation;\r
828
- });\r
829
- \r
830
- const isError = computed(() => {\r
831
- return formItem?.isError?.value || false;\r
832
- });\r
833
- \r
834
- return {\r
835
- form,\r
836
- size,\r
837
- disabled,\r
838
- orientation,\r
839
- isError\r
840
- };\r
846
+ export function last<T>(array: T[]): T | null {\r
847
+ return isArray(array) && array.length > 0 ? array[array.length - 1] : null;\r
841
848
  }\r
842
- `
849
+ `,
850
+ "composables/useDarkMode.ts": "export function useDarkMode() {\r\n const isDark = ref(false)\r\n\r\n const toggleDarkMode = () => {\r\n isDark.value = !isDark.value\r\n updateTheme()\r\n }\r\n\r\n const updateTheme = () => {\r\n const value = isDark.value\r\n uni.setStorageSync('darkMode', value)\r\n\r\n // #ifdef H5\r\n if (value) {\r\n document.documentElement.classList.add('dark')\r\n }\n else {\r\n document.documentElement.classList.remove('dark')\r\n }\r\n // #endif\n }\r\n\r\n const initTheme = () => {\r\n const saved = uni.getStorageSync('darkMode')\r\n if (saved !== '') {\r\n isDark.value = !!saved\r\n }\n else {\r\n isDark.value = false\r\n }\r\n updateTheme()\r\n }\r\n\r\n initTheme()\r\n\r\n return {\r\n isDark,\r\n toggleDarkMode,\r\n }\r\n}\r\n",
851
+ "composables/useFieldGroup.ts": "import { computed, getCurrentInstance, inject, onMounted, onUnmounted, ref, watch } from 'vue'\r\n\r\nexport interface FormValidateError {\r\n field: string\r\n message: string\r\n}\r\n\r\nexport interface UseFieldGroupProps {\r\n modelValue?: any\r\n}\r\n\r\nexport function useFieldGroup() {\r\n const errors = ref<Record<string, string>>({})\r\n const fields = ref(new Set<string>([]))\r\n const fieldInstances = ref<any[]>([])\r\n\r\n const addField = (field: any) => {\r\n fieldInstances.value.push(field)\r\n if (field.prop) {\r\n fields.value.add(field.prop)\r\n }\r\n }\r\n\r\n const removeField = (field: any) => {\r\n const index = fieldInstances.value.indexOf(field)\r\n if (index > -1) {\r\n fieldInstances.value.splice(index, 1)\r\n }\r\n if (field.prop) {\r\n fields.value.delete(field.prop)\r\n }\r\n }\r\n\r\n const errorLock = ref(false)\r\n\r\n function setError(prop: string, error: string) {\r\n if (errorLock.value) { return }\r\n if (prop !== '') {\r\n errors.value = { ...errors.value, [prop]: error }\r\n }\r\n }\r\n\r\n function removeError(prop: string) {\r\n if (prop !== '' && errors.value[prop] !== undefined) {\r\n const newErrors = { ...errors.value }\r\n delete newErrors[prop]\r\n errors.value = newErrors\r\n }\r\n }\r\n\r\n function getError(prop: string): string {\r\n if (prop !== '') { return errors.value[prop] ?? '' }\r\n return ''\r\n }\r\n\r\n async function getErrors(): Promise<FormValidateError[]> {\r\n return Object.entries(errors.value).map(([field, message]) => ({ field, message }))\r\n }\r\n\r\n function clearValidate(fieldsToClear?: string | string[]) {\r\n if (fieldsToClear) {\r\n const propsArray = Array.isArray(fieldsToClear) ? fieldsToClear : [fieldsToClear]\r\n propsArray.forEach(prop => removeError(prop))\r\n }\r\n else {\r\n errors.value = {}\r\n }\r\n }\r\n\r\n return {\r\n errors,\r\n fields,\r\n fieldInstances,\r\n addField,\r\n removeField,\r\n setError,\r\n removeError,\r\n getError,\r\n getErrors,\r\n clearValidate,\r\n }\r\n}\r\n\r\nexport interface UseFieldGroupItemProps {\r\n prop?: string\r\n label?: string\r\n labelPosition?: string\r\n labelWidth?: string | number\r\n trigger?: 'blur' | 'change' | 'none' | Array<'blur' | 'change'>\r\n ui?: any\r\n}\r\n\r\nexport function useFieldGroupItem(props: UseFieldGroupItemProps) {\r\n const form = inject<any>('rebornForm', undefined)\r\n const instance = getCurrentInstance()\r\n\r\n const error = computed(() => {\r\n if (!form || !props.prop) { return '' }\r\n return form.getError ? form.getError(props.prop) : ''\r\n })\r\n\r\n const labelPosition = computed(() => {\r\n return props.labelPosition || form?.props?.labelPosition || 'left'\r\n })\r\n\r\n const labelWidth = computed(() => {\r\n if (labelPosition.value === 'top') { return 'auto' }\r\n return props.labelWidth || form?.props?.labelWidth || 'auto'\r\n })\r\n\r\n const size = computed(() => {\r\n const s = form?.props?.size\r\n if (s && ['sm', 'md', 'lg'].includes(s)) {\r\n return s as 'sm' | 'md' | 'lg'\r\n }\r\n return 'sm'\r\n })\r\n\r\n const getBoundingClientRect = (callback: (res: any) => void) => {\r\n uni.createSelectorQuery()\r\n .in(instance?.proxy)\r\n .select('.re-form-item')\r\n .boundingClientRect(callback)\r\n .exec()\r\n }\r\n\r\n const validate = (trigger: 'blur' | 'change') => {\r\n if (!form || !props.prop) { return }\r\n\r\n let currentTrigger = props.trigger\r\n if (currentTrigger === undefined) {\r\n currentTrigger = form.props?.trigger\r\n }\r\n\r\n if (!currentTrigger || currentTrigger === 'none') { return }\r\n\r\n let shouldValidate = false\r\n if (Array.isArray(currentTrigger)) {\r\n shouldValidate = currentTrigger.includes(trigger)\r\n }\r\n else {\r\n shouldValidate = currentTrigger === trigger\r\n }\r\n\r\n if (shouldValidate && form.validateField) {\r\n form.validateField(props.prop)\r\n }\r\n }\r\n\r\n watch(() => props.prop, (newProp, oldProp) => {\r\n if (form) {\r\n if (oldProp) {\r\n form.removeField({ uid: instance?.uid, prop: oldProp })\r\n }\r\n if (newProp) {\r\n form.addField({ uid: instance?.uid, prop: newProp, getBoundingClientRect })\r\n }\r\n }\r\n })\r\n\r\n onMounted(() => {\r\n if (form && props.prop) {\r\n form.addField({ uid: instance?.uid, prop: props.prop, getBoundingClientRect })\r\n }\r\n })\r\n\r\n onUnmounted(() => {\r\n if (form && props.prop) {\r\n form.removeField({ uid: instance?.uid, prop: props.prop })\r\n }\r\n })\r\n\r\n return {\r\n form,\r\n error,\r\n labelPosition,\r\n labelWidth,\r\n size,\r\n getBoundingClientRect,\r\n validate,\r\n }\r\n}\r\n\r\nexport function useFormInject(props: any) {\r\n const form = inject<any>('rebornForm', null)\r\n const formItem = inject<any>('rebornFormItem', null)\r\n\r\n const size = computed(() => {\r\n return form?.props?.size || props.size\r\n })\r\n\r\n const disabled = computed(() => {\r\n return form?.props?.disabled || props.disabled\r\n })\r\n\r\n const orientation = computed(() => {\r\n return form?.props?.orientation || props.orientation\r\n })\r\n\r\n const isError = computed(() => {\r\n return formItem?.isError?.value || false\r\n })\r\n\r\n const validate = (trigger: 'blur' | 'change') => {\r\n if (formItem?.validate) {\r\n formItem.validate(trigger)\r\n }\r\n }\r\n\r\n return {\r\n form,\r\n size,\r\n disabled,\r\n orientation,\r\n isError,\r\n validate,\r\n }\r\n}\r\n"
843
852
  }
844
853
  };
845
854
 
@@ -1087,7 +1096,7 @@ function initCommand() {
1087
1096
  // package.json
1088
1097
  var package_default = {
1089
1098
  name: "reborn-ui",
1090
- version: "0.1.74",
1099
+ version: "0.1.77",
1091
1100
  description: "A CLI for Reborn UI",
1092
1101
  author: "1997liuyh-boop",
1093
1102
  license: "MIT",