diff --git a/cli/create.mjs b/cli/create.mjs
index c386488..600010f 100644
--- a/cli/create.mjs
+++ b/cli/create.mjs
@@ -43,11 +43,11 @@ export default async () => {
const hooksType = await select({
message: '请选择Hooks用途分类:',
choices: [
- { name: '通用', value: 'common', description: '通用' },
- { name: '业务', value: 'business', description: '业务相关' },
- { name: '状态', value: 'business', description: '操作状态相关' },
+ { name: '通用', value: 'Common', description: '通用' },
+ { name: '业务', value: 'Worker', description: '业务相关' },
+ { name: '状态', value: 'State', description: '操作状态相关' },
{ name: '文档对象', value: 'Dom', description: '操作Dom相关' },
- { name: '其他', value: 'other', description: '其他杂项' },
+ { name: '其他', value: 'Other', description: '其他杂项' },
],
});
diff --git a/docs/.vitepress/router.mjs b/docs/.vitepress/router.mjs
index 00cc24b..81a5ad5 100644
--- a/docs/.vitepress/router.mjs
+++ b/docs/.vitepress/router.mjs
@@ -19,7 +19,7 @@ const Router = {
'useDebounce',
],
Data: ['useTable'],
- Worker: ['useWorkerFunction'],
+ Worker: ['useCountDown','useWorkerFunction'],
};
function getRouterConfig(langPrefix = '/') {
diff --git a/docs/useCountDown/demo/index.vue b/docs/useCountDown/demo/index.vue
new file mode 100644
index 0000000..5f149cd
--- /dev/null
+++ b/docs/useCountDown/demo/index.vue
@@ -0,0 +1,19 @@
+
+ 开始倒计时
+
+ 总时间:{{ current }}
+
+
+
diff --git a/docs/useCountDown/demo/index2.vue b/docs/useCountDown/demo/index2.vue
new file mode 100644
index 0000000..0e4bf00
--- /dev/null
+++ b/docs/useCountDown/demo/index2.vue
@@ -0,0 +1,22 @@
+
+
+ 开始倒计时
+ 暂停倒计时
+
+
+ 总时间:{{ current }}
+
+
+
diff --git a/docs/useCountDown/demo/millisecond.vue b/docs/useCountDown/demo/millisecond.vue
new file mode 100644
index 0000000..71e5a63
--- /dev/null
+++ b/docs/useCountDown/demo/millisecond.vue
@@ -0,0 +1,20 @@
+
+ 开始倒计时
+
+ 总时间:{{ current }}
+
+
+
diff --git a/docs/useCountDown/index.md b/docs/useCountDown/index.md
new file mode 100644
index 0000000..2de7976
--- /dev/null
+++ b/docs/useCountDown/index.md
@@ -0,0 +1,46 @@
+# useCountDown
+
+提供倒计时管理能力。
+
+## 基本用法
+
+
+
+## 毫秒级渲染
+
+
+
+## 开始 / 暂停
+
+
+
+## API
+
+### CurrentTime 格式
+
+| 名称 | 说明 | 类型 |
+| ------------ | ---------------------- | -------- |
+| total | 剩余总时间(单位毫秒) | _number_ |
+| days | 剩余天数 | _number_ |
+| hours | 剩余小时 | _number_ |
+| minutes | 剩余分钟 | _number_ |
+| seconds | 剩余秒数 | _number_ |
+| milliseconds | 剩余毫秒 | _number_ |
+
+### 参数
+
+| 参数 | 说明 | 类型 | 默认值 |
+| ----------- | -------------------------- | -------------------------------- | ------- |
+| time | 倒计时时长,单位毫秒 | _number_ | - |
+| millisecond | 是否开启毫秒级渲染 | _boolean_ | `false` |
+| onChange | 倒计时改变时触发的回调函数 | _(current: CurrentTime) => void_ | - |
+| onFinish | 倒计时结束时触发的回调函数 | _() => void_ | - |
+
+### 返回值
+
+| 参数 | 说明 | 类型 |
+| ------- | ---------------------------------- | ----------------------- |
+| current | 当前剩余的时间 | _CurrentTime_ |
+| start | 开始倒计时 | _() => void_ |
+| pause | 暂停倒计时 | _() => void_ |
+| reset | 重置倒计时,支持传入新的倒计时时长 | _(time?: number): void_ |
diff --git a/packages/hooks/src/index.ts b/packages/hooks/src/index.ts
index 34b9e6f..023dc98 100644
--- a/packages/hooks/src/index.ts
+++ b/packages/hooks/src/index.ts
@@ -1,8 +1,10 @@
import useBoolean from './useBoolean';
import useToggle from './useToggle';
import useRect from "./useRect";
+import useCountDown from "./useCountDown";
export {
useBoolean,
useToggle,
- useRect
+ useRect,
+ useCountDown
};
\ No newline at end of file
diff --git a/packages/hooks/src/router.js b/packages/hooks/src/router.js
index 72a1cd6..c167939 100644
--- a/packages/hooks/src/router.js
+++ b/packages/hooks/src/router.js
@@ -1,5 +1,6 @@
// 路由映射表
const Router = {
- Dom: ["useRect"]
+ Dom: ['useRect'],
+ Worker: ['useCountDown'],
};
-export default Router;
\ No newline at end of file
+export default Router;
diff --git a/packages/hooks/src/useCountDown/demo/index.vue b/packages/hooks/src/useCountDown/demo/index.vue
new file mode 100644
index 0000000..5f149cd
--- /dev/null
+++ b/packages/hooks/src/useCountDown/demo/index.vue
@@ -0,0 +1,19 @@
+
+ 开始倒计时
+
+ 总时间:{{ current }}
+
+
+
diff --git a/packages/hooks/src/useCountDown/demo/index2.vue b/packages/hooks/src/useCountDown/demo/index2.vue
new file mode 100644
index 0000000..0e4bf00
--- /dev/null
+++ b/packages/hooks/src/useCountDown/demo/index2.vue
@@ -0,0 +1,22 @@
+
+
+ 开始倒计时
+ 暂停倒计时
+
+
+ 总时间:{{ current }}
+
+
+
diff --git a/packages/hooks/src/useCountDown/demo/millisecond.vue b/packages/hooks/src/useCountDown/demo/millisecond.vue
new file mode 100644
index 0000000..71e5a63
--- /dev/null
+++ b/packages/hooks/src/useCountDown/demo/millisecond.vue
@@ -0,0 +1,20 @@
+
+ 开始倒计时
+
+ 总时间:{{ current }}
+
+
+
diff --git a/packages/hooks/src/useCountDown/index.md b/packages/hooks/src/useCountDown/index.md
new file mode 100644
index 0000000..2de7976
--- /dev/null
+++ b/packages/hooks/src/useCountDown/index.md
@@ -0,0 +1,46 @@
+# useCountDown
+
+提供倒计时管理能力。
+
+## 基本用法
+
+
+
+## 毫秒级渲染
+
+
+
+## 开始 / 暂停
+
+
+
+## API
+
+### CurrentTime 格式
+
+| 名称 | 说明 | 类型 |
+| ------------ | ---------------------- | -------- |
+| total | 剩余总时间(单位毫秒) | _number_ |
+| days | 剩余天数 | _number_ |
+| hours | 剩余小时 | _number_ |
+| minutes | 剩余分钟 | _number_ |
+| seconds | 剩余秒数 | _number_ |
+| milliseconds | 剩余毫秒 | _number_ |
+
+### 参数
+
+| 参数 | 说明 | 类型 | 默认值 |
+| ----------- | -------------------------- | -------------------------------- | ------- |
+| time | 倒计时时长,单位毫秒 | _number_ | - |
+| millisecond | 是否开启毫秒级渲染 | _boolean_ | `false` |
+| onChange | 倒计时改变时触发的回调函数 | _(current: CurrentTime) => void_ | - |
+| onFinish | 倒计时结束时触发的回调函数 | _() => void_ | - |
+
+### 返回值
+
+| 参数 | 说明 | 类型 |
+| ------- | ---------------------------------- | ----------------------- |
+| current | 当前剩余的时间 | _CurrentTime_ |
+| start | 开始倒计时 | _() => void_ |
+| pause | 暂停倒计时 | _() => void_ |
+| reset | 重置倒计时,支持传入新的倒计时时长 | _(time?: number): void_ |
diff --git a/packages/hooks/src/useCountDown/index.ts b/packages/hooks/src/useCountDown/index.ts
new file mode 100644
index 0000000..3f20c38
--- /dev/null
+++ b/packages/hooks/src/useCountDown/index.ts
@@ -0,0 +1,175 @@
+import { computed, onActivated, onBeforeUnmount, onDeactivated, ref } from 'vue';
+import { cancelRaf, inBrowser, raf } from '../utils';
+
+export type CurrentTime = {
+ days: number;
+ hours: number;
+ total: number;
+ minutes: number;
+ seconds: number;
+ milliseconds: number;
+};
+
+export type UseCountDownOptions = {
+ time: number;
+ millisecond?: boolean;
+ onChange?: (current: CurrentTime) => void;
+ onFinish?: () => void;
+};
+
+// 单位: 毫秒
+const SECOND = 1000; // 一秒
+const MINUTE = 60 * SECOND; // 一分钟
+const HOUR = 60 * MINUTE; // 一个小时
+const DAY = 24 * HOUR; // 一天
+
+/**
+ * 将时间戳转换为更易读的当前时间对象。
+ * @param time 输入的时间戳,单位为毫秒。
+ * @returns 返回一个对象,包含总时间、天、小时、分钟、秒和毫秒。
+ */
+function parseTime(time: number): CurrentTime {
+ // 计算总天数
+ const days = Math.floor(time / DAY);
+ // 计算剩余时间的小时数
+ const hours = Math.floor((time % DAY) / HOUR);
+ // 计算剩余时间的分钟数
+ const minutes = Math.floor((time % HOUR) / MINUTE);
+ // 计算剩余时间的秒数
+ const seconds = Math.floor((time % MINUTE) / SECOND);
+ // 计算剩余时间的毫秒数
+ const milliseconds = Math.floor(time % SECOND);
+
+ return {
+ total: time,
+ days,
+ hours,
+ minutes,
+ seconds,
+ milliseconds,
+ };
+}
+
+// 判断两个时间是否相等
+function isSameSecond(time1: number, time2: number): boolean {
+ return Math.floor(time1 / 1000) === Math.floor(time2 / 1000);
+}
+
+function useCountDown(options: UseCountDownOptions) {
+ let rafId: number; // 帧动画id
+ let endTime: number; // 结束时间
+ let counting: boolean; // 是否正在倒计时
+ let deactivated: boolean; // 是否暂停
+
+ const remain = ref(options.time);
+ const current = computed(() => parseTime(remain.value));
+
+ // 暂停
+ const pause = () => {
+ counting = false;
+ cancelRaf(rafId);
+ };
+
+ // 开始
+ const start = () => {
+ if (!counting) {
+ endTime = Date.now() + remain.value;
+ counting = true;
+ tick();
+ }
+ };
+
+ // 重置
+ const reset = (totalTime: number = options.time) => {
+ pause();
+ remain.value = totalTime;
+ };
+
+ // 获取剩余时间, 默认为毫秒 超过结束时间 返回 0
+ const getCurrentRemain = () => Math.max(endTime - Date.now(), 0);
+
+ // 设置剩余时间
+ const setRemain = (value: number) => {
+ remain.value = value;
+ // 有 onChange 触发
+ options.onChange?.(current.value);
+
+ // 如果为0 暂停 并触发onFinish 告知结束了
+ if (value === 0) {
+ pause();
+ options.onFinish?.();
+ }
+ };
+
+ const microTick = () => {
+ rafId = raf(() => {
+ if (counting) {
+ setRemain(getCurrentRemain());
+
+ if (remain.value > 0) {
+ microTick();
+ }
+ }
+ });
+ };
+
+ const macroTick = () => {
+ rafId = raf(() => {
+ if (counting) {
+ const remainRemain = getCurrentRemain();
+
+ // 检查当前剩余时间是否与上一次记录的不同,或者已经归零。
+ if (!isSameSecond(remainRemain, remain.value) || remainRemain === 0) {
+ setRemain(remainRemain);
+ }
+
+ // 如果剩余时间仍大于0,则继续调度
+ if (remain.value > 0) {
+ macroTick();
+ }
+ }
+ });
+ };
+
+ const tick = () => {
+ // 检查是否在浏览器环境中,
+ if (!inBrowser) {
+ return;
+ }
+
+ // 根据配置选项决定执行毫秒的启动
+ if (options.millisecond) {
+ microTick();
+ } else {
+ macroTick();
+ }
+ };
+
+ onBeforeUnmount(pause);
+
+ // 组件被激活时,重新开始倒计时
+ onActivated(() => {
+ if (deactivated) {
+ counting = true;
+ deactivated = false;
+ tick();
+ }
+ });
+
+ // 当组件被暂停或停止活动时调用的函数。
+ onDeactivated(() => {
+ if (counting) {
+ pause();
+ deactivated = true;
+ }
+ });
+
+ return {
+ start,
+ pause,
+ reset,
+ current,
+ };
+}
+
+export default useCountDown;