| `['Hello', 1000, () => alert('Word')]` |
25 | | `loop` | False | Number | `1` or `'Infinity'` |
26 | | `wrapper` | False | String | `'div'` |
27 |
--------------------------------------------------------------------------------
/src/VTypical.ts:
--------------------------------------------------------------------------------
1 | import { h, onMounted, ref, defineComponent } from 'vue';
2 | import typing from './typing';
3 |
4 | const loopedTyping = typing;
5 |
6 | export default defineComponent({
7 | name: 'Typical',
8 | props: {
9 | steps: {
10 | type: Array,
11 | required: true,
12 | },
13 | wrapper: {
14 | type: String,
15 | default: 'div',
16 | },
17 | loop: {
18 | type: Number,
19 | default: 1,
20 | },
21 | },
22 | render() {
23 | return h(this.wrapper, {
24 | ref: 'myRef',
25 | });
26 | },
27 | setup: ({ steps, loop }) => {
28 | const myRef = ref(null);
29 |
30 | onMounted(() => {
31 | const dom = myRef.value;
32 | if (loop === Infinity) {
33 | typing(dom, ...steps, loopedTyping);
34 | } else if (typeof loop === 'number' && loop > 0) {
35 | typing(dom, ...Array(loop).fill(steps).flat());
36 | } else {
37 | typing(dom, ...steps);
38 | }
39 | });
40 |
41 | return {
42 | myRef,
43 | };
44 | },
45 | });
46 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2021-present Turkyden
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.
22 |
--------------------------------------------------------------------------------
/docs/public/logo.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/typing.ts:
--------------------------------------------------------------------------------
1 | export default async function typing(node, ...args) {
2 | for (const arg of args) {
3 | switch (typeof arg) {
4 | case 'string':
5 | await edit(node, arg);
6 | break;
7 | case 'number':
8 | await wait(arg);
9 | break;
10 | case 'function':
11 | await arg(node, ...args);
12 | break;
13 | default:
14 | await arg;
15 | }
16 | }
17 | }
18 |
19 | async function edit(node, text) {
20 | const overlap = getOverlap(node.textContent, text);
21 | await perform(node, [
22 | ...deleter(node.textContent, overlap),
23 | ...writer(text, overlap),
24 | ]);
25 | }
26 |
27 | async function wait(ms) {
28 | await new Promise((resolve) => setTimeout(resolve, ms));
29 | }
30 |
31 | async function perform(node, edits, speed = 60) {
32 | for (const op of editor(edits)) {
33 | op(node);
34 | await wait(speed + speed * (Math.random() - 0.5));
35 | }
36 | }
37 |
38 | export function* editor(edits) {
39 | for (const edit of edits) {
40 | yield (node) => requestAnimationFrame(() => (node.textContent = edit));
41 | }
42 | }
43 |
44 | export function* writer([...text], startIndex = 0, endIndex = text.length) {
45 | while (startIndex < endIndex) {
46 | yield text.slice(0, ++startIndex).join('');
47 | }
48 | }
49 |
50 | export function* deleter([...text], startIndex = 0, endIndex = text.length) {
51 | while (endIndex > startIndex) {
52 | yield text.slice(0, --endIndex).join('');
53 | }
54 | }
55 |
56 | export function getOverlap(start, [...end]) {
57 | return [...start, NaN].findIndex((char, i) => end[i] !== char);
58 | }
59 |
--------------------------------------------------------------------------------
/docs/.vitepress/config.js:
--------------------------------------------------------------------------------
1 | const base = process.env.NODE_ENV === 'production' ? '/' : '';
2 | const { resolve } = require('path');
3 |
4 | module.exports = {
5 | title: 'Vue Typical',
6 | description: '',
7 | // 扫描srcIncludes里面的 *.md文件
8 | srcIncludes: ['src'],
9 | alias: {
10 | // 为了能在demo中正确的使用 import { X } from 'vue-typical'
11 | [`vue-typical`]: resolve('./src'),
12 | },
13 | base,
14 | themeConfig: {
15 | logo: '/logo.svg',
16 | locales: {
17 | '/': {
18 | lang: 'en-US',
19 | title: 'VueTypical',
20 | description: '',
21 | label: 'English',
22 | selectText: 'Languages',
23 | nav: [
24 | { text: 'Guide', link: '/' },
25 | {
26 | text: 'Changelog',
27 | link:
28 | 'https://github.com/Turkyden/vue-typical/blob/master/CHANGELOG.md',
29 | },
30 | ],
31 | sidebar: [
32 | { text: 'Getting Started', link: '/' },
33 | {
34 | text: 'Demo',
35 | children: [
36 | { text: 'Basic Usage', link: '/basic/' },
37 | { text: 'Composition API', link: '/composition/' },
38 | ],
39 | },
40 | ],
41 | },
42 | '/zh/': {
43 | lang: 'zh-CN',
44 | title: 'VueTypical',
45 | description: '',
46 | label: '中文',
47 | selectText: '语言',
48 | nav: [
49 | { text: '入门指南', link: '/zh/' },
50 | {
51 | text: '更新日志',
52 | link:
53 | 'https://github.com/Turkyden/vue-typical/blob/master/CHANGELOG.md',
54 | },
55 | ],
56 | sidebar: [
57 | { text: '快速开始', link: '/zh/' },
58 | {
59 | text: '示例',
60 | children: [
61 | { text: '基本方法', link: '/zh/basic/' },
62 | { text: 'Composition API', link: '/zh/composition/' },
63 | ],
64 | },
65 | ],
66 | },
67 | },
68 | search: {
69 | searchMaxSuggestions: 10,
70 | },
71 | repo: 'Turkyden/vue-typical',
72 | repoLabel: 'Github',
73 | lastUpdated: true,
74 | prevLink: true,
75 | nextLink: true,
76 | },
77 | };
78 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "vue-typical",
3 | "version": "2.1.0",
4 | "description": "Vue Animated typing in ~400 bytes 🐡 of JavaScript.",
5 | "keywords": [
6 | "vue",
7 | "vue-component",
8 | "typical",
9 | "typing",
10 | "animation",
11 | "vue3",
12 | "components"
13 | ],
14 | "authors": {
15 | "name": "Turkyden",
16 | "email": "wj871287@gmail.com"
17 | },
18 | "homepage": "http://github.com/Turkyden/vue-typical",
19 | "repository": {
20 | "type": "git",
21 | "url": "git@github.com:Turkyden/vue-typical.git"
22 | },
23 | "bugs": {
24 | "url": "http://github.com/Turkyden/vue-typical/issues"
25 | },
26 | "license": "MIT",
27 | "main": "dist/vue-typical.umd.js",
28 | "module": "dist/vue-typical.es.js",
29 | "files": [
30 | "dist",
31 | "package.json",
32 | "README.md",
33 | "LICENSE"
34 | ],
35 | "types": "dist/src/index.d.ts",
36 | "scripts": {
37 | "start": "vitepress-fc dev --root=docs --host",
38 | "build": "vite build && yarn type",
39 | "type": "tsc -d",
40 | "test": "jest",
41 | "docs:build": "rimraf docs/dist && cross-env NODE_ENV=production vitepress-fc build --root=docs",
42 | "docs:serve": "cross-env NODE_ENV=production vitepress-fc serve --root=docs",
43 | "docs:build-serve": "cross-env NODE_ENV=true yarn docs:build && yarn docs:serve",
44 | "docs:deploy": "gh-pages -d docs/dist -t true",
45 | "docs:build-deploy": "yarn docs:build && yarn docs:deploy",
46 | "changelog": "conventional-changelog -p angular -i CHANGELOG.md -s",
47 | "lint": "eslint src/**/*.{vue,js,ts,tsx}",
48 | "lint:fix": "yarn lint -- --fix"
49 | },
50 | "devDependencies": {
51 | "@rollup/plugin-node-resolve": "^10.0.0",
52 | "@types/jest": "^26.0.15",
53 | "@typescript-eslint/eslint-plugin": "^4.8.1",
54 | "@typescript-eslint/parser": "^4.8.1",
55 | "@vitejs/plugin-vue": "^1.1.4",
56 | "@vitejs/plugin-vue-jsx": "^1.1.7",
57 | "@vue/eslint-config-prettier": "^6.0.0",
58 | "@vue/eslint-config-typescript": "^7.0.0",
59 | "@vue/test-utils": "^2.0.0-beta.10",
60 | "@vuedx/typescript-plugin-vue": "^0.6.3",
61 | "babel-jest": "^26.6.3",
62 | "conventional-changelog-cli": "^2.1.1",
63 | "cross-env": "^7.0.2",
64 | "eslint": "^7.13.0",
65 | "eslint-plugin-prettier": "^3.1.4",
66 | "eslint-plugin-vue": "^7.1.0",
67 | "gh-pages": "^3.1.0",
68 | "husky": "^4.3.0",
69 | "jest": "^26.6.3",
70 | "np": "^7.4.0",
71 | "prettier": "^2.1.2",
72 | "pretty-quick": "^3.1.0",
73 | "rimraf": "^3.0.2",
74 | "rollup-plugin-alias": "^2.2.0",
75 | "rollup-plugin-commonjs": "^10.1.0",
76 | "rollup-plugin-css-only": "^2.1.0",
77 | "rollup-plugin-typescript2": "^0.30.0",
78 | "rollup-plugin-vue": "^6.0.0-beta.11",
79 | "ts-jest": "^26.4.4",
80 | "typescript": "^4.2.2",
81 | "vite": "^2.4.1",
82 | "vitepress-for-component": "latest",
83 | "vue-jest": "^5.0.0-alpha.6"
84 | },
85 | "publishConfig": {
86 | "access": "public",
87 | "registry": "https://registry.npmjs.org"
88 | },
89 | "husky": {
90 | "hooks": {
91 | "pre-commit": "pretty-quick --staged"
92 | }
93 | }
94 | }
95 |
--------------------------------------------------------------------------------
/docs/index.en-US.md:
--------------------------------------------------------------------------------
1 | English | [简体中文](https://vue-typical.vercel.app/zh/)
2 |
3 | Vue Typical
4 |
5 | Vue Animated typing in ~400 bytes 🐡 of JavaScript. Preview →
6 |
7 |
8 |
9 |
10 |
11 |
16 |
17 | Live Demo ✨ https://vue-typical.vercel.app
18 |
19 | ## 📦 Installation
20 |
21 | Install with yarn
22 |
23 | ```bash
24 | yarn add vue-typical
25 | ```
26 |
27 | Or you can
28 |
29 | ```bash
30 | npm install vue-typical
31 | ```
32 |
33 | Or inject the script at your page by [jsdelivr CDN](https://www.jsdelivr.com/)
34 |
35 | ```html
36 |
37 | ```
38 |
39 | ## 🚀 Usage
40 |
41 | ### 1. Component
42 |
43 | You can import it as a custom component.
44 |
45 | ```vue | pure
46 |
47 |
53 |
54 |
55 |
64 | ```
65 |
66 | ### 2. Plugin API
67 |
68 | If you want to configure default options, you can register this plugin through the use API of Vue.js.
69 |
70 | ```tsx | pure
71 | // main.js or index.js
72 | import VTypical from 'vue-typical';
73 |
74 | Vue.use(VTypical, {
75 | /* options */
76 | });
77 | ```
78 |
79 | If you use the plugin API, the `VTypical` component will be registered as a global component just like when including it with the `script` tag, but you won't need to re-register it through the `components` property in your own components.
80 |
81 | ### 3. Composition API
82 |
83 | You can also create typical animation by the composition API `useTypical`.
84 |
85 | ```typescript | pure
86 | const ref = useTypical(options);
87 | ```
88 |
89 | Coming Soon ...
90 |
91 | ## 📑 Properties
92 |
93 | | Prop | Required | Type | Eg. |
94 | | --------- | -------- | ----------------------------------- | -------------------------------------- |
95 | | `steps` | True | Array | `['Hello', 1000, () => alert('Word')]` |
96 | | `loop` | False | Number | `1` or `'Infinity'` |
97 | | `wrapper` | False | String | `'div'` |
98 |
99 | ## ✨ Style
100 |
101 | Add the blink cursor effect with `blink` classname.
102 |
103 | ```css
104 | .blink::after {
105 | content: '|';
106 | animation: blink 1s infinite step-start;
107 | }
108 |
109 | @keyframes blink {
110 | 50% {
111 | opacity: 0;
112 | }
113 | }
114 | ```
115 |
116 | ## 🔢 Coming Soon
117 |
118 | - [ ] The demo of composition API in Vue 3.0
119 |
120 | ## 🔨 Contribute
121 |
122 | Install dependencies,
123 |
124 | ```bash
125 | $ npm i
126 | ```
127 |
128 | Start the dev server,
129 |
130 | ```bash
131 | $ npm start
132 | ```
133 |
134 | Build documentation,
135 |
136 | ```bash
137 | $ npm run docs:build
138 | ```
139 |
140 | Build library via `father-build`,
141 |
142 | ```bash
143 | $ npm run build
144 | ```
145 |
146 | ## ❤️ Contributors
147 |
148 | Thanks goes to these people:
149 |
150 | 
151 |
152 | Please Feel free to enjoy and participate in open source!
153 |
154 | ## ⭐ Stargazers
155 |
156 | Thanks for your star!
157 |
158 | [](https://github.com/Turkyden/vue-typical/stargazers)
159 |
160 | ## 🔖 License
161 |
162 | This library is based on [@camwiegert/typical](https://github.com/camwiegert/typical) work and it currently is just a wrapper for vue.
163 |
164 | Inspired by [@catalinmiron/react-typical](https://github.com/catalinmiron/react-typical).
165 |
166 | [MIT](https://github.com/Turkyden/vue-typical/blob/main/LICENSE) © [Turkyden](https://github.com/Turkyden)
167 |
--------------------------------------------------------------------------------
/docs/index.zh-CN.md:
--------------------------------------------------------------------------------
1 | English | [简体中文](https://vue-typical.vercel.app/zh/)
2 |
3 | Vue Typical
4 |
5 | Vue Animated typing in ~400 bytes 🐡 of JavaScript. Preview →
6 |
7 |
8 |
9 |
10 |
11 |
16 |
17 | Live Demo ✨ https://vue-typical.vercel.app
18 |
19 | ## 📦 Installation
20 |
21 | Install with yarn
22 |
23 | ```bash
24 | yarn add vue-typical
25 | ```
26 |
27 | Or you can
28 |
29 | ```bash
30 | npm install vue-typical
31 | ```
32 |
33 | Or inject the script at your page by [jsdelivr CDN](https://www.jsdelivr.com/)
34 |
35 | ```html
36 |
37 | ```
38 |
39 | ## 🚀 Usage
40 |
41 | ### 1. Component
42 |
43 | You can import it as a custom component.
44 |
45 | ```vue | pure
46 |
47 |
53 |
54 |
55 |
64 | ```
65 |
66 | ### 2. Plugin API
67 |
68 | If you want to configure default options, you can register this plugin through the use API of Vue.js.
69 |
70 | ```tsx | pure
71 | // main.js or index.js
72 | import VTypical from 'vue-typical';
73 |
74 | Vue.use(VTypical, {
75 | /* options */
76 | });
77 | ```
78 |
79 | If you use the plugin API, the `VTypical` component will be registered as a global component just like when including it with the `script` tag, but you won't need to re-register it through the `components` property in your own components.
80 |
81 | ### 3. Composition API
82 |
83 | You can also create typical animation by the composition API `useTypical`.
84 |
85 | ```typescript | pure
86 | const ref = useTypical(options);
87 | ```
88 |
89 | Coming Soon ...
90 |
91 | ## 📑 Properties
92 |
93 | | Prop | Required | Type | Eg. |
94 | | --------- | -------- | ----------------------------------- | -------------------------------------- |
95 | | `steps` | True | Array | `['Hello', 1000, () => alert('Word')]` |
96 | | `loop` | False | Number | `1` or `'Infinity'` |
97 | | `wrapper` | False | String | `'div'` |
98 |
99 | ## ✨ Style
100 |
101 | Add the blink cursor effect with `blink` classname.
102 |
103 | ```css
104 | .blink::after {
105 | content: '|';
106 | animation: blink 1s infinite step-start;
107 | }
108 |
109 | @keyframes blink {
110 | 50% {
111 | opacity: 0;
112 | }
113 | }
114 | ```
115 |
116 | ## 🔢 Coming Soon
117 |
118 | - [ ] The demo of composition API in Vue 3.0
119 |
120 | ## 🔨 Contribute
121 |
122 | Install dependencies,
123 |
124 | ```bash
125 | $ npm i
126 | ```
127 |
128 | Start the dev server,
129 |
130 | ```bash
131 | $ npm start
132 | ```
133 |
134 | Build documentation,
135 |
136 | ```bash
137 | $ npm run docs:build
138 | ```
139 |
140 | Build library via `father-build`,
141 |
142 | ```bash
143 | $ npm run build
144 | ```
145 |
146 | ## ❤️ Contributors
147 |
148 | Thanks goes to these people:
149 |
150 | 
151 |
152 | Please Feel free to enjoy and participate in open source!
153 |
154 | ## ⭐ Stargazers
155 |
156 | Thanks for your star!
157 |
158 | [](https://github.com/Turkyden/vue-typical/stargazers)
159 |
160 | ## 🔖 License
161 |
162 | This library is based on [@camwiegert/typical](https://github.com/camwiegert/typical) work and it currently is just a wrapper for vue.
163 |
164 | Inspired by [@catalinmiron/react-typical](https://github.com/catalinmiron/react-typical).
165 |
166 | [MIT](https://github.com/Turkyden/vue-typical/blob/main/LICENSE) © [Turkyden](https://github.com/Turkyden)
167 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | English | [简体中文](https://vue-typical.vercel.app/zh/)
2 |
3 | Vue Typical
4 |
5 | Vue Animated typing in ~400 bytes 🐡 of JavaScript. Preview →
6 |
7 |
8 |
9 |
10 |
11 |
16 |
17 | Live Demo ✨ https://vue-typical.vercel.app
18 |
19 | ## 📦 Installation
20 |
21 | Install with yarn
22 |
23 | ```bash
24 | yarn add vue-typical
25 | ```
26 |
27 | Or you can
28 |
29 | ```bash
30 | npm install vue-typical
31 | ```
32 |
33 | Or inject the script at your page by [jsdelivr CDN](https://www.jsdelivr.com/)
34 |
35 | ```html
36 |
37 | ```
38 |
39 | > 💡 This library Just support Vue 3 now
40 |
41 | If you are Vue 2, please see the [branch v1](https://github.com/Turkyden/vue-typical/tree/v1) and install [vue-typical@1.6.2](https://github.com/Turkyden/vue-typical/tree/v1)
42 |
43 | ## 🚀 Usage
44 |
45 | ### 1. Component
46 |
47 | You can import it as a custom component.
48 |
49 | ```vue | pure
50 |
51 |
57 |
58 |
59 |
68 | ```
69 |
70 | ### 2. Plugin API
71 |
72 | If you want to configure default options, you can register this plugin through the use API of Vue.js.
73 |
74 | ```tsx | pure
75 | // main.js or index.js
76 | import VTypical from 'vue-typical';
77 |
78 | Vue.use(VTypical, {
79 | /* options */
80 | });
81 | ```
82 |
83 | If you use the plugin API, the `VTypical` component will be registered as a global component just like when including it with the `script` tag, but you won't need to re-register it through the `components` property in your own components.
84 |
85 | ### 3. Composition API
86 |
87 | You can also create typical animation by the composition API `useTypical`.
88 |
89 | ```typescript | pure
90 | const ref = useTypical(options);
91 | ```
92 |
93 | Coming Soon ...
94 |
95 | ## 📑 Properties
96 |
97 | | Prop | Required | Type | Eg. |
98 | | --------- | -------- | ----------------------------------- | -------------------------------------- |
99 | | `steps` | True | Array | `['Hello', 1000, () => alert('Word')]` |
100 | | `loop` | False | Number | `1` or `'Infinity'` |
101 | | `wrapper` | False | String | `'div'` |
102 |
103 | ## ✨ Style
104 |
105 | Add the blink cursor effect with `blink` classname.
106 |
107 | ```css
108 | .blink::after {
109 | content: '|';
110 | animation: blink 1s infinite step-start;
111 | }
112 |
113 | @keyframes blink {
114 | 50% {
115 | opacity: 0;
116 | }
117 | }
118 | ```
119 |
120 | ## 🔢 Coming Soon
121 |
122 | - [ ] The demo of composition API in Vue 3.0
123 | - [ ] Supported Vue 2 & 3
124 |
125 | ## 🔨 Contribute
126 |
127 | Install dependencies,
128 |
129 | ```bash
130 | $ npm i
131 | ```
132 |
133 | Start the dev server,
134 |
135 | ```bash
136 | $ npm start
137 | ```
138 |
139 | Build documentation,
140 |
141 | ```bash
142 | $ npm run docs:build
143 | ```
144 |
145 | Build library via `father-build`,
146 |
147 | ```bash
148 | $ npm run build
149 | ```
150 |
151 | ## ❤️ Contributors
152 |
153 | Thanks goes to these people:
154 |
155 | 
156 |
157 | Please Feel free to enjoy and participate in open source!
158 |
159 | ## ⭐ Stargazers
160 |
161 | Thanks for your star!
162 |
163 | [](https://github.com/Turkyden/vue-typical/stargazers)
164 |
165 | ## 🔖 License
166 |
167 | This library is based on [@camwiegert/typical](https://github.com/camwiegert/typical) work and it currently is just a wrapper for vue.
168 |
169 | Inspired by [@catalinmiron/react-typical](https://github.com/catalinmiron/react-typical).
170 |
171 | [MIT](https://github.com/Turkyden/vue-typical/blob/main/LICENSE) © [Turkyden](https://github.com/Turkyden)
--------------------------------------------------------------------------------