├── .cursorignore
├── .eslintcache
├── .eslintrc.cjs
├── .github
└── workflows
│ └── algolia.yml
├── .gitignore
├── .npmignore
├── .prettierrc
├── .vscode
└── settings.json
├── LICENSE
├── README.md
├── README.us.md
├── api-extractor.json
├── dist
├── tsdoc-metadata.json
├── web-loading.cjs.js
├── web-loading.cjs.min.js
├── web-loading.d.ts
├── web-loading.esm-bundler.js
├── web-loading.js
└── web-loading.min.js
├── index.html
├── index.js
├── package-lock.json
├── package.json
├── rollup.config.js
├── src
├── ExtendLoading
│ └── index.ts
├── Webloading
│ ├── index.ts
│ ├── init.ts
│ ├── style.ts
│ └── type.ts
├── draw
│ ├── index.ts
│ ├── model
│ │ ├── BaseModel.ts
│ │ ├── Bean.ts
│ │ ├── Circular.ts
│ │ ├── Clock.ts
│ │ ├── Gear.ts
│ │ ├── Img.ts
│ │ ├── Pattern.ts
│ │ ├── Ring.ts
│ │ ├── Roll.ts
│ │ ├── Skeleton.ts
│ │ ├── Zoom.ts
│ │ └── index.ts
│ └── type.ts
├── index.ts
├── loading.ts
├── main.ts
├── type.ts
└── utils.ts
├── tsconfig.base.json
└── tsconfig.json
/.cursorignore:
--------------------------------------------------------------------------------
1 | /dist
2 | /lib
3 | /node_modules
4 | /coverage
5 | /.vscode
6 | /.idea
7 | .DS_Store
8 | *.log
9 | npm-debug.log*
10 | yarn-debug.log*
11 | yarn-error.log*
--------------------------------------------------------------------------------
/.eslintcache:
--------------------------------------------------------------------------------
1 | [{"F:\\tem-pro\\web-loading\\src\\draw\\index.ts":"1","F:\\tem-pro\\web-loading\\src\\draw\\model\\BaseModel.ts":"2","F:\\tem-pro\\web-loading\\src\\draw\\model\\Bean.ts":"3","F:\\tem-pro\\web-loading\\src\\draw\\model\\Circular.ts":"4","F:\\tem-pro\\web-loading\\src\\draw\\model\\Clock.ts":"5","F:\\tem-pro\\web-loading\\src\\draw\\model\\Gear.ts":"6","F:\\tem-pro\\web-loading\\src\\draw\\model\\Img.ts":"7","F:\\tem-pro\\web-loading\\src\\draw\\model\\index.ts":"8","F:\\tem-pro\\web-loading\\src\\draw\\model\\Pattern.ts":"9","F:\\tem-pro\\web-loading\\src\\draw\\model\\Ring.ts":"10","F:\\tem-pro\\web-loading\\src\\draw\\model\\Roll.ts":"11","F:\\tem-pro\\web-loading\\src\\draw\\model\\Skeleton.ts":"12","F:\\tem-pro\\web-loading\\src\\draw\\model\\Zoom.ts":"13","F:\\tem-pro\\web-loading\\src\\draw\\type.ts":"14","F:\\tem-pro\\web-loading\\src\\ExtendLoading\\index.ts":"15","F:\\tem-pro\\web-loading\\src\\index.ts":"16","F:\\tem-pro\\web-loading\\src\\loading.ts":"17","F:\\tem-pro\\web-loading\\src\\main.ts":"18","F:\\tem-pro\\web-loading\\src\\type.ts":"19","F:\\tem-pro\\web-loading\\src\\utils.ts":"20","F:\\tem-pro\\web-loading\\src\\Webloading\\index.ts":"21","F:\\tem-pro\\web-loading\\src\\Webloading\\init.ts":"22","F:\\tem-pro\\web-loading\\src\\Webloading\\style.ts":"23","F:\\tem-pro\\web-loading\\src\\Webloading\\type.ts":"24"},{"size":1239,"mtime":1745548174583,"results":"25","hashOfConfig":"26"},{"size":7988,"mtime":1745548412816,"results":"27","hashOfConfig":"26"},{"size":4565,"mtime":1745546611103,"results":"28","hashOfConfig":"26"},{"size":3816,"mtime":1745546611104,"results":"29","hashOfConfig":"26"},{"size":4610,"mtime":1745546611104,"results":"30","hashOfConfig":"26"},{"size":3418,"mtime":1745546611104,"results":"31","hashOfConfig":"26"},{"size":1716,"mtime":1745546611106,"results":"32","hashOfConfig":"26"},{"size":496,"mtime":1745546611107,"results":"33","hashOfConfig":"26"},{"size":8228,"mtime":1745546611106,"results":"34","hashOfConfig":"26"},{"size":3172,"mtime":1745546611107,"results":"35","hashOfConfig":"26"},{"size":6921,"mtime":1745546611107,"results":"36","hashOfConfig":"26"},{"size":4121,"mtime":1745546611107,"results":"37","hashOfConfig":"26"},{"size":4476,"mtime":1745546611107,"results":"38","hashOfConfig":"26"},{"size":4185,"mtime":1745546611108,"results":"39","hashOfConfig":"26"},{"size":1866,"mtime":1745546611101,"results":"40","hashOfConfig":"26"},{"size":562,"mtime":1745546611108,"results":"41","hashOfConfig":"26"},{"size":3909,"mtime":1745546611108,"results":"42","hashOfConfig":"26"},{"size":881,"mtime":1745546611108,"results":"43","hashOfConfig":"26"},{"size":2909,"mtime":1745547935920,"results":"44","hashOfConfig":"26"},{"size":6065,"mtime":1745547794768,"results":"45","hashOfConfig":"26"},{"size":5087,"mtime":1745547794766,"results":"46","hashOfConfig":"26"},{"size":1928,"mtime":1745547794766,"results":"47","hashOfConfig":"26"},{"size":3412,"mtime":1745548126713,"results":"48","hashOfConfig":"26"},{"size":238,"mtime":1745547794766,"results":"49","hashOfConfig":"26"},{"filePath":"50","messages":"51","suppressedMessages":"52","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},"1joz206",{"filePath":"53","messages":"54","suppressedMessages":"55","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},{"filePath":"56","messages":"57","suppressedMessages":"58","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},{"filePath":"59","messages":"60","suppressedMessages":"61","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},{"filePath":"62","messages":"63","suppressedMessages":"64","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},{"filePath":"65","messages":"66","suppressedMessages":"67","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},{"filePath":"68","messages":"69","suppressedMessages":"70","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},{"filePath":"71","messages":"72","suppressedMessages":"73","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},{"filePath":"74","messages":"75","suppressedMessages":"76","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},{"filePath":"77","messages":"78","suppressedMessages":"79","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},{"filePath":"80","messages":"81","suppressedMessages":"82","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},{"filePath":"83","messages":"84","suppressedMessages":"85","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},{"filePath":"86","messages":"87","suppressedMessages":"88","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},{"filePath":"89","messages":"90","suppressedMessages":"91","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},{"filePath":"92","messages":"93","suppressedMessages":"94","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},{"filePath":"95","messages":"96","suppressedMessages":"97","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},{"filePath":"98","messages":"99","suppressedMessages":"100","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},{"filePath":"101","messages":"102","suppressedMessages":"103","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},{"filePath":"104","messages":"105","suppressedMessages":"106","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},{"filePath":"107","messages":"108","suppressedMessages":"109","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},{"filePath":"110","messages":"111","suppressedMessages":"112","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},{"filePath":"113","messages":"114","suppressedMessages":"115","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},{"filePath":"116","messages":"117","suppressedMessages":"118","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},{"filePath":"119","messages":"120","suppressedMessages":"121","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},"F:\\tem-pro\\web-loading\\src\\draw\\index.ts",[],[],"F:\\tem-pro\\web-loading\\src\\draw\\model\\BaseModel.ts",[],[],"F:\\tem-pro\\web-loading\\src\\draw\\model\\Bean.ts",[],[],"F:\\tem-pro\\web-loading\\src\\draw\\model\\Circular.ts",[],[],"F:\\tem-pro\\web-loading\\src\\draw\\model\\Clock.ts",[],[],"F:\\tem-pro\\web-loading\\src\\draw\\model\\Gear.ts",[],[],"F:\\tem-pro\\web-loading\\src\\draw\\model\\Img.ts",[],[],"F:\\tem-pro\\web-loading\\src\\draw\\model\\index.ts",[],[],"F:\\tem-pro\\web-loading\\src\\draw\\model\\Pattern.ts",[],[],"F:\\tem-pro\\web-loading\\src\\draw\\model\\Ring.ts",[],[],"F:\\tem-pro\\web-loading\\src\\draw\\model\\Roll.ts",[],[],"F:\\tem-pro\\web-loading\\src\\draw\\model\\Skeleton.ts",[],[],"F:\\tem-pro\\web-loading\\src\\draw\\model\\Zoom.ts",[],[],"F:\\tem-pro\\web-loading\\src\\draw\\type.ts",[],[],"F:\\tem-pro\\web-loading\\src\\ExtendLoading\\index.ts",[],[],"F:\\tem-pro\\web-loading\\src\\index.ts",[],[],"F:\\tem-pro\\web-loading\\src\\loading.ts",[],[],"F:\\tem-pro\\web-loading\\src\\main.ts",[],[],"F:\\tem-pro\\web-loading\\src\\type.ts",[],[],"F:\\tem-pro\\web-loading\\src\\utils.ts",[],[],"F:\\tem-pro\\web-loading\\src\\Webloading\\index.ts",[],[],"F:\\tem-pro\\web-loading\\src\\Webloading\\init.ts",[],[],"F:\\tem-pro\\web-loading\\src\\Webloading\\style.ts",[],[],"F:\\tem-pro\\web-loading\\src\\Webloading\\type.ts",[],[]]
--------------------------------------------------------------------------------
/.eslintrc.cjs:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | root: true,
3 | env: {
4 | browser: true,
5 | node: true,
6 | es6: true,
7 | },
8 | parserOptions: {
9 | parser: "@typescript-eslint/parser",
10 | ecmaVersion: 2020,
11 | sourceType: "module",
12 | ecmaFeatures: {
13 | jsx: true,
14 | },
15 | },
16 | extends: ["plugin:@typescript-eslint/recommended", "plugin:prettier/recommended"],
17 | rules: {
18 | "@typescript-eslint/ban-ts-ignore": "off",
19 | "@typescript-eslint/explicit-function-return-type": "off",
20 | "@typescript-eslint/no-explicit-any": "off",
21 | "@typescript-eslint/no-var-requires": "off",
22 | "@typescript-eslint/no-empty-function": "off",
23 | "no-use-before-define": "off",
24 | "@typescript-eslint/no-use-before-define": "off",
25 | "@typescript-eslint/ban-ts-comment": "off",
26 | "@typescript-eslint/ban-types": "off",
27 | "@typescript-eslint/no-non-null-assertion": "off",
28 | "@typescript-eslint/explicit-module-boundary-types": "off",
29 | "@typescript-eslint/no-unused-vars": [
30 | "error",
31 | {
32 | argsIgnorePattern: "^_",
33 | varsIgnorePattern: "^_",
34 | },
35 | ],
36 | "no-unused-vars": [
37 | "error",
38 | {
39 | argsIgnorePattern: "^_",
40 | varsIgnorePattern: "^_",
41 | },
42 | ],
43 | "space-before-function-paren": "off",
44 | },
45 | };
46 |
--------------------------------------------------------------------------------
/.github/workflows/algolia.yml:
--------------------------------------------------------------------------------
1 | name: algolia
2 | on:
3 | push:
4 | branches:
5 | - master
6 | jobs:
7 | algolia:
8 | runs-on: ubuntu-latest
9 | steps:
10 | - uses: actions/checkout@v3
11 | - name: Get the content of algolia.json as config
12 | id: algolia_config
13 | run: echo "config=$(cat crawlerConfig.json | jq -r tostring)" >> $GITHUB_OUTPUT
14 | - name: Push indices to Algolia
15 | uses: signcl/docsearch-scraper-action@master
16 | env:
17 | APPLICATION_ID: ${{ secrets.APPLICATION_ID }}
18 | API_KEY: ${{ secrets.API_KEY }}
19 | CONFIG: ${{ steps.algolia_config.outputs.config }}
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | lib
3 | temp
4 | docs
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | src
2 | temp
3 | lib
4 | .eslint*
5 | docs
6 | api-extractor.json
7 | rollup.config*
8 | tsconfig*
9 | package-lock.json
--------------------------------------------------------------------------------
/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "eslintIntegration": true,
3 | "stylelintIntegration": true,
4 | "tabWidth": 2,
5 | "singleQuote": true,
6 | "semi": false,
7 | "trailingComma": "none",
8 | "htmlWhitespaceSensitivity": "ignore",
9 | "printWidth": 120,
10 | "endOfLine": "auto"
11 | }
12 |
--------------------------------------------------------------------------------
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "commentTranslate.source": "DarkCWK.youdao-youdao"
3 | }
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Apache License
2 | Version 2.0, January 2004
3 | http://www.apache.org/licenses/
4 |
5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6 |
7 | 1. Definitions.
8 |
9 | "License" shall mean the terms and conditions for use, reproduction,
10 | and distribution as defined by Sections 1 through 9 of this document.
11 |
12 | "Licensor" shall mean the copyright owner or entity authorized by
13 | the copyright owner that is granting the License.
14 |
15 | "Legal Entity" shall mean the union of the acting entity and all
16 | other entities that control, are controlled by, or are under common
17 | control with that entity. For the purposes of this definition,
18 | "control" means (i) the power, direct or indirect, to cause the
19 | direction or management of such entity, whether by contract or
20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
21 | outstanding shares, or (iii) beneficial ownership of such entity.
22 |
23 | "You" (or "Your") shall mean an individual or Legal Entity
24 | exercising permissions granted by this License.
25 |
26 | "Source" form shall mean the preferred form for making modifications,
27 | including but not limited to software source code, documentation
28 | source, and configuration files.
29 |
30 | "Object" form shall mean any form resulting from mechanical
31 | transformation or translation of a Source form, including but
32 | not limited to compiled object code, generated documentation,
33 | and conversions to other media types.
34 |
35 | "Work" shall mean the work of authorship, whether in Source or
36 | Object form, made available under the License, as indicated by a
37 | copyright notice that is included in or attached to the work
38 | (an example is provided in the Appendix below).
39 |
40 | "Derivative Works" shall mean any work, whether in Source or Object
41 | form, that is based on (or derived from) the Work and for which the
42 | editorial revisions, annotations, elaborations, or other modifications
43 | represent, as a whole, an original work of authorship. For the purposes
44 | of this License, Derivative Works shall not include works that remain
45 | separable from, or merely link (or bind by name) to the interfaces of,
46 | the Work and Derivative Works thereof.
47 |
48 | "Contribution" shall mean any work of authorship, including
49 | the original version of the Work and any modifications or additions
50 | to that Work or Derivative Works thereof, that is intentionally
51 | submitted to Licensor for inclusion in the Work by the copyright owner
52 | or by an individual or Legal Entity authorized to submit on behalf of
53 | the copyright owner. For the purposes of this definition, "submitted"
54 | means any form of electronic, verbal, or written communication sent
55 | to the Licensor or its representatives, including but not limited to
56 | communication on electronic mailing lists, source code control systems,
57 | and issue tracking systems that are managed by, or on behalf of, the
58 | Licensor for the purpose of discussing and improving the Work, but
59 | excluding communication that is conspicuously marked or otherwise
60 | designated in writing by the copyright owner as "Not a Contribution."
61 |
62 | "Contributor" shall mean Licensor and any individual or Legal Entity
63 | on behalf of whom a Contribution has been received by Licensor and
64 | subsequently incorporated within the Work.
65 |
66 | 2. Grant of Copyright License. Subject to the terms and conditions of
67 | this License, each Contributor hereby grants to You a perpetual,
68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69 | copyright license to reproduce, prepare Derivative Works of,
70 | publicly display, publicly perform, sublicense, and distribute the
71 | Work and such Derivative Works in Source or Object form.
72 |
73 | 3. Grant of Patent License. Subject to the terms and conditions of
74 | this License, each Contributor hereby grants to You a perpetual,
75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76 | (except as stated in this section) patent license to make, have made,
77 | use, offer to sell, sell, import, and otherwise transfer the Work,
78 | where such license applies only to those patent claims licensable
79 | by such Contributor that are necessarily infringed by their
80 | Contribution(s) alone or by combination of their Contribution(s)
81 | with the Work to which such Contribution(s) was submitted. If You
82 | institute patent litigation against any entity (including a
83 | cross-claim or counterclaim in a lawsuit) alleging that the Work
84 | or a Contribution incorporated within the Work constitutes direct
85 | or contributory patent infringement, then any patent licenses
86 | granted to You under this License for that Work shall terminate
87 | as of the date such litigation is filed.
88 |
89 | 4. Redistribution. You may reproduce and distribute copies of the
90 | Work or Derivative Works thereof in any medium, with or without
91 | modifications, and in Source or Object form, provided that You
92 | meet the following conditions:
93 |
94 | (a) You must give any other recipients of the Work or
95 | Derivative Works a copy of this License; and
96 |
97 | (b) You must cause any modified files to carry prominent notices
98 | stating that You changed the files; and
99 |
100 | (c) You must retain, in the Source form of any Derivative Works
101 | that You distribute, all copyright, patent, trademark, and
102 | attribution notices from the Source form of the Work,
103 | excluding those notices that do not pertain to any part of
104 | the Derivative Works; and
105 |
106 | (d) If the Work includes a "NOTICE" text file as part of its
107 | distribution, then any Derivative Works that You distribute must
108 | include a readable copy of the attribution notices contained
109 | within such NOTICE file, excluding those notices that do not
110 | pertain to any part of the Derivative Works, in at least one
111 | of the following places: within a NOTICE text file distributed
112 | as part of the Derivative Works; within the Source form or
113 | documentation, if provided along with the Derivative Works; or,
114 | within a display generated by the Derivative Works, if and
115 | wherever such third-party notices normally appear. The contents
116 | of the NOTICE file are for informational purposes only and
117 | do not modify the License. You may add Your own attribution
118 | notices within Derivative Works that You distribute, alongside
119 | or as an addendum to the NOTICE text from the Work, provided
120 | that such additional attribution notices cannot be construed
121 | as modifying the License.
122 |
123 | You may add Your own copyright statement to Your modifications and
124 | may provide additional or different license terms and conditions
125 | for use, reproduction, or distribution of Your modifications, or
126 | for any such Derivative Works as a whole, provided Your use,
127 | reproduction, and distribution of the Work otherwise complies with
128 | the conditions stated in this License.
129 |
130 | 5. Submission of Contributions. Unless You explicitly state otherwise,
131 | any Contribution intentionally submitted for inclusion in the Work
132 | by You to the Licensor shall be under the terms and conditions of
133 | this License, without any additional terms or conditions.
134 | Notwithstanding the above, nothing herein shall supersede or modify
135 | the terms of any separate license agreement you may have executed
136 | with Licensor regarding such Contributions.
137 |
138 | 6. Trademarks. This License does not grant permission to use the trade
139 | names, trademarks, service marks, or product names of the Licensor,
140 | except as required for reasonable and customary use in describing the
141 | origin of the Work and reproducing the content of the NOTICE file.
142 |
143 | 7. Disclaimer of Warranty. Unless required by applicable law or
144 | agreed to in writing, Licensor provides the Work (and each
145 | Contributor provides its Contributions) on an "AS IS" BASIS,
146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147 | implied, including, without limitation, any warranties or conditions
148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149 | PARTICULAR PURPOSE. You are solely responsible for determining the
150 | appropriateness of using or redistributing the Work and assume any
151 | risks associated with Your exercise of permissions under this License.
152 |
153 | 8. Limitation of Liability. In no event and under no legal theory,
154 | whether in tort (including negligence), contract, or otherwise,
155 | unless required by applicable law (such as deliberate and grossly
156 | negligent acts) or agreed to in writing, shall any Contributor be
157 | liable to You for damages, including any direct, indirect, special,
158 | incidental, or consequential damages of any character arising as a
159 | result of this License or out of the use or inability to use the
160 | Work (including but not limited to damages for loss of goodwill,
161 | work stoppage, computer failure or malfunction, or any and all
162 | other commercial damages or losses), even if such Contributor
163 | has been advised of the possibility of such damages.
164 |
165 | 9. Accepting Warranty or Additional Liability. While redistributing
166 | the Work or Derivative Works thereof, You may choose to offer,
167 | and charge a fee for, acceptance of support, warranty, indemnity,
168 | or other liability obligations and/or rights consistent with this
169 | License. However, in accepting such obligations, You may act only
170 | on Your own behalf and on Your sole responsibility, not on behalf
171 | of any other Contributor, and only if You agree to indemnify,
172 | defend, and hold each Contributor harmless for any liability
173 | incurred by, or claims asserted against, such Contributor by reason
174 | of your accepting any such warranty or additional liability.
175 |
176 | END OF TERMS AND CONDITIONS
177 |
178 | APPENDIX: How to apply the Apache License to your work.
179 |
180 | To apply the Apache License to your work, attach the following
181 | boilerplate notice, with the fields enclosed by brackets "[]"
182 | replaced with your own identifying information. (Don't include
183 | the brackets!) The text should be enclosed in the appropriate
184 | comment syntax for the file format. We also recommend that a
185 | file or class name and description of purpose be included on the
186 | same "printed page" as the copyright notice for easier
187 | identification within third-party archives.
188 |
189 | Copyright [yyyy] [name of copyright owner]
190 |
191 | Licensed under the Apache License, Version 2.0 (the "License");
192 | you may not use this file except in compliance with the License.
193 | You may obtain a copy of the License at
194 |
195 | http://www.apache.org/licenses/LICENSE-2.0
196 |
197 | Unless required by applicable law or agreed to in writing, software
198 | distributed under the License is distributed on an "AS IS" BASIS,
199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200 | See the License for the specific language governing permissions and
201 | limitations under the License.
202 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # web-loading
2 |
3 |
4 |
5 |
6 |
7 |
8 | 基于 Canvas 的高性能 Web 加载动画库,为您的应用提供丝滑加载效果
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 | ## 🔥 特性
24 |
25 | - 💪 基于 Canvas 绘制,性能优异
26 | - 🔥 使用 TypeScript 编写,提供完整的类型定义
27 | - 📦 开箱即用的多种加载动画效果
28 | - 🌈 支持三种挂载模式:DOM、全屏、移动端
29 | - 👓 提供完整的生命周期钩子函数
30 | - ⚡ 支持自定义动画模型
31 |
32 | ## 📋 更新日志
33 |
34 | - 优化源码文件结构
35 | - 新增无感刷新功能
36 | - 解决秒关闪屏bug
37 | - 更换打包方式,引入路径有变化
38 |
39 | ## 🎬 官方文档
40 |
41 | [查看官方文档和示例](https://tommyrunner.github.io/web-loading/)
42 |
43 | ## 介绍
44 |
45 | Web 中实现 loading 的方式有很多种,例如使用`css`动画、`js`操作元素、`gif`图片、`svg`动画、`ui`框架中自带`loading`等等,各有所优,操作元素可能更方便,但会影响性能或其他元素,动态图片性能很好,但自定义不理想。
46 |
47 | `WebLoading` 是一个基于 web 封装的`loading`动画插件,主要**model**是通过`Canvas`绘制,这种方式不会影响到界面中的元素,当然,`WebLoading`也提供了`html`配置兼容了**html**加载方式。默认的**model**模块都提供了独自的`options`配置属性,如果想更贴近业务可以使用`Custom`进行自定义,`WebLoading`提供了`BaseModel` 继承`class`让你更方便自定义自己的`loading`,或者`html`加载方式。
48 |
49 | ## 实现
50 |
51 | `WebLoading`中每一个**model**都是使用`Canvas`绘制,启动方式分别有`DOM`(元素挂载)、`FULL`(全屏)、`MINI`(移动端)。
52 |
53 | 原理大同小异,这里以`DOM`来讲述,首先我们需要`initLoading`初始化你需要渲染的**model**并提供自定义参数,当然,这个操作不是必须的,因为`WebLoading`已经初始化所有的默认数据,此时抛出操作`WebLoading`相关函数。
54 |
55 | 启动`WebLoading`调用`loading`函数需要一个`HtmlElement`元素,该元素必须拥有`children`,而不是一个单标签元素。启动`WebLoading`时会获取到这个挂载的元素,并在`children`添加一个`Canvas`,同时会计算该元素位置以及大小以最优显示同步到`Canvas`上。`WebLoading`会根据`options`参数来绘制具体的**model**,**model**中主要以`requestAnimationFrame`来进行递归回调渲染,以此来实现每一帧动画。
56 |
57 | 注意:如果配置是通过**html**渲染,那么就不会走上一步。
58 |
59 | `WebLoading`封装上主要分隔三层
60 |
61 | - 交互层:开发者与`WebLoading`的操作,例如初始化、启动、关闭、获取相关信息等等。
62 | - 逻辑层:获取到`WebLoading`接收`options`后进行初始化挂载的元素以及`canvas`等等。
63 | - model 层:继承`BaseModel`获取初始化后的`canvas`进行绘制模块。
64 |
65 | ## 安装
66 |
67 | > 根据自己的包管理工具下载。
68 |
69 | ```sh
70 | npm install web-loading
71 | ```
72 |
73 | ## 使用
74 |
75 | ### CDN引入
76 |
77 | ```html
78 |
79 |
83 | ```
84 |
85 | ### 工程项目引入
86 |
87 | ```typescript
88 | import type { LoadingType } from "web-loading";
89 | import { initLoading } from "web-loading";
90 | let webLoading: LoadingType = initLoading({
91 | // 自定义options
92 | })
93 | ```
94 |
95 | > - 参数
96 | > - `options?:OptionsType`
97 | > - 返回
98 | > - `webLoading:LoadingType`
99 |
100 | ### 获取元素
101 |
102 | ```typescript
103 | // 无框架情况
104 | let dom = document.querySelector('xxx')
105 | // vue
106 | let dom = ref()
107 | // ...如果是FULL或MINI不需要获取元素
108 | ```
109 |
110 | ### 启动
111 |
112 | ```typescript
113 | // 注意:在dom加载完成后再调用loading
114 | window.onload = function () {
115 | webLoading.loading(dom)
116 | }
117 | ```
118 |
119 | > 参数
120 | >
121 | > - `dom`:挂载的`HtmlElement`元素
122 | >
123 | > - `options?:OptionsType`,支持覆盖`options`。
124 |
125 | ## 启动方式
126 |
127 | `DOM`、`FULL`、`MINI`三种启动方式都需要基于`HtmlElement`,这里`FULL`、`MINI`是扩展的启动方式,参数中无须提供`HtemlElment`,是因为`WebLoading`已经处理的元素的创建到消失的流程。
128 |
129 | ```typescript
130 | import type { LoadingType } from "web-loading";
131 | import { fullLoading,miniLoading LOADING_TYPES } from "web-loading";
132 |
133 | let webLoading: LoadingType = fullLoading() // 全屏
134 | // let webLoading: LoadingType = miniLoading() // 移动端
135 |
136 | // 启动(如果是MINI或者FULL无需传递dom)
137 | webLoading.loading()
138 | ```
139 |
140 |
--------------------------------------------------------------------------------
/README.us.md:
--------------------------------------------------------------------------------
1 | # web-loading
2 |
3 |
4 |
5 |
6 |
7 |
8 | High-performance Web loading animation library based on Canvas, providing smooth loading effects for your applications
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 | ## 🔥 Features
24 |
25 | - 💪 Based on Canvas rendering, excellent performance
26 | - 🔥 Written in TypeScript, providing complete type definitions
27 | - 📦 Multiple out-of-the-box loading animation effects
28 | - 🌈 Supports three mounting modes: DOM, full-screen, and mobile
29 | - 👓 Provides complete lifecycle hook functions
30 | - ⚡ Supports custom animation models
31 |
32 | ## 📋 Changelog
33 |
34 | - Optimized source code structure
35 | - Added seamless refresh functionality
36 | - Fixed flash screen bug on quick close
37 | - Changed packaging method, import paths have changed
38 |
39 | ## 🎬 Official Documentation
40 |
41 | [View official documentation and examples](https://tommyrunner.github.io/web-loading/)
42 |
43 |
44 | ## introduce
45 |
46 | There are many ways to implement loading on the Web, such as using `css` animation,`js` operation element, `gif` image, `svg` animation, and `loading` in the `ui` framework. Each has its own advantages. The operation element may be more convenient, but it will affect performance or other elements. The dynamic image performance is good, but the customization is not ideal.
47 |
48 | `WebLoading`is a `loading`animation plug-in based on web encapsulation. The main **model** is drawn through` Canvas`. This method will not affect the elements in the interface. Of course, `WebLoading` also provides`html`configuration, which is compatible with the **html** loading method. The default **model** module provides its own `options` configuration attribute. If you want to be closer to the business, you can use`Custom`to customize it.`WebLoading`provides` BaseModel` inheritance `class` to make it easier for you to customize your own`loading`or`html`loading method.
49 |
50 | ## realization
51 |
52 | `Each **model** in WebLoading `is drawn using` Canvas`. The startup methods include `DOM` (element mount), `FULL` (full screen), and `MINI` (mobile terminal).
53 |
54 | The principle is very similar. For `DOM`, first we need `initLoading` to initialize the **model** that you need to render and provide custom parameters. Of course, this operation is not necessary, because `WebLoading` has initialized all the default data. At this time, the operation `WebLoading` related function is thrown.
55 |
56 | Starting the `WebLoading` and calling the `loading` function requires a `HtmlElement` element, which must have `children` instead of a single label element. When you start `WebLoading`, you will get the mounted element and add a `Canvas` in`children`. At the same time, the location and size of the element will be calculated and synchronized to` Canvas` for optimal display`WebLoading`will draw the specific **model** according to the` options` parameter, **In model** recursive callback rendering is mainly performed using `requestAnimationFrame` to achieve each frame of animation.
57 |
58 | Note: If the configuration is rendered through **html**, you will not go to the next step.
59 |
60 | `WebLoading ` package is mainly divided into three layers
61 |
62 | - Interaction layer: operations between developers and `WebLoading`, such as initialization, startup, shutdown, obtaining relevant information, etc.
63 | - Logical layer: get the elements to be initialized and mounted after `WebLoading` receives` options` and `canvas`.
64 | - Model layer: Inherit `BaseModel` to get the initialized `canvas` for drawing module.
65 |
66 | ## install
67 |
68 | > Download according to your own package management tool.
69 |
70 | ```sh
71 | npm install web-loading
72 | ```
73 |
74 | ## use
75 |
76 | ### CDN introduction
77 |
78 | ```html
79 |
80 |
84 | ```
85 |
86 | ### Introduction of engineering projects
87 |
88 | ```typescript
89 | import type { LoadingType } from "web-loading";
90 | import { initLoading } from "web-loading";
91 | let webLoading: LoadingType = initLoading({
92 | // Custom options
93 | })
94 | ```
95 |
96 | > - parameter
97 | > - `options?:OptionsType`
98 | > - return
99 | > - `webLoading:LoadingType`
100 |
101 | ### Get Element
102 |
103 | ```typescript
104 | // No frame
105 | let dom = document.querySelector('xxx')
106 | // vue
107 | let dom = ref()
108 | // ...If it is FULL or MINI, it is not necessary to obtain the element
109 | ```
110 |
111 | ### start-up
112 |
113 | ```typescript
114 | // Note: After the dom loading is completed, call loading
115 | window.onload = function () {
116 | webLoading.loading(dom)
117 | }
118 | ```
119 |
120 | > parameter
121 | >
122 | > - `dom`:Mounted`HtmlElement`element
123 | >
124 | > - `options?:OptionsType`, Support Overrides`options`.
125 |
126 | ## Start mode
127 |
128 | `DOM `,`FULL`,`MINI`All three startup methods need to be based on`HtmlElement`, where`FULL`and`MINI`are the extended startup methods, and`HtmlElement`is not required in the parameters, because`WebLoading`has processed the process from creation to disappearance of elements.
129 |
130 | ```typescript
131 | import type { LoadingType } from "web-loading";
132 | import { fullLoading,miniLoading LOADING_TYPES } from "web-loading";
133 |
134 | let webLoading: LoadingType = fullLoading() // full screen
135 | // let webLoading: LoadingType = miniLoading() // mobile terminal
136 |
137 | // Start (if it is MINI or FULL, there is no need to pass dom)
138 | webLoading.loading()
139 | ```
140 |
--------------------------------------------------------------------------------
/api-extractor.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://developer.microsoft.com/json-schemas/api-extractor/v7/api-extractor.schema.json",
3 |
4 | "mainEntryPointFilePath": "/lib/index.d.ts",
5 |
6 | "bundledPackages": [],
7 |
8 | "compiler": {
9 | "tsconfigFilePath": "/tsconfig.json"
10 | },
11 |
12 | "apiReport": {
13 | "enabled": false
14 | },
15 |
16 | "docModel": {
17 | "enabled": true
18 | },
19 |
20 | "dtsRollup": {
21 | "enabled": true,
22 | "untrimmedFilePath": "/dist/web-loading.d.ts"
23 | },
24 |
25 | "tsdocMetadata": {
26 | "enabled": true,
27 | "tsdocMetadataFilePath": "/dist/tsdoc-metadata.json"
28 | },
29 |
30 | "messages": {
31 | "compilerMessageReporting": {
32 | "default": {
33 | "logLevel": "warning"
34 | }
35 | },
36 |
37 | "extractorMessageReporting": {
38 | "default": {
39 | "logLevel": "warning"
40 | },
41 | "ae-forgotten-export": {
42 | "logLevel": "none"
43 | }
44 | },
45 |
46 | "tsdocMessageReporting": {
47 | "default": {
48 | "logLevel": "warning"
49 | },
50 | "tsdoc-undefined-tag": {
51 | "logLevel": "none"
52 | },
53 | "tsdoc-param-tag-with-invalid-type": {
54 | "logLevel": "none"
55 | },
56 | "tsdoc-param-tag-with-invalid-optional-name": {
57 | "logLevel": "none"
58 | },
59 | "tsdoc-escape-right-brace": {
60 | "logLevel": "none"
61 | },
62 | "tsdoc-malformed-inline-tag": {
63 | "logLevel": "none"
64 | }
65 | }
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/dist/tsdoc-metadata.json:
--------------------------------------------------------------------------------
1 | // This file is read by tools that parse documentation comments conforming to the TSDoc standard.
2 | // It should be published with your NPM package. It should not be tracked by Git.
3 | {
4 | "tsdocVersion": "0.12",
5 | "toolPackages": [
6 | {
7 | "packageName": "@microsoft/api-extractor",
8 | "packageVersion": "7.35.2"
9 | }
10 | ]
11 | }
12 |
--------------------------------------------------------------------------------
/dist/web-loading.cjs.min.js:
--------------------------------------------------------------------------------
1 | "use strict";var t,e,i,o;function n(){return{custom:null,html:"",type:exports.LOADING_TYPES.DOM,extendClass:"extend",model:exports.MODEL_TYPES.GEAR,text:"加载中...",textGap:8,fontSize:12,fontFamily:"Microsoft YaHei",delay:65,notFeel:0,delayInto:320,optimization:!1,zIndex:"2001",themeColor:"rgba(64,158,255,1)",bgColor:"rgba(0, 0, 0, 0.8)",shadowColor:"rgba(64,158,255,0.6)",shadowOffsetX:2,shadowOffsetY:2,shadowBlur:5,pointerEvents:!1,toast:!0}}Object.defineProperty(exports,"__esModule",{value:!0}),exports.LOADING_TYPES=void 0,(t=exports.LOADING_TYPES||(exports.LOADING_TYPES={})).DOM="dom",t.FULL="full",t.MINI="mini",exports.MODEL_TYPES=void 0,(e=exports.MODEL_TYPES||(exports.MODEL_TYPES={})).GEAR="Gear",e.RING="Ring",e.ZOOM="Zoom",e.PATTERN="Pattern",e.CLOCK="Clock",e.BEAN="Bean",e.ROLL="Roll",e.CIRCULAR="Circular",e.IMG="Img",e.SKELETON="Skeleton",exports.HOOKS_CALL_KEY=void 0,(i=exports.HOOKS_CALL_KEY||(exports.HOOKS_CALL_KEY={})).BEFORE_CLOSE="beforeClose",i.CLOSED="closed",exports.LOG_TYPES=void 0,(o=exports.LOG_TYPES||(exports.LOG_TYPES={}))[o.INFO=1]="INFO",o[o.WARN=2]="WARN",o[o.ERROR=3]="ERROR";var s,a,r,h,l=function(){function t(){}return t.info=function(t){this.call(t,exports.LOG_TYPES.INFO)},t.warn=function(t){this.call(t,exports.LOG_TYPES.WARN)},t.error=function(t){this.call(t,exports.LOG_TYPES.ERROR)},t.call=function(t,e,i){void 0===e&&(e=exports.LOG_TYPES.INFO),void 0===i&&(i={color:n().themeColor,bgColor:n().bgColor});var o=i.bgColor;2===e&&(o="#fffbe5"),3===e&&(o="#fff0f0");var s="\n background:".concat(o,";\n font-size:14px;\n color:").concat(i.color,";\n padding: 4px;\n border: 1px solid;");console.log("%c web-loading:".concat(t," "),s)},t}();function c(t){switch(d(t)){case"object":return Object.keys(t).length>0;case"array":return t.length>0;case"undefined":default:return void 0!==t;case"null":return null!==t}}function p(t){window.requestAnimationFrame?window.cancelAnimationFrame(t):window.clearInterval(t)}function d(t){try{return Object.prototype.toString.call(t).split(" ")[1].split("]")[0].toLowerCase()}catch(t){return"not-type"}}function x(t,e){var i=null,o={transition:"transitionend",OTransition:"oTransitionEnd",MozTransition:"transitionend",WebkitTransition:"webkitTransitionEnd"};for(var n in o)if(void 0!==t.style[n]){i=o[n];break}if(i){var s=function(){e(),t.removeEventListener(i,s)};t.addEventListener(i,s)}else e()}function u(){var t=String(Date.now());return window.crypto&&window.crypto.randomUUID&&(t=window.crypto.randomUUID()),"wl_"+t.replace(/-/g,"")}exports.ZOOM_ACTION=void 0,(s=exports.ZOOM_ACTION||(exports.ZOOM_ACTION={})).SCALE="scale",s.WAVE="wave",s.HEIGHT="height",exports.PATTERN_CHART=void 0,(a=exports.PATTERN_CHART||(exports.PATTERN_CHART={})).RECT="rect",a.ARC="arc",a.TRIANGLE="triangle",a.HEART="heart",a.POLYGON="polygon",exports.ROLL_CHART=void 0,(r=exports.ROLL_CHART||(exports.ROLL_CHART={})).RECT="rect",r.WHEEL="wheel",r.WINDMILL="windmill",exports.CIRCULAR_ACTION=void 0,(h=exports.CIRCULAR_ACTION||(exports.CIRCULAR_ACTION={})).COLLISION="collision",h.ROTATE="rotate";var f=function(){function t(t,e,i,o,n,s,a,r){this.modelDefOptions=void 0,this.limits=void 0,this.modelDefCall=void 0,this.webLog=l,this.stepClear=1,this.w=t,this.h=e,this.canvas=i,this.ctx=i.getContext("2d"),this.options=o,this.element=n,this.initContextCall(s,a,r)}return t.prototype._$initBaseContext=function(){this.clearRect(),this.ctx.resetTransform();var t=this.options,e=this.canvas.width,i=this.canvas.height;this.ctx.fillStyle=t.themeColor,this.ctx.strokeStyle=t.themeColor,this.ctx.shadowColor=t.shadowColor,this.ctx.font="".concat(t.fontSize,"px ").concat(t.fontFamily," small-caps"),this.ctx.textAlign="center",this.ctx.textBaseline="middle",this.ctx.translate(e/2,i/2);var o=window.devicePixelRatio||1;this.ctx.scale(o,o),this.ctx.save()},t.prototype._$initEvent=function(){var t=this;this.element.$store&&this.element.$store.hookCall.beforeClose((function(){t.clearRect()}))},t.prototype._$animationFrame=function(t){var e=this,i=this.element.$store;if(i){window.requestAnimationFrame||(i.animationId=window.setInterval(t,this.options.delay));var o=Date.now()+this.options.delay;t.call(this);var n=function(){Date.now()>o&&(t.call(e),o=Date.now()+e.options.delay),i.animationId=window.requestAnimationFrame(n)};i.animationId=window.requestAnimationFrame(n)}},t.prototype.initContextCall=function(t,e,i){var o=this;this._$initBaseContext(),this._$initEvent(),c(t)&&(this.modelDefOptions=t,this.options=Object.assign(t,this.options),this.element.$store&&(this.element.$store.options=this.options),e&&e.length&&this.options.toast&&e.forEach((function(t){var e=o.options[t.key];c(e)&&!t.limit(e)&&l.warn(t.message)}))),c(e)&&(this.limits=e),c(i)&&i.call(this,this)},t.prototype.run=function(t){var e=this.element.$store;e&&e.animationId&&this.clearAnimationFrame(e.animationId),this._$animationFrame(t)},t.prototype.clearAnimationFrame=function(t){p(t)},t.prototype.clearRect=function(t,e,i,o){var n=this.canvas.width,s=this.canvas.height;if(c(t)&&c(e)&&c(i)&&c(o))this.ctx.clearRect(t,e,i,o);else if(c(t)&&c(e)&&c(i)&&!c(o)){var a=i-this.stepClear,r=Math.sqrt(i*i-a*a),h=t-a,l=e-r,p=2*a,d=2*r;this.stepClear<=i?(this.ctx.clearRect(h,l,p,d),this.stepClear+=1,this.clearRect(t,e,i)):this.stepClear=1}else this.ctx.clearRect(-n,-s,2*n,2*s)},t.prototype.drawRadiusRect=function(t,e,i,o,n){this.ctx.beginPath(),this.ctx.arc(t+n,e+n,n,1*Math.PI,1.5*Math.PI),this.ctx.lineTo(t+i-n,e),this.ctx.arc(t+i-n,e+n,n,1.5*Math.PI,0),this.ctx.lineTo(t+i,e+o-n),this.ctx.arc(t+i-n,e+o-n,n,0,.5*Math.PI),this.ctx.lineTo(t+n,e+o),this.ctx.arc(t+n,e+o-n,n,.5*Math.PI,Math.PI),this.ctx.lineTo(t,e+n),this.ctx.closePath()},t.prototype.drawText=function(t){var e=this.options,i=Object.assign({esGap:e.textGap||0,x:0,text:e.text||"",textColor:e.themeColor||"rgba(64,158,255,1)"},t);this.ctx.save(),this.ctx.beginPath(),this.ctx.fillStyle=i.textColor,this.ctx.fillText(i.text,i.x,(e.textGap||0)+(e.fontSize||0)+i.esGap),this.ctx.closePath(),this.ctx.restore()},t}(),m=function(t,e){return m=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(t,e){t.__proto__=e}||function(t,e){for(var i in e)Object.prototype.hasOwnProperty.call(e,i)&&(t[i]=e[i])},m(t,e)};function w(t,e){if("function"!=typeof e&&null!==e)throw new TypeError("Class extends value "+String(e)+" is not a constructor or null");function i(){this.constructor=t}m(t,e),t.prototype=null===e?Object.create(e):(i.prototype=e.prototype,new i)}var v={lineStart:10,lineEnd:16,lineStartSkew:0,lineEndSkew:0,lineWidth:4,lineCap:"round",lineNum:10,direction:!0},g=[{key:"lineNum",message:"lineNum value 4-18",limit:function(t){return t>=4&&t<=18}}],S=function(t){function e(e,i,o,n,s){var a=t.call(this,e,i,o,n,s,v,g,(function(t){var e=t.options;t.ctx.lineCap=e.lineCap,t.ctx.lineWidth=e.lineWidth,t.ctx.save()}))||this;return a.optimization(a.options.textGap+a.options.lineEnd),a.aps=Array.from({length:a.options.lineNum},(function(t,e){return e})),a.run(a.draw),a}return w(e,t),e.prototype.draw=function(){this.clearRect(),this.controller(),this.drawGear();var t=this.options;this.drawText({esGap:t.lineEnd})},e.prototype.controller=function(){var t=this;this.options.direction?this.aps=this.aps.map((function(e){return e-1<=0?t.aps.length-1:e-1})):this.aps=this.aps.map((function(e){return e+1>t.aps.length?0:e+1}))},e.prototype.drawGear=function(){var t=this.options;this.ctx.save(),this.ctx.shadowOffsetX=t.shadowOffsetX,this.ctx.shadowOffsetY=t.shadowOffsetY,this.ctx.shadowBlur=t.shadowBlur;for(var e=0;ethis.h&&(this.options.lineStart=this.h/6-5,this.options.lineEnd=this.h/6,this.options.textGap=2)},e}(f),C={zoomGap:10,maxSize:16,zoomNum:5,lineWidth:10,zoomHeight:2,lineCap:"round",action:exports.ZOOM_ACTION.SCALE,direction:!0,zoomColors:[]},T=[{key:"lineWidth",message:"lineWidth(default:10) <= maxSize(default:16)",limit:function(t){return t<=C.maxSize}},{key:"maxSize",message:"lineWidth(default:10) <= maxSize(default:16)",limit:function(t){return C.lineWidth<=t}}],y=function(t){function e(e,i,o,n,s){var a=t.call(this,e,i,o,n,s,C,T,(function(t){var e=t.options;t.ctx.lineCap=e.lineCap,t.ctx.lineWidth=e.lineWidth,t.ctx.translate(-(e.lineWidth*(e.zoomNum+1)+e.zoomGap*(e.zoomNum+1))/2,-e.zoomHeight/2),t.ctx.save()}))||this;return a.zoomIndex=a.options.direction?0:a.options.zoomNum-1,a.list=Array.from({length:a.options.zoomNum},(function(t,e){return Object.assign({value:a.options.lineWidth,state:0})})),a.run(a.draw),a}return w(e,t),e.prototype.draw=function(){this.clearRect(),this.drawZoom();var t=this.options;this.drawText({esGap:t.maxSize,x:(t.lineWidth*(t.zoomNum+1)+t.zoomGap*(t.zoomNum+1))/2}),this.controller()},e.prototype.controller=function(){var t=this.options;t.direction&&this.zoomIndex>=t.zoomNum?this.zoomIndex=0:t.direction&&this.zoomIndex<0&&(this.zoomIndex=t.zoomNum-1)},e.prototype.drawZoom=function(){for(var t=this.options,e=0;e=t.lineWidth&&this.list[e].value--,t.action===exports.ZOOM_ACTION.SCALE&&(this.ctx.lineWidth=this.list[e].value),e===this.zoomIndex&&(this.list[e].value>t.maxSize&&(this.list[e].state=2,t.direction?this.zoomIndex++:this.zoomIndex-1>=0?this.zoomIndex--:this.zoomIndex=t.zoomNum-1),this.list[e].value<=t.lineWidth&&(this.list[e].state=1)),this.ctx.beginPath(),t.zoomColors.length>0&&t.zoomColors[e]?this.ctx.strokeStyle=t.zoomColors[e]:this.ctx.strokeStyle=t.themeColor;var i=0,o=t.zoomHeight;t.action!==exports.ZOOM_ACTION.HEIGHT&&t.action!==exports.ZOOM_ACTION.WAVE||(i=-this.list[e].value),t.action===exports.ZOOM_ACTION.WAVE&&(o=-this.list[e].value),this.ctx.moveTo((e+1)*(t.lineWidth+t.zoomGap),i),this.ctx.lineTo((e+1)*(t.lineWidth+t.zoomGap),o),this.ctx.stroke(),this.ctx.closePath()}},e}(f),O={arcGap:Math.PI/4,ringGap:10,lineWidth:2,ringNum:2,radius:6,lineCap:"round",turn:10,ringsTurn:[Math.PI,Math.PI/4],direction:!0},E=[{key:"ringNum",message:"ringNum value 1-10",limit:function(t){return t>=1&&t<=10}},{key:"ringsTurn",message:"ringsTurn size ".concat(O.ringNum),limit:function(t){return t.length<=O.ringNum}}],I=function(t){function e(e,i,o,n,s){var a=t.call(this,e,i,o,n,s,O,E,(function(t){var e=t.options;t.ctx.lineCap=e.lineCap,t.ctx.lineWidth=e.lineWidth,t.ctx.save()}))||this;return a.rotate=10,a.run(a.draw),a}return w(e,t),e.prototype.draw=function(){this.clearRect(),this.controller();var t=this.options;this.drawText({esGap:t.ringNum*(t.radius+t.ringGap/2)})},e.prototype.controller=function(){this.ctx.save();var t=this.options,e=this.rotate*Math.PI/180*(t.direction?1:-1);this.ctx.rotate(e),this.ctx.shadowOffsetX=t.shadowOffsetX,this.ctx.shadowOffsetY=t.shadowOffsetY,this.ctx.shadowBlur=t.shadowBlur;for(var i=1;i<=t.ringNum;i++)this.drawRing(t.radius+(i-1)*t.ringGap,t.arcGap,t.ringsTurn&&t.ringsTurn.length>0?t.ringsTurn[i-1]:Math.PI/i);this.rotate+=t.turn,this.ctx.restore()},e.prototype.drawRing=function(t,e,i){void 0===e&&(e=1),void 0===i&&(i=0),this.ctx.beginPath(),this.ctx.arc(0,0,t,e+i,Math.PI+i),this.ctx.stroke(),this.ctx.closePath(),this.ctx.beginPath(),this.ctx.arc(0,0,t,Math.PI+e+i,i),this.ctx.stroke(),this.ctx.closePath()},e}(f),b={beanSize:15,pointLength:15},P=[{key:"pointLength",message:"pointLength value >= 5",limit:function(t){return t>=5}},{key:"beanSize",message:"beanSize value >= 5",limit:function(t){return t>=5}}],R=function(t){function e(e,i,o,n,s){var a=t.call(this,e,i,o,n,s,b,P)||this;return a.bean={turn:30,state:1,beanState:1,nowX:-a.options.pointLength*a.options.beanSize/2-3*a.options.beanSize,beans:Array.from({length:a.options.pointLength},(function(){return 1})),beanAnimaIndex:0},a.options.delay=10,a.run(a.draw),a}return w(e,t),e.prototype.draw=function(){var t=this.options;this.clearRect(),this.ctx.save(),this.ctx.beginPath(),this.ctx.translate(this.bean.nowX,0),this.ctx.arc(0,0,t.beanSize,(360-this.bean.turn)*Math.PI/180,this.bean.turn*Math.PI/180,!0),this.ctx.lineTo(0,0),this.ctx.fill(),this.ctx.closePath(),this.ctx.restore(),this.drawPoint(),this.drawFilter(),this.drawText({esGap:t.beanSize}),this.controller()},e.prototype.controller=function(){var t=this.options;this.bean.nowX>=t.pointLength*t.beanSize/2+2*t.beanSize&&(this.bean.nowX=-t.pointLength*t.beanSize/2-3*t.beanSize,this.bean.beanAnimaIndex=0),this.bean.nowX<=-t.pointLength*t.beanSize/2&&(this.bean.beanState=2),this.bean.turn<=0&&(this.bean.state=2),this.bean.turn>=30&&(this.bean.state=1),1===this.bean.state&&(this.bean.turn-=1),2===this.bean.state&&(this.bean.turn+=1),1===this.bean.beanState&&(this.bean.nowX-=1),2===this.bean.beanState&&(this.bean.nowX+=1)},e.prototype.drawPoint=function(){var t=this.options;this.ctx.save(),this.setShadow(),this.ctx.translate(-t.pointLength*t.beanSize/2,0);for(var e=0;e=5&&t<=24}},{key:"delay",message:"Pattern.delay not allowed update",limit:function(t){return t===n().delay}}],N=function(t){function e(e,i,o,n,s){var a=t.call(this,e,i,o,n,s,k,_,(function(t){t.options.delay=10}))||this;return a.pattern={color:a.randomState("chartColors"),nowHeight:10,chart:a.randomState("charts"),shadow:0,nowState:1,turn:0},a.run(a.draw),a}return w(e,t),e.prototype.draw=function(){var t=this.options;this.clearRect(),this.ctx.save(),this.ctx.beginPath(),this.ctx.translate(0,this.pattern.nowHeight),this.ctx.rotate(this.pattern.turn/Math.PI*2),this.ctx.fillStyle=this.pattern.color,this.selectChart(0,0,t.chartSize),this.ctx.closePath(),this.ctx.restore(),this.drawShadow(),this.clearRect(-this.w,0,2*this.w,this.h),this.controller(t),this.drawText({textColor:this.pattern.color})},e.prototype.controller=function(t){this.pattern.turn+=10,1===this.pattern.nowState?(this.pattern.nowHeight--,this.pattern.shadow+=.2):2===this.pattern.nowState&&(this.pattern.nowHeight++,this.pattern.shadow-=.2),this.pattern.shadow=Math.floor(100*this.pattern.shadow)/100,this.pattern.nowHeight<=-t.chartSize&&this.pattern.nowHeight%8==0&&(t.delay+=.5,t.delay=Math.floor(100*t.delay)/100),this.pattern.nowHeight<=-t.maxHeight?this.pattern.nowState=2:this.pattern.nowHeight>=t.chartSize&&(this.pattern.nowState=1,t.delay=10,this.pattern.chart=this.randomState("charts"),this.pattern.color=this.randomState("chartColors"))},e.prototype.selectChart=function(t,e,i){switch(this.pattern.chart){case exports.PATTERN_CHART.RECT:this.drawRect(t,e,i);break;case exports.PATTERN_CHART.ARC:this.drawArc(t,e,i);break;case exports.PATTERN_CHART.TRIANGLE:this.drawTriangle(t,e,i);break;case exports.PATTERN_CHART.HEART:this.drawHeart(t,e,i);break;case exports.PATTERN_CHART.POLYGON:this.drawPolygon(t,e,i)}},e.prototype.randomState=function(t){var e=this.options;return e[t][parseInt(String(Math.random()*e[t].length))]},e.prototype.drawShadow=function(){this.ctx.save(),this.ctx.beginPath(),this.setShadow(),this.ctx.globalAlpha=.2,this.ctx.strokeStyle=this.pattern.color,this.ctx.moveTo(-this.pattern.shadow/2,0),this.ctx.lineTo(this.pattern.shadow,0),this.ctx.stroke(),this.ctx.beginPath(),this.ctx.restore()},e.prototype.drawRect=function(t,e,i){this.ctx.save(),this.ctx.beginPath(),this.setShadow(),this.ctx.translate(-i/2,-i/2),this.ctx.fillRect(t,e,i,i),this.ctx.closePath(),this.ctx.restore()},e.prototype.drawArc=function(t,e,i){this.ctx.save(),this.ctx.beginPath(),this.setShadow(),this.ctx.arc(t,e,i/2,0,2*Math.PI),this.ctx.fill(),this.ctx.closePath(),this.ctx.restore()},e.prototype.drawTriangle=function(t,e,i){this.ctx.save(),this.ctx.beginPath(),this.setShadow(),this.ctx.translate(-i/2,-i/2*Math.sqrt(3)/2),this.ctx.moveTo(t,e),this.ctx.lineTo(i,0),this.ctx.lineTo(i/2,i/2*Math.sqrt(3)),this.ctx.lineTo(t,e),this.ctx.fill(),this.ctx.closePath(),this.ctx.restore()},e.prototype.drawHeart=function(t,e,i){i/=2,this.ctx.save(),this.ctx.beginPath(),this.setShadow(),this.ctx.translate(0,-2*i/2),this.ctx.moveTo(t,e),this.ctx.bezierCurveTo(i/2,-i,3*i,-i/2,e,2*i),this.ctx.moveTo(t,e),this.ctx.bezierCurveTo(-i/2,-i,3*-i,-i/2,e,2*i),this.ctx.fill(),this.ctx.closePath(),this.ctx.restore()},e.prototype.drawPolygon=function(t,e,i){this.ctx.save(),this.ctx.beginPath(),this.setShadow(),this.ctx.translate(-i/2,-i/2),this.ctx.moveTo(t,e),this.ctx.lineTo(i,e),this.ctx.lineTo(i+i/2,i/2),this.ctx.lineTo(i,i/2+i/2),this.ctx.lineTo(t,i),this.ctx.lineTo(t-i/2,i-i/2),this.ctx.lineTo(t,e),this.ctx.fill(),this.ctx.closePath(),this.ctx.restore()},e.prototype.setShadow=function(){var t=this.options;this.ctx.shadowColor=this.pattern.color,this.ctx.shadowOffsetX=t.shadowOffsetX,this.ctx.shadowOffsetY=t.shadowOffsetY,this.ctx.shadowBlur=t.shadowBlur},e}(f),G={rollGap:12,childNum:4,rollSize:16,showChild:!0,chart:exports.ROLL_CHART.WHEEL,windmills:["#1ab3ea","#de6834","#30925d","#f48ea5"],windmillPointColor:"#f2c31f",fixed:!1},M=[{key:"childNum",message:"chartSize value 4-10",limit:function(t){return t>=4&&t<=10}},{key:"delay",message:"Roll.delay not allowed update",limit:function(t){return t===n().delay}}],H=function(t){function e(e,i,o,n,s){var a=t.call(this,e,i,o,n,s,G,M)||this;return a.Roll={turn:1,nowX:a.options.fixed?0:a.options.childNum/2*(a.options.rollSize+a.options.rollGap)+a.options.rollGap/2,state:2,child:[]},a.run(a.draw),a}return w(e,t),e.prototype.draw=function(){this.clearRect(),this.drawGround(),this.drawChild(),this.ctx.save(),this.ctx.beginPath(),this.ctx.translate(-this.Roll.nowX,0),this.ctx.rotate(this.Roll.turn*Math.PI/180),this.selectChart(),this.controller(),this.ctx.restore();var t=this.options;this.drawText({esGap:t.rollSize})},e.prototype.selectChart=function(){switch(this.options.chart){case exports.ROLL_CHART.RECT:this.drawRect();break;case exports.ROLL_CHART.WHEEL:this.drawWheel();break;case exports.ROLL_CHART.WINDMILL:this.drawWindmill()}},e.prototype.controller=function(){var t=this.options;if(1===this.Roll.state&&(this.Roll.turn-=10,t.delay<20&&!t.fixed&&(t.delay+=2)),2===this.Roll.state&&(this.Roll.turn+=10,t.delay>10&&!t.fixed&&(t.delay-=5)),!t.fixed){this.Roll.nowX<=-t.childNum/2*(t.rollSize+t.rollGap/1.6)&&(this.Roll.state=1),this.Roll.nowX>=t.childNum/2*(t.rollSize+t.rollGap)+t.rollGap/2&&(this.Roll.state=2),1===this.Roll.state&&this.Roll.nowX++,2===this.Roll.state&&this.Roll.nowX--;var e=this.Roll.child;this.Roll.nowX%(t.rollSize+t.rollGap)==0&&2===this.Roll.state&&e.push({turn:this.Roll.turn,x:this.Roll.nowX}),1===this.Roll.state&&e[e.length-1]&&e[e.length-1].x===this.Roll.nowX&&this.Roll.child.pop()}},e.prototype.drawRect=function(){var t=this.options;this.ctx.save(),this.setShadow(),this.ctx.translate(-t.rollSize/2,-t.rollSize/2),this.ctx.fillRect(0,0,t.rollSize,t.rollSize),this.ctx.restore()},e.prototype.drawWheel=function(){var t=this.options;this.ctx.save(),this.ctx.lineWidth=4,this.ctx.beginPath(),this.ctx.arc(0,0,t.rollSize/6,0,2*Math.PI),this.ctx.stroke(),this.ctx.closePath(),this.ctx.beginPath(),this.ctx.arc(0,0,t.rollSize/2,0,2*Math.PI),this.ctx.stroke(),this.ctx.closePath(),this.ctx.beginPath(),this.ctx.arc(0,0,t.rollSize,0,2*Math.PI),this.ctx.stroke(),this.ctx.closePath();for(var e=0;e<6;e++)this.ctx.beginPath(),this.ctx.moveTo(0,t.rollSize/2),this.ctx.lineTo(0,t.rollSize),this.ctx.stroke(),this.ctx.rotate(60*Math.PI/180),this.ctx.closePath();this.ctx.restore()},e.prototype.drawWindmill=function(){var t=this.options;this.ctx.save();for(var e=0;e0?e.some((function(e){return t.classList.contains(e)}))&&this.skeleton.push({title:t.nodeName,element:t}):this.skeleton.push({title:t.nodeName,element:t})},e.prototype.controller=function(t){for(var e=this.options,i=0,o=Array.from(t);i=.9&&(this.state=2),this.colorFlow<=.1&&(this.state=1),1===this.state&&(this.colorFlow+=.06),2===this.state&&(this.colorFlow-=.06))},e}(f)};function X(t,e,i,o,n){try{if(!n.$store)return;var s=n.$store.model;if(s)s.initContextCall(s.modelDefOptions,s.limits,s.modelDefCall);else{s=o.custom?new o.custom(t,e,i,o,n):new F[o.model](t,e,i,o,n)}}catch(t){l.error("draw error("+t+")")}}var B=window;function K(){var t;return(t={})[exports.HOOKS_CALL_KEY.BEFORE_CLOSE]=[],t[exports.HOOKS_CALL_KEY.CLOSED]=[],t}function $(t){var e;return(e={})[exports.HOOKS_CALL_KEY.BEFORE_CLOSE]=function(e){t[exports.HOOKS_CALL_KEY.BEFORE_CLOSE].push(e)},e[exports.HOOKS_CALL_KEY.CLOSED]=function(e){t[exports.HOOKS_CALL_KEY.CLOSED].push(e)},e}var j=window;function U(t,e,i,o){var n=e.pointerEvents?t.scrollWidth:t.clientWidth,s=e.pointerEvents?t.scrollHeight:t.clientHeight,a=j.getComputedStyle(t),r=t.style,h=o.style;return e.type!==exports.LOADING_TYPES.DOM||e.pointerEvents||(t.style.pointerEvents="none"),a.position&&"static"!==a.position||(r.position="relative"),o.id=i,h.opacity="0",h.position="absolute",h.left="".concat(e.pointerEvents?0:t.scrollLeft,"px"),h.top="".concat(e.pointerEvents?0:t.scrollTop,"px"),h.zIndex=e.zIndex,h.transition="".concat(e.delayInto/1e3,"s ease-in-out"),h.backgroundColor=e.bgColor,h.borderRadius=a.borderRadius,"htmlcanvaselement"===d(o)?Z(o,n,s):"htmldivelement"===d(o)&&(h.width="".concat(n,"px"),h.height="".concat(s,"px"),h.display="flex",h.alignItems="center",h.justifyContent="center"),t.append(o),j.setTimeout((function(){return h.opacity="1"}),0),x(t,(function(){t.$store&&(t.$store.loadingId=i)})),t}function Z(t,e,i){var o=j.devicePixelRatio||1;t.width=e*o,t.height=i*o,t.style.width="".concat(e,"px"),t.style.height="".concat(i,"px")}var q=window,V=function(){function t(t){this.canvas=null,this.htmlElement=null,this.loadingId=null,this.element=null,this.hooks=null,this.resizeTimeId=null,this.options=Object.assign(n(),t)}return t.prototype.resize=function(t,e){var i=this;this.resizeTimeId||(this.resizeTimeId=q.setTimeout((function(){var o=e,n=t.clientWidth,s=t.clientHeight;o.width>t.clientWidth&&(n=t.offsetWidth,s=t.offsetHeight),i.canvas?(Z(o,n,s),t.$store&&X(n,s,o,i.options,t)):i.htmlElement&&(i.htmlElement.style.width="".concat(n,"px"),i.htmlElement.style.height="".concat(s,"px")),i.resizeTimeId=null}),this.options.delayInto))},t.prototype.close=function(t,e){var i=this,o=this.options,n=t.$store;q.setTimeout((function(){!function(t,e,i){i.style.opacity="0",e.type!==exports.LOADING_TYPES.DOM&&(t.style.boxShadow="none")}(t,o,e),o.type!==exports.LOADING_TYPES.DOM||o.pointerEvents||(t.style.pointerEvents="auto"),x(t,(function(){n&&(n.model=null,i.callEvent(exports.HOOKS_CALL_KEY.BEFORE_CLOSE),n.animationId&&p(n.animationId)),o.type!==exports.LOADING_TYPES.DOM?t.remove():e.remove(),i.loadingId=null,i.callEvent(exports.HOOKS_CALL_KEY.CLOSED),i.hooks=K()}))}),n&&!n.loadingId?o.delayInto:0)},t.prototype.draw=function(t){var e=this.options;if(e.html){var i={content:B.document.createElement("div"),loadingId:u()},o=i.content,n=i.loadingId;this.htmlElement=o,this.htmlElement.innerHTML=e.html,this.loadingId=n,this.element=U(t,e,n,o)}else{var s={canvas:B.document.createElement("canvas"),hooks:K(),loadingId:u()},a=s.canvas,r=s.hooks;n=s.loadingId;this.canvas=a,this.hooks=r,this.loadingId=n,function(t,e,i){t.$store={options:e,animationId:void 0,loadingId:null,model:null,hookCall:$(i)}}(t,e,r),this.element=U(t,e,n,a),t.$store?X(a.offsetWidth,a.offsetHeight,a,this.options,t):l.error("WebLoading:canvas or ctx null")}},t.prototype.callEvent=function(t){this.hooks&&this.hooks[t].forEach((function(t){t()}))},t}(),J=window,Q=function(){function t(t){this.options=t,this.extendEl=this.initStyle()}return t.prototype.initStyle=function(){this.extendEl=J.document.createElement("div");var t=this.options,e="100vw",i="100vh",o="0px";return t&&(this.extendEl.classList.add("wl_"+(t.extendClass||"loading")),t.type===exports.LOADING_TYPES.MINI&&(e="180px",i="160px",o="10px"),this.extendEl.style.cssText="\n position:fixed;\n width:".concat(e,";\n height:").concat(i,";\n top:50%;\n left:50%;\n transform:translate(-50%, -50%);\n border-radius: ").concat(o,";\n z-index: ").concat(t.zIndex,";\n box-shadow:\n 2.8px 2.8px 2.2px rgba(0, 0, 0, 0.02),\n 6.7px 6.7px 5.3px rgba(0, 0, 0, 0.028),\n 12.5px 12.5px 10px rgba(0, 0, 0, 0.035),\n 22.3px 22.3px 17.9px rgba(0, 0, 0, 0.042),\n 41.8px 41.8px 33.4px rgba(0, 0, 0, 0.05),\n 100px 100px 80px rgba(0, 0, 0, 0.07)\n ;\n ")),J.document.body.appendChild(this.extendEl),this.extendEl},t.prototype.getElement=function(){return this.extendEl},t}(),tt=window;function et(t){var e=new V(t),i=null;function o(t){if(e.element){var i=e.canvas;e.htmlElement&&(i=e.htmlElement),i?e[t](e.element,i):l.warn("Animation element not found!")}}return{loading:function(t,o){var n=Object.assign(e.options,o);if(!e.loadingId&&!i)if(n.type!==exports.LOADING_TYPES.DOM&&(t=new Q(n).getElement()),t){var s=new Promise((function(t){tt.setTimeout((function(){t(!0)}),n.notFeel)})),a=new Promise((function(t){i=t}));Promise.race([s,a]).then((function(o){o?e.draw(t):n.type!==exports.LOADING_TYPES.DOM&&t.remove(),i=null}))}else l.error("The loading function cannot find an HTMLElement element!")},resize:function(){o("resize")},close:function(){i&&i(!1),o("close")},update:function(t){var i=e.canvas,o=Object.assign(e.options,t),n=e.element;i&&o&&n&&n.$store&&X(i.offsetWidth,i.offsetHeight,i,o,n)},getOptions:function(){return e.options},getLoadingId:function(){return e.loadingId}}}function it(t,e){return et(Object.assign(n(),e||{},{type:t}))}var ot=window;ot.BaseModel=f,ot.initLoading=function(t){return et(t)},ot.miniLoading=function(t){return it(exports.LOADING_TYPES.MINI,t)},ot.fullLoading=function(t){return it(exports.LOADING_TYPES.FULL,t)},exports.BaseModel=f,exports.fullLoading=function(t){return it(exports.LOADING_TYPES.FULL,t)},exports.initLoading=et,exports.miniLoading=function(t){return it(exports.LOADING_TYPES.MINI,t)};
2 |
--------------------------------------------------------------------------------
/dist/web-loading.d.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * @packageDocumentation
3 | * @description 包文档说明
4 | */
5 |
6 | /**
7 | * @description 日志输出类
8 | * @public
9 | */
10 | declare class $Log {
11 | /**
12 | * @description 输出信息日志
13 | * @param {string} message - 日志内容
14 | */
15 | static info(message: string): void;
16 | /**
17 | * @description 输出警告日志
18 | * @param {string} message - 日志内容
19 | */
20 | static warn(message: string): void;
21 | /**
22 | * @description 输出错误日志
23 | * @param {string} message - 日志内容
24 | */
25 | static error(message: string): void;
26 | /**
27 | * @description 调用日志输出
28 | * @param {string} message - 日志内容
29 | * @param {LOG_TYPES} type - 日志类型
30 | * @param {LogConfigType} config - 日志配置
31 | */
32 | static call(message: string, type?: LOG_TYPES, config?: LogConfigType): void;
33 | }
34 |
35 | /**
36 | * @description 基础模型类
37 | * @public
38 | */
39 | export declare class BaseModel {
40 | w: number;
41 | h: number;
42 | canvas: HTMLCanvasElement;
43 | ctx: CanvasRenderingContext2D;
44 | options: Required;
45 | element: ElementType;
46 | modelDefOptions: T | undefined;
47 | limits: Array | undefined;
48 | modelDefCall: ((model: BaseModel) => void) | undefined;
49 | webLog: $Log;
50 | private stepClear;
51 | /**
52 | * @description 自定义基础模型
53 | * @param {number} w - Canvas宽度
54 | * @param {number} h - Canvas高度
55 | * @param {HTMLCanvasElement} canvas - Canvas元素
56 | * @param {Required} options - 配置选项
57 | * @param {ElementType} element - 容器元素
58 | * @param {T} [modelDefOptions] - 模型默认选项(可选)
59 | * @param {Array} [limits] - 模型默认限制(可选)
60 | * @param {(model: BaseModel) => void} [modelDefCall] - 提供模型初始化的回调函数,通常在模型中初始化"canvas"或"画笔"(可选)
61 | */
62 | constructor(w: number, h: number, canvas: HTMLCanvasElement, options: Required, element: ElementType, modelDefOptions?: T, limits?: Array, modelDefCall?: (model: BaseModel) => void);
63 | private _$initBaseContext;
64 | private _$initEvent;
65 | /**
66 | * @description 封装requestAnimationFrame触发动画针
67 | * @param {() => void} fun - 触发函数
68 | * @private
69 | */
70 | private _$animationFrame;
71 | /**
72 | * @description 初始化画笔属性
73 | * @param {T} [modelDefOptions] - 提供模型初始化的选项
74 | * @param {Array} [limits] - 提供模型初始化的限制
75 | * @param {(model: BaseModel) => void} [modelDefCall] - 提供模型初始化的回调函数
76 | */
77 | initContextCall(modelDefOptions?: T, limits?: Array, modelDefCall?: (model: BaseModel) => void): void;
78 | /**
79 | * @description 开始动画
80 | * @param {() => void} fun - 动画函数
81 | */
82 | run(fun: () => void): void;
83 | /**
84 | * @description 取消animationFrame动画针
85 | * @param {number} id - 动画ID
86 | */
87 | clearAnimationFrame(id: number): void;
88 | /**
89 | * @description 清空画布
90 | * @param {number} [x] - x坐标
91 | * @param {number} [y] - y坐标
92 | * @param {number} [w_r] - 宽度或半径
93 | * @param {number} [h] - 高度
94 | */
95 | clearRect(x?: number, y?: number, w_r?: number, h?: number): void;
96 | /**
97 | * @description 绘制圆角矩形
98 | * @param {number} x - x坐标
99 | * @param {number} y - y坐标
100 | * @param {number} w - 宽度
101 | * @param {number} h - 高度
102 | * @param {number} r - 圆角半径
103 | */
104 | drawRadiusRect(x: number, y: number, w: number, h: number, r: number): void;
105 | /**
106 | * @description 绘制文本
107 | * @param {DrawTextParamsType} [params] - 文本参数
108 | * DrawTextParamsType:
109 | * esGap?: 额外空隙
110 | * x?: X轴位置
111 | * text?: 文本内容
112 | * textColor?: 文本颜色
113 | */
114 | drawText(params?: DrawTextParamsType): void;
115 | }
116 |
117 | /**
118 | * @description 豆形模型配置类型
119 | * @public
120 | */
121 | export declare interface BeanOptionsType extends OptionsType {
122 | beanSize?: number;
123 | pointLength?: number;
124 | }
125 |
126 | /**
127 | * @description 圆形动作枚举
128 | * @public
129 | */
130 | export declare enum CIRCULAR_ACTION {
131 | COLLISION = "collision",
132 | ROTATE = "rotate"
133 | }
134 |
135 | /**
136 | * @description 圆形模型配置类型
137 | * @public
138 | */
139 | export declare interface CircularOptionsType extends OptionsType {
140 | arcSize?: number;
141 | arcGap?: number;
142 | arcColors?: Array;
143 | action?: CIRCULAR_ACTION;
144 | }
145 |
146 | /**
147 | * @description 时钟模型配置类型
148 | * @public
149 | */
150 | export declare interface ClockOptionsType extends OptionsType {
151 | textTime?: 'time' | 's' | '';
152 | lineColors?: Array;
153 | lineCap?: CanvasLineCap;
154 | lineWidth?: number;
155 | clockSize?: number;
156 | clockGap?: number;
157 | hLine?: boolean;
158 | mLine?: boolean;
159 | sLine?: boolean;
160 | }
161 |
162 | /**
163 | * @description 绘制文本参数类型
164 | * @public
165 | */
166 | export declare interface DrawTextParamsType {
167 | esGap?: number;
168 | x?: number;
169 | text?: string;
170 | textColor?: string;
171 | }
172 |
173 | /**
174 | * @description 元素存储类型接口
175 | * @public
176 | */
177 | export declare interface ElementStoreType {
178 | options: OptionsType;
179 | animationId: number | undefined;
180 | loadingId: string | null;
181 | hookCall: HooksCallType;
182 | model: BaseModel | null;
183 | }
184 |
185 | /**
186 | * @description 元素类型接口
187 | * @public
188 | */
189 | export declare interface ElementType extends HTMLElement {
190 | loadingId?: string | null;
191 | $store?: ElementStoreType;
192 | }
193 |
194 | /**
195 | * @description 全屏加载
196 | * @param {OptionsType} options - 配置选项
197 | * @returns {LoadingType} 返回加载操作对象
198 | * @public
199 | */
200 | export declare function fullLoading(options?: OptionsType): LoadingType;
201 |
202 | /**
203 | * @description 齿轮模型配置类型
204 | * @public
205 | */
206 | export declare interface GearOptionsType extends OptionsType {
207 | lineStart?: number;
208 | lineEnd?: number;
209 | lineStartSkew?: number;
210 | lineEndSkew?: number;
211 | lineWidth?: number;
212 | lineCap?: CanvasLineCap;
213 | lineNum?: number;
214 | direction?: boolean;
215 | }
216 |
217 | /**
218 | * @description 钩子调用键枚举
219 | * @public
220 | */
221 | export declare enum HOOKS_CALL_KEY {
222 | BEFORE_CLOSE = "beforeClose",
223 | CLOSED = "closed"
224 | }
225 |
226 | /**
227 | * @description 钩子调用类型
228 | * 映射键是枚举
229 | */
230 | declare type HooksCallType = {
231 | [key in T]: (...args: any[]) => void;
232 | };
233 |
234 | /**
235 | * @description 图片模型配置类型
236 | * @public
237 | */
238 | export declare interface ImageOptionsType extends OptionsType {
239 | src?: string;
240 | width?: number;
241 | height?: number;
242 | turn?: boolean;
243 | }
244 |
245 | /**
246 | * @description 初始化加载动画
247 | * @param {OptionsType} options - 配置选项
248 | * @returns {LoadingType} 返回加载操作对象
249 | * @public
250 | */
251 | export declare function initLoading(options?: OptionsType): LoadingType;
252 |
253 | /**
254 | * @description 限制类型接口
255 | * @public
256 | */
257 | export declare interface LimitType {
258 | key: string;
259 | message: string;
260 | limit: (key: any) => boolean;
261 | }
262 |
263 | /**
264 | * @description 支持的加载方法
265 | * @public
266 | */
267 | export declare enum LOADING_TYPES {
268 | DOM = "dom",
269 | FULL = "full",
270 | MINI = "mini"
271 | }
272 |
273 | /**
274 | * @description 加载类型接口
275 | * @public
276 | */
277 | export declare interface LoadingType {
278 | loading: (dom: ElementType, options?: OptionsType) => void;
279 | resize: () => void;
280 | close: () => void;
281 | update: (options?: OptionsType) => void;
282 | getOptions: () => OptionsType;
283 | getLoadingId: () => string | null;
284 | }
285 |
286 | /**
287 | * @description 日志类型枚举
288 | * @public
289 | */
290 | export declare enum LOG_TYPES {
291 | INFO = 1,
292 | WARN = 2,
293 | ERROR = 3
294 | }
295 |
296 | /**
297 | * @description 日志配置类型
298 | * @public
299 | */
300 | export declare type LogConfigType = {
301 | color?: string;
302 | bgColor?: string;
303 | };
304 |
305 | /**
306 | * @description 迷你加载
307 | * @param {OptionsType} options - 配置选项
308 | * @returns {LoadingType} 返回加载操作对象
309 | * @public
310 | */
311 | export declare function miniLoading(options?: OptionsType): LoadingType;
312 |
313 | /**
314 | * @description 支持的模型类型
315 | * @public
316 | */
317 | export declare enum MODEL_TYPES {
318 | GEAR = "Gear",
319 | RING = "Ring",
320 | ZOOM = "Zoom",
321 | PATTERN = "Pattern",
322 | CLOCK = "Clock",
323 | BEAN = "Bean",
324 | ROLL = "Roll",
325 | CIRCULAR = "Circular",
326 | IMG = "Img",
327 | SKELETON = "Skeleton"
328 | }
329 |
330 | /**
331 | * @description 配置选项接口
332 | * @public
333 | */
334 | export declare interface OptionsType {
335 | custom?: typeof BaseModel | null;
336 | type?: LOADING_TYPES;
337 | extendClass?: string | null | undefined;
338 | model?: MODEL_TYPES;
339 | html?: string;
340 | text?: string;
341 | textGap?: number;
342 | fontSize?: number;
343 | fontFamily?: string;
344 | delay?: number;
345 | delayInto?: number;
346 | notFeel?: number;
347 | optimization?: boolean;
348 | zIndex?: string;
349 | themeColor?: string;
350 | bgColor?: string;
351 | shadowColor?: string;
352 | shadowOffsetX?: number;
353 | shadowOffsetY?: number;
354 | shadowBlur?: number;
355 | pointerEvents?: boolean;
356 | toast?: boolean;
357 | }
358 |
359 | /**
360 | * @description 图案类型枚举
361 | * @public
362 | */
363 | export declare enum PATTERN_CHART {
364 | RECT = "rect",
365 | ARC = "arc",
366 | TRIANGLE = "triangle",
367 | HEART = "heart",
368 | POLYGON = "polygon"
369 | }
370 |
371 | /**
372 | * @description 图案模型配置类型
373 | * @public
374 | */
375 | export declare interface PatternOptionsType extends OptionsType {
376 | charts?: Array;
377 | chartSize?: number;
378 | chartColors?: Array;
379 | maxHeight?: number;
380 | }
381 |
382 | /**
383 | * @description 环形模型配置类型
384 | * @public
385 | */
386 | export declare interface RingOptionsType extends OptionsType {
387 | ringGap?: number;
388 | arcGap?: number;
389 | lineWidth?: number;
390 | ringNum?: number;
391 | radius?: number;
392 | lineCap?: CanvasLineCap;
393 | turn?: number;
394 | ringsTurn?: Array;
395 | direction?: boolean;
396 | }
397 |
398 | /**
399 | * @description 滚动图表类型枚举
400 | * @public
401 | */
402 | export declare enum ROLL_CHART {
403 | RECT = "rect",
404 | WHEEL = "wheel",
405 | WINDMILL = "windmill"
406 | }
407 |
408 | /**
409 | * @description 滚动模型配置类型
410 | * @public
411 | */
412 | export declare interface RollOptionsType extends OptionsType {
413 | rollGap?: number;
414 | rollSize?: number;
415 | showChild?: boolean;
416 | childNum?: number;
417 | chart?: ROLL_CHART;
418 | windmills?: Array;
419 | windmillPointColor?: string;
420 | fixed?: boolean;
421 | }
422 |
423 | /**
424 | * @description 骨架屏模型配置类型
425 | * @public
426 | */
427 | export declare interface SkeletonOptionsType extends OptionsType {
428 | skeletonColor?: string;
429 | skeletonAnimationColor?: string;
430 | radius?: number;
431 | animation?: boolean;
432 | deep?: boolean;
433 | appointElementClass?: Array;
434 | }
435 |
436 | /**
437 | * @description 缩放动作枚举
438 | * @public
439 | */
440 | export declare enum ZOOM_ACTION {
441 | SCALE = "scale",
442 | WAVE = "wave",
443 | HEIGHT = "height"
444 | }
445 |
446 | /**
447 | * @description 缩放模型配置类型
448 | * @public
449 | */
450 | export declare interface ZoomOptionsType extends OptionsType {
451 | maxSize?: number;
452 | zoomGap?: number;
453 | zoomHeight?: number;
454 | zoomNum?: number;
455 | zoomColors?: Array;
456 | lineCap?: CanvasLineCap;
457 | lineWidth?: number;
458 | action: ZOOM_ACTION;
459 | direction?: boolean;
460 | }
461 |
462 | export { }
463 |
--------------------------------------------------------------------------------
/dist/web-loading.min.js:
--------------------------------------------------------------------------------
1 | !function(t,i){"object"==typeof exports&&"undefined"!=typeof module?i(exports):"function"==typeof define&&define.amd?define(["exports"],i):i((t="undefined"!=typeof globalThis?globalThis:t||self).rustOption={})}(this,(function(t){"use strict";var i,e,o,n;function s(){return{custom:null,html:"",type:t.LOADING_TYPES.DOM,extendClass:"extend",model:t.MODEL_TYPES.GEAR,text:"加载中...",textGap:8,fontSize:12,fontFamily:"Microsoft YaHei",delay:65,notFeel:0,delayInto:320,optimization:!1,zIndex:"2001",themeColor:"rgba(64,158,255,1)",bgColor:"rgba(0, 0, 0, 0.8)",shadowColor:"rgba(64,158,255,0.6)",shadowOffsetX:2,shadowOffsetY:2,shadowBlur:5,pointerEvents:!1,toast:!0}}t.LOADING_TYPES=void 0,(i=t.LOADING_TYPES||(t.LOADING_TYPES={})).DOM="dom",i.FULL="full",i.MINI="mini",t.MODEL_TYPES=void 0,(e=t.MODEL_TYPES||(t.MODEL_TYPES={})).GEAR="Gear",e.RING="Ring",e.ZOOM="Zoom",e.PATTERN="Pattern",e.CLOCK="Clock",e.BEAN="Bean",e.ROLL="Roll",e.CIRCULAR="Circular",e.IMG="Img",e.SKELETON="Skeleton",t.HOOKS_CALL_KEY=void 0,(o=t.HOOKS_CALL_KEY||(t.HOOKS_CALL_KEY={})).BEFORE_CLOSE="beforeClose",o.CLOSED="closed",t.LOG_TYPES=void 0,(n=t.LOG_TYPES||(t.LOG_TYPES={}))[n.INFO=1]="INFO",n[n.WARN=2]="WARN",n[n.ERROR=3]="ERROR";var a,r,h,l,c=function(){function i(){}return i.info=function(i){this.call(i,t.LOG_TYPES.INFO)},i.warn=function(i){this.call(i,t.LOG_TYPES.WARN)},i.error=function(i){this.call(i,t.LOG_TYPES.ERROR)},i.call=function(i,e,o){void 0===e&&(e=t.LOG_TYPES.INFO),void 0===o&&(o={color:s().themeColor,bgColor:s().bgColor});var n=o.bgColor;2===e&&(n="#fffbe5"),3===e&&(n="#fff0f0");var a="\n background:".concat(n,";\n font-size:14px;\n color:").concat(o.color,";\n padding: 4px;\n border: 1px solid;");console.log("%c web-loading:".concat(i," "),a)},i}();function d(t){switch(u(t)){case"object":return Object.keys(t).length>0;case"array":return t.length>0;case"undefined":default:return void 0!==t;case"null":return null!==t}}function p(t){window.requestAnimationFrame?window.cancelAnimationFrame(t):window.clearInterval(t)}function u(t){try{return Object.prototype.toString.call(t).split(" ")[1].split("]")[0].toLowerCase()}catch(t){return"not-type"}}function x(t,i){var e=null,o={transition:"transitionend",OTransition:"oTransitionEnd",MozTransition:"transitionend",WebkitTransition:"webkitTransitionEnd"};for(var n in o)if(void 0!==t.style[n]){e=o[n];break}if(e){var s=function(){i(),t.removeEventListener(e,s)};t.addEventListener(e,s)}else i()}function f(){var t=String(Date.now());return window.crypto&&window.crypto.randomUUID&&(t=window.crypto.randomUUID()),"wl_"+t.replace(/-/g,"")}t.ZOOM_ACTION=void 0,(a=t.ZOOM_ACTION||(t.ZOOM_ACTION={})).SCALE="scale",a.WAVE="wave",a.HEIGHT="height",t.PATTERN_CHART=void 0,(r=t.PATTERN_CHART||(t.PATTERN_CHART={})).RECT="rect",r.ARC="arc",r.TRIANGLE="triangle",r.HEART="heart",r.POLYGON="polygon",t.ROLL_CHART=void 0,(h=t.ROLL_CHART||(t.ROLL_CHART={})).RECT="rect",h.WHEEL="wheel",h.WINDMILL="windmill",t.CIRCULAR_ACTION=void 0,(l=t.CIRCULAR_ACTION||(t.CIRCULAR_ACTION={})).COLLISION="collision",l.ROTATE="rotate";var m=function(){function t(t,i,e,o,n,s,a,r){this.modelDefOptions=void 0,this.limits=void 0,this.modelDefCall=void 0,this.webLog=c,this.stepClear=1,this.w=t,this.h=i,this.canvas=e,this.ctx=e.getContext("2d"),this.options=o,this.element=n,this.initContextCall(s,a,r)}return t.prototype._$initBaseContext=function(){this.clearRect(),this.ctx.resetTransform();var t=this.options,i=this.canvas.width,e=this.canvas.height;this.ctx.fillStyle=t.themeColor,this.ctx.strokeStyle=t.themeColor,this.ctx.shadowColor=t.shadowColor,this.ctx.font="".concat(t.fontSize,"px ").concat(t.fontFamily," small-caps"),this.ctx.textAlign="center",this.ctx.textBaseline="middle",this.ctx.translate(i/2,e/2);var o=window.devicePixelRatio||1;this.ctx.scale(o,o),this.ctx.save()},t.prototype._$initEvent=function(){var t=this;this.element.$store&&this.element.$store.hookCall.beforeClose((function(){t.clearRect()}))},t.prototype._$animationFrame=function(t){var i=this,e=this.element.$store;if(e){window.requestAnimationFrame||(e.animationId=window.setInterval(t,this.options.delay));var o=Date.now()+this.options.delay;t.call(this);var n=function(){Date.now()>o&&(t.call(i),o=Date.now()+i.options.delay),e.animationId=window.requestAnimationFrame(n)};e.animationId=window.requestAnimationFrame(n)}},t.prototype.initContextCall=function(t,i,e){var o=this;this._$initBaseContext(),this._$initEvent(),d(t)&&(this.modelDefOptions=t,this.options=Object.assign(t,this.options),this.element.$store&&(this.element.$store.options=this.options),i&&i.length&&this.options.toast&&i.forEach((function(t){var i=o.options[t.key];d(i)&&!t.limit(i)&&c.warn(t.message)}))),d(i)&&(this.limits=i),d(e)&&e.call(this,this)},t.prototype.run=function(t){var i=this.element.$store;i&&i.animationId&&this.clearAnimationFrame(i.animationId),this._$animationFrame(t)},t.prototype.clearAnimationFrame=function(t){p(t)},t.prototype.clearRect=function(t,i,e,o){var n=this.canvas.width,s=this.canvas.height;if(d(t)&&d(i)&&d(e)&&d(o))this.ctx.clearRect(t,i,e,o);else if(d(t)&&d(i)&&d(e)&&!d(o)){var a=e-this.stepClear,r=Math.sqrt(e*e-a*a),h=t-a,l=i-r,c=2*a,p=2*r;this.stepClear<=e?(this.ctx.clearRect(h,l,c,p),this.stepClear+=1,this.clearRect(t,i,e)):this.stepClear=1}else this.ctx.clearRect(-n,-s,2*n,2*s)},t.prototype.drawRadiusRect=function(t,i,e,o,n){this.ctx.beginPath(),this.ctx.arc(t+n,i+n,n,1*Math.PI,1.5*Math.PI),this.ctx.lineTo(t+e-n,i),this.ctx.arc(t+e-n,i+n,n,1.5*Math.PI,0),this.ctx.lineTo(t+e,i+o-n),this.ctx.arc(t+e-n,i+o-n,n,0,.5*Math.PI),this.ctx.lineTo(t+n,i+o),this.ctx.arc(t+n,i+o-n,n,.5*Math.PI,Math.PI),this.ctx.lineTo(t,i+n),this.ctx.closePath()},t.prototype.drawText=function(t){var i=this.options,e=Object.assign({esGap:i.textGap||0,x:0,text:i.text||"",textColor:i.themeColor||"rgba(64,158,255,1)"},t);this.ctx.save(),this.ctx.beginPath(),this.ctx.fillStyle=e.textColor,this.ctx.fillText(e.text,e.x,(i.textGap||0)+(i.fontSize||0)+e.esGap),this.ctx.closePath(),this.ctx.restore()},t}(),w=function(t,i){return w=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(t,i){t.__proto__=i}||function(t,i){for(var e in i)Object.prototype.hasOwnProperty.call(i,e)&&(t[e]=i[e])},w(t,i)};function v(t,i){if("function"!=typeof i&&null!==i)throw new TypeError("Class extends value "+String(i)+" is not a constructor or null");function e(){this.constructor=t}w(t,i),t.prototype=null===i?Object.create(i):(e.prototype=i.prototype,new e)}var g={lineStart:10,lineEnd:16,lineStartSkew:0,lineEndSkew:0,lineWidth:4,lineCap:"round",lineNum:10,direction:!0},S=[{key:"lineNum",message:"lineNum value 4-18",limit:function(t){return t>=4&&t<=18}}],C=function(t){function i(i,e,o,n,s){var a=t.call(this,i,e,o,n,s,g,S,(function(t){var i=t.options;t.ctx.lineCap=i.lineCap,t.ctx.lineWidth=i.lineWidth,t.ctx.save()}))||this;return a.optimization(a.options.textGap+a.options.lineEnd),a.aps=Array.from({length:a.options.lineNum},(function(t,i){return i})),a.run(a.draw),a}return v(i,t),i.prototype.draw=function(){this.clearRect(),this.controller(),this.drawGear();var t=this.options;this.drawText({esGap:t.lineEnd})},i.prototype.controller=function(){var t=this;this.options.direction?this.aps=this.aps.map((function(i){return i-1<=0?t.aps.length-1:i-1})):this.aps=this.aps.map((function(i){return i+1>t.aps.length?0:i+1}))},i.prototype.drawGear=function(){var t=this.options;this.ctx.save(),this.ctx.shadowOffsetX=t.shadowOffsetX,this.ctx.shadowOffsetY=t.shadowOffsetY,this.ctx.shadowBlur=t.shadowBlur;for(var i=0;ithis.h&&(this.options.lineStart=this.h/6-5,this.options.lineEnd=this.h/6,this.options.textGap=2)},i}(m),T={zoomGap:10,maxSize:16,zoomNum:5,lineWidth:10,zoomHeight:2,lineCap:"round",action:t.ZOOM_ACTION.SCALE,direction:!0,zoomColors:[]},y=[{key:"lineWidth",message:"lineWidth(default:10) <= maxSize(default:16)",limit:function(t){return t<=T.maxSize}},{key:"maxSize",message:"lineWidth(default:10) <= maxSize(default:16)",limit:function(t){return T.lineWidth<=t}}],O=function(i){function e(t,e,o,n,s){var a=i.call(this,t,e,o,n,s,T,y,(function(t){var i=t.options;t.ctx.lineCap=i.lineCap,t.ctx.lineWidth=i.lineWidth,t.ctx.translate(-(i.lineWidth*(i.zoomNum+1)+i.zoomGap*(i.zoomNum+1))/2,-i.zoomHeight/2),t.ctx.save()}))||this;return a.zoomIndex=a.options.direction?0:a.options.zoomNum-1,a.list=Array.from({length:a.options.zoomNum},(function(t,i){return Object.assign({value:a.options.lineWidth,state:0})})),a.run(a.draw),a}return v(e,i),e.prototype.draw=function(){this.clearRect(),this.drawZoom();var t=this.options;this.drawText({esGap:t.maxSize,x:(t.lineWidth*(t.zoomNum+1)+t.zoomGap*(t.zoomNum+1))/2}),this.controller()},e.prototype.controller=function(){var t=this.options;t.direction&&this.zoomIndex>=t.zoomNum?this.zoomIndex=0:t.direction&&this.zoomIndex<0&&(this.zoomIndex=t.zoomNum-1)},e.prototype.drawZoom=function(){for(var i=this.options,e=0;e=i.lineWidth&&this.list[e].value--,i.action===t.ZOOM_ACTION.SCALE&&(this.ctx.lineWidth=this.list[e].value),e===this.zoomIndex&&(this.list[e].value>i.maxSize&&(this.list[e].state=2,i.direction?this.zoomIndex++:this.zoomIndex-1>=0?this.zoomIndex--:this.zoomIndex=i.zoomNum-1),this.list[e].value<=i.lineWidth&&(this.list[e].state=1)),this.ctx.beginPath(),i.zoomColors.length>0&&i.zoomColors[e]?this.ctx.strokeStyle=i.zoomColors[e]:this.ctx.strokeStyle=i.themeColor;var o=0,n=i.zoomHeight;i.action!==t.ZOOM_ACTION.HEIGHT&&i.action!==t.ZOOM_ACTION.WAVE||(o=-this.list[e].value),i.action===t.ZOOM_ACTION.WAVE&&(n=-this.list[e].value),this.ctx.moveTo((e+1)*(i.lineWidth+i.zoomGap),o),this.ctx.lineTo((e+1)*(i.lineWidth+i.zoomGap),n),this.ctx.stroke(),this.ctx.closePath()}},e}(m),E={arcGap:Math.PI/4,ringGap:10,lineWidth:2,ringNum:2,radius:6,lineCap:"round",turn:10,ringsTurn:[Math.PI,Math.PI/4],direction:!0},I=[{key:"ringNum",message:"ringNum value 1-10",limit:function(t){return t>=1&&t<=10}},{key:"ringsTurn",message:"ringsTurn size ".concat(E.ringNum),limit:function(t){return t.length<=E.ringNum}}],b=function(t){function i(i,e,o,n,s){var a=t.call(this,i,e,o,n,s,E,I,(function(t){var i=t.options;t.ctx.lineCap=i.lineCap,t.ctx.lineWidth=i.lineWidth,t.ctx.save()}))||this;return a.rotate=10,a.run(a.draw),a}return v(i,t),i.prototype.draw=function(){this.clearRect(),this.controller();var t=this.options;this.drawText({esGap:t.ringNum*(t.radius+t.ringGap/2)})},i.prototype.controller=function(){this.ctx.save();var t=this.options,i=this.rotate*Math.PI/180*(t.direction?1:-1);this.ctx.rotate(i),this.ctx.shadowOffsetX=t.shadowOffsetX,this.ctx.shadowOffsetY=t.shadowOffsetY,this.ctx.shadowBlur=t.shadowBlur;for(var e=1;e<=t.ringNum;e++)this.drawRing(t.radius+(e-1)*t.ringGap,t.arcGap,t.ringsTurn&&t.ringsTurn.length>0?t.ringsTurn[e-1]:Math.PI/e);this.rotate+=t.turn,this.ctx.restore()},i.prototype.drawRing=function(t,i,e){void 0===i&&(i=1),void 0===e&&(e=0),this.ctx.beginPath(),this.ctx.arc(0,0,t,i+e,Math.PI+e),this.ctx.stroke(),this.ctx.closePath(),this.ctx.beginPath(),this.ctx.arc(0,0,t,Math.PI+i+e,e),this.ctx.stroke(),this.ctx.closePath()},i}(m),P={beanSize:15,pointLength:15},R=[{key:"pointLength",message:"pointLength value >= 5",limit:function(t){return t>=5}},{key:"beanSize",message:"beanSize value >= 5",limit:function(t){return t>=5}}],L=function(t){function i(i,e,o,n,s){var a=t.call(this,i,e,o,n,s,P,R)||this;return a.bean={turn:30,state:1,beanState:1,nowX:-a.options.pointLength*a.options.beanSize/2-3*a.options.beanSize,beans:Array.from({length:a.options.pointLength},(function(){return 1})),beanAnimaIndex:0},a.options.delay=10,a.run(a.draw),a}return v(i,t),i.prototype.draw=function(){var t=this.options;this.clearRect(),this.ctx.save(),this.ctx.beginPath(),this.ctx.translate(this.bean.nowX,0),this.ctx.arc(0,0,t.beanSize,(360-this.bean.turn)*Math.PI/180,this.bean.turn*Math.PI/180,!0),this.ctx.lineTo(0,0),this.ctx.fill(),this.ctx.closePath(),this.ctx.restore(),this.drawPoint(),this.drawFilter(),this.drawText({esGap:t.beanSize}),this.controller()},i.prototype.controller=function(){var t=this.options;this.bean.nowX>=t.pointLength*t.beanSize/2+2*t.beanSize&&(this.bean.nowX=-t.pointLength*t.beanSize/2-3*t.beanSize,this.bean.beanAnimaIndex=0),this.bean.nowX<=-t.pointLength*t.beanSize/2&&(this.bean.beanState=2),this.bean.turn<=0&&(this.bean.state=2),this.bean.turn>=30&&(this.bean.state=1),1===this.bean.state&&(this.bean.turn-=1),2===this.bean.state&&(this.bean.turn+=1),1===this.bean.beanState&&(this.bean.nowX-=1),2===this.bean.beanState&&(this.bean.nowX+=1)},i.prototype.drawPoint=function(){var t=this.options;this.ctx.save(),this.setShadow(),this.ctx.translate(-t.pointLength*t.beanSize/2,0);for(var i=0;i=5&&t<=24}},{key:"delay",message:"Pattern.delay not allowed update",limit:function(t){return t===s().delay}}],G=function(i){function e(t,e,o,n,s){var a=i.call(this,t,e,o,n,s,_,N,(function(t){t.options.delay=10}))||this;return a.pattern={color:a.randomState("chartColors"),nowHeight:10,chart:a.randomState("charts"),shadow:0,nowState:1,turn:0},a.run(a.draw),a}return v(e,i),e.prototype.draw=function(){var t=this.options;this.clearRect(),this.ctx.save(),this.ctx.beginPath(),this.ctx.translate(0,this.pattern.nowHeight),this.ctx.rotate(this.pattern.turn/Math.PI*2),this.ctx.fillStyle=this.pattern.color,this.selectChart(0,0,t.chartSize),this.ctx.closePath(),this.ctx.restore(),this.drawShadow(),this.clearRect(-this.w,0,2*this.w,this.h),this.controller(t),this.drawText({textColor:this.pattern.color})},e.prototype.controller=function(t){this.pattern.turn+=10,1===this.pattern.nowState?(this.pattern.nowHeight--,this.pattern.shadow+=.2):2===this.pattern.nowState&&(this.pattern.nowHeight++,this.pattern.shadow-=.2),this.pattern.shadow=Math.floor(100*this.pattern.shadow)/100,this.pattern.nowHeight<=-t.chartSize&&this.pattern.nowHeight%8==0&&(t.delay+=.5,t.delay=Math.floor(100*t.delay)/100),this.pattern.nowHeight<=-t.maxHeight?this.pattern.nowState=2:this.pattern.nowHeight>=t.chartSize&&(this.pattern.nowState=1,t.delay=10,this.pattern.chart=this.randomState("charts"),this.pattern.color=this.randomState("chartColors"))},e.prototype.selectChart=function(i,e,o){switch(this.pattern.chart){case t.PATTERN_CHART.RECT:this.drawRect(i,e,o);break;case t.PATTERN_CHART.ARC:this.drawArc(i,e,o);break;case t.PATTERN_CHART.TRIANGLE:this.drawTriangle(i,e,o);break;case t.PATTERN_CHART.HEART:this.drawHeart(i,e,o);break;case t.PATTERN_CHART.POLYGON:this.drawPolygon(i,e,o)}},e.prototype.randomState=function(t){var i=this.options;return i[t][parseInt(String(Math.random()*i[t].length))]},e.prototype.drawShadow=function(){this.ctx.save(),this.ctx.beginPath(),this.setShadow(),this.ctx.globalAlpha=.2,this.ctx.strokeStyle=this.pattern.color,this.ctx.moveTo(-this.pattern.shadow/2,0),this.ctx.lineTo(this.pattern.shadow,0),this.ctx.stroke(),this.ctx.beginPath(),this.ctx.restore()},e.prototype.drawRect=function(t,i,e){this.ctx.save(),this.ctx.beginPath(),this.setShadow(),this.ctx.translate(-e/2,-e/2),this.ctx.fillRect(t,i,e,e),this.ctx.closePath(),this.ctx.restore()},e.prototype.drawArc=function(t,i,e){this.ctx.save(),this.ctx.beginPath(),this.setShadow(),this.ctx.arc(t,i,e/2,0,2*Math.PI),this.ctx.fill(),this.ctx.closePath(),this.ctx.restore()},e.prototype.drawTriangle=function(t,i,e){this.ctx.save(),this.ctx.beginPath(),this.setShadow(),this.ctx.translate(-e/2,-e/2*Math.sqrt(3)/2),this.ctx.moveTo(t,i),this.ctx.lineTo(e,0),this.ctx.lineTo(e/2,e/2*Math.sqrt(3)),this.ctx.lineTo(t,i),this.ctx.fill(),this.ctx.closePath(),this.ctx.restore()},e.prototype.drawHeart=function(t,i,e){e/=2,this.ctx.save(),this.ctx.beginPath(),this.setShadow(),this.ctx.translate(0,-2*e/2),this.ctx.moveTo(t,i),this.ctx.bezierCurveTo(e/2,-e,3*e,-e/2,i,2*e),this.ctx.moveTo(t,i),this.ctx.bezierCurveTo(-e/2,-e,3*-e,-e/2,i,2*e),this.ctx.fill(),this.ctx.closePath(),this.ctx.restore()},e.prototype.drawPolygon=function(t,i,e){this.ctx.save(),this.ctx.beginPath(),this.setShadow(),this.ctx.translate(-e/2,-e/2),this.ctx.moveTo(t,i),this.ctx.lineTo(e,i),this.ctx.lineTo(e+e/2,e/2),this.ctx.lineTo(e,e/2+e/2),this.ctx.lineTo(t,e),this.ctx.lineTo(t-e/2,e-e/2),this.ctx.lineTo(t,i),this.ctx.fill(),this.ctx.closePath(),this.ctx.restore()},e.prototype.setShadow=function(){var t=this.options;this.ctx.shadowColor=this.pattern.color,this.ctx.shadowOffsetX=t.shadowOffsetX,this.ctx.shadowOffsetY=t.shadowOffsetY,this.ctx.shadowBlur=t.shadowBlur},e}(m),M={rollGap:12,childNum:4,rollSize:16,showChild:!0,chart:t.ROLL_CHART.WHEEL,windmills:["#1ab3ea","#de6834","#30925d","#f48ea5"],windmillPointColor:"#f2c31f",fixed:!1},H=[{key:"childNum",message:"chartSize value 4-10",limit:function(t){return t>=4&&t<=10}},{key:"delay",message:"Roll.delay not allowed update",limit:function(t){return t===s().delay}}],W=function(i){function e(t,e,o,n,s){var a=i.call(this,t,e,o,n,s,M,H)||this;return a.Roll={turn:1,nowX:a.options.fixed?0:a.options.childNum/2*(a.options.rollSize+a.options.rollGap)+a.options.rollGap/2,state:2,child:[]},a.run(a.draw),a}return v(e,i),e.prototype.draw=function(){this.clearRect(),this.drawGround(),this.drawChild(),this.ctx.save(),this.ctx.beginPath(),this.ctx.translate(-this.Roll.nowX,0),this.ctx.rotate(this.Roll.turn*Math.PI/180),this.selectChart(),this.controller(),this.ctx.restore();var t=this.options;this.drawText({esGap:t.rollSize})},e.prototype.selectChart=function(){switch(this.options.chart){case t.ROLL_CHART.RECT:this.drawRect();break;case t.ROLL_CHART.WHEEL:this.drawWheel();break;case t.ROLL_CHART.WINDMILL:this.drawWindmill()}},e.prototype.controller=function(){var t=this.options;if(1===this.Roll.state&&(this.Roll.turn-=10,t.delay<20&&!t.fixed&&(t.delay+=2)),2===this.Roll.state&&(this.Roll.turn+=10,t.delay>10&&!t.fixed&&(t.delay-=5)),!t.fixed){this.Roll.nowX<=-t.childNum/2*(t.rollSize+t.rollGap/1.6)&&(this.Roll.state=1),this.Roll.nowX>=t.childNum/2*(t.rollSize+t.rollGap)+t.rollGap/2&&(this.Roll.state=2),1===this.Roll.state&&this.Roll.nowX++,2===this.Roll.state&&this.Roll.nowX--;var i=this.Roll.child;this.Roll.nowX%(t.rollSize+t.rollGap)==0&&2===this.Roll.state&&i.push({turn:this.Roll.turn,x:this.Roll.nowX}),1===this.Roll.state&&i[i.length-1]&&i[i.length-1].x===this.Roll.nowX&&this.Roll.child.pop()}},e.prototype.drawRect=function(){var t=this.options;this.ctx.save(),this.setShadow(),this.ctx.translate(-t.rollSize/2,-t.rollSize/2),this.ctx.fillRect(0,0,t.rollSize,t.rollSize),this.ctx.restore()},e.prototype.drawWheel=function(){var t=this.options;this.ctx.save(),this.ctx.lineWidth=4,this.ctx.beginPath(),this.ctx.arc(0,0,t.rollSize/6,0,2*Math.PI),this.ctx.stroke(),this.ctx.closePath(),this.ctx.beginPath(),this.ctx.arc(0,0,t.rollSize/2,0,2*Math.PI),this.ctx.stroke(),this.ctx.closePath(),this.ctx.beginPath(),this.ctx.arc(0,0,t.rollSize,0,2*Math.PI),this.ctx.stroke(),this.ctx.closePath();for(var i=0;i<6;i++)this.ctx.beginPath(),this.ctx.moveTo(0,t.rollSize/2),this.ctx.lineTo(0,t.rollSize),this.ctx.stroke(),this.ctx.rotate(60*Math.PI/180),this.ctx.closePath();this.ctx.restore()},e.prototype.drawWindmill=function(){var t=this.options;this.ctx.save();for(var i=0;i0?i.some((function(i){return t.classList.contains(i)}))&&this.skeleton.push({title:t.nodeName,element:t}):this.skeleton.push({title:t.nodeName,element:t})},i.prototype.controller=function(t){for(var i=this.options,e=0,o=Array.from(t);e=.9&&(this.state=2),this.colorFlow<=.1&&(this.state=1),1===this.state&&(this.colorFlow+=.06),2===this.state&&(this.colorFlow-=.06))},i}(m)};function B(t,i,e,o,n){try{if(!n.$store)return;var s=n.$store.model;if(s)s.initContextCall(s.modelDefOptions,s.limits,s.modelDefCall);else{s=o.custom?new o.custom(t,i,e,o,n):new X[o.model](t,i,e,o,n)}}catch(t){c.error("draw error("+t+")")}}var K=window;function $(){var i;return(i={})[t.HOOKS_CALL_KEY.BEFORE_CLOSE]=[],i[t.HOOKS_CALL_KEY.CLOSED]=[],i}function j(i){var e;return(e={})[t.HOOKS_CALL_KEY.BEFORE_CLOSE]=function(e){i[t.HOOKS_CALL_KEY.BEFORE_CLOSE].push(e)},e[t.HOOKS_CALL_KEY.CLOSED]=function(e){i[t.HOOKS_CALL_KEY.CLOSED].push(e)},e}var U=window;function Z(i,e,o,n){var s=e.pointerEvents?i.scrollWidth:i.clientWidth,a=e.pointerEvents?i.scrollHeight:i.clientHeight,r=U.getComputedStyle(i),h=i.style,l=n.style;return e.type!==t.LOADING_TYPES.DOM||e.pointerEvents||(i.style.pointerEvents="none"),r.position&&"static"!==r.position||(h.position="relative"),n.id=o,l.opacity="0",l.position="absolute",l.left="".concat(e.pointerEvents?0:i.scrollLeft,"px"),l.top="".concat(e.pointerEvents?0:i.scrollTop,"px"),l.zIndex=e.zIndex,l.transition="".concat(e.delayInto/1e3,"s ease-in-out"),l.backgroundColor=e.bgColor,l.borderRadius=r.borderRadius,"htmlcanvaselement"===u(n)?q(n,s,a):"htmldivelement"===u(n)&&(l.width="".concat(s,"px"),l.height="".concat(a,"px"),l.display="flex",l.alignItems="center",l.justifyContent="center"),i.append(n),U.setTimeout((function(){return l.opacity="1"}),0),x(i,(function(){i.$store&&(i.$store.loadingId=o)})),i}function q(t,i,e){var o=U.devicePixelRatio||1;t.width=i*o,t.height=e*o,t.style.width="".concat(i,"px"),t.style.height="".concat(e,"px")}var V=window,J=function(){function i(t){this.canvas=null,this.htmlElement=null,this.loadingId=null,this.element=null,this.hooks=null,this.resizeTimeId=null,this.options=Object.assign(s(),t)}return i.prototype.resize=function(t,i){var e=this;this.resizeTimeId||(this.resizeTimeId=V.setTimeout((function(){var o=i,n=t.clientWidth,s=t.clientHeight;o.width>t.clientWidth&&(n=t.offsetWidth,s=t.offsetHeight),e.canvas?(q(o,n,s),t.$store&&B(n,s,o,e.options,t)):e.htmlElement&&(e.htmlElement.style.width="".concat(n,"px"),e.htmlElement.style.height="".concat(s,"px")),e.resizeTimeId=null}),this.options.delayInto))},i.prototype.close=function(i,e){var o=this,n=this.options,s=i.$store;V.setTimeout((function(){!function(i,e,o){o.style.opacity="0",e.type!==t.LOADING_TYPES.DOM&&(i.style.boxShadow="none")}(i,n,e),n.type!==t.LOADING_TYPES.DOM||n.pointerEvents||(i.style.pointerEvents="auto"),x(i,(function(){s&&(s.model=null,o.callEvent(t.HOOKS_CALL_KEY.BEFORE_CLOSE),s.animationId&&p(s.animationId)),n.type!==t.LOADING_TYPES.DOM?i.remove():e.remove(),o.loadingId=null,o.callEvent(t.HOOKS_CALL_KEY.CLOSED),o.hooks=$()}))}),s&&!s.loadingId?n.delayInto:0)},i.prototype.draw=function(t){var i=this.options;if(i.html){var e={content:K.document.createElement("div"),loadingId:f()},o=e.content,n=e.loadingId;this.htmlElement=o,this.htmlElement.innerHTML=i.html,this.loadingId=n,this.element=Z(t,i,n,o)}else{var s={canvas:K.document.createElement("canvas"),hooks:$(),loadingId:f()},a=s.canvas,r=s.hooks;n=s.loadingId;this.canvas=a,this.hooks=r,this.loadingId=n,function(t,i,e){t.$store={options:i,animationId:void 0,loadingId:null,model:null,hookCall:j(e)}}(t,i,r),this.element=Z(t,i,n,a),t.$store?B(a.offsetWidth,a.offsetHeight,a,this.options,t):c.error("WebLoading:canvas or ctx null")}},i.prototype.callEvent=function(t){this.hooks&&this.hooks[t].forEach((function(t){t()}))},i}(),Q=window,tt=function(){function i(t){this.options=t,this.extendEl=this.initStyle()}return i.prototype.initStyle=function(){this.extendEl=Q.document.createElement("div");var i=this.options,e="100vw",o="100vh",n="0px";return i&&(this.extendEl.classList.add("wl_"+(i.extendClass||"loading")),i.type===t.LOADING_TYPES.MINI&&(e="180px",o="160px",n="10px"),this.extendEl.style.cssText="\n position:fixed;\n width:".concat(e,";\n height:").concat(o,";\n top:50%;\n left:50%;\n transform:translate(-50%, -50%);\n border-radius: ").concat(n,";\n z-index: ").concat(i.zIndex,";\n box-shadow:\n 2.8px 2.8px 2.2px rgba(0, 0, 0, 0.02),\n 6.7px 6.7px 5.3px rgba(0, 0, 0, 0.028),\n 12.5px 12.5px 10px rgba(0, 0, 0, 0.035),\n 22.3px 22.3px 17.9px rgba(0, 0, 0, 0.042),\n 41.8px 41.8px 33.4px rgba(0, 0, 0, 0.05),\n 100px 100px 80px rgba(0, 0, 0, 0.07)\n ;\n ")),Q.document.body.appendChild(this.extendEl),this.extendEl},i.prototype.getElement=function(){return this.extendEl},i}(),it=window;function et(i){var e=new J(i),o=null;function n(t){if(e.element){var i=e.canvas;e.htmlElement&&(i=e.htmlElement),i?e[t](e.element,i):c.warn("Animation element not found!")}}return{loading:function(i,n){var s=Object.assign(e.options,n);if(!e.loadingId&&!o)if(s.type!==t.LOADING_TYPES.DOM&&(i=new tt(s).getElement()),i){var a=new Promise((function(t){it.setTimeout((function(){t(!0)}),s.notFeel)})),r=new Promise((function(t){o=t}));Promise.race([a,r]).then((function(n){n?e.draw(i):s.type!==t.LOADING_TYPES.DOM&&i.remove(),o=null}))}else c.error("The loading function cannot find an HTMLElement element!")},resize:function(){n("resize")},close:function(){o&&o(!1),n("close")},update:function(t){var i=e.canvas,o=Object.assign(e.options,t),n=e.element;i&&o&&n&&n.$store&&B(i.offsetWidth,i.offsetHeight,i,o,n)},getOptions:function(){return e.options},getLoadingId:function(){return e.loadingId}}}function ot(t,i){return et(Object.assign(s(),i||{},{type:t}))}var nt=window;nt.BaseModel=m,nt.initLoading=function(t){return et(t)},nt.miniLoading=function(i){return ot(t.LOADING_TYPES.MINI,i)},nt.fullLoading=function(i){return ot(t.LOADING_TYPES.FULL,i)},t.BaseModel=m,t.fullLoading=function(i){return ot(t.LOADING_TYPES.FULL,i)},t.initLoading=et,t.miniLoading=function(i){return ot(t.LOADING_TYPES.MINI,i)},Object.defineProperty(t,"__esModule",{value:!0})}));
2 |
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | web-loading
7 |
8 |
9 |
10 |
11 |
12 |
13 | id:
14 | 001
15 |
16 |
17 | user:
18 | use1
19 |
20 |
21 |
22 |
23 |
57 |
58 |
85 |
86 |
--------------------------------------------------------------------------------
/index.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 | if (process.env.NODE_ENV === 'production') {
3 | module.exports = require('./dist/web-loading.cjs.min.js')
4 | } else {
5 | module.exports = require('./dist/web-loading.cjs.js')
6 | }
7 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "web-loading",
3 | "version": "1.5.2",
4 | "main": "./dist/web-loading.js",
5 | "typings": "./dist/web-loading.d.ts",
6 | "jsdelivr": "./dist/web-loading.js",
7 | "unpkg": "./dist/web-loading.js",
8 | "module": "./dist/web-loading.esm-bundler.js",
9 | "scripts": {
10 | "build": "tsc && rollup -c rollup.config.js && api-extractor run",
11 | "lint:es": "eslint --cache --max-warnings 0 \"src/**/*.{vue,ts,tsx}\" --fix",
12 | "push": "npm run build & npm publish"
13 | },
14 | "devDependencies": {
15 | "@microsoft/api-extractor": "^7.35.2",
16 | "@rollup/plugin-commonjs": "^21.0.3",
17 | "@rollup/plugin-inject": "^4.0.4",
18 | "@rollup/plugin-json": "^4.1.0",
19 | "@rollup/plugin-node-resolve": "^13.1.3",
20 | "@rollup/plugin-replace": "^4.0.0",
21 | "@typescript-eslint/eslint-plugin": "^5.59.5",
22 | "eslint": "^8.27.0",
23 | "eslint-config-prettier": "^8.5.0",
24 | "eslint-plugin-prettier": "^4.2.1",
25 | "rollup": "^2.70.1",
26 | "rollup-plugin-terser": "^7.0.2",
27 | "ts-loader": "^9.4.2",
28 | "typescript": "^4.6.4"
29 | },
30 | "repository": {
31 | "type": "git",
32 | "url": "https://github.com/tommyrunner/web-loading.git"
33 | },
34 | "dependencies": {
35 | "tslib": "^2.5.0"
36 | },
37 | "keywords": [
38 | "Vue3",
39 | "HTML",
40 | "Canvas",
41 | "Loading"
42 | ],
43 | "peerDependencies": {
44 | "vue": "^3.0.0"
45 | },
46 | "author": "tommyrunner",
47 | "license": "ISC"
48 | }
49 |
--------------------------------------------------------------------------------
/rollup.config.js:
--------------------------------------------------------------------------------
1 | import { defineConfig } from 'rollup'
2 | import { join } from 'path'
3 |
4 | import { terser as rollupTerser } from 'rollup-plugin-terser'
5 | import rollupJSON from '@rollup/plugin-json'
6 | import rollupCommonJS from '@rollup/plugin-commonjs'
7 | import rollupReplace from '@rollup/plugin-replace'
8 | import rollupNodeResolve from '@rollup/plugin-node-resolve'
9 |
10 | /**
11 | * @typedef {'umd' | 'cjs' | 'esm-bundler'} OutputFormat
12 | */
13 |
14 | /**
15 | * @param {OutputFormat} format
16 | * @param {boolean} minify
17 | * @returns {string}
18 | */
19 | function replaceProcessNodeEnv(format, minify) {
20 | switch (format) {
21 | case 'umd':
22 | case 'cjs':
23 | return minify ? '"production"' : '"development"'
24 | case 'esm-bundler':
25 | return 'process.env.NODE_ENV'
26 | default:
27 | throw new TypeError(`Unsupport format: ${format}`)
28 | }
29 | }
30 |
31 | /**
32 | * @param {OutputFormat} format
33 | * @param {boolean} minify
34 | * @returns {string}
35 | */
36 | function replaceDev(format, minify) {
37 | switch (format) {
38 | case 'umd':
39 | case 'cjs':
40 | return minify ? 'false' : 'true'
41 | case 'esm-bundler':
42 | return 'process.env.NODE_ENV !== "production"'
43 | default:
44 | throw new TypeError(`Unsupport format: ${format}`)
45 | }
46 | }
47 |
48 | /**
49 | * @param {OutputFormat} format
50 | * @param {boolean} minify
51 | * @returns {import('rollup').RollupOptions}
52 | */
53 | function createOption(format, minify) {
54 | const name = `web-loading${format === 'umd' ? '' : `.${format}`}${minify ? '.min' : ''}.js`
55 | return {
56 | input: join(__dirname, 'lib/index.js'),
57 | plugins: [
58 | rollupNodeResolve({
59 | mainFields: ['browser', 'module', 'main']
60 | }),
61 | rollupJSON(),
62 |
63 | rollupReplace({
64 | preventAssignment: true,
65 | 'process.env.NODE_ENV': replaceProcessNodeEnv(format, minify),
66 | __DEV__: replaceDev(format, minify),
67 | __VERSION__: JSON.stringify(require('./package.json').version)
68 | }),
69 | rollupCommonJS({
70 | transformMixedEsModules: true,
71 | extensions: ['.js', 'jsx', '.ts', '.tsx']
72 | }),
73 | {
74 | name: 'typescript-class-pure',
75 | transform(code) {
76 | return code.replace(/\/\*\* @class \*\/ \(function/g, '/*#__PURE__*/ (function')
77 | }
78 | },
79 | ...(minify
80 | ? [
81 | rollupTerser({
82 | output: {
83 | comments: false
84 | },
85 | module: format === 'esm-bundler'
86 | })
87 | ]
88 | : [])
89 | ],
90 | output: {
91 | file: join(__dirname, 'dist', name),
92 | format: format === 'esm-bundler' ? 'esm' : format,
93 | name: 'rustOption',
94 | exports: 'named'
95 | }
96 | }
97 | }
98 |
99 | export default defineConfig([
100 | createOption('umd', false),
101 | createOption('umd', true),
102 | createOption('cjs', false),
103 | createOption('cjs', true),
104 | createOption('esm-bundler', false)
105 | ])
106 |
--------------------------------------------------------------------------------
/src/ExtendLoading/index.ts:
--------------------------------------------------------------------------------
1 | import { OptionsType } from '../type'
2 | import { LOADING_TYPES } from '../utils'
3 | const $window = window
4 | /**
5 | * @description 扩展加载类
6 | */
7 | export default class ExtendLoading {
8 | extendEl: HTMLElement
9 | options?: OptionsType
10 | /**
11 | * @description 构造函数
12 | * @param {OptionsType} [options] - 配置选项
13 | */
14 | constructor(options?: OptionsType) {
15 | this.options = options
16 | this.extendEl = this.initStyle()
17 | }
18 | /**
19 | * @description 初始化扩展容器元素样式
20 | * @returns {HTMLElement} 扩展容器元素
21 | * @private
22 | */
23 | private initStyle(): HTMLElement {
24 | this.extendEl = $window.document.createElement('div')
25 | const op = this.options
26 | let w = '100vw',
27 | h = '100vh',
28 | borderRadius = '0px'
29 | if (op) {
30 | this.extendEl.classList.add('wl_' + (op.extendClass || 'loading'))
31 | if (op.type === LOADING_TYPES.MINI) {
32 | w = '180px'
33 | h = '160px'
34 | borderRadius = '10px'
35 | }
36 | this.extendEl.style.cssText = `
37 | position:fixed;
38 | width:${w};
39 | height:${h};
40 | top:50%;
41 | left:50%;
42 | transform:translate(-50%, -50%);
43 | border-radius: ${borderRadius};
44 | z-index: ${op.zIndex};
45 | box-shadow:
46 | 2.8px 2.8px 2.2px rgba(0, 0, 0, 0.02),
47 | 6.7px 6.7px 5.3px rgba(0, 0, 0, 0.028),
48 | 12.5px 12.5px 10px rgba(0, 0, 0, 0.035),
49 | 22.3px 22.3px 17.9px rgba(0, 0, 0, 0.042),
50 | 41.8px 41.8px 33.4px rgba(0, 0, 0, 0.05),
51 | 100px 100px 80px rgba(0, 0, 0, 0.07)
52 | ;
53 | `
54 | }
55 | $window.document.body.appendChild(this.extendEl)
56 | return this.extendEl
57 | }
58 | /**
59 | * @description 获取元素
60 | * @returns {HTMLElement} 扩展容器元素
61 | */
62 | getElement(): HTMLElement {
63 | return this.extendEl
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/src/Webloading/index.ts:
--------------------------------------------------------------------------------
1 | import type { ElementType, OptionsType } from '../type'
2 | import type { HooksType } from './type'
3 | import { LOADING_TYPES, getDefOptions, clearAnimationFrame, $Log, HOOKS_CALL_KEY, onTransitionEndEvent } from '../utils'
4 | import drawController from '../draw/index'
5 | import { initStore, initHooksCall, initCanvas, initHtml } from './init'
6 | import { initContentStyle, setupCanvas, clearStyle } from './style'
7 | const $window = window
8 | /**
9 | * @description Web加载类
10 | */
11 | export default class WebLoading {
12 | // canvas动画元素
13 | canvas: HTMLCanvasElement | null = null
14 | // Html动画元素
15 | htmlElement: HTMLDivElement | null = null
16 | // 动画元素ID
17 | loadingId: string | null = null
18 | // 容器元素
19 | element: ElementType | null = null
20 | // 配置选项
21 | options: Required
22 | // 钩子
23 | hooks: HooksType | null = null
24 | // 大小调整控制
25 | resizeTimeId: number | null = null
26 | /**
27 | * @description 构造函数
28 | * @param {OptionsType} [options] - 配置选项
29 | */
30 | constructor(options?: OptionsType) {
31 | // 初始化默认配置
32 | this.options = Object.assign(getDefOptions(), options)
33 | }
34 | /**
35 | * @description 重置动画容器大小
36 | * @param {ElementType} element - 容器元素
37 | * @param {HTMLCanvasElement | HTMLDivElement} animaEl - 动画元素
38 | */
39 | resize(element: ElementType, animaEl: HTMLCanvasElement | HTMLDivElement) {
40 | if (!this.resizeTimeId)
41 | this.resizeTimeId = $window.setTimeout(() => {
42 | const canvas = animaEl as HTMLCanvasElement
43 | let w = element.clientWidth,
44 | h = element.clientHeight
45 | if (canvas.width > element.clientWidth) {
46 | // 收缩时需要计算滚动条
47 | w = element.offsetWidth
48 | h = element.offsetHeight
49 | }
50 | if (this.canvas) {
51 | setupCanvas(canvas, w, h)
52 | if (element.$store) drawController(w, h, canvas, this.options, element)
53 | } else if (this.htmlElement) {
54 | this.htmlElement.style.width = `${w}px`
55 | this.htmlElement.style.height = `${h}px`
56 | }
57 | this.resizeTimeId = null
58 | }, this.options.delayInto)
59 | }
60 | /**
61 | * @description 关闭动画
62 | * @param {ElementType} element - 容器元素
63 | * @param {HTMLCanvasElement | HTMLDivElement} animaEl - 动画元素
64 | */
65 | close(element: ElementType, animaEl: HTMLCanvasElement | HTMLDivElement) {
66 | const op = this.options
67 | const $store = element.$store
68 | $window.setTimeout(
69 | () => {
70 | // 触发关闭动画
71 | clearStyle(element, op, animaEl)
72 | if (op.type === LOADING_TYPES.DOM && !op.pointerEvents) {
73 | element.style.pointerEvents = 'auto'
74 | }
75 | // 防止二次关闭,如果二次关闭,需要等待上一个动画结束后再清除缓存
76 | onTransitionEndEvent(element, () => {
77 | // 结束canvas动画前需要结束样式
78 | if ($store) {
79 | // 清除模型
80 | $store.model = null
81 | // 关闭前回调
82 | this.callEvent(HOOKS_CALL_KEY.BEFORE_CLOSE)
83 | // 停止animationFrame
84 | if ($store.animationId) clearAnimationFrame($store.animationId)
85 | }
86 | // 如果dom是扩展的,清除父元素(父元素由webLoading创建)
87 | if (op.type !== LOADING_TYPES.DOM) element.remove()
88 | else animaEl.remove()
89 | // 擦除状态
90 | this.loadingId = null
91 | // 关闭后回调
92 | this.callEvent(HOOKS_CALL_KEY.CLOSED)
93 | // 重置钩子
94 | this.hooks = initHooksCall()
95 | })
96 | },
97 | // 如果是二次关闭,需要主动添加延迟
98 | $store && !$store.loadingId ? op.delayInto : 0
99 | )
100 | }
101 |
102 | /**
103 | * @description 绘制动画
104 | * @param {ElementType} element - 容器元素
105 | */
106 | draw(element: ElementType) {
107 | const op = this.options
108 | // 兼容html
109 | if (op.html) {
110 | // 初始化基础数据
111 | const { content, loadingId } = initHtml()
112 | this.htmlElement = content
113 | this.htmlElement.innerHTML = op.html
114 | this.loadingId = loadingId
115 | // 初始化样式
116 | this.element = initContentStyle(element, op, loadingId, content)
117 | } else {
118 | // 初始化基础数据
119 | const { canvas, hooks, loadingId } = initCanvas()
120 | this.canvas = canvas
121 | this.hooks = hooks
122 | this.loadingId = loadingId
123 | // 初始化存储
124 | initStore(element, op, hooks)
125 | // 初始化样式
126 | this.element = initContentStyle(element, op, loadingId, canvas)
127 | if (element.$store) {
128 | drawController(canvas.offsetWidth, canvas.offsetHeight, canvas, this.options, element)
129 | } else {
130 | $Log.error('WebLoading:canvas or ctx null')
131 | }
132 | }
133 | }
134 | /**
135 | * @description 触发钩子
136 | * @param {HOOKS_CALL_KEY} hooksKey - 钩子键
137 | * @private
138 | */
139 | private callEvent(hooksKey: HOOKS_CALL_KEY) {
140 | if (this.hooks)
141 | this.hooks[hooksKey].forEach((event: () => void) => {
142 | event()
143 | })
144 | }
145 | }
146 |
--------------------------------------------------------------------------------
/src/Webloading/init.ts:
--------------------------------------------------------------------------------
1 | import type { ElementType, HooksCallType, OptionsType } from '../type'
2 | import type { HooksType } from './type'
3 | import { HOOKS_CALL_KEY, createLoadingId } from '../utils'
4 | const $window = window
5 | /**
6 | * @description 初始化$store
7 | * @param {ElementType} element - 容器元素
8 | * @param {OptionsType} options - 配置选项
9 | * @param {HooksType} hooks - 钩子函数
10 | */
11 | export function initStore(element: ElementType, options: OptionsType, hooks: HooksType) {
12 | // 存储状态
13 | element.$store = {
14 | options,
15 | animationId: undefined,
16 | loadingId: null,
17 | model: null,
18 | hookCall: initStoreHooksCall(hooks)
19 | }
20 | }
21 | /**
22 | * @description 初始化钩子调用
23 | * @returns {HooksType} 钩子类型对象
24 | */
25 | export function initHooksCall(): HooksType {
26 | return {
27 | [HOOKS_CALL_KEY.BEFORE_CLOSE]: [],
28 | [HOOKS_CALL_KEY.CLOSED]: []
29 | }
30 | }
31 | /**
32 | * @description 初始化存储钩子调用
33 | * @param {HooksType} hooks - 钩子
34 | * @returns {HooksCallType} 钩子调用类型
35 | */
36 | export function initStoreHooksCall(hooks: HooksType): HooksCallType {
37 | return {
38 | [HOOKS_CALL_KEY.BEFORE_CLOSE]: (fun: (params?: any) => any) => {
39 | hooks[HOOKS_CALL_KEY.BEFORE_CLOSE].push(fun)
40 | },
41 | [HOOKS_CALL_KEY.CLOSED]: (fun: (params?: any) => any) => {
42 | hooks[HOOKS_CALL_KEY.CLOSED].push(fun)
43 | }
44 | }
45 | }
46 | /**
47 | * @description 初始化Canvas
48 | * @returns {{canvas: HTMLCanvasElement, hooks: HooksType, loadingId: string}} Canvas初始化对象
49 | */
50 | export function initCanvas() {
51 | return {
52 | canvas: $window.document.createElement('canvas'),
53 | hooks: initHooksCall(),
54 | loadingId: createLoadingId()
55 | }
56 | }
57 | /**
58 | * @description 初始化HTML
59 | * @returns {{content: HTMLDivElement, loadingId: string}} HTML初始化对象
60 | */
61 | export function initHtml() {
62 | return {
63 | content: $window.document.createElement('div'),
64 | loadingId: createLoadingId()
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/src/Webloading/style.ts:
--------------------------------------------------------------------------------
1 | import type { ElementType, OptionsType } from '../type'
2 | import { LOADING_TYPES, toType, onTransitionEndEvent } from '../utils'
3 | const $window = window
4 | /**
5 | * @description 初始化内容样式
6 | * @param {ElementType} element - 容器元素
7 | * @param {Required} op - 配置选项
8 | * @param {string} loadingId - 加载ID
9 | * @param {HTMLCanvasElement | HTMLDivElement} animaEl - 动画元素
10 | * @returns {ElementType} 容器元素
11 | */
12 | export function initContentStyle(
13 | element: ElementType,
14 | op: Required,
15 | loadingId: string,
16 | animaEl: HTMLCanvasElement | HTMLDivElement
17 | ): ElementType {
18 | // 客户端获取真实宽高,如果启用穿透则获取滚动宽高
19 | const elementW = op.pointerEvents ? element.scrollWidth : element.clientWidth,
20 | elementH = op.pointerEvents ? element.scrollHeight : element.clientHeight,
21 | readElementStyle = $window.getComputedStyle(element),
22 | elementStyle = element.style,
23 | contentStyle = animaEl.style
24 | if (op.type === LOADING_TYPES.DOM && !op.pointerEvents) {
25 | element.style.pointerEvents = 'none'
26 | }
27 | if (!readElementStyle.position || readElementStyle.position === 'static') elementStyle.position = 'relative'
28 | // 初始化canvas样式
29 | animaEl.id = loadingId
30 | contentStyle.opacity = '0'
31 | contentStyle.position = 'absolute'
32 | contentStyle.left = `${op.pointerEvents ? 0 : element.scrollLeft}px`
33 | contentStyle.top = `${op.pointerEvents ? 0 : element.scrollTop}px`
34 | contentStyle.zIndex = op.zIndex
35 | contentStyle.transition = `${op.delayInto / 1000}s ease-in-out`
36 | contentStyle.backgroundColor = op.bgColor
37 | contentStyle.borderRadius = readElementStyle.borderRadius
38 | // 设置canvas大小
39 | if (toType(animaEl) === 'htmlcanvaselement') {
40 | setupCanvas(animaEl as HTMLCanvasElement, elementW, elementH)
41 | } else if (toType(animaEl) === 'htmldivelement') {
42 | // 初始化兼容html样式
43 | contentStyle.width = `${elementW}px`
44 | contentStyle.height = `${elementH}px`
45 | // 居中
46 | contentStyle.display = 'flex'
47 | contentStyle.alignItems = 'center'
48 | contentStyle.justifyContent = 'center'
49 | }
50 | // 注入
51 | element.append(animaEl)
52 | // 触发进入动画
53 | $window.setTimeout(() => (contentStyle.opacity = '1'), 0)
54 | onTransitionEndEvent(element, () => {
55 | // 等待所有元素出现并完成(动画结束)
56 | if (element.$store) element.$store.loadingId = loadingId
57 | })
58 | return element
59 | }
60 | /**
61 | * @description 处理放大失真,同时更改高度和宽度也会重置canvas的所有内容
62 | * @param {HTMLCanvasElement} canvas - Canvas元素
63 | * @param {number} w - 宽度
64 | * @param {number} h - 高度
65 | */
66 | export function setupCanvas(canvas: HTMLCanvasElement, w: number, h: number) {
67 | const dpr = $window.devicePixelRatio || 1
68 | canvas.width = w * dpr
69 | canvas.height = h * dpr
70 | canvas.style.width = `${w}px`
71 | canvas.style.height = `${h}px`
72 | }
73 | /**
74 | * @description 清除样式
75 | * @param {ElementType} element - 容器元素
76 | * @param {OptionsType} op - 配置选项
77 | * @param {HTMLCanvasElement | HTMLDivElement} canvas - Canvas或DIV元素
78 | */
79 | export function clearStyle(element: ElementType, op: OptionsType, canvas: HTMLCanvasElement | HTMLDivElement) {
80 | // 首先视觉过渡
81 | canvas.style.opacity = '0'
82 | // 清除扩展
83 | if (op.type !== LOADING_TYPES.DOM) {
84 | element.style.boxShadow = 'none'
85 | }
86 | }
87 |
--------------------------------------------------------------------------------
/src/Webloading/type.ts:
--------------------------------------------------------------------------------
1 | import { HOOKS_CALL_KEY } from '../utils'
2 | /**
3 | * @description 钩子类型
4 | * @template T - 扩展自钩子调用键
5 | */
6 | export type HooksType = {
7 | [key in T]: Array<(...args: any[]) => void>
8 | }
9 |
--------------------------------------------------------------------------------
/src/draw/index.ts:
--------------------------------------------------------------------------------
1 | import type { ElementType, OptionsType } from '../type'
2 | import { $Log } from '../utils'
3 | import models from './model'
4 | import BaseModel from './model/BaseModel'
5 | /**
6 | * @description 模型控制器:控制显示的模型
7 | * @param {number} w - 宽度
8 | * @param {number} h - 高度
9 | * @param {HTMLCanvasElement} canvas - Canvas元素
10 | * @param {Required} options - 配置选项
11 | * @param {ElementType} element - 元素
12 | */
13 | export default function drawController(
14 | w: number,
15 | h: number,
16 | canvas: HTMLCanvasElement,
17 | options: Required,
18 | element: ElementType
19 | ) {
20 | try {
21 | if (!element.$store) return
22 | let storeModel = element.$store.model
23 | if (!storeModel) {
24 | let model = null
25 | if (!options.custom) model = new (models[options.model] as typeof BaseModel)(w, h, canvas, options, element)
26 | else model = new options.custom(w, h, canvas, options, element)
27 | storeModel = model
28 | } else {
29 | // 优化:因为更改高度和宽度后canvas会被重置,需要重新初始化
30 | storeModel.initContextCall(storeModel.modelDefOptions, storeModel.limits, storeModel.modelDefCall)
31 | }
32 | } catch (e) {
33 | $Log.error('draw error(' + e + ')')
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/src/draw/model/BaseModel.ts:
--------------------------------------------------------------------------------
1 | import type { OptionsType, LimitType, ElementType } from '../../type'
2 | import { isNull, clearAnimationFrame, $Log } from '../../utils'
3 | import { DrawTextParamsType } from '../type'
4 | /**
5 | * @description 基础模型类
6 | * @public
7 | */
8 | export default class BaseModel {
9 | w: number
10 | h: number
11 | canvas: HTMLCanvasElement
12 | ctx: CanvasRenderingContext2D
13 | options: Required
14 | element: ElementType
15 | // 模型默认选项
16 | modelDefOptions: T | undefined = undefined
17 | // 模型限制
18 | limits: Array | undefined = undefined
19 | // 提供模型初始化的回调函数
20 | modelDefCall: ((model: BaseModel) => void) | undefined = undefined
21 | webLog: $Log = $Log
22 | private stepClear = 1
23 | /**
24 | * @description 自定义基础模型
25 | * @param {number} w - Canvas宽度
26 | * @param {number} h - Canvas高度
27 | * @param {HTMLCanvasElement} canvas - Canvas元素
28 | * @param {Required} options - 配置选项
29 | * @param {ElementType} element - 容器元素
30 | * @param {T} [modelDefOptions] - 模型默认选项(可选)
31 | * @param {Array} [limits] - 模型默认限制(可选)
32 | * @param {(model: BaseModel) => void} [modelDefCall] - 提供模型初始化的回调函数,通常在模型中初始化"canvas"或"画笔"(可选)
33 | */
34 | constructor(
35 | w: number,
36 | h: number,
37 | canvas: HTMLCanvasElement,
38 | options: Required,
39 | element: ElementType,
40 | modelDefOptions?: T,
41 | limits?: Array,
42 | modelDefCall?: (model: BaseModel) => void
43 | ) {
44 | this.w = w
45 | this.h = h
46 | this.canvas = canvas
47 | // 默认获取2d画笔
48 | this.ctx = canvas.getContext('2d')!
49 | this.options = options
50 | this.element = element
51 | // 初始化模型
52 | this.initContextCall(modelDefOptions, limits, modelDefCall)
53 | }
54 | // 初始化画笔
55 | private _$initBaseContext() {
56 | this.clearRect()
57 | this.ctx.resetTransform()
58 | // 默认主题颜色
59 | const op = this.options,
60 | defW = this.canvas.width,
61 | defH = this.canvas.height
62 | this.ctx.fillStyle = op.themeColor!
63 | this.ctx.strokeStyle = op.themeColor!
64 | this.ctx.shadowColor = op.shadowColor!
65 | this.ctx.font = `${op.fontSize}px ${op.fontFamily} small-caps`
66 | this.ctx.textAlign = 'center'
67 | this.ctx.textBaseline = 'middle'
68 | this.ctx.translate(defW / 2, defH / 2)
69 | // 同步大小处理失真
70 | const dpr = window.devicePixelRatio || 1
71 | this.ctx.scale(dpr, dpr)
72 | this.ctx.save()
73 | }
74 | // 初始化默认事件
75 | private _$initEvent() {
76 | // 关闭前清空画布
77 | if (this.element.$store)
78 | this.element.$store.hookCall.beforeClose(() => {
79 | this.clearRect()
80 | })
81 | }
82 | /**
83 | * @description 封装requestAnimationFrame触发动画针
84 | * @param {() => void} fun - 触发函数
85 | * @private
86 | */
87 | private _$animationFrame(fun: () => void) {
88 | const $store = this.element.$store
89 | if (!$store) return
90 | // 兼容处理
91 | if (!window.requestAnimationFrame) {
92 | $store.animationId = window.setInterval(fun, this.options.delay)
93 | }
94 | // 使用时间轴控制触发时间
95 | let endTime = Date.now() + this.options.delay!
96 | fun.call(this)
97 | const run = () => {
98 | if (Date.now() > endTime) {
99 | fun.call(this)
100 | endTime = Date.now() + this.options.delay!
101 | }
102 | $store.animationId = window.requestAnimationFrame(run)
103 | }
104 | $store.animationId = window.requestAnimationFrame(run)
105 | }
106 | /**
107 | * @description 初始化画笔属性
108 | * @param {T} [modelDefOptions] - 提供模型初始化的选项
109 | * @param {Array} [limits] - 提供模型初始化的限制
110 | * @param {(model: BaseModel) => void} [modelDefCall] - 提供模型初始化的回调函数
111 | */
112 | initContextCall(modelDefOptions?: T, limits?: Array, modelDefCall?: (model: BaseModel) => void) {
113 | // 初始化基础点上下文
114 | this._$initBaseContext()
115 | // 初始化基础钩子事件
116 | this._$initEvent()
117 | // 初始化模型选项
118 | if (isNull(modelDefOptions)) {
119 | // 记录选项
120 | this.modelDefOptions = modelDefOptions
121 | this.options = Object.assign(modelDefOptions, this.options)
122 | if (this.element.$store) this.element.$store.options = this.options
123 | // 判断属性值是否需要限制(仅用于提示)
124 | if (limits && limits.length && this.options.toast) {
125 | limits.forEach((l: LimitType) => {
126 | const mayKey = this.options[l.key as keyof typeof this.options]
127 | if (isNull(mayKey) && !l.limit(mayKey)) $Log.warn(l.message)
128 | })
129 | }
130 | }
131 | if (isNull(limits)) this.limits = limits
132 | if (isNull(modelDefCall)) {
133 | // this.modelDefCall = modelDefCall
134 | modelDefCall.call(this, this)
135 | }
136 | }
137 | /**
138 | * @description 开始动画
139 | * @param {() => void} fun - 动画函数
140 | */
141 | run(fun: () => void) {
142 | // 如果已经处于加载状态,无需重新实例化
143 | const $store = this.element.$store
144 | if ($store && $store.animationId) this.clearAnimationFrame($store.animationId)
145 | this._$animationFrame(fun)
146 | }
147 | /**
148 | * @description 取消animationFrame动画针
149 | * @param {number} id - 动画ID
150 | */
151 | clearAnimationFrame(id: number) {
152 | clearAnimationFrame(id)
153 | }
154 | /**
155 | * @description 清空画布
156 | * @param {number} [x] - x坐标
157 | * @param {number} [y] - y坐标
158 | * @param {number} [w_r] - 宽度或半径
159 | * @param {number} [h] - 高度
160 | */
161 | clearRect(x?: number, y?: number, w_r?: number, h?: number) {
162 | const defW = this.canvas.width,
163 | defH = this.canvas.height
164 | // 因为起点已设置为中心,需要扩展
165 | if (isNull(x) && isNull(y) && isNull(w_r) && isNull(h)) {
166 | this.ctx.clearRect(x, y, w_r, h)
167 | }
168 | // 清空圆形区域
169 | else if (isNull(x) && isNull(y) && isNull(w_r) && !isNull(h)) {
170 | const calcWidth = w_r - this.stepClear
171 | const calcHeight = Math.sqrt(w_r * w_r - calcWidth * calcWidth)
172 | const posX = x - calcWidth
173 | const posY = y - calcHeight
174 | const widthX = 2 * calcWidth
175 | const heightY = 2 * calcHeight
176 | if (this.stepClear <= w_r) {
177 | this.ctx.clearRect(posX, posY, widthX, heightY)
178 | this.stepClear += 1
179 | this.clearRect(x, y, w_r)
180 | } else {
181 | this.stepClear = 1
182 | }
183 | } else this.ctx.clearRect(-defW, -defH, defW * 2, defH * 2)
184 | }
185 | /**
186 | * @description 绘制圆角矩形
187 | * @param {number} x - x坐标
188 | * @param {number} y - y坐标
189 | * @param {number} w - 宽度
190 | * @param {number} h - 高度
191 | * @param {number} r - 圆角半径
192 | */
193 | drawRadiusRect(x: number, y: number, w: number, h: number, r: number) {
194 | this.ctx.beginPath()
195 | this.ctx.arc(x + r, y + r, r, 1 * Math.PI, 1.5 * Math.PI)
196 | this.ctx.lineTo(x + w - r, y)
197 | this.ctx.arc(x + w - r, y + r, r, 1.5 * Math.PI, 0)
198 | this.ctx.lineTo(x + w, y + h - r)
199 | this.ctx.arc(x + w - r, y + h - r, r, 0, 0.5 * Math.PI)
200 | this.ctx.lineTo(x + r, y + h)
201 | this.ctx.arc(x + r, y + h - r, r, 0.5 * Math.PI, Math.PI)
202 | this.ctx.lineTo(x, y + r)
203 | this.ctx.closePath()
204 | }
205 | /**
206 | * @description 绘制文本
207 | * @param {DrawTextParamsType} [params] - 文本参数
208 | * DrawTextParamsType:
209 | * esGap?: 额外空隙
210 | * x?: X轴位置
211 | * text?: 文本内容
212 | * textColor?: 文本颜色
213 | */
214 | drawText(params?: DrawTextParamsType) {
215 | const op = this.options
216 | const pm: Required = Object.assign(
217 | { esGap: op.textGap || 0, x: 0, text: op.text || '', textColor: op.themeColor || 'rgba(64,158,255,1)' },
218 | params
219 | )
220 | this.ctx.save()
221 | this.ctx.beginPath()
222 | this.ctx.fillStyle = pm.textColor
223 | this.ctx.fillText(pm.text, pm.x, (op.textGap || 0) + (op.fontSize || 0) + pm.esGap)
224 | this.ctx.closePath()
225 | this.ctx.restore()
226 | }
227 | }
228 |
--------------------------------------------------------------------------------
/src/draw/model/Bean.ts:
--------------------------------------------------------------------------------
1 | import type { ElementType, LimitType } from '../../type'
2 | import type { BeanOptionsType } from '../type'
3 | import BaseModel from './BaseModel'
4 | /**
5 | * @description Bean模型默认配置选项
6 | */
7 | const modelDefOptions: BeanOptionsType = {
8 | beanSize: 15,
9 | pointLength: 15
10 | }
11 |
12 | /**
13 | * @description Bean模型限制条件
14 | */
15 | const limits: Array = [
16 | {
17 | key: 'pointLength',
18 | message: 'pointLength value >= 5',
19 | limit: (key: any) => {
20 | return key >= 5
21 | }
22 | },
23 | {
24 | key: 'beanSize',
25 | message: 'beanSize value >= 5',
26 | limit: (key: any) => {
27 | return key >= 5
28 | }
29 | }
30 | ]
31 | /**
32 | * @description Bean类型接口
33 | */
34 | interface BeanType {
35 | turn: number
36 | state: number
37 | nowX: number
38 | beans: Array
39 | beanState: number
40 | beanAnimaIndex: number
41 | }
42 | /**
43 | * @description Bean模型类
44 | * @extends BaseModel
45 | */
46 | export default class Bean extends BaseModel {
47 | bean: BeanType
48 | /**
49 | * @description 构造函数
50 | * @param {number} w - 宽度
51 | * @param {number} h - 高度
52 | * @param {HTMLCanvasElement} canvas - Canvas元素
53 | * @param {Required} options - 配置选项
54 | * @param {ElementType} element - 容器元素
55 | */
56 | constructor(
57 | w: number,
58 | h: number,
59 | canvas: HTMLCanvasElement,
60 | options: Required,
61 | element: ElementType
62 | ) {
63 | super(w, h, canvas, options, element, modelDefOptions, limits)
64 | this.bean = {
65 | turn: 30,
66 | state: 1,
67 | beanState: 1,
68 | nowX: -(this.options.pointLength * this.options.beanSize) / 2 - this.options.beanSize * 3,
69 | beans: Array.from({ length: this.options.pointLength }, () => 1),
70 | beanAnimaIndex: 0
71 | }
72 | this.options.delay = 10
73 | this.run(this.draw)
74 | }
75 | /**
76 | * @description 绘制豆形
77 | */
78 | draw() {
79 | const op = this.options
80 | this.clearRect()
81 | this.ctx.save()
82 | this.ctx.beginPath()
83 | this.ctx.translate(this.bean.nowX, 0)
84 | this.ctx.arc(0, 0, op.beanSize, ((360 - this.bean.turn) * Math.PI) / 180, (this.bean.turn * Math.PI) / 180, true)
85 | this.ctx.lineTo(0, 0)
86 | this.ctx.fill()
87 | this.ctx.closePath()
88 | this.ctx.restore()
89 | // 绘制点
90 | this.drawPoint()
91 | // 绘制过滤器
92 | this.drawFilter()
93 | // 绘制文本
94 | this.drawText({ esGap: op.beanSize })
95 | // 控制进程
96 | this.controller()
97 | }
98 | /**
99 | * @description 控制豆形动画
100 | */
101 | controller() {
102 | const op = this.options
103 | if (this.bean.nowX >= (op.pointLength * op.beanSize) / 2 + op.beanSize * 2) {
104 | this.bean.nowX = -(op.pointLength * op.beanSize) / 2 - op.beanSize * 3
105 | this.bean.beanAnimaIndex = 0
106 | }
107 | if (this.bean.nowX <= -(op.pointLength * op.beanSize) / 2) {
108 | this.bean.beanState = 2
109 | }
110 | if (this.bean.turn <= 0) this.bean.state = 2
111 | if (this.bean.turn >= 30) this.bean.state = 1
112 | if (this.bean.state === 1) this.bean.turn -= 1
113 | if (this.bean.state === 2) this.bean.turn += 1
114 | if (this.bean.beanState === 1) this.bean.nowX -= 1
115 | if (this.bean.beanState === 2) this.bean.nowX += 1
116 | }
117 | /**
118 | * @description 绘制点
119 | */
120 | drawPoint() {
121 | const op = this.options
122 | this.ctx.save()
123 | this.setShadow()
124 | this.ctx.translate(-(op.pointLength * op.beanSize) / 2, 0)
125 | for (let i = 0; i < op.pointLength && i < this.bean.beanAnimaIndex; i++) {
126 | this.ctx.beginPath()
127 | if (i < this.bean.beanAnimaIndex) this.ctx.arc(op.beanSize * i, 0, op.beanSize / 4, 0, Math.PI * 2)
128 | this.ctx.fill()
129 | this.ctx.closePath()
130 | }
131 | this.bean.beanAnimaIndex += 0.2
132 | this.ctx.restore()
133 | }
134 | /**
135 | * @description 绘制过滤器
136 | */
137 | drawFilter() {
138 | const op = this.options
139 | // 眼睛
140 | this.clearRect(-op.beanSize / 3 + this.bean.nowX, -op.beanSize / 2, op.beanSize / 4)
141 | // 轨迹
142 | this.clearRect(
143 | -(op.pointLength * op.beanSize) / 2 - op.beanSize / 2 + 0.2,
144 | -this.h,
145 | this.bean.nowX + (op.pointLength * op.beanSize) / 2 - op.beanSize / 2,
146 | this.h * 2
147 | )
148 | // 进入
149 | this.clearRect(-(op.pointLength * op.beanSize) / 2, -this.h, -180, this.h * 2)
150 | // 离开
151 | this.clearRect((op.pointLength * op.beanSize) / 2, -this.h, 180, this.h * 2)
152 | }
153 | /**
154 | * @description 设置阴影
155 | */
156 | setShadow() {
157 | const op = this.options
158 | this.ctx.shadowOffsetX = op.shadowOffsetX
159 | this.ctx.shadowOffsetY = op.shadowOffsetY
160 | this.ctx.shadowBlur = op.shadowBlur
161 | }
162 | }
163 |
--------------------------------------------------------------------------------
/src/draw/model/Circular.ts:
--------------------------------------------------------------------------------
1 | import type { ElementType } from '../../type'
2 | import type { CircularOptionsType } from '../type'
3 | import { CIRCULAR_ACTION } from '../../utils'
4 | import BaseModel from './BaseModel'
5 | /**
6 | * @description Circular模型默认配置选项
7 | */
8 | const modelDefOptions: CircularOptionsType = {
9 | arcSize: 8,
10 | arcGap: 2,
11 | arcColors: ['#ec7546', '#8364a4', '#ff6c6e', '#5bc6ab'],
12 | action: CIRCULAR_ACTION.COLLISION
13 | }
14 |
15 | /**
16 | * @description 碰撞类型接口
17 | */
18 | interface CollisionType {
19 | key: number
20 | state: boolean
21 | y: number
22 | x: number
23 | }
24 | /**
25 | * @description 圆形模型类
26 | * @extends BaseModel
27 | */
28 | export default class Circular extends BaseModel {
29 | collisionPoint: Array
30 | turn: number
31 | /**
32 | * @description 构造函数
33 | * @param {number} w - 宽度
34 | * @param {number} h - 高度
35 | * @param {HTMLCanvasElement} canvas - Canvas元素
36 | * @param {Required} options - 配置选项
37 | * @param {ElementType} element - 容器元素
38 | */
39 | constructor(
40 | w: number,
41 | h: number,
42 | canvas: HTMLCanvasElement,
43 | options: Required,
44 | element: ElementType
45 | ) {
46 | super(w, h, canvas, options, element, modelDefOptions)
47 | // 初始化数据
48 | const op = this.options
49 | const gap = op.arcSize * 2 + op.arcGap
50 | this.collisionPoint = [
51 | {
52 | key: 0,
53 | state: false,
54 | y: -gap,
55 | x: 0
56 | },
57 | {
58 | key: 1,
59 | state: false,
60 | y: gap,
61 | x: 0
62 | },
63 | {
64 | key: 2,
65 | state: false,
66 | y: 0,
67 | x: gap
68 | },
69 | {
70 | key: 3,
71 | state: false,
72 | y: 0,
73 | x: -gap
74 | }
75 | ]
76 | this.turn = 0
77 | this.run(this.draw)
78 | }
79 | /**
80 | * @description 绘制圆形
81 | */
82 | draw() {
83 | this.clearRect()
84 | this.ctx.save()
85 | const op = this.options
86 | // 控制进程
87 | if (op.action === CIRCULAR_ACTION.COLLISION) this.controller()
88 | // 旋转
89 | else if (op.action === CIRCULAR_ACTION.ROTATE) {
90 | this.ctx.rotate((this.turn * Math.PI) / 180)
91 | this.turn += 10
92 | }
93 | this.drawCircular()
94 | this.ctx.restore()
95 | this.drawText({ esGap: op.arcSize * 4 + op.arcGap * 2 })
96 | }
97 | /**
98 | * @description 控制圆形动画
99 | */
100 | controller() {
101 | const op = this.options
102 | this.collisionPoint.forEach((cp: CollisionType) => {
103 | let key: 'y' | 'x' = 'y'
104 | switch (cp.key) {
105 | case 0:
106 | key = 'y'
107 | break
108 | case 1:
109 | key = 'y'
110 | break
111 | case 2:
112 | key = 'x'
113 | break
114 | case 3:
115 | key = 'x'
116 | break
117 | }
118 | const gap = op.arcSize * 2 + op.arcGap
119 | if (cp[key] === -gap) cp.state = true
120 | if (cp[key] === gap) cp.state = false
121 | if (cp.state) cp[key]++
122 | else cp[key]--
123 | })
124 | }
125 | /**
126 | * @description 绘制圆形元素
127 | */
128 | drawCircular() {
129 | const op = this.options
130 | this.collisionPoint.forEach((cp: CollisionType, index: number) => {
131 | this.ctx.save()
132 | this.ctx.beginPath()
133 | if (op.arcColors[index]) {
134 | const color = op.arcColors[index]
135 | this.setShadow(color)
136 | this.ctx.fillStyle = color
137 | }
138 | this.ctx.arc(cp.x, cp.y, op.arcSize, 0, Math.PI * 2)
139 | this.ctx.fill()
140 | this.ctx.closePath()
141 | this.ctx.restore()
142 | })
143 | }
144 | /**
145 | * @description 设置阴影
146 | * @param {string} [color] - 阴影颜色
147 | */
148 | setShadow(color?: string) {
149 | const op = this.options
150 | color && (this.ctx.shadowColor = color)
151 | this.ctx.shadowOffsetX = op.shadowOffsetX
152 | this.ctx.shadowOffsetY = op.shadowOffsetY
153 | this.ctx.shadowBlur = op.shadowBlur
154 | }
155 | }
156 |
--------------------------------------------------------------------------------
/src/draw/model/Clock.ts:
--------------------------------------------------------------------------------
1 | import type { ElementType, LimitType } from '../../type'
2 | import type { ClockOptionsType } from '../type'
3 | import BaseModel from './BaseModel'
4 | /**
5 | * @description 时钟模型默认配置选项
6 | */
7 | const modelDefOptions: ClockOptionsType = {
8 | lineCap: 'round',
9 | lineWidth: 2,
10 | lineColors: ['#d4d4d4', '#06ab2d', '#8a0303'],
11 | clockSize: 15,
12 | clockGap: 4,
13 | hLine: true,
14 | mLine: false,
15 | sLine: true,
16 | textTime: ''
17 | }
18 |
19 | /**
20 | * @description 时钟模型限制条件
21 | */
22 | const limits: Array = [
23 | {
24 | key: 'lineColors',
25 | message: 'lineColors.length <= 3',
26 | limit: (key: any) => {
27 | return key.length <= 3
28 | }
29 | }
30 | ]
31 | /**
32 | * @description 时钟模型类
33 | * @extends BaseModel
34 | */
35 | export default class Clock extends BaseModel {
36 | nowTime: number
37 | nowS: number
38 | /**
39 | * @description 构造函数
40 | * @param {number} w - 宽度
41 | * @param {number} h - 高度
42 | * @param {HTMLCanvasElement} canvas - Canvas元素
43 | * @param {Required} options - 配置选项
44 | * @param {ElementType} element - 容器元素
45 | */
46 | constructor(
47 | w: number,
48 | h: number,
49 | canvas: HTMLCanvasElement,
50 | options: Required,
51 | element: ElementType
52 | ) {
53 | super(w, h, canvas, options, element, modelDefOptions, limits, (model) => {
54 | const op = model.options
55 | model.ctx.lineCap = op.lineCap
56 | model.ctx.lineWidth = op.lineWidth
57 | model.ctx.save()
58 | })
59 | this.nowTime = -1
60 | this.nowS = 0
61 | this.run(this.draw)
62 | }
63 | /**
64 | * @description 绘制时钟
65 | */
66 | draw() {
67 | this.clearRect()
68 | // 绘制时钟
69 | this.drawClock()
70 | }
71 | /**
72 | * @description 绘制时钟元素
73 | */
74 | drawClock() {
75 | const op = this.options
76 | const s = new Date().getSeconds()
77 | const m = new Date().getMinutes()
78 | const h = new Date().getHours()
79 | // 顶部
80 | this.ctx.save()
81 | this.ctx.beginPath()
82 | this.setShadow()
83 | this.ctx.moveTo(-5, -(op.clockSize + op.clockGap))
84 | this.ctx.lineTo(5, -(op.clockSize + op.clockGap))
85 | this.ctx.stroke()
86 | this.ctx.closePath()
87 | // 外圆
88 | this.ctx.beginPath()
89 | this.setShadow()
90 | this.ctx.arc(0, 0, op.clockSize, 0, Math.PI * 2)
91 | this.ctx.stroke()
92 | this.ctx.closePath()
93 | this.ctx.restore()
94 | // 刻度
95 | this.ctx.save()
96 | for (let i = 0; i < 12; i++) {
97 | this.ctx.beginPath()
98 | this.ctx.rotate(((360 / 12) * Math.PI) / 180)
99 | this.ctx.moveTo(op.clockSize - op.clockGap, 0)
100 | this.ctx.lineTo(op.clockSize - op.clockGap, 0)
101 | this.ctx.stroke()
102 | this.ctx.closePath()
103 | }
104 | this.ctx.restore()
105 | // 时针
106 | if (op.hLine) {
107 | this.ctx.save()
108 | this.ctx.beginPath()
109 | this.ctx.lineWidth = op.lineWidth * 1.6
110 | if (op.lineColors[0]) this.ctx.strokeStyle = op.lineColors[0]
111 | // 初始化点
112 | this.ctx.rotate((-90 * Math.PI) / 180)
113 | this.ctx.rotate((((h * 360) / 60) * Math.PI) / 180)
114 | this.ctx.moveTo(-1, 0)
115 | this.ctx.lineTo(op.clockSize / 2, 0)
116 | this.ctx.stroke()
117 | this.ctx.closePath()
118 | this.ctx.restore()
119 | }
120 | // 分针
121 | if (op.mLine) {
122 | this.ctx.save()
123 | this.ctx.beginPath()
124 | if (op.lineColors[1]) this.ctx.strokeStyle = op.lineColors[1]
125 | this.ctx.lineWidth = op.lineWidth * 1.2
126 | // 初始化点
127 | this.ctx.rotate((-90 * Math.PI) / 180)
128 | this.ctx.rotate((((m * 360) / 60) * Math.PI) / 180)
129 | this.ctx.moveTo(-1, 0)
130 | this.ctx.lineTo(op.clockSize / 2 + op.clockGap, 0)
131 | this.ctx.stroke()
132 | this.ctx.closePath()
133 | this.ctx.restore()
134 | }
135 | // 秒针
136 | if (op.sLine) {
137 | this.ctx.save()
138 | this.ctx.beginPath()
139 | if (op.lineColors[2]) this.ctx.strokeStyle = op.lineColors[2]
140 | this.ctx.rotate((-90 * Math.PI) / 180)
141 | this.ctx.rotate((((s * 360) / 60) * Math.PI) / 180)
142 | this.ctx.moveTo(-1, 0)
143 | this.ctx.lineTo(op.clockSize - op.clockGap, 0)
144 | this.ctx.stroke()
145 | this.ctx.closePath()
146 | this.ctx.restore()
147 | if (this.nowS !== s) this.nowTime++
148 | this.nowS = s
149 | }
150 | if (op.textTime === 'time') op.text = `${h} : ${m} : ${s}`
151 | if (op.textTime === 's') op.text = this.nowTime + 's'
152 | this.drawText({ esGap: op.clockSize * 2 })
153 | }
154 | /**
155 | * @description 设置阴影
156 | */
157 | setShadow() {
158 | const op = this.options
159 | this.ctx.shadowOffsetX = op.shadowOffsetX
160 | this.ctx.shadowOffsetY = op.shadowOffsetY
161 | this.ctx.shadowBlur = op.shadowBlur
162 | }
163 | }
164 |
--------------------------------------------------------------------------------
/src/draw/model/Gear.ts:
--------------------------------------------------------------------------------
1 | import type { ElementType, LimitType } from '../../type'
2 | import type { GearOptionsType } from '../type'
3 | import BaseModel from './BaseModel'
4 | /**
5 | * @description 齿轮模型默认配置选项
6 | */
7 | const modelDefOptions: GearOptionsType = {
8 | lineStart: 10,
9 | lineEnd: 16,
10 | lineStartSkew: 0,
11 | lineEndSkew: 0,
12 | lineWidth: 4,
13 | lineCap: 'round',
14 | lineNum: 10,
15 | direction: true
16 | }
17 | /**
18 | * @description 齿轮模型限制条件
19 | */
20 | const limits: Array = [
21 | {
22 | key: 'lineNum',
23 | message: 'lineNum value 4-18',
24 | limit: (key: any) => {
25 | return key >= 4 && key <= 18
26 | }
27 | }
28 | ]
29 | /**
30 | * @description 齿轮模型类
31 | * @extends BaseModel
32 | */
33 | export default class Gear extends BaseModel {
34 | aps: Array
35 | /**
36 | * @description 构造函数
37 | * @param {number} w - 宽度
38 | * @param {number} h - 高度
39 | * @param {HTMLCanvasElement} canvas - Canvas元素
40 | * @param {Required} options - 配置选项
41 | * @param {ElementType} element - 容器元素
42 | */
43 | constructor(
44 | w: number,
45 | h: number,
46 | canvas: HTMLCanvasElement,
47 | options: Required,
48 | element: ElementType
49 | ) {
50 | // 1.初始化
51 | super(w, h, canvas, options, element, modelDefOptions, limits, (model) => {
52 | // 模型额外初始化回调函数
53 | const op = model.options
54 | model.ctx.lineCap = op.lineCap
55 | model.ctx.lineWidth = op.lineWidth
56 | model.ctx.save()
57 | })
58 | this.optimization(this.options.textGap + this.options.lineEnd)
59 | // 2.启动动画针并记录状态
60 | this.aps = Array.from({ length: this.options.lineNum }, (_, _index) => _index)
61 | this.run(this.draw)
62 | }
63 |
64 | /**
65 | * @description 绘制齿轮
66 | */
67 | draw() {
68 | this.clearRect()
69 | // 控制进程
70 | this.controller()
71 | // 绘制齿轮
72 | this.drawGear()
73 | // 绘制文本
74 | const op = this.options
75 | this.drawText({ esGap: op.lineEnd })
76 | }
77 | /**
78 | * @description 控制齿轮动画
79 | */
80 | controller() {
81 | const op = this.options
82 | if (op.direction) this.aps = this.aps.map((a) => (a - 1 <= 0 ? this.aps.length - 1 : a - 1))
83 | else this.aps = this.aps.map((a) => (a + 1 > this.aps.length ? 0 : a + 1))
84 | }
85 | /**
86 | * @description 绘制齿轮元素
87 | */
88 | drawGear() {
89 | const op = this.options
90 | this.ctx.save()
91 | // 设置阴影
92 | this.ctx.shadowOffsetX = op.shadowOffsetX
93 | this.ctx.shadowOffsetY = op.shadowOffsetY
94 | this.ctx.shadowBlur = op.shadowBlur
95 | // 绘制加载齿轮
96 | for (let i = 0; i < this.aps.length; i++) {
97 | this.ctx.beginPath()
98 | this.ctx.globalAlpha = this.aps[i] / 10
99 | this.ctx.moveTo(op.lineEndSkew, op.lineStart)
100 | this.ctx.lineTo(op.lineStartSkew, op.lineEnd)
101 | this.ctx.stroke()
102 | this.ctx.closePath()
103 | this.ctx.rotate((2 * Math.PI) / op.lineNum)
104 | }
105 | this.ctx.restore()
106 | }
107 | /**
108 | * @description 优化处理(主要优化默认文本位置)
109 | * @param {number} textY - 文本Y坐标
110 | */
111 | optimization(textY: number) {
112 | // 如果开启优化(优化字体位置)
113 | if (this.options.optimization) {
114 | // 根据宽高调整
115 | if (textY * 4 > this.h) {
116 | this.options.lineStart = this.h / 6 - 5
117 | this.options.lineEnd = this.h / 6
118 | this.options.textGap = 2
119 | }
120 | }
121 | }
122 | }
123 |
--------------------------------------------------------------------------------
/src/draw/model/Img.ts:
--------------------------------------------------------------------------------
1 | import type { ElementType } from '../../type'
2 | import type { ImageOptionsType } from '../type'
3 | import BaseModel from './BaseModel'
4 | /**
5 | * @description 图片模型默认配置选项
6 | */
7 | const modelDefOptions: ImageOptionsType = {
8 | src: 'https://tommyrunner.github.io/web-loading/images/logo.png',
9 | width: 52,
10 | height: 52,
11 | turn: true
12 | }
13 | /**
14 | * @description 图片模型类
15 | * @extends BaseModel
16 | */
17 | export default class Img extends BaseModel {
18 | img: HTMLImageElement
19 | turn: number
20 | /**
21 | * @description 构造函数
22 | * @param {number} w - 宽度
23 | * @param {number} h - 高度
24 | * @param {HTMLCanvasElement} canvas - Canvas元素
25 | * @param {Required} options - 配置选项
26 | * @param {ElementType} element - 容器元素
27 | */
28 | constructor(
29 | w: number,
30 | h: number,
31 | canvas: HTMLCanvasElement,
32 | options: Required,
33 | element: ElementType
34 | ) {
35 | super(w, h, canvas, options, element, modelDefOptions)
36 | this.img = new Image()
37 | this.img.src = this.options.src
38 | this.turn = 10
39 | this.img.onload = () => {
40 | this.run(this.draw)
41 | }
42 | }
43 | /**
44 | * @description 绘制图片
45 | */
46 | draw() {
47 | this.clearRect()
48 | this.drawImg()
49 | const op = this.options
50 | this.drawText({ esGap: op.height / 2 })
51 | }
52 | /**
53 | * @description 绘制图片元素
54 | */
55 | drawImg() {
56 | const op = this.options
57 | this.ctx.save()
58 | if (op.turn) this.ctx.rotate((this.turn * Math.PI) / 180)
59 | this.ctx.drawImage(this.img, -op.width / 2, -op.height / 2, op.width, op.height)
60 | this.ctx.closePath()
61 | this.ctx.restore()
62 | this.turn += 10
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/src/draw/model/Pattern.ts:
--------------------------------------------------------------------------------
1 | import type { ElementType, LimitType } from '../../type'
2 | import type { PatternOptionsType } from '../type'
3 | import { getDefOptions, PATTERN_CHART } from '../../utils'
4 | import BaseModel from './BaseModel'
5 | /**
6 | * @description 图案模型默认配置选项
7 | */
8 | const modelDefOptions: PatternOptionsType = {
9 | charts: [PATTERN_CHART.ARC, PATTERN_CHART.RECT, PATTERN_CHART.TRIANGLE, PATTERN_CHART.HEART, PATTERN_CHART.POLYGON],
10 | chartColors: ['#409EFF', '#67C23A', '#E6A23C', '#F56C6C', '#0960bd'],
11 | maxHeight: 60,
12 | chartSize: 12
13 | }
14 | /**
15 | * @description 图案模型限制条件
16 | */
17 | const limits: Array = [
18 | {
19 | key: 'chartSize',
20 | message: 'chartSize value 5-24',
21 | limit: (key: any) => {
22 | return key >= 5 && key <= 24
23 | }
24 | },
25 | {
26 | key: 'delay',
27 | message: 'Pattern.delay not allowed update',
28 | limit: (key: any) => {
29 | return key === getDefOptions().delay
30 | }
31 | }
32 | ]
33 | /**
34 | * @description 图案类型接口
35 | */
36 | interface PatternType {
37 | // 当前高度
38 | nowHeight: number
39 | // 当前图形
40 | chart: PATTERN_CHART
41 | // 当前颜色
42 | color: string
43 | // 当前旋转角度
44 | turn: number
45 | // 阴影
46 | shadow: number
47 | // 0: 初始化, 1: 上升, 2: 下降
48 | nowState: number
49 | }
50 | /**
51 | * @description 图案模型类
52 | * @extends BaseModel
53 | */
54 | export default class Pattern extends BaseModel {
55 | pattern: PatternType
56 | /**
57 | * @description 构造函数
58 | * @param {number} w - 宽度
59 | * @param {number} h - 高度
60 | * @param {HTMLCanvasElement} canvas - Canvas元素
61 | * @param {Required} options - 配置选项
62 | * @param {ElementType} element - 容器元素
63 | */
64 | constructor(
65 | w: number,
66 | h: number,
67 | canvas: HTMLCanvasElement,
68 | options: Required,
69 | element: ElementType
70 | ) {
71 | super(w, h, canvas, options, element, modelDefOptions, limits, (model) => {
72 | model.options.delay = 10
73 | })
74 | this.pattern = {
75 | color: this.randomState('chartColors'),
76 | nowHeight: 10,
77 | chart: this.randomState('charts'),
78 | shadow: 0,
79 | nowState: 1,
80 | turn: 0
81 | }
82 | this.run(this.draw)
83 | }
84 | /**
85 | * @description 绘制图案
86 | */
87 | draw() {
88 | const op = this.options
89 | this.clearRect()
90 | this.ctx.save()
91 | this.ctx.beginPath()
92 | this.ctx.translate(0, this.pattern.nowHeight)
93 | this.ctx.rotate((this.pattern.turn / Math.PI) * 2)
94 | this.ctx.fillStyle = this.pattern.color
95 | this.selectChart(0, 0, op.chartSize)
96 | this.ctx.closePath()
97 | this.ctx.restore()
98 | this.drawShadow()
99 | // 清空隐藏部分
100 | this.clearRect(-this.w, 0, this.w * 2, this.h)
101 | // 控制值变化
102 | this.controller(op)
103 | this.drawText({ textColor: this.pattern.color })
104 | }
105 | /**
106 | * @description 控制图案动画
107 | * @param {Required} op - 配置选项
108 | */
109 | controller(op: Required) {
110 | this.pattern.turn += 10 // 角度
111 | // 高度和阴影
112 | if (this.pattern.nowState === 1) {
113 | this.pattern.nowHeight--
114 | this.pattern.shadow += 0.2
115 | } else if (this.pattern.nowState === 2) {
116 | this.pattern.nowHeight++
117 | this.pattern.shadow -= 0.2
118 | }
119 | this.pattern.shadow = Math.floor(this.pattern.shadow * 100) / 100
120 | // 速度
121 | if (this.pattern.nowHeight <= -op.chartSize && this.pattern.nowHeight % 8 == 0) {
122 | op.delay += 0.5
123 | op.delay = Math.floor(op.delay * 100) / 100
124 | }
125 | // 范围
126 | if (this.pattern.nowHeight <= -op.maxHeight) {
127 | this.pattern.nowState = 2
128 | } else if (this.pattern.nowHeight >= op.chartSize) {
129 | this.pattern.nowState = 1
130 | op.delay = 10
131 | // 切换图形
132 | this.pattern.chart = this.randomState('charts')
133 | // 切换颜色
134 | this.pattern.color = this.randomState('chartColors')
135 | }
136 | }
137 | /**
138 | * @description 选择图形类型
139 | * @param {number} x - x坐标
140 | * @param {number} y - y坐标
141 | * @param {number} size - 尺寸
142 | */
143 | selectChart(x: number, y: number, size: number) {
144 | switch (this.pattern.chart) {
145 | case PATTERN_CHART.RECT:
146 | this.drawRect(x, y, size)
147 | break
148 | case PATTERN_CHART.ARC:
149 | this.drawArc(x, y, size)
150 | break
151 | case PATTERN_CHART.TRIANGLE:
152 | this.drawTriangle(x, y, size)
153 | break
154 | case PATTERN_CHART.HEART:
155 | this.drawHeart(x, y, size)
156 | break
157 | case PATTERN_CHART.POLYGON:
158 | this.drawPolygon(x, y, size)
159 | break
160 | }
161 | }
162 | /**
163 | * @description 随机选择状态
164 | * @param {any} key - 键名
165 | * @returns {PATTERN_CHART} 返回随机选择的图形类型
166 | */
167 | randomState(key: any): PATTERN_CHART {
168 | const op: any = this.options
169 | return op[key][parseInt(String(Math.random() * op[key].length))]
170 | }
171 | /**
172 | * @description 绘制阴影
173 | */
174 | drawShadow() {
175 | this.ctx.save()
176 | this.ctx.beginPath()
177 | this.setShadow()
178 | this.ctx.globalAlpha = 0.2
179 | this.ctx.strokeStyle = this.pattern.color
180 | this.ctx.moveTo(-this.pattern.shadow / 2, 0)
181 | this.ctx.lineTo(this.pattern.shadow, 0)
182 | this.ctx.stroke()
183 | this.ctx.beginPath()
184 | this.ctx.restore()
185 | }
186 | /**
187 | * @description 绘制矩形
188 | * @param {number} x - x坐标
189 | * @param {number} y - y坐标
190 | * @param {number} size - 尺寸
191 | */
192 | drawRect(x: number, y: number, size: number) {
193 | this.ctx.save()
194 | this.ctx.beginPath()
195 | this.setShadow()
196 | this.ctx.translate(-size / 2, -size / 2)
197 | this.ctx.fillRect(x, y, size, size)
198 | this.ctx.closePath()
199 | this.ctx.restore()
200 | }
201 | /**
202 | * @description 绘制圆形
203 | * @param {number} x - x坐标
204 | * @param {number} y - y坐标
205 | * @param {number} size - 尺寸
206 | */
207 | drawArc(x: number, y: number, size: number) {
208 | this.ctx.save()
209 | this.ctx.beginPath()
210 | this.setShadow()
211 | this.ctx.arc(x, y, size / 2, 0, Math.PI * 2)
212 | this.ctx.fill()
213 | this.ctx.closePath()
214 | this.ctx.restore()
215 | }
216 | /**
217 | * @description 绘制三角形
218 | * @param {number} x - x坐标
219 | * @param {number} y - y坐标
220 | * @param {number} size - 尺寸
221 | */
222 | drawTriangle(x: number, y: number, size: number) {
223 | this.ctx.save()
224 | this.ctx.beginPath()
225 | this.setShadow()
226 | this.ctx.translate(-size / 2, -((size / 2) * Math.sqrt(3)) / 2)
227 | this.ctx.moveTo(x, y)
228 | this.ctx.lineTo(size, 0)
229 | this.ctx.lineTo(size / 2, (size / 2) * Math.sqrt(3))
230 | this.ctx.lineTo(x, y)
231 | this.ctx.fill()
232 | this.ctx.closePath()
233 | this.ctx.restore()
234 | }
235 | /**
236 | * @description 绘制心形
237 | * @param {number} x - x坐标
238 | * @param {number} y - y坐标
239 | * @param {number} size - 尺寸
240 | */
241 | drawHeart(x: number, y: number, size: number) {
242 | size = size / 2
243 | this.ctx.save()
244 | this.ctx.beginPath()
245 | this.setShadow()
246 | this.ctx.translate(0, -(size * 2) / 2)
247 | this.ctx.moveTo(x, y)
248 | this.ctx.bezierCurveTo(size / 2, -size, size * 3, -size / 2, y, size * 2)
249 | this.ctx.moveTo(x, y)
250 | this.ctx.bezierCurveTo(-size / 2, -size, -size * 3, -size / 2, y, size * 2)
251 | this.ctx.fill()
252 | this.ctx.closePath()
253 | this.ctx.restore()
254 | }
255 | /**
256 | * @description 绘制多边形
257 | * @param {number} x - x坐标
258 | * @param {number} y - y坐标
259 | * @param {number} size - 尺寸
260 | */
261 | drawPolygon(x: number, y: number, size: number) {
262 | this.ctx.save()
263 | this.ctx.beginPath()
264 | this.setShadow()
265 | this.ctx.translate(-size / 2, -size / 2)
266 | this.ctx.moveTo(x, y)
267 | this.ctx.lineTo(size, y)
268 | this.ctx.lineTo(size + size / 2, size / 2)
269 | this.ctx.lineTo(size, size / 2 + size / 2)
270 | this.ctx.lineTo(x, size)
271 | this.ctx.lineTo(x - size / 2, size - size / 2)
272 | this.ctx.lineTo(x, y)
273 | this.ctx.fill()
274 | this.ctx.closePath()
275 | this.ctx.restore()
276 | }
277 |
278 | /**
279 | * @description 设置阴影
280 | */
281 | setShadow() {
282 | const op = this.options
283 | this.ctx.shadowColor = this.pattern.color
284 | this.ctx.shadowOffsetX = op.shadowOffsetX
285 | this.ctx.shadowOffsetY = op.shadowOffsetY
286 | this.ctx.shadowBlur = op.shadowBlur
287 | }
288 | }
289 |
--------------------------------------------------------------------------------
/src/draw/model/Ring.ts:
--------------------------------------------------------------------------------
1 | import type { ElementType, LimitType } from '../../type'
2 | import type { RingOptionsType } from '../type'
3 | import BaseModel from './BaseModel'
4 | /**
5 | * @description 环形模型默认配置选项
6 | */
7 | const modelDefOptions: RingOptionsType = {
8 | arcGap: Math.PI / 4,
9 | ringGap: 10,
10 | lineWidth: 2,
11 | ringNum: 2,
12 | radius: 6,
13 | lineCap: 'round',
14 | turn: 10,
15 | ringsTurn: [Math.PI, Math.PI / 4],
16 | direction: true
17 | }
18 | /**
19 | * @description 环形模型限制条件
20 | */
21 | const limits: Array = [
22 | {
23 | key: 'ringNum',
24 | message: 'ringNum value 1-10',
25 | limit: (key: any) => {
26 | return key >= 1 && key <= 10
27 | }
28 | },
29 | {
30 | key: 'ringsTurn',
31 | message: `ringsTurn size ${modelDefOptions.ringNum}`,
32 | limit: (key: any) => {
33 | return key.length <= modelDefOptions.ringNum!
34 | }
35 | }
36 | ]
37 | /**
38 | * @description 环形模型类
39 | * @extends BaseModel
40 | */
41 | export default class Ring extends BaseModel {
42 | /** 每次旋转的角度(默认每次旋转10) */
43 | rotate: number
44 | /**
45 | * @description 构造函数
46 | * @param {number} w - 宽度
47 | * @param {number} h - 高度
48 | * @param {HTMLCanvasElement} canvas - Canvas元素
49 | * @param {Required} options - 配置选项
50 | * @param {ElementType} element - 容器元素
51 | */
52 | constructor(
53 | w: number,
54 | h: number,
55 | canvas: HTMLCanvasElement,
56 | options: Required,
57 | element: ElementType
58 | ) {
59 | super(w, h, canvas, options, element, modelDefOptions, limits, (model) => {
60 | const op = model.options
61 | model.ctx.lineCap = op.lineCap
62 | model.ctx.lineWidth = op.lineWidth
63 | model.ctx.save()
64 | })
65 | this.rotate = 10
66 | this.run(this.draw)
67 | }
68 | /**
69 | * @description 绘制环形
70 | */
71 | draw() {
72 | this.clearRect()
73 | this.controller()
74 | const op = this.options
75 | this.drawText({ esGap: op.ringNum * (op.radius + op.ringGap / 2) })
76 | }
77 | /**
78 | * @description 控制环形动画
79 | */
80 | controller() {
81 | this.ctx.save()
82 | const op = this.options
83 | const rotate = ((this.rotate * Math.PI) / 180) * (op.direction ? 1 : -1)
84 | this.ctx.rotate(rotate)
85 | this.ctx.shadowOffsetX = op.shadowOffsetX
86 | this.ctx.shadowOffsetY = op.shadowOffsetY
87 | this.ctx.shadowBlur = op.shadowBlur
88 | for (let i = 1; i <= op.ringNum; i++) {
89 | this.drawRing(
90 | op.radius + (i - 1) * op.ringGap,
91 | op.arcGap,
92 | op.ringsTurn && op.ringsTurn.length > 0 ? op.ringsTurn[i - 1] : Math.PI / i
93 | )
94 | }
95 | this.rotate += op.turn
96 | this.ctx.restore()
97 | }
98 | /**
99 | * @description 绘制单个环形
100 | * @param {number} r - 半径
101 | * @param {number} [arcGap=1] - 弧线间隔
102 | * @param {number} [angle=0] - 角度
103 | */
104 | drawRing(r: number, arcGap = 1, angle = 0) {
105 | // 第一个弧
106 | this.ctx.beginPath()
107 | this.ctx.arc(0, 0, r, arcGap + angle, Math.PI + angle)
108 | this.ctx.stroke()
109 | this.ctx.closePath()
110 | // 第二个弧
111 | this.ctx.beginPath()
112 | this.ctx.arc(0, 0, r, Math.PI + arcGap + angle, angle)
113 | this.ctx.stroke()
114 | this.ctx.closePath()
115 | }
116 | }
117 |
--------------------------------------------------------------------------------
/src/draw/model/Roll.ts:
--------------------------------------------------------------------------------
1 | import type { ElementType, LimitType } from '../../type'
2 | import type { RollOptionsType } from '../type'
3 | import { getDefOptions, ROLL_CHART } from '../../utils'
4 | import BaseModel from './BaseModel'
5 | /**
6 | * @description 滚动模型默认配置选项
7 | */
8 | const modelDefOptions: RollOptionsType = {
9 | rollGap: 12,
10 | childNum: 4,
11 | rollSize: 16,
12 | showChild: true,
13 | chart: ROLL_CHART.WHEEL,
14 | windmills: ['#1ab3ea', '#de6834', '#30925d', '#f48ea5'],
15 | windmillPointColor: '#f2c31f',
16 | fixed: false
17 | }
18 |
19 | /**
20 | * @description 滚动模型限制条件
21 | */
22 | const limits: Array = [
23 | {
24 | key: 'childNum',
25 | message: 'chartSize value 4-10',
26 | limit: (key: any) => {
27 | return key >= 4 && key <= 10
28 | }
29 | },
30 | {
31 | key: 'delay',
32 | message: 'Roll.delay not allowed update',
33 | limit: (key: any) => {
34 | return key === getDefOptions().delay
35 | }
36 | }
37 | ]
38 | /**
39 | * @description 子元素类型接口
40 | */
41 | interface childType {
42 | turn: number
43 | x: number
44 | }
45 | /**
46 | * @description 滚动类型接口
47 | */
48 | interface RollType {
49 | turn: number
50 | nowX: number
51 | state: number
52 | child: Array
53 | }
54 | /**
55 | * @description 滚动模型类
56 | * @extends BaseModel
57 | */
58 | export default class Roll extends BaseModel {
59 | Roll: RollType
60 | /**
61 | * @description 构造函数
62 | * @param {number} w - 宽度
63 | * @param {number} h - 高度
64 | * @param {HTMLCanvasElement} canvas - Canvas元素
65 | * @param {Required} options - 配置选项
66 | * @param {ElementType} element - 容器元素
67 | */
68 | constructor(
69 | w: number,
70 | h: number,
71 | canvas: HTMLCanvasElement,
72 | options: Required,
73 | element: ElementType
74 | ) {
75 | super(w, h, canvas, options, element, modelDefOptions, limits)
76 | this.Roll = {
77 | turn: 1,
78 | nowX: this.options.fixed
79 | ? 0
80 | : (this.options.childNum / 2) * (this.options.rollSize + this.options.rollGap) + this.options.rollGap / 2,
81 | state: 2,
82 | child: []
83 | }
84 | this.run(this.draw)
85 | }
86 | /**
87 | * @description 绘制滚动模型
88 | */
89 | draw() {
90 | this.clearRect()
91 | // 地面
92 | this.drawGround()
93 | // 绘制子元素
94 | this.drawChild()
95 | this.ctx.save()
96 | this.ctx.beginPath()
97 | this.ctx.translate(-this.Roll.nowX, 0)
98 | this.ctx.rotate((this.Roll.turn * Math.PI) / 180)
99 | // 绘制图形
100 | this.selectChart()
101 | // 控制进程
102 | this.controller()
103 | this.ctx.restore()
104 | // 绘制文本
105 | const op = this.options
106 | this.drawText({ esGap: op.rollSize })
107 | }
108 | /**
109 | * @description 选择图形类型
110 | */
111 | selectChart() {
112 | const op = this.options
113 | switch (op.chart) {
114 | case ROLL_CHART.RECT:
115 | this.drawRect()
116 | break
117 | case ROLL_CHART.WHEEL:
118 | this.drawWheel()
119 | break
120 | case ROLL_CHART.WINDMILL:
121 | this.drawWindmill()
122 | break
123 | }
124 | }
125 | /**
126 | * @description 控制滚动动画
127 | */
128 | controller() {
129 | const op = this.options
130 | if (this.Roll.state === 1) {
131 | this.Roll.turn -= 10
132 | if (op.delay < 20 && !op.fixed) op.delay += 2
133 | }
134 | if (this.Roll.state === 2) {
135 | this.Roll.turn += 10
136 | if (op.delay > 10 && !op.fixed) op.delay -= 5
137 | }
138 | if (op.fixed) return
139 | if (this.Roll.nowX <= -(op.childNum / 2) * (op.rollSize + op.rollGap / 1.6)) this.Roll.state = 1
140 | if (this.Roll.nowX >= (op.childNum / 2) * (op.rollSize + op.rollGap) + op.rollGap / 2) this.Roll.state = 2
141 | if (this.Roll.state === 1) this.Roll.nowX++
142 | if (this.Roll.state === 2) this.Roll.nowX--
143 |
144 | const child = this.Roll.child
145 | if (this.Roll.nowX % (op.rollSize + op.rollGap) == 0 && this.Roll.state === 2) {
146 | child.push({ turn: this.Roll.turn, x: this.Roll.nowX })
147 | }
148 | if (this.Roll.state === 1 && child[child.length - 1] && child[child.length - 1].x === this.Roll.nowX) {
149 | this.Roll.child.pop()
150 | }
151 | }
152 | /**
153 | * @description 绘制矩形
154 | */
155 | drawRect() {
156 | const op = this.options
157 | this.ctx.save()
158 | this.setShadow()
159 | this.ctx.translate(-op.rollSize / 2, -op.rollSize / 2)
160 | this.ctx.fillRect(0, 0, op.rollSize, op.rollSize)
161 | this.ctx.restore()
162 | }
163 | /**
164 | * @description 绘制轮子
165 | */
166 | drawWheel() {
167 | const op = this.options
168 | this.ctx.save()
169 | this.ctx.lineWidth = 4
170 | // 内圆1
171 | this.ctx.beginPath()
172 | this.ctx.arc(0, 0, op.rollSize / 6, 0, Math.PI * 2)
173 | this.ctx.stroke()
174 | this.ctx.closePath()
175 | // 内圆2
176 | this.ctx.beginPath()
177 | this.ctx.arc(0, 0, op.rollSize / 2, 0, Math.PI * 2)
178 | this.ctx.stroke()
179 | this.ctx.closePath()
180 | // 外圆
181 | this.ctx.beginPath()
182 | this.ctx.arc(0, 0, op.rollSize, 0, Math.PI * 2)
183 | this.ctx.stroke()
184 | this.ctx.closePath()
185 | // 轮辐
186 | for (let i = 0; i < 6; i++) {
187 | this.ctx.beginPath()
188 | this.ctx.moveTo(0, op.rollSize / 2)
189 | this.ctx.lineTo(0, op.rollSize)
190 | this.ctx.stroke()
191 | this.ctx.rotate(((360 / 6) * Math.PI) / 180)
192 | this.ctx.closePath()
193 | }
194 | this.ctx.restore()
195 | }
196 | /**
197 | * @description 绘制风车
198 | */
199 | drawWindmill() {
200 | const op = this.options
201 | this.ctx.save()
202 | for (let i = 0; i < op.windmills.length; i++) {
203 | this.ctx.beginPath()
204 | this.ctx.fillStyle = op.windmills[i]
205 | this.ctx.arc(-op.rollSize / 2, 0, op.rollSize, 0, Math.PI)
206 | this.ctx.fill()
207 | this.ctx.closePath()
208 | this.ctx.rotate(((360 / 4) * Math.PI) / 180)
209 | }
210 | // 上层固定点
211 | this.ctx.beginPath()
212 | this.ctx.fillStyle = op.windmillPointColor
213 | this.ctx.arc(0, 0, op.rollSize / 2, 0, Math.PI * 2)
214 | this.ctx.fill()
215 | this.ctx.restore()
216 | }
217 | /**
218 | * @description 绘制子元素
219 | */
220 | drawChild() {
221 | const op = this.options
222 | if (!op.showChild) return
223 | this.Roll.child.forEach((c, index) => {
224 | this.ctx.save()
225 | this.ctx.translate(-c.x, 0)
226 | this.ctx.globalAlpha = (index + 1) / 10
227 | this.ctx.rotate((c.turn * Math.PI) / 180)
228 | this.selectChart()
229 | this.ctx.restore()
230 | })
231 | }
232 | /**
233 | * @description 绘制地面
234 | */
235 | drawGround() {
236 | const op = this.options
237 | if (op.chart !== ROLL_CHART.WHEEL) return
238 | this.ctx.save()
239 | this.ctx.beginPath()
240 | this.ctx.lineWidth = 3
241 | this.ctx.globalAlpha = 0.03
242 | this.ctx.moveTo(-(op.childNum / 2) * (op.rollSize + op.rollGap / 1.6), op.rollSize + 3)
243 | this.ctx.lineTo((op.childNum / 2) * (op.rollSize + op.rollGap) + op.rollGap / 2, op.rollSize + 3)
244 | this.ctx.stroke()
245 | this.ctx.closePath()
246 | this.ctx.restore()
247 | }
248 | /**
249 | * @description 设置阴影
250 | */
251 | setShadow() {
252 | const op = this.options
253 | this.ctx.shadowOffsetX = op.shadowOffsetX
254 | this.ctx.shadowOffsetY = op.shadowOffsetY
255 | this.ctx.shadowBlur = op.shadowBlur
256 | }
257 | }
258 |
--------------------------------------------------------------------------------
/src/draw/model/Skeleton.ts:
--------------------------------------------------------------------------------
1 | import type { ElementType } from '../../type'
2 | import type { SkeletonOptionsType } from '../type'
3 | import BaseModel from './BaseModel'
4 | /**
5 | * @description 骨架屏模型默认配置选项
6 | */
7 | const modelDefOptions: SkeletonOptionsType = {
8 | skeletonColor: 'rgb(240, 240, 240)',
9 | skeletonAnimationColor: 'rgb(226, 226, 226)',
10 | radius: 5,
11 | animation: true,
12 | deep: true,
13 | appointElementClass: []
14 | }
15 | /**
16 | * @description 骨架屏元素类型接口
17 | */
18 | interface SkeletonType {
19 | element: HTMLElement
20 | title: string
21 | }
22 | /**
23 | * @description 骨架屏模型类
24 | * @extends BaseModel
25 | */
26 | export default class Skeleton extends BaseModel {
27 | skeleton: Array
28 | colorFlow: number
29 | state: number
30 | /**
31 | * @description 构造函数
32 | * @param {number} w - 宽度
33 | * @param {number} h - 高度
34 | * @param {HTMLCanvasElement} canvas - Canvas元素
35 | * @param {Required} options - 配置选项
36 | * @param {ElementType} element - 容器元素
37 | */
38 | constructor(
39 | w: number,
40 | h: number,
41 | canvas: HTMLCanvasElement,
42 | options: Required,
43 | element: ElementType
44 | ) {
45 | super(w, h, canvas, options, element, modelDefOptions, [], (model) => {
46 | const op = model.options
47 | // 重新初始化canvas
48 | model.ctx.translate(-model.w / 2, -model.h / 2)
49 | model.canvas.width = model.element.scrollWidth
50 | model.canvas.height = model.element.scrollHeight
51 | model.ctx.fillStyle = op.skeletonColor
52 | })
53 | this.skeleton = []
54 | this.colorFlow = 0
55 | this.state = 1
56 | this.controller(this.element.children)
57 | this.run(this.draw)
58 | }
59 | /**
60 | * @description 绘制骨架屏
61 | */
62 | draw() {
63 | this.clearRect()
64 | this.drawSkeleton()
65 | }
66 | /**
67 | * @description 将元素添加到骨架屏元素列表中
68 | * @param {Element} element - 要添加的元素
69 | */
70 | addElementToSkeleton(element: Element) {
71 | const op = this.options
72 | const filter = op.appointElementClass
73 | // 如果设置了appointElementClass,只处理具有该类的元素
74 | if (filter && filter.length > 0) {
75 | if (filter.some((c) => element.classList.contains(c))) {
76 | this.skeleton.push({ title: element.nodeName, element: element as HTMLElement })
77 | }
78 | } else {
79 | this.skeleton.push({ title: element.nodeName, element: element as HTMLElement })
80 | }
81 | }
82 |
83 | /**
84 | * @description 控制器函数,处理DOM元素
85 | * @param {HTMLCollection} els - HTML元素集合
86 | */
87 | controller(els: HTMLCollection) {
88 | const op = this.options
89 | for (const e of Array.from(els)) {
90 | // 排除绘制canvas元素
91 | if (this.canvas === e) continue
92 | if (op.deep) {
93 | if (e.children.length <= 0) {
94 | this.addElementToSkeleton(e)
95 | } else {
96 | // 如果指定了appointElementClass,则包含父元素,否则只包含所有根元素
97 | if (op.appointElementClass && op.appointElementClass.length) this.addElementToSkeleton(e)
98 | this.controller(e.children)
99 | }
100 | } else {
101 | this.addElementToSkeleton(e)
102 | }
103 | }
104 | }
105 | /**
106 | * @description 绘制骨架元素
107 | */
108 | drawSkeleton() {
109 | const op = this.options
110 | const linearGradient = this.ctx.createLinearGradient(0, 0, this.w, this.h)
111 | linearGradient.addColorStop(0, op.skeletonColor)
112 | linearGradient.addColorStop(this.colorFlow, op.skeletonAnimationColor)
113 | linearGradient.addColorStop(1, op.skeletonColor)
114 | if (op.animation) this.ctx.fillStyle = linearGradient
115 | this.skeleton.forEach((s) => {
116 | const el = s.element
117 | // 处理圆角露出问题
118 | this.drawRadiusRect(el.offsetLeft, el.offsetTop, el.offsetWidth, el.offsetHeight, op.radius)
119 | this.ctx.fill()
120 | })
121 | if (op.animation) {
122 | if (this.colorFlow >= 0.9) this.state = 2
123 | if (this.colorFlow <= 0.1) this.state = 1
124 | if (this.state === 1) this.colorFlow += 0.06
125 | if (this.state === 2) this.colorFlow -= 0.06
126 | }
127 | }
128 | }
129 |
--------------------------------------------------------------------------------
/src/draw/model/Zoom.ts:
--------------------------------------------------------------------------------
1 | import type { ElementType } from '../../type'
2 | import type { ZoomOptionsType } from '../type'
3 | import { ZOOM_ACTION } from '../../utils'
4 | import BaseModel from './BaseModel'
5 | /**
6 | * @description 缩放模型默认配置选项
7 | */
8 | const modelDefOptions: ZoomOptionsType = {
9 | zoomGap: 10,
10 | maxSize: 16,
11 | zoomNum: 5,
12 | lineWidth: 10,
13 | zoomHeight: 2,
14 | lineCap: 'round',
15 | action: ZOOM_ACTION.SCALE,
16 | direction: true,
17 | zoomColors: []
18 | }
19 | /**
20 | * @description 列表类型接口
21 | */
22 | interface ListType {
23 | value: number
24 | state: number
25 | }
26 | /**
27 | * @description 缩放模型限制条件
28 | */
29 | const limits = [
30 | {
31 | key: 'lineWidth',
32 | message: 'lineWidth(default:10) <= maxSize(default:16)',
33 | limit: (key: any) => {
34 | return key <= modelDefOptions.maxSize!
35 | }
36 | },
37 | {
38 | key: 'maxSize',
39 | message: 'lineWidth(default:10) <= maxSize(default:16)',
40 | limit: (key: any) => {
41 | return modelDefOptions.lineWidth! <= key
42 | }
43 | }
44 | ]
45 | /**
46 | * @description 缩放模型类
47 | * @extends BaseModel
48 | */
49 | export default class Zoom extends BaseModel {
50 | list: Array
51 | zoomIndex: number
52 | /**
53 | * @description 构造函数
54 | * @param {number} w - 宽度
55 | * @param {number} h - 高度
56 | * @param {HTMLCanvasElement} canvas - Canvas元素
57 | * @param {Required} options - 配置选项
58 | * @param {ElementType} element - 容器元素
59 | */
60 | constructor(
61 | w: number,
62 | h: number,
63 | canvas: HTMLCanvasElement,
64 | options: Required,
65 | element: ElementType
66 | ) {
67 | super(w, h, canvas, options, element, modelDefOptions, limits, (model) => {
68 | const op = model.options
69 | model.ctx.lineCap = op.lineCap
70 | model.ctx.lineWidth = op.lineWidth
71 | // 居中 ((缩放宽度 * 数量+1)+(缩放间隙 * 数量+1))/2, 因为第一个缩放会移动所需的数量+1, 高度/2
72 | model.ctx.translate(-(op.lineWidth * (op.zoomNum + 1) + op.zoomGap * (op.zoomNum + 1)) / 2, -op.zoomHeight / 2)
73 | model.ctx.save()
74 | })
75 | this.zoomIndex = this.options.direction ? 0 : this.options.zoomNum - 1
76 | this.list = Array.from({ length: this.options.zoomNum }, (_, _index) =>
77 | Object.assign({
78 | value: this.options.lineWidth,
79 | // 0: 初始, 1: 变大, 2: 变小
80 | state: 0
81 | })
82 | )
83 | this.run(this.draw)
84 | }
85 | /**
86 | * @description 绘制缩放模型
87 | */
88 | draw() {
89 | this.clearRect()
90 | // 绘制缩放
91 | this.drawZoom()
92 | const op = this.options
93 | this.drawText({ esGap: op.maxSize, x: (op.lineWidth * (op.zoomNum + 1) + op.zoomGap * (op.zoomNum + 1)) / 2 })
94 | // 控制进程
95 | this.controller()
96 | }
97 | /**
98 | * @description 控制缩放动画
99 | */
100 | controller() {
101 | const op = this.options
102 | if (op.direction && this.zoomIndex >= op.zoomNum) this.zoomIndex = 0
103 | else if (op.direction && this.zoomIndex < 0) this.zoomIndex = op.zoomNum - 1
104 | }
105 | /**
106 | * @description 绘制缩放元素
107 | */
108 | drawZoom() {
109 | const op = this.options
110 | for (let i = 0; i < op.zoomNum; i++) {
111 | // 处理变化
112 | if (this.list[i].state === 1) this.list[i].value += 2
113 | else if (this.list[i].state === 2 && this.list[i].value >= op.lineWidth) this.list[i].value--
114 | if (op.action === ZOOM_ACTION.SCALE) this.ctx.lineWidth = this.list[i].value
115 | // 状态变化
116 | if (i === this.zoomIndex) {
117 | if (this.list[i].value > op.maxSize) {
118 | this.list[i].state = 2
119 | op.direction
120 | ? this.zoomIndex++
121 | : this.zoomIndex - 1 >= 0
122 | ? this.zoomIndex--
123 | : (this.zoomIndex = op.zoomNum - 1)
124 | }
125 | if (this.list[i].value <= op.lineWidth) this.list[i].state = 1
126 | }
127 | // 根据数量绘制
128 | this.ctx.beginPath()
129 | if (op.zoomColors.length > 0 && op.zoomColors[i]) this.ctx.strokeStyle = op.zoomColors[i]
130 | else this.ctx.strokeStyle = op.themeColor
131 | let sH = 0,
132 | eH = op.zoomHeight
133 | if (op.action === ZOOM_ACTION.HEIGHT || op.action === ZOOM_ACTION.WAVE) {
134 | sH = -this.list[i].value
135 | }
136 | if (op.action === ZOOM_ACTION.WAVE) {
137 | eH = -this.list[i].value
138 | }
139 | this.ctx.moveTo((i + 1) * (op.lineWidth + op.zoomGap), sH)
140 | this.ctx.lineTo((i + 1) * (op.lineWidth + op.zoomGap), eH)
141 | this.ctx.stroke()
142 | this.ctx.closePath()
143 | }
144 | }
145 | }
146 |
--------------------------------------------------------------------------------
/src/draw/model/index.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * @description 动态导入所有模型
3 | * @note 动态引入会导致404,因此使用静态导入
4 | */
5 | import Gear from './Gear'
6 | import Zoom from './Zoom'
7 | import Ring from './Ring'
8 | import Bean from './Bean'
9 | import Clock from './Clock'
10 | import Pattern from './Pattern'
11 | import Roll from './Roll'
12 | import Circular from './Circular'
13 | import Img from './Img'
14 | import Skeleton from './Skeleton'
15 | export default { Gear, Zoom, Ring, Bean, Clock, Pattern, Roll, Circular, Img, Skeleton }
16 |
--------------------------------------------------------------------------------
/src/draw/type.ts:
--------------------------------------------------------------------------------
1 | import { OptionsType } from '../type'
2 | import { PATTERN_CHART, ROLL_CHART, ZOOM_ACTION, CIRCULAR_ACTION } from '../utils'
3 | /**
4 | * @description 绘制文本参数类型
5 | * @public
6 | */
7 | export interface DrawTextParamsType {
8 | esGap?: number
9 | x?: number
10 | text?: string
11 | textColor?: string
12 | }
13 | /**
14 | * @description 齿轮模型配置类型
15 | * @public
16 | */
17 | export interface GearOptionsType extends OptionsType {
18 | // 线条起始
19 | lineStart?: number
20 | // 线条结束
21 | lineEnd?: number
22 | // 线条起始倾斜度
23 | lineStartSkew?: number
24 | // 线条结束倾斜度
25 | lineEndSkew?: number
26 | // 线条宽度
27 | lineWidth?: number
28 | // 线条样式
29 | lineCap?: CanvasLineCap
30 | // 线条数量
31 | lineNum?: number
32 | // 方向:true-正向,然后逆向
33 | direction?: boolean
34 | }
35 | /**
36 | * @description 环形模型配置类型
37 | * @public
38 | */
39 | export interface RingOptionsType extends OptionsType {
40 | // 环形间隔
41 | ringGap?: number
42 | // 弧线间隔
43 | arcGap?: number
44 | // 线条宽度
45 | lineWidth?: number
46 | // 环形数量
47 | ringNum?: number
48 | // 半径
49 | radius?: number
50 | // 线条样式
51 | lineCap?: CanvasLineCap
52 | // 旋转角度
53 | turn?: number
54 | // 多个环形的初始角度
55 | ringsTurn?: Array
56 | // 方向:true-正向,然后逆向
57 | direction?: boolean
58 | }
59 | /**
60 | * @description 缩放模型配置类型
61 | * @public
62 | */
63 | export interface ZoomOptionsType extends OptionsType {
64 | // 缩放最大尺寸
65 | maxSize?: number
66 | // 缩放间距
67 | zoomGap?: number
68 | // 缩放高度
69 | zoomHeight?: number
70 | // 缩放数量
71 | zoomNum?: number
72 | // 自定义缩放颜色
73 | zoomColors?: Array
74 | // 线条样式
75 | lineCap?: CanvasLineCap
76 | // 线条宽度
77 | lineWidth?: number
78 | // 动作类型
79 | action: ZOOM_ACTION
80 | // 方向:true-正向,然后逆向
81 | direction?: boolean
82 | }
83 | /**
84 | * @description 图案模型配置类型
85 | * @public
86 | */
87 | export interface PatternOptionsType extends OptionsType {
88 | // 支持的图形
89 | charts?: Array
90 | // 图形尺寸大小
91 | chartSize?: number
92 | // 动态颜色
93 | chartColors?: Array
94 | // 最大高度
95 | maxHeight?: number
96 | }
97 | /**
98 | * @description 时钟模型配置类型
99 | * @public
100 | */
101 | export interface ClockOptionsType extends OptionsType {
102 | // 文本显示模式:time-日期时间,s-秒
103 | textTime?: 'time' | 's' | ''
104 | // 线条颜色
105 | lineColors?: Array
106 | // 线条样式
107 | lineCap?: CanvasLineCap
108 | // 线条宽度
109 | lineWidth?: number
110 | // 时钟尺寸
111 | clockSize?: number
112 | // 时钟间隔
113 | clockGap?: number
114 | // 时分秒指针
115 | hLine?: boolean
116 | mLine?: boolean
117 | sLine?: boolean
118 | }
119 | /**
120 | * @description 豆形模型配置类型
121 | * @public
122 | */
123 | export interface BeanOptionsType extends OptionsType {
124 | // 豆形尺寸
125 | beanSize?: number
126 | // 豆形中的点数量
127 | pointLength?: number
128 | }
129 | /**
130 | * @description 滚动模型配置类型
131 | * @public
132 | */
133 | export interface RollOptionsType extends OptionsType {
134 | // 滚动间隔
135 | rollGap?: number
136 | // 滚动尺寸
137 | rollSize?: number
138 | // 显示子元素
139 | showChild?: boolean
140 | // 子元素数量
141 | childNum?: number
142 | // 显示的图形
143 | chart?: ROLL_CHART
144 | // 风车图形时的叶片颜色
145 | windmills?: Array
146 | // 风车中心颜色
147 | windmillPointColor?: string
148 | // 中心是否固定
149 | fixed?: boolean
150 | }
151 | /**
152 | * @description 图片模型配置类型
153 | * @public
154 | */
155 | export interface ImageOptionsType extends OptionsType {
156 | src?: string
157 | width?: number
158 | height?: number
159 | turn?: boolean
160 | }
161 | /**
162 | * @description 骨架屏模型配置类型
163 | * @public
164 | */
165 | export interface SkeletonOptionsType extends OptionsType {
166 | skeletonColor?: string
167 | skeletonAnimationColor?: string
168 | radius?: number
169 | animation?: boolean
170 | deep?: boolean
171 | appointElementClass?: Array
172 | }
173 | /**
174 | * @description 圆形模型配置类型
175 | * @public
176 | */
177 | export interface CircularOptionsType extends OptionsType {
178 | // 弧线尺寸
179 | arcSize?: number
180 | // 弧线间隔
181 | arcGap?: number
182 | // 弧线颜色
183 | arcColors?: Array
184 | // 动作类型
185 | action?: CIRCULAR_ACTION
186 | }
187 |
--------------------------------------------------------------------------------
/src/index.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * @packageDocumentation
3 | * @description 包文档说明
4 | */
5 | export { OptionsType, LoadingType, ElementStoreType, ElementType, LimitType, LogConfigType } from './type'
6 | import './main'
7 | export * from './draw/type'
8 | export {
9 | LOADING_TYPES,
10 | LOG_TYPES,
11 | MODEL_TYPES,
12 | HOOKS_CALL_KEY,
13 | ZOOM_ACTION,
14 | PATTERN_CHART,
15 | ROLL_CHART,
16 | CIRCULAR_ACTION
17 | } from './utils'
18 | import BaseModel from './draw/model/BaseModel'
19 | import initLoading, { fullLoading, miniLoading } from './loading'
20 | export { BaseModel, initLoading, fullLoading, miniLoading }
21 |
--------------------------------------------------------------------------------
/src/loading.ts:
--------------------------------------------------------------------------------
1 | import WebLoading from './WebLoading/index'
2 | import ExtendLoading from './ExtendLoading'
3 | import type { OptionsType, LoadingType, ElementType } from './type'
4 | import { LOADING_TYPES, getDefOptions, $Log } from './utils'
5 | import drawController from './draw/index'
6 | const $window = window
7 | /**
8 | * @description 初始化加载动画
9 | * @param {OptionsType} options - 配置选项
10 | * @returns {LoadingType} 返回加载操作对象
11 | * @public
12 | */
13 | export default function initLoading(options?: OptionsType): LoadingType {
14 | const webLoading = new WebLoading(options)
15 | let feelPromiseResolve: ((value: boolean | PromiseLike) => void) | null = null
16 | const resize = () => {
17 | utlWL('resize')
18 | }
19 | const loading = (dom: ElementType, options?: OptionsType) => {
20 | // 保存最后传入的参数
21 | const op = Object.assign(webLoading.options, options)
22 | // 防止重复注册
23 | if (!webLoading.loadingId && !feelPromiseResolve) {
24 | // 创建扩展DOM
25 | if (op.type !== LOADING_TYPES.DOM) {
26 | dom = new ExtendLoading(op).getElement() as ElementType
27 | }
28 | if (!dom) $Log.error('The loading function cannot find an HTMLElement element!')
29 | else {
30 | // 通过Promise.race处理无感加载
31 | const loadingPromise = new Promise((res) => {
32 | // 如果notFeel的时间超过关闭时间,则认为是无感加载
33 | $window.setTimeout(() => {
34 | res(true)
35 | }, op.notFeel)
36 | })
37 | const feelPromise = new Promise((res) => {
38 | feelPromiseResolve = res
39 | })
40 | Promise.race([loadingPromise, feelPromise]).then((res) => {
41 | if (res) webLoading.draw(dom)
42 | else {
43 | // 处理扩展DOM
44 | if (op.type !== LOADING_TYPES.DOM) dom.remove()
45 | }
46 | feelPromiseResolve = null
47 | })
48 | }
49 | }
50 | }
51 | const update = (options?: OptionsType) => {
52 | const canvas = webLoading.canvas
53 | const op = Object.assign(webLoading.options, options)
54 | const element = webLoading.element
55 | if (canvas && op && element && element.$store)
56 | drawController(canvas.offsetWidth, canvas.offsetHeight, canvas, op, element)
57 | }
58 | const close = () => {
59 | feelPromiseResolve && feelPromiseResolve(false)
60 | utlWL('close')
61 | }
62 | // 获取基本信息
63 | const getLoadingId = () => webLoading.loadingId
64 | const getOptions = () => webLoading.options
65 | // WebLoading操作工具函数
66 | function utlWL(key: 'resize' | 'close') {
67 | if (webLoading.element) {
68 | // canvas元素
69 | let temEl: HTMLCanvasElement | HTMLDivElement | null = webLoading.canvas
70 | // html元素
71 | if (webLoading.htmlElement) temEl = webLoading.htmlElement
72 | // 设置
73 | if (temEl) webLoading[key](webLoading.element, temEl)
74 | else $Log.warn('Animation element not found!')
75 | }
76 | }
77 | return {
78 | loading,
79 | resize,
80 | close,
81 | update,
82 | getOptions,
83 | getLoadingId
84 | }
85 | }
86 | /**
87 | * @description 扩展加载方法
88 | * @param {LOADING_TYPES} type - 加载类型
89 | * @param {OptionsType} options - 配置选项
90 | * @returns {LoadingType} 返回加载操作对象
91 | * @private
92 | */
93 | export function _$extendLoading(type: LOADING_TYPES, options?: OptionsType) {
94 | return initLoading(Object.assign(getDefOptions(), options || {}, { type }))
95 | }
96 | /**
97 | * @description 全屏加载
98 | * @param {OptionsType} options - 配置选项
99 | * @returns {LoadingType} 返回加载操作对象
100 | * @public
101 | */
102 | export function fullLoading(options?: OptionsType) {
103 | return _$extendLoading(LOADING_TYPES.FULL, options)
104 | }
105 | /**
106 | * @description 迷你加载
107 | * @param {OptionsType} options - 配置选项
108 | * @returns {LoadingType} 返回加载操作对象
109 | * @public
110 | */
111 | export function miniLoading(options?: OptionsType) {
112 | return _$extendLoading(LOADING_TYPES.MINI, options)
113 | }
114 |
--------------------------------------------------------------------------------
/src/main.ts:
--------------------------------------------------------------------------------
1 | import type { OptionsType, WindowType } from './type'
2 | import _$BaseModel from './draw/model/BaseModel'
3 | import { LOADING_TYPES } from './utils'
4 | import _$initLoading, { _$extendLoading } from './loading'
5 | const $window: WindowType = window
6 | $window.BaseModel = _$BaseModel
7 | // 初始化
8 | $window.initLoading = function (options?: OptionsType) {
9 | return _$initLoading(options)
10 | }
11 | // 扩展加载方法
12 | // 移动端
13 | $window.miniLoading = (options?: OptionsType) => {
14 | return _$extendLoading(LOADING_TYPES.MINI, options)
15 | }
16 | // 全屏
17 | $window.fullLoading = (options?: OptionsType) => {
18 | return _$extendLoading(LOADING_TYPES.FULL, options)
19 | }
20 | /**
21 | * @description JS项目单独导入方法
22 | * @public
23 | */
24 | export default {
25 | initLoading: $window.initLoading,
26 | miniLoading: $window.miniLoading,
27 | fullLoading: $window.fullLoading,
28 | BaseModel: $window.BaseModel
29 | }
30 |
--------------------------------------------------------------------------------
/src/type.ts:
--------------------------------------------------------------------------------
1 | import { MODEL_TYPES, LOADING_TYPES, HOOKS_CALL_KEY } from './utils'
2 | import BaseModel from './draw/model/BaseModel'
3 | /**
4 | * @description 配置选项接口
5 | * @public
6 | */
7 | export interface OptionsType {
8 | // 自定义模型
9 | custom?: typeof BaseModel | null
10 | // 加载启动模式(默认DEF)[只读]
11 | type?: LOADING_TYPES
12 | // mini模式的类名
13 | extendClass?: string | null | undefined
14 | // 模型类型
15 | model?: MODEL_TYPES
16 | // Html加载内容
17 | html?: string
18 | // 文本内容
19 | text?: string
20 | // 文本间距
21 | textGap?: number
22 | // 字体大小
23 | fontSize?: number
24 | // 字体系列
25 | fontFamily?: string
26 | // 延迟时间
27 | delay?: number
28 | // 进入延迟
29 | delayInto?: number
30 | // 无感刷新
31 | notFeel?: number
32 | // 性能优化
33 | optimization?: boolean
34 | // 加载层级
35 | zIndex?: string
36 | // 主题颜色
37 | themeColor?: string
38 | // 背景颜色
39 | bgColor?: string
40 | // 阴影颜色
41 | shadowColor?: string
42 | // 阴影X偏移
43 | shadowOffsetX?: number
44 | // 阴影Y偏移
45 | shadowOffsetY?: number
46 | // 阴影模糊度
47 | shadowBlur?: number
48 | // 事件穿透(DOM模式)
49 | pointerEvents?: boolean
50 | // 显示提示
51 | toast?: boolean
52 | }
53 | /**
54 | * @description 扩展Window类型接口
55 | */
56 | export interface WindowType extends Window {
57 | BaseModel?: typeof BaseModel
58 | initLoading?: (options: OptionsType) => LoadingType
59 | fullLoading?: (options: OptionsType) => LoadingType
60 | miniLoading?: (options: OptionsType) => LoadingType
61 | }
62 | /**
63 | * @description 加载类型接口
64 | * @public
65 | */
66 | export interface LoadingType {
67 | loading: (dom: ElementType, options?: OptionsType) => void
68 | resize: () => void
69 | close: () => void
70 | update: (options?: OptionsType) => void
71 | getOptions: () => OptionsType
72 | getLoadingId: () => string | null
73 | }
74 | /**
75 | * @description 钩子调用类型
76 | * 映射键是枚举
77 | */
78 | export type HooksCallType = {
79 | [key in T]: (...args: any[]) => void
80 | }
81 | /**
82 | * @description 元素存储类型接口
83 | * @public
84 | */
85 | export interface ElementStoreType {
86 | // 保存最终合并的选项参数
87 | options: OptionsType
88 | // 用于记录动画状态
89 | animationId: number | undefined
90 | // 记录loading元素id
91 | loadingId: string | null
92 | // loading的钩子函数
93 | hookCall: HooksCallType
94 | // 使用中的模型
95 | model: BaseModel | null
96 | }
97 | /**
98 | * @description 元素类型接口
99 | * @public
100 | */
101 | export interface ElementType extends HTMLElement {
102 | loadingId?: string | null
103 | $store?: ElementStoreType
104 | }
105 | /**
106 | * @description 限制类型接口
107 | * @public
108 | */
109 | export interface LimitType {
110 | key: string
111 | message: string
112 | limit: (key: any) => boolean
113 | }
114 | /**
115 | * @description 日志配置类型
116 | * @public
117 | */
118 | export type LogConfigType = {
119 | // 文本颜色
120 | color?: string
121 | // 背景提示颜色
122 | bgColor?: string
123 | }
124 |
--------------------------------------------------------------------------------
/src/utils.ts:
--------------------------------------------------------------------------------
1 | import { LogConfigType, OptionsType } from './type'
2 | /**
3 | * @description 支持的加载方法
4 | * @public
5 | */
6 | export enum LOADING_TYPES {
7 | DOM = 'dom',
8 | FULL = 'full',
9 | MINI = 'mini'
10 | }
11 | /**
12 | * @description 支持的模型类型
13 | * @public
14 | */
15 | export enum MODEL_TYPES {
16 | // 齿轮
17 | GEAR = 'Gear',
18 | // 环形
19 | RING = 'Ring',
20 | // 缩放
21 | ZOOM = 'Zoom',
22 | // 图案
23 | PATTERN = 'Pattern',
24 | // 时钟
25 | CLOCK = 'Clock',
26 | // 豆形
27 | BEAN = 'Bean',
28 | // 滚动
29 | ROLL = 'Roll',
30 | // 圆形
31 | CIRCULAR = 'Circular',
32 | // 图片
33 | IMG = 'Img',
34 | // 骨架屏
35 | SKELETON = 'Skeleton'
36 | }
37 | /**
38 | * @description 返回默认配置
39 | * @returns {Required} 返回默认配置对象
40 | * @public
41 | */
42 | export function getDefOptions(): Required {
43 | return {
44 | custom: null,
45 | html: '',
46 | type: LOADING_TYPES.DOM,
47 | extendClass: 'extend',
48 | model: MODEL_TYPES.GEAR,
49 | text: '加载中...',
50 | textGap: 8,
51 | fontSize: 12,
52 | fontFamily: 'Microsoft YaHei',
53 | delay: 65,
54 | notFeel: 0,
55 | delayInto: 320,
56 | optimization: false,
57 | zIndex: '2001',
58 | themeColor: 'rgba(64,158,255,1)',
59 | bgColor: 'rgba(0, 0, 0, 0.8)',
60 | shadowColor: 'rgba(64,158,255,0.6)',
61 | shadowOffsetX: 2,
62 | shadowOffsetY: 2,
63 | shadowBlur: 5,
64 | pointerEvents: false,
65 | toast: true
66 | }
67 | }
68 | /**
69 | * @description 钩子调用键枚举
70 | * @public
71 | */
72 | export enum HOOKS_CALL_KEY {
73 | BEFORE_CLOSE = 'beforeClose',
74 | CLOSED = 'closed'
75 | }
76 | /**
77 | * @description 日志类型枚举
78 | * @public
79 | */
80 | export enum LOG_TYPES {
81 | INFO = 1,
82 | WARN = 2,
83 | ERROR = 3
84 | }
85 | /**
86 | * @description 日志输出类
87 | * @public
88 | */
89 | export class $Log {
90 | /**
91 | * @description 输出信息日志
92 | * @param {string} message - 日志内容
93 | */
94 | static info(message: string) {
95 | this.call(message, LOG_TYPES.INFO)
96 | }
97 | /**
98 | * @description 输出警告日志
99 | * @param {string} message - 日志内容
100 | */
101 | static warn(message: string) {
102 | this.call(message, LOG_TYPES.WARN)
103 | }
104 | /**
105 | * @description 输出错误日志
106 | * @param {string} message - 日志内容
107 | */
108 | static error(message: string) {
109 | this.call(message, LOG_TYPES.ERROR)
110 | }
111 | /**
112 | * @description 调用日志输出
113 | * @param {string} message - 日志内容
114 | * @param {LOG_TYPES} type - 日志类型
115 | * @param {LogConfigType} config - 日志配置
116 | */
117 | static call(
118 | message: string,
119 | type: LOG_TYPES = LOG_TYPES.INFO,
120 | config: LogConfigType = {
121 | color: getDefOptions().themeColor,
122 | bgColor: getDefOptions().bgColor
123 | }
124 | ) {
125 | let bgColor = config.bgColor
126 | // 警告颜色不能被更改
127 | if (type === 2) bgColor = '#fffbe5'
128 | // 错误颜色不能被更改
129 | if (type === 3) bgColor = '#fff0f0'
130 | const style = `
131 | background:${bgColor};
132 | font-size:14px;
133 | color:${config.color};
134 | padding: 4px;
135 | border: 1px solid;`
136 | console.log(`%c web-loading:${message} `, style)
137 | }
138 | }
139 | /**
140 | * @description 判断值是否为空
141 | * @param {any} value - 待判断的值
142 | * @returns {boolean} 返回判断结果
143 | * @public
144 | */
145 | export function isNull(value: any): value is boolean | ((...args: any[]) => any) {
146 | switch (toType(value)) {
147 | case 'object':
148 | return Object.keys(value).length > 0
149 | case 'array':
150 | return value.length > 0
151 | case 'undefined':
152 | return value !== undefined
153 | case 'null':
154 | return value !== null
155 | default:
156 | return value !== undefined
157 | }
158 | }
159 | /**
160 | * @description 清除动画帧
161 | * @param {number} id - 动画帧ID
162 | * @public
163 | */
164 | export function clearAnimationFrame(id: number) {
165 | if (!window.requestAnimationFrame) {
166 | window.clearInterval(id)
167 | } else {
168 | window.cancelAnimationFrame(id)
169 | }
170 | }
171 | /**
172 | * @description 获取数据类型
173 | * @param {any} key - 待检测的值
174 | * @returns {string | 'not-type'} 返回类型字符串
175 | * @public
176 | */
177 | export function toType(key: any): string | 'not-type' {
178 | try {
179 | const type = Object.prototype.toString.call(key)
180 | const t1 = type.split(' ')[1]
181 | const t2 = t1.split(']')[0]
182 | return t2.toLowerCase()
183 | } catch (e) {
184 | return 'not-type'
185 | }
186 | }
187 | /**
188 | * @description 监听动画结束事件
189 | * @param {HTMLElement} el - 元素
190 | * @param {() => void} fun - 执行函数
191 | * @public
192 | */
193 | export function onTransitionEndEvent(el: HTMLElement, fun: () => void) {
194 | let transitionsName: string | null = null
195 | const transitions: { [key in string]: string } = {
196 | transition: 'transitionend',
197 | OTransition: 'oTransitionEnd',
198 | MozTransition: 'transitionend',
199 | WebkitTransition: 'webkitTransitionEnd'
200 | }
201 | for (const t in transitions) {
202 | if (el.style[t as any] !== undefined) {
203 | transitionsName = transitions[t]
204 | break
205 | }
206 | }
207 | if (!transitionsName) {
208 | fun()
209 | } else {
210 | const transitionFun = () => {
211 | fun()
212 | el.removeEventListener(transitionsName as string, transitionFun)
213 | }
214 | el.addEventListener(transitionsName, transitionFun)
215 | }
216 | }
217 | /**
218 | * @description 创建唯一的loadingID
219 | * @returns {string} 返回生成的唯一ID
220 | * @public
221 | */
222 | export function createLoadingId() {
223 | let id = String(Date.now())
224 | if (window.crypto && window.crypto.randomUUID) id = window.crypto.randomUUID()
225 | return 'wl_' + id.replace(/-/g, '')
226 | }
227 | /**
228 | * @description 缩放动作枚举
229 | * @public
230 | */
231 | export enum ZOOM_ACTION {
232 | SCALE = 'scale',
233 | WAVE = 'wave',
234 | HEIGHT = 'height'
235 | }
236 | /**
237 | * @description 图案类型枚举
238 | * @public
239 | */
240 | export enum PATTERN_CHART {
241 | RECT = 'rect',
242 | ARC = 'arc',
243 | TRIANGLE = 'triangle',
244 | HEART = 'heart',
245 | POLYGON = 'polygon'
246 | }
247 | /**
248 | * @description 滚动图表类型枚举
249 | * @public
250 | */
251 | export enum ROLL_CHART {
252 | RECT = 'rect',
253 | WHEEL = 'wheel',
254 | WINDMILL = 'windmill'
255 | }
256 | /**
257 | * @description 圆形动作枚举
258 | * @public
259 | */
260 | export enum CIRCULAR_ACTION {
261 | COLLISION = 'collision',
262 | ROTATE = 'rotate'
263 | }
264 |
--------------------------------------------------------------------------------
/tsconfig.base.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "strict": true,
4 | "allowJs": true,
5 | "noUnusedLocals": true,
6 | "noUnusedParameters": true,
7 | "noImplicitAny": true,
8 | "noImplicitThis": true,
9 | "noImplicitReturns": true,
10 | "moduleResolution": "node",
11 | "resolveJsonModule": true,
12 | "importHelpers": true,
13 | "noEmitHelpers": true
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.base.json",
3 | "compilerOptions": {
4 | "target": "ES5",
5 | "module": "ESNext",
6 | "declaration": true,
7 | "outDir": "lib"
8 | },
9 | "include": [
10 | "./src/**/*"
11 | ]
12 | }
13 |
--------------------------------------------------------------------------------