├── .browserslistrc ├── .editorconfig ├── .github ├── FUNDING.yml └── workflows │ ├── ci.yml │ ├── deploy-demo.yml │ └── release.yml ├── .gitignore ├── .npmrc ├── .stylelintignore ├── .vscode ├── extensions.json └── settings.json ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── eslint.config.js ├── examples └── demo │ ├── index.html │ ├── package.json │ ├── public │ └── vite.svg │ ├── src │ ├── App.vue │ ├── main.ts │ ├── style.scss │ └── types.ts │ ├── uno.config.ts │ └── vite.config.ts ├── package.json ├── pnpm-lock.yaml ├── pnpm-workspace.yaml ├── readme └── xiaohe-vue-count-to.gif ├── src ├── components │ ├── CountTo │ │ ├── index.ts │ │ ├── index.vue │ │ └── types.ts │ └── index.ts ├── constant.ts ├── index.ts └── types.ts ├── stylelint.config.js ├── test └── index.test.ts ├── tsconfig.json ├── tsconfig.node.json └── vite.config.ts /.browserslistrc: -------------------------------------------------------------------------------- 1 | >0.3%, defaults -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # [EditorConfig](https://editorconfig.org) 2 | # [配置项](https://github.com/editorconfig/editorconfig/wiki/EditorConfig-Properties) 3 | 4 | root = true 5 | 6 | [*] 7 | # 字符编码 8 | charset = utf-8 9 | # 结尾换行符 (lf, cr, crlf) 10 | end_of_line = lf 11 | # 去除行尾空格 12 | trim_trailing_whitespace = true 13 | # 在文件结尾插入新行 14 | insert_final_newline = false 15 | # 单行最大长度 16 | max_line_length = off 17 | 18 | [*.{js,jsx,ts,tsx,vue,json}] 19 | # 缩进风格 (space, tab) 20 | indent_style = space 21 | # 缩进大小 22 | indent_size = 2 23 | # 单个tab大小 24 | tab_width = 2 -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: [xiaohe0601] -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | 8 | pull_request: 9 | branches: 10 | - main 11 | 12 | jobs: 13 | lint: 14 | runs-on: ubuntu-latest 15 | steps: 16 | - uses: actions/checkout@v3 17 | 18 | - name: Install pnpm 19 | uses: pnpm/action-setup@v2 20 | 21 | - name: Set node 22 | uses: actions/setup-node@v3 23 | with: 24 | node-version: lts/* 25 | 26 | - name: Setup 27 | run: npm i -g @antfu/ni 28 | 29 | - name: Install 30 | run: nci 31 | 32 | - name: Lint code 33 | run: nr lint 34 | 35 | - name: Lint type 36 | run: nr lint:type 37 | 38 | - name: Lint style 39 | run: nr style 40 | 41 | test: 42 | runs-on: ${{ matrix.os }} 43 | 44 | strategy: 45 | matrix: 46 | node: [lts/*] 47 | os: [ubuntu-latest, windows-latest, macos-latest] 48 | fail-fast: false 49 | 50 | steps: 51 | - uses: actions/checkout@v3 52 | 53 | - name: Install pnpm 54 | uses: pnpm/action-setup@v2 55 | 56 | - name: Set node ${{ matrix.node }} 57 | uses: actions/setup-node@v3 58 | with: 59 | node-version: ${{ matrix.node }} 60 | 61 | - name: Setup 62 | run: npm i -g @antfu/ni 63 | 64 | - name: Install 65 | run: nci 66 | 67 | - name: Build 68 | run: nr build 69 | 70 | - name: Test 71 | run: nr test -------------------------------------------------------------------------------- /.github/workflows/deploy-demo.yml: -------------------------------------------------------------------------------- 1 | name: Deploy demo 2 | 3 | permissions: 4 | contents: write 5 | 6 | on: 7 | push: 8 | branches: 9 | - main 10 | 11 | jobs: 12 | deploy: 13 | runs-on: ubuntu-latest 14 | steps: 15 | - uses: actions/checkout@v3 16 | with: 17 | fetch-depth: 0 18 | 19 | - name: Install pnpm 20 | uses: pnpm/action-setup@v2 21 | 22 | - name: Set node 23 | uses: actions/setup-node@v3 24 | with: 25 | node-version: lts/* 26 | 27 | - name: Setup 28 | run: npm i -g @antfu/ni 29 | 30 | - name: Install 31 | run: | 32 | cd examples/demo 33 | nci 34 | 35 | - name: Build 36 | run: | 37 | nr build 38 | nr build:demo 39 | > examples/demo/dist/.nojekyll 40 | 41 | - name: Deploy 42 | uses: JamesIves/github-pages-deploy-action@v4 43 | with: 44 | branch: demo/gh-pages 45 | folder: examples/demo/dist -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Release 2 | 3 | permissions: 4 | contents: write 5 | 6 | on: 7 | push: 8 | tags: 9 | - "v*" 10 | 11 | jobs: 12 | release: 13 | runs-on: ubuntu-latest 14 | steps: 15 | - uses: actions/checkout@v3 16 | with: 17 | fetch-depth: 0 18 | 19 | - name: Install pnpm 20 | uses: pnpm/action-setup@v2 21 | 22 | - name: Set node 23 | uses: actions/setup-node@v3 24 | with: 25 | node-version: lts/* 26 | 27 | - run: npx changelogithub 28 | env: 29 | GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}} -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # 其他包管理器LOCK文件 2 | package-lock.json 3 | yarn.lock 4 | 5 | # 本地文件 6 | .env.local 7 | .env.*.local 8 | *.local 9 | 10 | # 日志文件 11 | logs 12 | *.log 13 | npm-debug.log* 14 | yarn-debug.log* 15 | yarn-error.log* 16 | pnpm-debug.log* 17 | lerna-debug.log* 18 | 19 | # 制品文件 20 | node_modules 21 | dist 22 | dist-ssr 23 | 24 | # IDE文件 25 | .vscode/* 26 | !.vscode/extensions.json 27 | !.vscode/settings.json 28 | .project 29 | .idea 30 | .hbuilderx 31 | .DS_Store 32 | *.suo 33 | *.ntvs* 34 | *.njsproj 35 | *.sln 36 | *.sw? -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | strict-peer-dependencies=false 2 | auto-install-peers=true 3 | shamefully-hoist=true 4 | ignore-workspace-root-check=true 5 | shell-emulator=true -------------------------------------------------------------------------------- /.stylelintignore: -------------------------------------------------------------------------------- 1 | dist 2 | node_modules 3 | public -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": [ 3 | "vue.volar", 4 | "vue.vscode-typescript-vue-plugin", 5 | "dbaeumer.vscode-eslint", 6 | "stylelint.vscode-stylelint", 7 | "editorConfig.editorConfig", 8 | "antfu.vite" 9 | ] 10 | } -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | // Enable the ESlint flat config support 3 | "eslint.experimental.useFlatConfig": true, 4 | 5 | // Disable the default formatter, use eslint instead 6 | "prettier.enable": false, 7 | "editor.formatOnSave": false, 8 | 9 | // Auto fix 10 | "editor.codeActionsOnSave": { 11 | "source.fixAll.eslint": "explicit", 12 | "source.organizeImports": "never" 13 | }, 14 | 15 | // Silent the stylistic rules in you IDE, but still auto fix them 16 | "eslint.rules.customizations": [ 17 | { "rule": "style/*", "severity": "off" }, 18 | { "rule": "*-indent", "severity": "off" }, 19 | { "rule": "*-spacing", "severity": "off" }, 20 | { "rule": "*-spaces", "severity": "off" }, 21 | { "rule": "*-order", "severity": "off" }, 22 | { "rule": "*-dangle", "severity": "off" }, 23 | { "rule": "*-newline", "severity": "off" }, 24 | { "rule": "*quotes", "severity": "off" }, 25 | { "rule": "*semi", "severity": "off" } 26 | ], 27 | 28 | // Enable eslint for all supported languages 29 | "eslint.validate": [ 30 | "javascript", 31 | "javascriptreact", 32 | "typescript", 33 | "typescriptreact", 34 | "vue", 35 | "html", 36 | "markdown", 37 | "json", 38 | "jsonc", 39 | "yaml" 40 | ] 41 | } -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | Please refer to https://github.com/antfu/contribute -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 xiaohe0601 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |
2 |

