Browse Source

feat[hooks]: use-preloader

chenlei 1 year ago
parent
commit
9299b6e47a
7 changed files with 13230 additions and 18832 deletions
  1. 1 1
      .npmrc
  2. 5 0
      packages/docs/.umirc.ts
  3. 86 0
      packages/docs/docs/utils/hooks.md
  4. 4 1
      packages/hooks/package.json
  5. 1 0
      packages/hooks/src/index.ts
  6. 145 0
      packages/hooks/src/usePreloader.ts
  7. 12988 18830
      pnpm-lock.yaml

+ 1 - 1
.npmrc

@@ -1,4 +1,4 @@
-registry=https://registry.npm.taobao.org
+registry=https://registry.npmmirror.com
 
 shamefully-hoist=true
 strict-peer-dependencies=false

+ 5 - 0
packages/docs/.umirc.ts

@@ -18,6 +18,7 @@ export default defineConfig({
     },
   ],
   alias: {
+    "@dage/hooks": join(__dirname, "../hooks/dist/"),
     "@dage/utils": join(__dirname, "../utils/dist/"),
     "@dage/service": join(__dirname, "../service/dist/"),
     "@dage/pc-components": join(__dirname, "../pc-components/dist/"),
@@ -86,6 +87,10 @@ export default defineConfig({
     ],
     "/utils": [
       {
+        title: "hooks",
+        path: "/utils/hooks",
+      },
+      {
         title: "EventBus 事件",
         path: "/utils/eventbus",
       },

+ 86 - 0
packages/docs/docs/utils/hooks.md

@@ -0,0 +1,86 @@
+## React
+
+```bash
+yarn add @dage/hooks --registry http://192.168.20.245:4873/
+```
+
+### useCreate
+
+在组件首次创建时调用
+
+```typescript
+useCreate(cb: Function): void
+```
+
+### usePreloader
+
+预加载资源
+
+```typescript
+usePreloader({
+  list: string[],
+  /**
+   * 百分比保留几位小数
+   */
+  decimals?: number;
+  success?: Function;
+  error?: Function;
+}): {
+  status: PRELOADER_STATUS;
+  /**
+    进度
+   */
+  percent: string;
+  /**
+    已接收字节长度
+   */
+  downloadLength: number;
+  /**
+    总字节长度
+   */
+  totalLength: number;
+  /**
+   * 开始预加载
+   */
+  start: () => void;
+  /**
+   * 中断请求
+   */
+  abort: () => void;
+}
+```
+
+#### 示例
+
+```tsx
+import React from "react";
+import { usePreloader, PRELOADER_STATUS } from "@dage/hooks";
+import { Progress, Space, Button } from "antd";
+
+export default () => {
+  const { percent, status, start, abort } = usePreloader({
+    list: [
+      "https://houseoss.4dkankan.com/project/yzdyh-dadu/media/end.bad6e656.mp4",
+      "https://houseoss.4dkankan.com/project/yzdyh-dadu/media/epilogue.ff00412e.mp4",
+    ],
+  });
+
+  return (
+    <>
+      <Space wrap>
+        <Progress type="circle" percent={percent} />
+      </Space>
+
+      {status === PRELOADER_STATUS.LOADING ? (
+        <Button danger style={{ marginLeft: "20px" }} onClick={() => abort()}>
+          中断
+        </Button>
+      ) : (
+        <Button style={{ marginLeft: "20px" }} onClick={() => start()}>
+          开始预加载
+        </Button>
+      )}
+    </>
+  );
+};
+```

+ 4 - 1
packages/hooks/package.json

@@ -26,5 +26,8 @@
   "peerDependencies": {
     "react": ">=17"
   },
-  "license": "MIT"
+  "license": "MIT",
+  "dependencies": {
+    "lodash": "^4.17.21"
+  }
 }

+ 1 - 0
packages/hooks/src/index.ts

@@ -1 +1,2 @@
 export * from "./useCreate";
+export * from "./usePreloader";

+ 145 - 0
packages/hooks/src/usePreloader.ts

@@ -0,0 +1,145 @@
+import { throttle } from "lodash";
+import { useCallback, useRef, useState } from "react";
+
+export enum PRELOADER_STATUS {
+  ERROR = "error",
+  LOADING = "loading",
+  WAITING = "waiting",
+  DONE = "done",
+  ABORT = "abort",
+}
+
+export function usePreloader(params: {
+  list: string[];
+  /**
+   * 百分比保留几位小数
+   */
+  decimals?: number;
+  success?: Function;
+  error?: Function;
+}) {
+  const decimals = params.decimals ? params.decimals * 10 : 1;
+  const fetchControllerStack = useRef<AbortController[]>([]);
+  const _list = useRef(params.list);
+
+  /**
+   * 总字节长度
+   */
+  const totalLength = useRef(0);
+  const fileSizeStack = useRef<number[]>([]);
+  /**
+   * 已接收字节长度
+   */
+  const downloadLength = useRef(0);
+  /**
+   * 进度
+   */
+  const [percent, setPercent] = useState("0");
+  const [status, setStatus] = useState<PRELOADER_STATUS>(
+    PRELOADER_STATUS.WAITING
+  );
+
+  const setThrottlePercent = useCallback(
+    throttle((val: string) => setPercent(val), 200),
+    []
+  );
+
+  const handlePreload = (url: string) => {
+    const controller = new AbortController();
+    const signal = controller.signal;
+
+    return new Promise((res, rej) => {
+      fetch(url, { signal })
+        .then((response) => {
+          const contentLength = Number(response.headers.get("Content-Length"));
+          fileSizeStack.current.push(contentLength);
+
+          if (response.ok) {
+            return response.body;
+          }
+        })
+        .then(async (body) => {
+          if (!body) return;
+
+          const reader = body.getReader();
+          const loop = true;
+
+          while (loop) {
+            const { value, done } = await reader.read();
+
+            if (done) break;
+
+            downloadLength.current += value.length;
+
+            if (fileSizeStack.current.length === _list.current.length) {
+              const mediaSize = fileSizeStack.current.reduce(
+                (pre, cur) => pre + cur
+              );
+              const percent = Math.round(
+                (downloadLength.current / mediaSize) * 100 * decimals
+              );
+
+              setThrottlePercent((percent / decimals).toFixed(params.decimals));
+            }
+          }
+
+          res(true);
+        })
+        .catch((err) => {
+          rej(err);
+        });
+
+      fetchControllerStack.current.push(controller);
+    });
+  };
+
+  const resetParams = () => {
+    abort();
+
+    totalLength.current = 0;
+    downloadLength.current = 0;
+    fileSizeStack.current = [];
+    setThrottlePercent("0");
+  };
+
+  const start = () => {
+    if (status === PRELOADER_STATUS.LOADING) return;
+
+    if ([PRELOADER_STATUS.ERROR, PRELOADER_STATUS.ABORT].includes(status)) {
+      resetParams();
+    }
+
+    setStatus(PRELOADER_STATUS.LOADING);
+
+    Promise.all(_list.current.map((url) => handlePreload(url)))
+      .then(() => {
+        params.success?.();
+        setStatus(PRELOADER_STATUS.DONE);
+      })
+      .catch((err: Error) => {
+        if (err.name === "AbortError") return;
+
+        abort();
+        params.error?.(err);
+        setStatus(PRELOADER_STATUS.ERROR);
+      });
+  };
+
+  const abort = () => {
+    fetchControllerStack.current.forEach((controller) => {
+      controller.abort();
+    });
+    fetchControllerStack.current = [];
+
+    setStatus(PRELOADER_STATUS.ABORT);
+  };
+
+  return {
+    status,
+    totalLength,
+    downloadLength,
+    percent,
+    start,
+    abort,
+  };
+}

File diff suppressed because it is too large
+ 12988 - 18830
pnpm-lock.yaml