xiaohe-vue-count-to

3 | ⏱️ 一个实现数字平滑变化的Vue小组件 4 |
5 | 6 |
7 | 8 | [![github stars][github-stars-src]][github-stars-href] 9 | [![npm version][npm-version-src]][npm-version-href] 10 | [![npm downloads][npm-downloads-src]][npm-downloads-href] 11 | [![bundle][bundle-src]][bundle-href] 12 | [![JSDocs][jsdocs-src]][jsdocs-href] 13 | [![License][license-src]][license-href] 14 | 15 | 小何 / [github@xiaohe0601](https://github.com/xiaohe0601) / [gitee@xiaohe0601](https://gitee.com/xiaohe0601) 16 | 17 | ### 🎉 特性 18 | 19 | - 🍜 支持非线性动画 20 | - 🍟 支持中途打断动画 21 | - 🐼 支持Vue 2/3 22 | - 🧀 支持TypeScript 23 | - 🍳 支持免费商用 24 | - 🥗 更多特性等你发掘... 25 | 26 | ### 🎮 示例 27 | 28 |
29 | xiaohe-vue-count-to 30 |
31 | 32 | > 还可以[试一试](https://count-to.netlify.app)在线DEMO 33 | 34 | ### 🚁 安装 35 | 36 | #### PNPM 37 | 38 | ``` shell 39 | pnpm add xiaohe-vue-count-to 40 | ``` 41 | 42 | #### YARN 43 | 44 | ``` shell 45 | yarn add xiaohe-vue-count-to 46 | ``` 47 | 48 | #### NPM 49 | 50 | ``` shell 51 | npm install xiaohe-vue-count-to 52 | ``` 53 | 54 | #### 另外 55 | 56 | > 如果 `Vue` 版本小于 `2.7`,还需要安装 `@vue/composition-api` 57 | 58 | ``` shell 59 | pnpm add @vue/composition-api 60 | ``` 61 | 62 | ### 🛹 使用 63 | 64 | #### 简单使用 65 | 66 | ``` vue 67 | 74 | 75 | 87 | ``` 88 | 89 | #### 控制动画 90 | 91 | - 仅 `Vue 3` 可直接通过 `ref` 获取到 `xiaohe-transition` 实例用于控制动画 92 | 93 | ``` vue 94 | 101 | 102 | 125 | ``` 126 | 127 | - `Vue 2/3` 均可通过 `inited` 事件获取到 `xiaohe-transition` 实例用于控制动画 128 | 129 | ``` vue 130 | 137 | 138 | 170 | ``` 171 | 172 | #### 属性(Props) 173 | 174 | |参数|说明|类型|可选值|默认值| 175 | |---|---|---|---|---| 176 | |value|数值|`NumberLike`|-|0| 177 | |decimals|小数位数|`NumberLike`|-|0| 178 | |duration|动画时长(单位: ms)|`NumberLike`|-|2000| 179 | |preset|预设曲线|`BezierCurvePresetLike`|linear/ease/ease-in/ease-out/ease-in-out|linear| 180 | |bezier|自定义曲线(优先级高于preset)|`BezierCurve`|-|-| 181 | |delay|延迟开始时间(单位: ms)|`NumberLike`|-|0| 182 | |fps|帧率|`NumberLike`|-|-1| 183 | |decimal `0.3.0`|小数点|`string`|-|.| 184 | |use-group-value `0.3.0`|是否启用数值分组(示例:10000000.00 -> 10,000,000.00)|`boolean`|-|false| 185 | |use-indian-style-group `0.3.0`|是否使用印度风格数值分组(示例:10000000.00 -> 1,00,00,000.00)|`boolean`|-|false| 186 | |separator `0.3.0`|分隔符|`string`|-|,| 187 | |numerals `0.3.0`|自定义数字|`string[]`|-|-| 188 | 189 | #### 事件(Events) 190 | 191 | |事件|说明|回调参数| 192 | |---|---|---| 193 | |inited|`transition` 初始化完成|(instance: `Transition`)| 194 | |change|数值变化|(value: `string`, instance: `Transition`)| 195 | |started|动画开始|(instance: `Transition`)| 196 | |paused|动画暂停|(instance: `Transition`)| 197 | |resumed|动画继续|(instance: `Transition`)| 198 | |stopped|动画停止|(instance: `Transition`)| 199 | |completed|动画完成|(instance: `Transition`)| 200 | 201 | #### 插槽(Slots) 202 | 203 | |名称|说明|类型| 204 | |---|---|---| 205 | |default|默认内容|{ value: `string` }| 206 | 207 | #### 实例(Inst) 208 | 209 | |属性|说明|类型| 210 | |---|---|---| 211 | |transition|`xiaohe-transition` 实例|`Transition`| 212 | 213 | #### 类型定义 214 | 215 | > 请查看 [jsdocs.io](https://www.jsdocs.io/package/xiaohe-vue-count-to) 216 | 217 | ### 🛸 链接 218 | 219 | - [xiaohe-transition](https://github.com/xiaohe0601/xiaohe-transition) 🏀 一个简单易用的贝塞尔曲线过渡动画工具 220 | 221 | ### 🐶 讨论交流 222 | 223 | - ❓:若有疑问或BUG反馈,可提交[issues](https://github.com/xiaohe0601/xiaohe-vue-count-to/issues) 224 | - 📫:[xiaohe0601@outlook.com](mailto:xiaohe0601@outlook.com) 225 | - 🐧:暂未开通 226 | 227 | ### 🏆 开源协议 228 | 229 | - MIT [LICENSE](./LICENSE) 230 | 231 | 232 | 233 | [github-stars-src]: https://img.shields.io/github/stars/xiaohe0601/xiaohe-vue-count-to?style=flat&colorA=080f12&colorB=1fa669&logo=GitHub 234 | [github-stars-href]: https://github.com/xiaohe0601/xiaohe-vue-count-to 235 | [npm-version-src]: https://img.shields.io/npm/v/xiaohe-vue-count-to?style=flat&colorA=080f12&colorB=1fa669 236 | [npm-version-href]: https://npmjs.com/package/xiaohe-vue-count-to 237 | [npm-downloads-src]: https://img.shields.io/npm/dm/xiaohe-vue-count-to?style=flat&colorA=080f12&colorB=1fa669 238 | [npm-downloads-href]: https://npmjs.com/package/xiaohe-vue-count-to 239 | [bundle-src]: https://img.shields.io/bundlephobia/minzip/xiaohe-vue-count-to?style=flat&colorA=080f12&colorB=1fa669&label=minzip 240 | [bundle-href]: https://bundlephobia.com/result?p=xiaohe-vue-count-to 241 | [jsdocs-src]: https://img.shields.io/badge/jsdocs-reference-080f12?style=flat&colorA=080f12&colorB=1fa669 242 | [jsdocs-href]: https://www.jsdocs.io/package/xiaohe-vue-count-to 243 | [license-src]: https://img.shields.io/github/license/xiaohe0601/xiaohe-vue-count-to.svg?style=flat&colorA=080f12&colorB=1fa669 244 | [license-href]: https://github.com/xiaohe0601/xiaohe-vue-count-to/blob/main/LICENSE -------------------------------------------------------------------------------- /eslint.config.js: -------------------------------------------------------------------------------- 1 | import defineConfig from "@antfu/eslint-config"; 2 | 3 | // 配置参考 4 | // https://github.com/antfu/eslint-config 5 | // https://github.com/antfu/eslint-flat-config-viewer 6 | export default defineConfig({ 7 | stylistic: { 8 | indent: 2, 9 | quotes: "double" 10 | }, 11 | ignores: [] 12 | }, { 13 | rules: { 14 | "curly": ["error", "all"], 15 | "no-console": ["warn", { 16 | allow: [ 17 | "warn", 18 | "error" 19 | ] 20 | }], 21 | "dot-notation": ["off"], 22 | "symbol-description": ["off"], 23 | "no-extra-boolean-cast": ["off"], 24 | "prefer-promise-reject-errors": ["off"], 25 | "style/semi": ["error", "always"], 26 | "style/eol-last": ["error", "never"], 27 | "style/brace-style": ["error", "1tbs"], 28 | "style/quote-props": ["off"], 29 | "style/comma-dangle": ["error", "never"], 30 | "style/arrow-parens": ["error", "always"], 31 | "style/padded-blocks": ["error", { 32 | blocks: "never", 33 | classes: "always", 34 | switches: "never" 35 | }], 36 | "style/operator-linebreak": ["error", "before", { 37 | overrides: { 38 | "=": "after" 39 | } 40 | }], 41 | "style/no-multiple-empty-lines": ["error", { 42 | max: 2, 43 | maxBOF: 0, 44 | maxEOF: 0 45 | }], 46 | "style/member-delimiter-style": ["error", { 47 | singleline: { 48 | delimiter: "semi", 49 | requireLast: true 50 | }, 51 | multiline: { 52 | delimiter: "semi", 53 | requireLast: true 54 | }, 55 | multilineDetection: "brackets" 56 | }], 57 | "ts/ban-ts-comment": ["off"], 58 | "ts/prefer-ts-expect-error": ["off"], 59 | "import/no-self-import": ["off"], 60 | "import/newline-after-import": ["error", { 61 | count: 1, 62 | considerComments: false 63 | }], 64 | "vue/quote-props": ["off"], 65 | "vue/block-order": ["error", { 66 | order: ["template", "script:not([setup])", "script[setup]", "style:not([scoped])", "style[scoped]"] 67 | }], 68 | "vue/html-self-closing": ["error", { 69 | html: { 70 | void: "any", 71 | normal: "never", 72 | component: "never" 73 | }, 74 | svg: "never", 75 | math: "never" 76 | }], 77 | "vue/custom-event-name-casing": ["error", "kebab-case"], 78 | "vue/first-attribute-linebreak": ["error", { 79 | singleline: "beside", 80 | multiline: "beside" 81 | }], 82 | "vue/html-closing-bracket-newline": ["error", { 83 | singleline: "never", 84 | multiline: "never" 85 | }], 86 | "vue/component-name-in-template-casing": ["error", "kebab-case"], 87 | "vue/singleline-html-element-content-newline": ["off"], 88 | "vue/multiline-html-element-content-newline": ["off"], 89 | "node/prefer-global/process": ["error", "always"], 90 | "antfu/consistent-list-newline": ["off"] 91 | } 92 | }); -------------------------------------------------------------------------------- /examples/demo/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | xiaohe-vue-count-to 12 | 13 | 14 | 15 |
16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /examples/demo/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "demo", 3 | "type": "module", 4 | "version": "1.0.0", 5 | "private": true, 6 | "scripts": { 7 | "serve": "vite", 8 | "build": "vite build", 9 | "preview": "vite preview" 10 | }, 11 | "dependencies": { 12 | "ress": "^5.0.2", 13 | "xiaohe-vue-count-to": "workspace:^" 14 | }, 15 | "devDependencies": { 16 | "naive-ui": "^2.37.3", 17 | "unocss": "^0.58.3" 18 | } 19 | } -------------------------------------------------------------------------------- /examples/demo/public/vite.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /examples/demo/src/App.vue: -------------------------------------------------------------------------------- 1 | 115 | 116 | 160 | 161 | -------------------------------------------------------------------------------- /examples/demo/src/main.ts: -------------------------------------------------------------------------------- 1 | import "ress"; 2 | import "virtual:uno.css"; 3 | import { createApp } from "vue"; 4 | import App from "./App.vue"; 5 | 6 | const app = createApp(App); 7 | 8 | app.mount("#app"); -------------------------------------------------------------------------------- /examples/demo/src/style.scss: -------------------------------------------------------------------------------- 1 | html { 2 | font-family: MiSans, -apple-system, BlinkMacSystemFont, "Helvetica Neue", Helvetica, Segoe UI, Arial, Roboto, "PingFang SC", "miui", "Hiragino Sans GB", "Microsoft Yahei", sans-serif; 3 | } 4 | 5 | html, body, #app { 6 | --at-apply: w-full h-full; 7 | } 8 | 9 | #app { 10 | --at-apply: relative text-14px text-black; 11 | } 12 | 13 | .app-container { 14 | --at-apply: w-980px mx-a; 15 | } -------------------------------------------------------------------------------- /examples/demo/src/types.ts: -------------------------------------------------------------------------------- 1 | export type OptionalObject = T | undefined; 2 | export type OptionalNumber = OptionalObject; 3 | export type OptionalString = OptionalObject; 4 | export type OptionalBoolean = OptionalObject; 5 | 6 | export type NullableObject = T | null; 7 | export type NullableNumber = NullableObject; 8 | export type NullableString = NullableObject; 9 | export type NullableBoolean = NullableObject; 10 | 11 | export type NumberLike = number | `${number}`; 12 | 13 | export type Arrayable = T | T[]; -------------------------------------------------------------------------------- /examples/demo/uno.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig, presetUno, transformerDirectives } from "unocss"; 2 | 3 | export default defineConfig({ 4 | presets: [ 5 | presetUno() 6 | ], 7 | transformers: [ 8 | transformerDirectives() 9 | ] 10 | }); -------------------------------------------------------------------------------- /examples/demo/vite.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from "vite"; 2 | import VuePlugin from "@vitejs/plugin-vue"; 3 | import UnoCSS from "unocss/vite"; 4 | 5 | export default defineConfig({ 6 | plugins: [ 7 | VuePlugin(), 8 | UnoCSS() 9 | ] 10 | }); -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "xiaohe-vue-count-to", 3 | "type": "module", 4 | "version": "0.3.2", 5 | "packageManager": "pnpm@8.14.0", 6 | "description": "⏱️ 一个实现数字平滑变化的Vue小组件", 7 | "author": "xiaohe0601 ", 8 | "license": "MIT", 9 | "funding": "https://github.com/sponsors/xiaohe0601", 10 | "homepage": "https://count-to.netlify.app", 11 | "repository": { 12 | "type": "git", 13 | "url": "git+https://github.com/xiaohe0601/xiaohe-vue-count-to.git" 14 | }, 15 | "bugs": "https://github.com/xiaohe0601/xiaohe-vue-count-to/issues", 16 | "keywords": [ 17 | "xiaohe", 18 | "count-to", 19 | "bezier", 20 | "transition", 21 | "animation" 22 | ], 23 | "sideEffects": false, 24 | "exports": { 25 | ".": { 26 | "types": "./dist/index.d.ts", 27 | "import": "./dist/index.js", 28 | "require": "./dist/index.umd.cjs" 29 | } 30 | }, 31 | "main": "./dist/index.umd.cjs", 32 | "module": "./dist/index.js", 33 | "types": "./dist/index.d.ts", 34 | "typesVersions": { 35 | "*": { 36 | "*": [ 37 | "./dist/*", 38 | "./dist/index.d.ts" 39 | ] 40 | } 41 | }, 42 | "files": [ 43 | "dist" 44 | ], 45 | "scripts": { 46 | "serve": "vite", 47 | "serve:demo": "nr --dir examples/demo serve", 48 | "build": "vite build", 49 | "build:demo": "nr --dir examples/demo build", 50 | "preview": "vite preview", 51 | "preview:demo": "nr --dir examples/demo preview", 52 | "release": "nr build && bumpp && npm publish", 53 | "test": "vitest", 54 | "test:ui": "vitest --ui", 55 | "test:coverage": "vitest run --coverage", 56 | "lint": "eslint src", 57 | "lint:fix": "eslint src --fix", 58 | "lint:type": "vue-tsc --noEmit", 59 | "style": "stylelint src/**/*.{css,scss,vue,html} --aei", 60 | "style:fix": "stylelint src/**/*.{css,scss,vue,html} --aei --fix", 61 | "prepare": "simple-git-hooks" 62 | }, 63 | "dependencies": { 64 | "vue": "^3.4.14", 65 | "vue-demi": "^0.14.6", 66 | "xiaohe-transition": "^1.1.0" 67 | }, 68 | "peerDependencies": { 69 | "vue": ">=2.5.0 || >=3.0.0" 70 | }, 71 | "devDependencies": { 72 | "@antfu/eslint-config": "^2.6.2", 73 | "@antfu/ni": "^0.21.12", 74 | "@antfu/utils": "^0.7.7", 75 | "@types/node": "^20.11.3", 76 | "@vitejs/plugin-vue": "^5.0.3", 77 | "autoprefixer": "^10.4.16", 78 | "bumpp": "^9.2.1", 79 | "eslint": "^8.56.0", 80 | "esno": "^4.0.0", 81 | "lint-staged": "^15.2.0", 82 | "rimraf": "^5.0.5", 83 | "sass": "^1.69.7", 84 | "simple-git-hooks": "^2.9.0", 85 | "stylelint": "^16.1.0", 86 | "stylelint-config-html": "^1.1.0", 87 | "stylelint-config-recess-order": "^4.4.0", 88 | "stylelint-config-recommended": "^14.0.0", 89 | "stylelint-config-recommended-vue": "^1.5.0", 90 | "stylelint-config-standard": "^36.0.0", 91 | "stylelint-config-standard-scss": "^12.0.0", 92 | "stylelint-order": "^6.0.4", 93 | "typescript": "^5.3.3", 94 | "vite": "^5.0.11", 95 | "vite-plugin-dts": "^3.7.1", 96 | "vitest": "^1.2.0", 97 | "vue-tsc": "^1.8.27" 98 | }, 99 | "simple-git-hooks": { 100 | "pre-commit": "pnpm lint-staged" 101 | }, 102 | "lint-staged": { 103 | "*.{js,jsx,ts,tsx,vue}": "eslint --fix", 104 | "*.{css,scss,vue,html}": "stylelint --aei --fix" 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /pnpm-workspace.yaml: -------------------------------------------------------------------------------- 1 | packages: 2 | - playground 3 | - docs 4 | - packages/* 5 | - examples/* -------------------------------------------------------------------------------- /readme/xiaohe-vue-count-to.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaohe0601/xiaohe-vue-count-to/c3885483aabffd2d35979de7ff94a3c2cd44984a/readme/xiaohe-vue-count-to.gif -------------------------------------------------------------------------------- /src/components/CountTo/index.ts: -------------------------------------------------------------------------------- 1 | import CountTo from "./index.vue"; 2 | 3 | export * from "./types.ts"; 4 | 5 | export { CountTo }; -------------------------------------------------------------------------------- /src/components/CountTo/index.vue: -------------------------------------------------------------------------------- 1 | 176 | 177 | -------------------------------------------------------------------------------- /src/components/CountTo/types.ts: -------------------------------------------------------------------------------- 1 | import type { BezierCurve, BezierCurvePreset, Transition } from "xiaohe-transition"; 2 | import type { NumberLike } from "../../types.ts"; 3 | 4 | export type BezierCurvePresetLike = BezierCurvePreset | "linear" | "ease" | "ease-in" | "ease-out" | "ease-in-out"; 5 | 6 | export interface CountToProps { 7 | /** 8 | * 数值 9 | */ 10 | value?: NumberLike; 11 | /** 12 | * 小数位数 13 | */ 14 | decimals?: NumberLike; 15 | /** 16 | * 动画时长(单位: ms) 17 | */ 18 | duration?: NumberLike; 19 | /** 20 | * 预设曲线 21 | */ 22 | preset?: BezierCurvePresetLike; 23 | /** 24 | * 自定义曲线(优先级高于preset) 25 | */ 26 | bezier?: BezierCurve; 27 | /** 28 | * 延迟开始时间(单位: ms) 29 | */ 30 | delay?: NumberLike; 31 | /** 32 | * 帧率(即每秒回调多少次,若小于或等于0则使用requestAnimationFrame自动处理) 33 | */ 34 | fps?: NumberLike; 35 | /** 36 | * 小数点 37 | * 38 | * @since 0.3.0 39 | */ 40 | decimal?: string; 41 | /** 42 | * 是否启用数值分组(示例:10000000.00 -> 10,000,000.00) 43 | * 44 | * @since 0.3.0 45 | */ 46 | useGroupValue?: boolean; 47 | /** 48 | * 是否使用印度风格数值分组(示例:10000000.00 -> 1,00,00,000.00) 49 | * 50 | * @since 0.3.0 51 | */ 52 | useIndianStyleGroup?: boolean; 53 | /** 54 | * 分隔符 55 | * 56 | * @since 0.3.0 57 | */ 58 | separator?: string; 59 | /** 60 | * 自定义数字 61 | * 62 | * @since 0.3.0 63 | */ 64 | numerals?: string[]; 65 | } 66 | 67 | export interface CountToEmits { 68 | /** 69 | * transition初始化完成 70 | */ 71 | (e: "inited", instance: Transition): void; 72 | 73 | /** 74 | * 数值变化 75 | */ 76 | (e: "change", value: string, instance: Transition): void; 77 | 78 | /** 79 | * 动画开始 80 | */ 81 | (e: "started", instance: Transition): void; 82 | 83 | /** 84 | * 动画暂停 85 | */ 86 | (e: "paused", instance: Transition): void; 87 | 88 | /** 89 | * 动画继续 90 | */ 91 | (e: "resumed", instance: Transition): void; 92 | 93 | /** 94 | * 动画停止 95 | */ 96 | (e: "stopped", instance: Transition): void; 97 | 98 | /** 99 | * 动画完成 100 | */ 101 | (e: "completed", instance: Transition): void; 102 | } 103 | 104 | export interface CountToSlots { 105 | default(props: { value: string; }): any; 106 | } 107 | 108 | export interface CountToInst { 109 | transition: Transition; 110 | } 111 | 112 | export {}; -------------------------------------------------------------------------------- /src/components/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./CountTo/index.ts"; -------------------------------------------------------------------------------- /src/constant.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * class前缀 3 | */ 4 | export const CLASS_PREFIX: string = "xh"; -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./constant.ts"; 2 | export * from "./components/index.ts"; -------------------------------------------------------------------------------- /src/types.ts: -------------------------------------------------------------------------------- 1 | export type OptionalObject = T | undefined; 2 | export type OptionalNumber = OptionalObject; 3 | export type OptionalString = OptionalObject; 4 | export type OptionalBoolean = OptionalObject; 5 | 6 | export type NullableObject = T | null; 7 | export type NullableNumber = NullableObject; 8 | export type NullableString = NullableObject; 9 | export type NullableBoolean = NullableObject; 10 | 11 | export type NumberLike = number | `${number}`; 12 | 13 | export type Arrayable = T | T[]; -------------------------------------------------------------------------------- /stylelint.config.js: -------------------------------------------------------------------------------- 1 | // 配置参考 2 | // https://stylelint.io/user-guide/rules 3 | // https://github.com/stylelint-scss/stylelint-scss#list-of-rules 4 | 5 | /** @type {import("stylelint").Config} */ 6 | export default { 7 | defaultSeverity: "error", 8 | extends: [ 9 | "stylelint-config-standard", 10 | "stylelint-config-standard-scss", 11 | "stylelint-config-recommended", 12 | "stylelint-config-recommended-vue", 13 | "stylelint-config-html", 14 | "stylelint-config-recess-order" 15 | ], 16 | plugins: [ 17 | "stylelint-order" 18 | ], 19 | rules: { 20 | "unit-no-unknown": null, 21 | "no-empty-source": null, 22 | "color-hex-length": ["long", { 23 | message: "16进制颜色需要使用长符号(#ffffff)风格" 24 | }], 25 | "at-rule-no-unknown": null, 26 | "value-keyword-case": null, 27 | "length-zero-no-unit": [true, { 28 | ignore: ["custom-properties"], 29 | ignoreFunctions: ["/^--/", "var", "calc"] 30 | }], 31 | "alpha-value-notation": ["number"], 32 | "no-duplicate-selectors": null, 33 | "selector-class-pattern": ["^([#a-z][$#{}a-z0-9]*)((-{1,2}|_{2})[$#{}a-z0-9]+)*$", { 34 | message: "class命名需要符合BEM风格(block-element[__element][--modifier])" 35 | }], 36 | "color-function-notation": ["legacy", { 37 | ignore: ["with-var-inside"] 38 | }], 39 | "font-family-name-quotes": null, 40 | "no-descending-specificity": null, 41 | "selector-type-no-unknown": [true, { 42 | ignore: ["custom-elements", "default-namespace"], 43 | ignoreTypes: ["page", "rich-text", "scroll-view"] 44 | }], 45 | "selector-pseudo-class-no-unknown": [true, { 46 | ignorePseudoClasses: ["deep"] 47 | }], 48 | "custom-property-empty-line-before": null, 49 | "font-family-no-missing-generic-family-keyword": null, 50 | "declaration-block-no-redundant-longhand-properties": [true, { 51 | ignoreShorthands: ["inset"] 52 | }], 53 | "scss/at-import-partial-extension": ["always"], 54 | "scss/at-extend-no-missing-placeholder": null 55 | } 56 | }; -------------------------------------------------------------------------------- /test/index.test.ts: -------------------------------------------------------------------------------- 1 | import { describe, expect, it } from "vitest"; 2 | 3 | describe("should", () => { 4 | it("exported", () => { 5 | expect(1).toEqual(1); 6 | }); 7 | }); -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "allowImportingTsExtensions": true, 4 | "isolatedModules": true, 5 | "jsx": "preserve", 6 | "lib": ["ESNext", "DOM"], 7 | "module": "ESNext", 8 | "moduleResolution": "Bundler", 9 | "noFallthroughCasesInSwitch": true, 10 | "noUnusedLocals": true, 11 | "noUnusedParameters": true, 12 | "resolveJsonModule": true, 13 | "skipLibCheck": true, 14 | "strict": true, 15 | "target": "ESNext", 16 | "useDefineForClassFields": true 17 | }, 18 | "include": ["src/**/*.ts", "src/**/*.tsx", "src/**/*.vue"], 19 | "references": [{ "path": "./tsconfig.node.json" }] 20 | } -------------------------------------------------------------------------------- /tsconfig.node.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "allowSyntheticDefaultImports": true, 4 | "composite": true, 5 | "module": "ESNext", 6 | "moduleResolution": "Bundler", 7 | "skipLibCheck": true 8 | }, 9 | "include": ["vite.config.ts"] 10 | } -------------------------------------------------------------------------------- /vite.config.ts: -------------------------------------------------------------------------------- 1 | import path from "node:path"; 2 | import { defineConfig } from "vite"; 3 | import VuePlugin from "@vitejs/plugin-vue"; 4 | import DtsPlugin from "vite-plugin-dts"; 5 | import Autoprefixer from "autoprefixer"; 6 | 7 | function resolve(...paths: string[]): string { 8 | return path.resolve(__dirname, ...paths); 9 | } 10 | 11 | export default defineConfig({ 12 | build: { 13 | lib: { 14 | entry: resolve("src", "index.ts"), 15 | name: "XhCountTo", 16 | formats: ["es", "cjs", "umd"], 17 | fileName: "index" 18 | }, 19 | rollupOptions: { 20 | external: ["vue", "vue-demi", "xiaohe-transition"], 21 | output: { 22 | globals: { 23 | "vue": "Vue", 24 | "vue-demi": "VueDemi", 25 | "xiaohe-transition": "XhTransition" 26 | } 27 | } 28 | } 29 | }, 30 | optimizeDeps: { 31 | exclude: ["vue-demi"] 32 | }, 33 | plugins: [ 34 | VuePlugin(), 35 | DtsPlugin({ 36 | rollupTypes: true 37 | }) 38 | ], 39 | css: { 40 | postcss: { 41 | plugins: [Autoprefixer()] 42 | } 43 | } 44 | }); --------------------------------------------------------------------------------