├── .github
└── workflows
│ └── deploy.yml
├── .gitignore
├── .husky
├── commit-msg
└── pre-commit
├── LICENSE
├── README.md
├── commitlint.config.js
├── package.json
├── packages
├── vue3-scroll-seamless-docs
│ ├── docs
│ │ ├── .vitepress
│ │ │ ├── components
│ │ │ │ ├── echarts.min.js
│ │ │ │ ├── example01.vue
│ │ │ │ ├── example02.vue
│ │ │ │ ├── example03.vue
│ │ │ │ └── example04.vue
│ │ │ ├── config.ts
│ │ │ └── theme
│ │ │ │ └── index.ts
│ │ ├── guide
│ │ │ ├── 01-basic.md
│ │ │ ├── 02-direction-bottom.md
│ │ │ ├── 03-direction-right.md
│ │ │ ├── 04-step.md
│ │ │ ├── 05-hoverStop.md
│ │ │ ├── 06-singleStop.md
│ │ │ ├── 07-singleStopTime.md
│ │ │ ├── 08-echart.md
│ │ │ ├── 09-array-property-update.md
│ │ │ ├── 10-array-length-update.md
│ │ │ ├── 11-daynamicData.md
│ │ │ ├── a-switch.md
│ │ │ ├── events.md
│ │ │ ├── index.md
│ │ │ ├── issuesSolution.md
│ │ │ ├── notice.md
│ │ │ ├── properties.md
│ │ │ └── usage.md
│ │ └── index.md
│ ├── package.json
│ └── shims.vue.d.ts
└── vue3-scroll-seamless
│ ├── .eslintignore
│ ├── .eslintrc.cjs
│ ├── dist
│ ├── vue3-scroll-seamless.iife.js
│ ├── vue3-scroll-seamless.mjs
│ └── vue3-scroll-seamless.umd.js
│ ├── index.html
│ ├── package.json
│ ├── src
│ ├── _test_
│ │ └── seamlessScroll.spec.ts
│ ├── components
│ │ └── seamlessScroll.vue
│ ├── demo.vue
│ ├── entry.ts
│ ├── index.ts
│ ├── shims.vue.d.ts
│ └── utils
│ │ └── index.ts
│ ├── tsconfig.json
│ └── vite.config.ts
├── pnpm-lock.yaml
├── pnpm-workspace.yaml
└── scripts
└── preinstall.js
/.github/workflows/deploy.yml:
--------------------------------------------------------------------------------
1 | name: Deploy
2 |
3 | on:
4 | push:
5 | branches:
6 | - master
7 |
8 | jobs:
9 | deploy:
10 | strategy:
11 | matrix:
12 | node-version: [16]
13 | runs-on: ubuntu-latest
14 | steps:
15 | - uses: actions/checkout@v3
16 | - uses: pnpm/action-setup@v2.2.2
17 | with:
18 | version: 7
19 | - name: Use Node.js ${{ matrix.node-version }}
20 | uses: actions/setup-node@v3
21 | with:
22 | node-version: ${{ matrix.node-version }}
23 | cache: 'pnpm'
24 | - name: Install dependencies
25 | run: pnpm install --no-frozen-lockfile
26 | - name: Run Build
27 | run: cd packages/vue3-scroll-seamless-docs&&pnpm docs:build
28 | - name: Deploy
29 | uses: peaceiris/actions-gh-pages@v3
30 | with:
31 | github_token: ${{ secrets.GITHUB_TOKEN }}
32 | publish_dir: packages/vue3-scroll-seamless-docs/docs/.vitepress/dist
33 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | **/node_modules/
2 | **/coverage/
3 | **/.pnpm-debug
4 | package-lock.json
5 | .DS_Store
6 | .spress
7 | .vercel
8 | .idea
9 | /test
10 | *.zip
--------------------------------------------------------------------------------
/.husky/commit-msg:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | . "$(dirname "$0")/_/husky.sh"
3 |
4 | echo ====代码提交验证====
5 | npx --no -- commitlint --edit "$1"
6 |
7 |
--------------------------------------------------------------------------------
/.husky/pre-commit:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | . "$(dirname "$0")/_/husky.sh"
3 |
4 | echo ====代码静态检查====
5 | cd packages/vue3-scroll-seamless
6 | pnpm run lint
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2022-present xiaofulzm
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 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
vue3-scroll-seamless
2 |
3 |
4 | 基于vue3简单的无缝滚动
5 |
6 |
7 |
8 |
9 |
10 | ## Features
11 |
12 | * 初始配置支持
13 | * 兼容多种平台
14 | * 多技术栈版本支持
15 |
16 | ## Documentation
17 | 要查看 [实时示例](https://xiaofulzm.github.io/vue3-scroll-seamless/guide/01-basic.html) 和文档,请访问 [vue3-scroll-seamless-docs](https://xiaofulzm.github.io/vue3-scroll-seamless/)。
18 |
19 | ## Cares
20 | 原作者库以及js版本,可以切换到这里[chenxuan0000](https://github.com/chenxuan0000/vue-seamless-scroll), 本人只是重构了vue3版本
21 |
22 | ## Contribution
23 | 欢迎大家提出建议和优化,期待你的`Pull Request`。
24 |
25 | ## License
26 | [ MIT License ](LICENSE)
27 |
--------------------------------------------------------------------------------
/commitlint.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {extends: ['@commitlint/config-conventional']}
2 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "vue3-scroll-seamless",
3 | "version": "1.0.7",
4 | "description": "A simple, Seamless scrolling for Vue3.js",
5 | "homepage": "https://xiaofulzm.github.io/vue3-scroll-seamless/",
6 | "repository": {
7 | "type": "git",
8 | "url": "https://xiaofulzm.github.io/vue3-scroll-seamless/"
9 | },
10 | "main": "index.js",
11 | "scripts": {
12 | "preinstall": "node ./scripts/preinstall.js",
13 | "prepare": "husky install"
14 | },
15 | "keywords": [
16 | "vue",
17 | "vuejs",
18 | "ui",
19 | "components",
20 | "Seamless",
21 | "scroll"
22 | ],
23 | "author": "luxiaofu",
24 | "license": "ISC",
25 | "dependencies": {
26 | "@commitlint/cli": "17.0.2",
27 | "@commitlint/config-conventional": "17.0.2",
28 | "husky": "^8.0.1",
29 | "vite": "^3.1.4",
30 | "vitepress": "1.0.0-alpha.21",
31 | "vue": "^3.2.40"
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/packages/vue3-scroll-seamless-docs/docs/.vitepress/components/example01.vue:
--------------------------------------------------------------------------------
1 |
35 |
36 |
37 |
38 |
39 |
44 |
45 | -
46 |
{{item.title}}
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
87 |
--------------------------------------------------------------------------------
/packages/vue3-scroll-seamless-docs/docs/.vitepress/components/example02.vue:
--------------------------------------------------------------------------------
1 |
65 |
66 |
67 |
68 |
69 |
74 |
75 | -
76 | {{i}}
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
113 |
--------------------------------------------------------------------------------
/packages/vue3-scroll-seamless-docs/docs/.vitepress/components/example03.vue:
--------------------------------------------------------------------------------
1 |
54 |
55 |
56 |
57 |
58 |
64 |
65 | -
66 |
{{item.title}}
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
116 |
--------------------------------------------------------------------------------
/packages/vue3-scroll-seamless-docs/docs/.vitepress/components/example04.vue:
--------------------------------------------------------------------------------
1 |
29 |
30 |
31 |
32 |
33 |
34 |
35 | -
36 |
{{ item.title }}
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
94 |
--------------------------------------------------------------------------------
/packages/vue3-scroll-seamless-docs/docs/.vitepress/config.ts:
--------------------------------------------------------------------------------
1 |
2 |
3 | const config = {
4 | base:'/vue3-scroll-seamless/',
5 | title:'vue3-scroll-seamless',
6 | themeConfig:{
7 | siteTitle: 'vue3-scroll-seamless',
8 | nav: [
9 | { text: '指南', link: '/guide/' },
10 | { text: '更新日志', link: '/configs' },
11 | {
12 | text: '其他版本',
13 | items: [
14 | { text: 'Vue2版本', link: 'https://github.com/chenxuan0000/vue-seamless-scroll' },
15 | { text: 'JavaScript版本', link: 'https://github.com/chenxuan0000/seamless-scroll' },
16 | ]
17 | },
18 | ],
19 | socialLinks: [
20 | { icon: 'github', link: 'https://github.com/xiaofulzm/vue3-scroll-seamless' }
21 | ],
22 | sidebar: {// 侧边栏
23 | '/guide/': [
24 | {
25 | text: '指南',
26 | items: [
27 | { text: '安装', link: '/guide/' },
28 | { text: '使用', link: '/guide/usage' },
29 | { text: '组件配置', link: '/guide/properties' },
30 | { text: '回调事件', link: '/guide/events' },
31 | { text: '注意项', link: '/guide/notice' },
32 | { text: '常见Issues', link: '/guide/issuesSolution' }
33 | ]
34 | },
35 | {
36 | text: '示例',
37 | items: [
38 | { text: '01 - 默认配置', link: '/guide/01-basic' },
39 | { text: '02 - 向下滚动', link: '/guide/02-direction-bottom' },
40 | { text: '03 - 向右滚动', link: '/guide/03-direction-right' },
41 | { text: '04 - 滚动速度', link: '/guide/04-step' },
42 | { text: '05 - 静止鼠标悬停停止', link: '/guide/05-hoverStop' },
43 | { text: '06 - 单步停顿', link: '/guide/06-singleStop' },
44 | { text: '07 - 单行停顿时间', link: '/guide/07-singleStopTime' },
45 | // { text: '08 - switch控制切换', link: '/guide/08-switch' },
46 | { text: '08 - echart图表无缝滚动', link: '/guide/08-echart' },
47 | { text: '09 - 复杂结构数组属性更新问题', link: '/guide/09-array-property-update' },
48 | { text: '10 - 滚动列表动态追加数据', link: '/guide/10-array-length-update' },
49 | { text: '11 - 固定高度动态数据', link: '/guide/11-daynamicData' }
50 |
51 | ]
52 | }
53 | ],
54 |
55 | },
56 | footer: {
57 | message: '欢迎给出一些意见和优化,期待你的 Pull Request.',
58 | copyright: '如有建议和疑问请前往issues.'
59 | }
60 | }
61 | }
62 |
63 | export default config;
--------------------------------------------------------------------------------
/packages/vue3-scroll-seamless-docs/docs/.vitepress/theme/index.ts:
--------------------------------------------------------------------------------
1 |
2 | // 导入scroll 组件
3 | import Example01from from "../components/example01.vue";
4 | import Example02 from "../components/example02.vue";
5 | import Example03 from "../components/example03.vue";
6 | import Example04 from "../components/example04.vue";
7 |
8 |
9 | import {vue3ScrollSeamless} from "vue3-scroll-seamless";
10 |
11 |
12 | import DefaultTheme from 'vitepress/theme'
13 |
14 | export default {
15 | ...DefaultTheme,
16 | enhanceApp({ app }) {
17 | // 注册一个全局组件
18 | app.component('Example01from',Example01from)
19 | app.component('Example02',Example02)
20 | app.component('Example04',Example04)
21 | app.component('vue3ScrollSeamless',vue3ScrollSeamless)
22 | }
23 | }
--------------------------------------------------------------------------------
/packages/vue3-scroll-seamless-docs/docs/guide/01-basic.md:
--------------------------------------------------------------------------------
1 | # 01-默认配置
2 |
3 |
4 |
5 | ```vue
6 |
37 |
38 |
39 |
40 |
45 |
46 | -
47 |
{{item.title}}
48 |
49 |
50 |
51 |
52 |
53 |
77 |
78 | ```
79 |
--------------------------------------------------------------------------------
/packages/vue3-scroll-seamless-docs/docs/guide/02-direction-bottom.md:
--------------------------------------------------------------------------------
1 | # 02 - 向下滚动
2 |
3 |
4 |
5 | ```vue
6 |
37 |
38 |
39 |
40 |
45 |
46 | -
47 |
{{item.title}}
48 |
49 |
50 |
51 |
52 |
53 |
77 |
78 | ```
79 |
--------------------------------------------------------------------------------
/packages/vue3-scroll-seamless-docs/docs/guide/03-direction-right.md:
--------------------------------------------------------------------------------
1 | # 03 - 向右滚动
2 |
3 |
4 | ```vue
5 |
15 |
16 |
17 |
18 |
23 |
24 | -
25 |
{{item.title}}
26 |
27 |
28 |
29 |
30 |
31 |
61 |
62 | ```
63 |
--------------------------------------------------------------------------------
/packages/vue3-scroll-seamless-docs/docs/guide/04-step.md:
--------------------------------------------------------------------------------
1 | # 04 - 滚动速度
2 |
3 |
4 |
5 | ```vue
6 |
37 |
38 |
39 |
40 |
45 |
46 | -
47 |
{{item.title}}
48 |
49 |
50 |
51 |
52 |
53 |
77 |
78 | ```
79 |
--------------------------------------------------------------------------------
/packages/vue3-scroll-seamless-docs/docs/guide/05-hoverStop.md:
--------------------------------------------------------------------------------
1 | # 05 - 静止鼠标悬停停止
2 |
3 |
4 |
5 | ```vue
6 |
37 |
38 |
39 |
40 |
45 |
46 | -
47 |
{{item.title}}
48 |
49 |
50 |
51 |
52 |
53 |
77 |
78 | ```
79 |
--------------------------------------------------------------------------------
/packages/vue3-scroll-seamless-docs/docs/guide/06-singleStop.md:
--------------------------------------------------------------------------------
1 | # 06 - 单步停顿
2 |
3 |
4 |
5 | ```vue
6 |
37 |
38 |
39 |
40 |
45 |
46 | -
47 |
{{item.title}}
48 |
49 |
50 |
51 |
52 |
53 |
77 |
78 | ```
79 |
--------------------------------------------------------------------------------
/packages/vue3-scroll-seamless-docs/docs/guide/07-singleStopTime.md:
--------------------------------------------------------------------------------
1 | # 07 - 单行停顿时间
2 |
3 |
4 |
5 |
6 | ```vue
7 |
39 |
40 |
41 |
42 |
47 |
48 | -
49 |
{{item.title}}
50 |
51 |
52 |
53 |
54 |
55 |
79 |
80 | ```
81 |
--------------------------------------------------------------------------------
/packages/vue3-scroll-seamless-docs/docs/guide/08-echart.md:
--------------------------------------------------------------------------------
1 | # 08 - echart图表无缝滚动
2 |
3 |
4 | ```vue
5 |
62 |
63 |
64 |
65 |
70 |
71 | -
72 |
{{item.title}}
73 |
74 |
75 |
76 |
77 |
78 |
107 |
108 | ```
109 |
--------------------------------------------------------------------------------
/packages/vue3-scroll-seamless-docs/docs/guide/09-array-property-update.md:
--------------------------------------------------------------------------------
1 | # 09 - 复杂结构数组属性更新问题
2 |
3 |
4 |
5 | ```vue
6 |
52 |
53 |
54 |
55 |
56 |
62 |
63 | -
64 |
{{item.title}}
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
111 |
112 | ```
113 |
--------------------------------------------------------------------------------
/packages/vue3-scroll-seamless-docs/docs/guide/10-array-length-update.md:
--------------------------------------------------------------------------------
1 | 10 - 滚动列表动态追加数据
2 |
3 | # 09 - 复杂结构数组属性更新问题
4 |
5 |
6 |
7 | ```vue
8 |
55 |
56 |
57 |
58 |
59 |
65 |
66 | -
67 |
{{item.title}}
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
114 |
115 | ```
116 |
--------------------------------------------------------------------------------
/packages/vue3-scroll-seamless-docs/docs/guide/11-daynamicData.md:
--------------------------------------------------------------------------------
1 | 11 - 动态数据
2 |
3 | # 01 - 动态数据
4 |
5 |
6 |
7 | ```vue
8 |
36 |
37 |
38 |
39 |
40 |
41 |
42 | -
43 |
{{ item.title }}
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
101 |
102 |
103 | ```
104 |
--------------------------------------------------------------------------------
/packages/vue3-scroll-seamless-docs/docs/guide/a-switch.md:
--------------------------------------------------------------------------------
1 | # 08 - switch控制切换
2 |
3 |
4 |
5 | ```vue
6 |
38 |
39 |
40 |
41 |
46 |
47 | -
48 |
{{item.title}}
49 |
50 |
51 |
52 |
53 |
54 |
78 |
79 | ```
80 |
--------------------------------------------------------------------------------
/packages/vue3-scroll-seamless-docs/docs/guide/events.md:
--------------------------------------------------------------------------------
1 | # 回调事件
2 |
3 | ```vue
4 |
9 |
10 |
11 |
12 |
18 |
19 | -
20 |
{{item.title}}
21 |
22 |
23 |
24 |
25 |
26 | ```
--------------------------------------------------------------------------------
/packages/vue3-scroll-seamless-docs/docs/guide/index.md:
--------------------------------------------------------------------------------
1 | # 安装
2 | ## NPM
3 | ```
4 | npm install vue3-scroll-seamless --save
5 | ```
6 | ## PNPM
7 | ```
8 | pnpm install vue3-scroll-seamless --save
9 | ```
10 | ## Yarn
11 | ```
12 | yarn add vue3-seamless-scroll
13 | ```
14 |
--------------------------------------------------------------------------------
/packages/vue3-scroll-seamless-docs/docs/guide/issuesSolution.md:
--------------------------------------------------------------------------------
1 | # 常见Issues
2 |
3 | 1、**`事件无法被复制的问题`**
4 |
5 | 组件本身没有对copy的html做一个节点的深度事件复制(类似jq的clone(true))
6 |
7 | 解决方法
8 |
9 | ::: tip
10 | 事件代理,给父元素绑定对应事件,在需要的子元素上进行事件补获。(推荐)
11 | :::
12 | ::: danger
13 | 简单的直接原生js进行`addEventListener`,存在异步数据无法绑定上问题。(不推荐)
14 | :::
--------------------------------------------------------------------------------
/packages/vue3-scroll-seamless-docs/docs/guide/notice.md:
--------------------------------------------------------------------------------
1 | # 注意项
2 | ::: tip
3 | - 1.最外层容器需要手动设置`width、height、overflow:hidden`
4 |
5 | - 2.左右的无缝滚动需要给主内容区域(即默认slot插槽提供)设定合适的`css width`属性(否则无法正确计算实际宽度)。
6 | 也可以通过给他设置为`display:flex;`无需设置`css width`属性
7 |
8 | - 3.step值不建议太小,不然会有卡顿效果(如果设置了单步滚动,step需是单步大小的约数,否则无法保证单步滚动结束的位置是否准确。~~~~~,比如单步设置的30,step不能为4)
9 | :::
10 | ::: danger
11 | 目前涉及到switch控制切换的功能暂未实现
12 | :::
13 |
--------------------------------------------------------------------------------
/packages/vue3-scroll-seamless-docs/docs/guide/properties.md:
--------------------------------------------------------------------------------
1 | # 组件配置
2 | ## dataList
3 | - type: `Array`
4 | - required: `true`
5 | ::: tip
6 | 无缝滚动list数据 , 组件内部只关注data数组的length。
7 | :::
8 | ## classOption
9 | ### step
10 | - type: `Number`
11 | - required: `false`
12 | - default:`1`
13 | ::: tip
14 | 数值越大速度滚动越快。
15 |
16 | step 值不建议太小,不然会有卡顿效果(如果设置了单步滚动,step 需是单步大小的约数,否则无法保证单步滚动结束的位置是否准确。~~~~~,比如单步设置的 30,step 不能为 4)
17 | :::
18 |
19 | ### limitMoveNum
20 |
21 | - type: `Number`
22 | - required: `false`
23 | - default:`5`
24 | ::: tip
25 | 开启无缝滚动的数据量。
26 | :::
27 |
28 | ### hoverStop
29 |
30 | - type: `Boolean`
31 | - required: `false`
32 | - default:`false`
33 | ::: tip
34 | 是否启用鼠标 hover 控制。
35 | :::
36 |
37 | ### direction
38 |
39 | - type: `Number`
40 | - required: `false`
41 | - default:`1`
42 | ::: tip
43 | 方向: 0 往下 1 往上 2 向左 3 向右。
44 | :::
45 |
46 | ### openTouch
47 |
48 |
53 | ::: danger
54 | 暂不支持
55 | :::
56 |
57 | ### singleHeight
58 |
59 | - type: `Number`
60 | - required: `false`
61 | - default:`0`
62 | ::: tip
63 | 单步运动停止的高度(默认值 0 是无缝不停止的滚动),direction 为 0|1 时生效。
64 | :::
65 |
66 | ### singleWidth
67 |
68 | - type: `Number`
69 | - required: `false`
70 | - default:`0`
71 | ::: tip
72 | 单步运动停止的宽度(默认值 0 是无缝不停止的滚动),direction 为 2|3 时生效
73 | :::
74 |
75 | ### waitTime
76 |
77 | - type: `Number`
78 | - required: `false`
79 | - default:`1000`
80 | ::: tip
81 | 单步停止等待时间(默认值 1000ms)。
82 | :::
83 |
84 | ### switchOffset
85 | ::: danger
86 | 暂不支持
87 | :::
88 |
93 |
94 | ### autoPlay
95 | ::: danger
96 | 暂未支持
97 | :::
98 |
99 |
104 |
105 | ### switchSingleStep
106 | ::: danger
107 | 暂未支持
108 | :::
109 |
114 |
115 | ### switchDelay
116 |
117 | - type: `Number`
118 | - required: `false`
119 | - default:`400`
120 | ::: tip
121 | 单步切换的动画时间(ms)。
122 | :::
123 |
124 | ### switchDisabledClass
125 | ::: danger
126 | 暂未支持
127 | :::
128 |
133 |
134 | ### isSingleRemUnit
135 |
136 | - type: `Boolean`
137 | - required: `false`
138 | - default:`false`
139 | ::: tip
140 | singleHeight and singleWidth 是否开启 rem 度量。
141 | :::
142 |
143 | ### navigation
144 | ::: danger
145 | 暂未支持
146 | :::
147 |
152 |
--------------------------------------------------------------------------------
/packages/vue3-scroll-seamless-docs/docs/guide/usage.md:
--------------------------------------------------------------------------------
1 | # 使用
2 | ## 组件注册与使用
3 |
4 | ```vue
5 |
6 |
7 |
35 |
36 |
37 |
38 |
43 |
44 | -
45 |
{{item.title}}
46 |
47 |
48 |
49 |
50 |
51 |
75 | ```
76 |
77 | ## 全局注册
78 | ```js
79 | // **main.js**
80 | import {vue3ScrollSeamless} from "vue3-scroll-seamless";
81 | createApp(App).component('vue3ScrollSeamless',vue3ScrollSeamless).mount('#app')
82 | ```
--------------------------------------------------------------------------------
/packages/vue3-scroll-seamless-docs/docs/index.md:
--------------------------------------------------------------------------------
1 | ---
2 | layout: home
3 |
4 | hero:
5 | name: vue3-scroll-seamless
6 | tagline: 一个简单的基于vue3.js的无缝滚动
7 | actions:
8 | - theme: brand
9 | text: 开始
10 | link: /guide/
11 | - theme: alt
12 | text: 在 GitHub 上查看
13 | link: https://github.com/xiaofulzm/vue3-scroll-seamless
14 |
15 | features:
16 | - title: "多样化配置支持"
17 | details: 目前支持上下左右无缝滚动,单步滚动。
18 | - title: 兼容多平台
19 | details: IE12+、Firefox、Chrome、Safari、iOS、Android。
20 | - title: 多技术栈版本支持
21 | details: 目前有Vue3、Vue2、JavaScript版本。
22 | ---
23 |
24 |
--------------------------------------------------------------------------------
/packages/vue3-scroll-seamless-docs/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "vue3-scroll-seamless-docs",
3 | "version": "1.0.0",
4 | "description": "",
5 | "main": "index.js",
6 | "scripts": {
7 | "docs:dev": "vitepress dev docs ",
8 | "docs:build": "vitepress build docs",
9 | "docs:serve": "vitepress serve docs"
10 | },
11 | "keywords": [],
12 | "author": "",
13 | "license": "ISC",
14 | "dependencies": {
15 | "vitepress": "1.0.0-alpha.21",
16 | "vue3-scroll-seamless":"../vue3-scroll-seamless"
17 | },
18 | "devDependencies": {
19 | "@types/node": "^18.11.9"
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/packages/vue3-scroll-seamless-docs/shims.vue.d.ts:
--------------------------------------------------------------------------------
1 | declare module "*.vue" {
2 | import { DefineComponent } from "vue";
3 | const component: DefineComponent<{}, {}, any>;
4 | export default component;
5 | }
6 |
--------------------------------------------------------------------------------
/packages/vue3-scroll-seamless/.eslintignore:
--------------------------------------------------------------------------------
1 | *.sh
2 | node_modules
3 | lib
4 | coverage
5 | *.md
6 | *.scss
7 | *.woff
8 | *.ttf
9 | src/index.ts
10 | dist
--------------------------------------------------------------------------------
/packages/vue3-scroll-seamless/.eslintrc.cjs:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | root: true,
3 | env: {
4 | browser: true,
5 | es2020: true,
6 | node: true,
7 | jest: true
8 | },
9 | globals: {
10 | ga: true,
11 | chrome: true,
12 | __DEV__: true
13 | },
14 | // 解析 .vue 文件
15 | parser: 'vue-eslint-parser',
16 | extends: [
17 | 'plugin:json/recommended',
18 | 'plugin:vue/vue3-essential',
19 | 'eslint:recommended',
20 | '@vue/prettier'
21 | ],
22 | plugins: ['@typescript-eslint'],
23 | parserOptions: {
24 | parser: '@typescript-eslint/parser' // 解析 .ts 文件
25 | },
26 | rules: {
27 | 'no-console': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
28 | 'no-debugger': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
29 | 'prettier/prettier': 'error',
30 | 'vue/multi-word-component-names': 'off',
31 | }
32 | }
--------------------------------------------------------------------------------
/packages/vue3-scroll-seamless/dist/vue3-scroll-seamless.iife.js:
--------------------------------------------------------------------------------
1 | var vue3ScrollSeamless = function(exports, vue) {
2 | "use strict";
3 | function animationFrame() {
4 | window.cancelAnimationFrame = function() {
5 | return window.cancelAnimationFrame || window.webkitCancelAnimationFrame || window.mozCancelAnimationFrame || window.oCancelAnimationFrame || window.msCancelAnimationFrame || function(id) {
6 | return window.clearTimeout(id);
7 | };
8 | }();
9 | window.requestAnimationFrame = function() {
10 | return window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || window.oRequestAnimationFrame || window.msRequestAnimationFrame || function(callback) {
11 | return window.setTimeout(callback, 1e3 / 60);
12 | };
13 | }();
14 | }
15 | function arrayEqual(arr1, arr2) {
16 | if (arr1 === arr2)
17 | return true;
18 | if (arr1.length !== arr2.length)
19 | return false;
20 | for (var i = 0; i < arr1.length; ++i) {
21 | if (arr1[i] !== arr2[i])
22 | return false;
23 | }
24 | return true;
25 | }
26 | const _hoisted_1 = ["innerHTML"];
27 | const _sfc_main = /* @__PURE__ */ vue.defineComponent({
28 | __name: "seamlessScroll",
29 | props: {
30 | dataList: {
31 | type: Array,
32 | default: []
33 | },
34 | classOptions: {
35 | type: Object,
36 | default: {}
37 | }
38 | },
39 | emits: ["ScrollEnd"],
40 | setup(__props, { emit: __emit }) {
41 | animationFrame();
42 | const slotList = vue.ref(null);
43 | const wrap = vue.ref(null);
44 | const realBox = vue.ref(null);
45 | let copyHtml = vue.ref(), initData = vue.reactive({
46 | xPos: 0,
47 | yPos: 0,
48 | delay: 0,
49 | ease: "ease-in",
50 | height: 0,
51 | width: 0,
52 | realBoxWidth: 0,
53 | realBoxHeight: 0,
54 | isHover: false,
55 | reqFrame: null,
56 | singleWaitTime: null
57 | });
58 | const defaultOption = {
59 | step: 1,
60 | limitMoveNum: 5,
61 | hoverStop: true,
62 | direction: 1,
63 | openTouch: true,
64 | singleHeight: 0,
65 | singleWidth: 0,
66 | waitTime: 1e3,
67 | switchOffset: 30,
68 | autoPlay: true,
69 | navigation: false,
70 | switchSingleStep: 134,
71 | switchDelay: 400,
72 | switchDisabledClass: "disabled",
73 | isSingleRemUnit: false
74 | };
75 | const emit = __emit;
76 | const Props = __props;
77 | vue.onBeforeMount(() => {
78 | initData.ease = "ease-in";
79 | initData.isHover = false;
80 | initData.reqFrame = null;
81 | initData.singleWaitTime = null;
82 | });
83 | vue.onMounted(() => {
84 | _initMove();
85 | });
86 | vue.onBeforeUnmount(() => {
87 | _cancle();
88 | clearTimeout(initData.singleWaitTime);
89 | });
90 | const options = vue.computed(() => {
91 | return { ...defaultOption, ...Props.classOptions };
92 | });
93 | const isHorizontal = vue.computed(() => options.value.direction > 1).value;
94 | const float = vue.computed(() => {
95 | let isFloat;
96 | if (isHorizontal) {
97 | isFloat = { float: "left", overflow: "hidden" };
98 | } else {
99 | isFloat = { overflow: "hidden" };
100 | }
101 | return isFloat;
102 | });
103 | const pos = vue.computed(() => {
104 | return {
105 | transform: `translate(${initData.xPos}px,${initData.yPos}px)`,
106 | transition: `all ${initData.ease} ${initData.delay}ms`,
107 | overflow: "hidden"
108 | };
109 | });
110 | const navigation = vue.computed(() => options.value.navigation).value;
111 | const autoPlay = vue.computed(() => {
112 | if (navigation)
113 | return false;
114 | return options.value.autoPlay;
115 | });
116 | const scrollSwitch = vue.computed(
117 | () => Props.dataList.length >= options.value.limitMoveNum
118 | );
119 | const hoverStopSwitch = vue.computed(
120 | () => options.value.hoverStop && autoPlay.value && scrollSwitch.value
121 | );
122 | const baseFontSize = vue.computed(
123 | () => options.value.isSingleRemUnit ? parseInt(window.getComputedStyle(document.documentElement, null).fontSize) : 1
124 | ).value;
125 | const realSingleStopWidth = vue.computed(
126 | () => options.value.singleWidth * baseFontSize
127 | ).value;
128 | const realSingleStopHeight = vue.computed(
129 | () => options.value.singleHeight * baseFontSize
130 | ).value;
131 | const gap = vue.ref(1);
132 | const step = vue.computed(() => {
133 | let singleStep;
134 | let step2 = options.value.step;
135 | if (isHorizontal) {
136 | singleStep = realSingleStopWidth;
137 | } else {
138 | singleStep = realSingleStopHeight;
139 | }
140 | if (singleStep > 0 && singleStep % step2 > 0) {
141 | console.error(
142 | "\u5982\u679C\u8BBE\u7F6E\u4E86\u5355\u6B65\u6EDA\u52A8,step\u9700\u662F\u5355\u6B65\u5927\u5C0F\u7684\u7EA6\u6570,\u5426\u5219\u65E0\u6CD5\u4FDD\u8BC1\u5355\u6B65\u6EDA\u52A8\u7ED3\u675F\u7684\u4F4D\u7F6E\u662F\u5426\u51C6\u786E~~~~~"
143 | );
144 | }
145 | return step2;
146 | }).value;
147 | vue.watch(
148 | () => Props.dataList,
149 | (newValue, oldValue) => {
150 | _dataWarm(newValue);
151 | if (!arrayEqual(newValue, oldValue)) {
152 | reset();
153 | }
154 | }
155 | );
156 | vue.watch(autoPlay, (newBol) => {
157 | if (newBol) {
158 | reset();
159 | } else {
160 | _stopMove();
161 | }
162 | });
163 | function reset() {
164 | _cancle();
165 | _initMove();
166 | }
167 | function changeEnter() {
168 | if (hoverStopSwitch.value)
169 | _stopMove();
170 | }
171 | function changeLeave() {
172 | if (hoverStopSwitch.value)
173 | _startMove();
174 | }
175 | function _dataWarm(data) {
176 | if (data.length > 100) {
177 | console.warn(
178 | `\u6570\u636E\u8FBE\u5230\u4E86${data.length}\u6761\u6709\u70B9\u591A\u54E6~,\u53EF\u80FD\u4F1A\u9020\u6210\u90E8\u5206\u8001\u65E7\u6D4F\u89C8\u5668\u5361\u987F\u3002`
179 | );
180 | }
181 | }
182 | async function _initMove() {
183 | await vue.nextTick();
184 | const { switchDelay } = options.value;
185 | _dataWarm(Props.dataList);
186 | copyHtml.value = "";
187 | if (isHorizontal) {
188 | initData.height = wrap.value.offsetHeight;
189 | initData.width = wrap.value.offsetWidth;
190 | let slotListWidth = slotList.value.offsetWidth;
191 | if (autoPlay.value) {
192 | slotListWidth = slotListWidth * 2 + 1;
193 | }
194 | realBox.value.style.width = slotListWidth + "px";
195 | initData.realBoxWidth = slotListWidth;
196 | }
197 | if (autoPlay.value) {
198 | initData.ease = "ease-in";
199 | initData.delay = 0;
200 | } else {
201 | initData.ease = "linear";
202 | initData.delay = switchDelay;
203 | return;
204 | }
205 | if (scrollSwitch.value) {
206 | let initTimer = null;
207 | copyHtml.value = slotList.value.innerHTML;
208 | gap.value = Math.ceil(realBox.value.offsetHeight / copyHtml.value.clientHeight);
209 | if (initTimer)
210 | clearTimeout(initTimer);
211 | initTimer = setTimeout(() => {
212 | initData.realBoxHeight = realBox.value.offsetHeight;
213 | _move();
214 | }, 0);
215 | } else {
216 | _cancle();
217 | initData.xPos = 0;
218 | initData.yPos = 0;
219 | }
220 | }
221 | function _move() {
222 | if (initData.isHover)
223 | return;
224 | _cancle();
225 | initData.reqFrame = requestAnimationFrame(function() {
226 | const h = initData.realBoxHeight / 2;
227 | const w = initData.realBoxWidth / 2;
228 | let { direction, waitTime } = options.value;
229 | if (direction === 1) {
230 | if (Math.abs(initData.yPos) >= h) {
231 | emit("ScrollEnd");
232 | initData.yPos = 0;
233 | }
234 | initData.yPos -= step;
235 | } else if (direction === 0) {
236 | if (initData.yPos >= 0) {
237 | emit("ScrollEnd");
238 | initData.yPos = h * -1;
239 | }
240 | initData.yPos += step;
241 | } else if (direction === 2) {
242 | if (Math.abs(initData.xPos) >= w) {
243 | emit("ScrollEnd");
244 | initData.xPos = 0;
245 | }
246 | initData.xPos -= step;
247 | } else if (direction === 3) {
248 | if (initData.xPos >= 0) {
249 | emit("ScrollEnd");
250 | initData.xPos = w * -1;
251 | }
252 | initData.xPos += step;
253 | }
254 | if (initData.singleWaitTime)
255 | clearTimeout(initData.singleWaitTime);
256 | if (realSingleStopHeight) {
257 | if (Math.abs(initData.yPos) % realSingleStopHeight < step) {
258 | initData.singleWaitTime = setTimeout(() => {
259 | _move();
260 | }, waitTime);
261 | } else {
262 | _move();
263 | }
264 | } else if (realSingleStopWidth) {
265 | if (Math.abs(initData.xPos) % realSingleStopWidth < step) {
266 | initData.singleWaitTime = setTimeout(() => {
267 | _move();
268 | }, waitTime);
269 | } else {
270 | _move();
271 | }
272 | } else {
273 | _move();
274 | }
275 | });
276 | }
277 | function _cancle() {
278 | cancelAnimationFrame(initData.reqFrame || null);
279 | }
280 | function _stopMove() {
281 | initData.isHover = true;
282 | if (initData.singleWaitTime)
283 | clearTimeout(initData.singleWaitTime);
284 | _cancle();
285 | }
286 | function _startMove() {
287 | initData.isHover = false;
288 | _move();
289 | }
290 | return (_ctx, _cache) => {
291 | return vue.openBlock(), vue.createElementBlock("div", {
292 | ref_key: "wrap",
293 | ref: wrap
294 | }, [
295 | vue.createElementVNode("div", {
296 | onMouseenter: changeEnter,
297 | onMouseleave: changeLeave,
298 | ref_key: "realBox",
299 | ref: realBox,
300 | style: vue.normalizeStyle(pos.value)
301 | }, [
302 | vue.createElementVNode("div", {
303 | style: vue.normalizeStyle(float.value),
304 | ref_key: "slotList",
305 | ref: slotList
306 | }, [
307 | vue.renderSlot(_ctx.$slots, "default")
308 | ], 4),
309 | (vue.openBlock(true), vue.createElementBlock(vue.Fragment, null, vue.renderList(gap.value, (i) => {
310 | return vue.openBlock(), vue.createElementBlock("div", {
311 | style: vue.normalizeStyle(float.value),
312 | innerHTML: vue.unref(copyHtml),
313 | key: i
314 | }, null, 12, _hoisted_1);
315 | }), 128))
316 | ], 36)
317 | ], 512);
318 | };
319 | }
320 | });
321 | const entry = {
322 | install(app) {
323 | app.component(_sfc_main.name, _sfc_main);
324 | }
325 | };
326 | exports.default = entry;
327 | exports.vue3ScrollSeamless = _sfc_main;
328 | Object.defineProperties(exports, { __esModule: { value: true }, [Symbol.toStringTag]: { value: "Module" } });
329 | return exports;
330 | }({}, Vue);
331 |
--------------------------------------------------------------------------------
/packages/vue3-scroll-seamless/dist/vue3-scroll-seamless.mjs:
--------------------------------------------------------------------------------
1 | import { defineComponent, ref, reactive, onBeforeMount, onMounted, onBeforeUnmount, computed, watch, nextTick, openBlock, createElementBlock, createElementVNode, normalizeStyle, renderSlot, Fragment, renderList, unref } from "vue";
2 | function animationFrame() {
3 | window.cancelAnimationFrame = function() {
4 | return window.cancelAnimationFrame || window.webkitCancelAnimationFrame || window.mozCancelAnimationFrame || window.oCancelAnimationFrame || window.msCancelAnimationFrame || function(id) {
5 | return window.clearTimeout(id);
6 | };
7 | }();
8 | window.requestAnimationFrame = function() {
9 | return window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || window.oRequestAnimationFrame || window.msRequestAnimationFrame || function(callback) {
10 | return window.setTimeout(callback, 1e3 / 60);
11 | };
12 | }();
13 | }
14 | function arrayEqual(arr1, arr2) {
15 | if (arr1 === arr2)
16 | return true;
17 | if (arr1.length !== arr2.length)
18 | return false;
19 | for (var i = 0; i < arr1.length; ++i) {
20 | if (arr1[i] !== arr2[i])
21 | return false;
22 | }
23 | return true;
24 | }
25 | const _hoisted_1 = ["innerHTML"];
26 | const _sfc_main = /* @__PURE__ */ defineComponent({
27 | __name: "seamlessScroll",
28 | props: {
29 | dataList: {
30 | type: Array,
31 | default: []
32 | },
33 | classOptions: {
34 | type: Object,
35 | default: {}
36 | }
37 | },
38 | emits: ["ScrollEnd"],
39 | setup(__props, { emit: __emit }) {
40 | animationFrame();
41 | const slotList = ref(null);
42 | const wrap = ref(null);
43 | const realBox = ref(null);
44 | let copyHtml = ref(), initData = reactive({
45 | xPos: 0,
46 | yPos: 0,
47 | delay: 0,
48 | ease: "ease-in",
49 | height: 0,
50 | width: 0,
51 | realBoxWidth: 0,
52 | realBoxHeight: 0,
53 | isHover: false,
54 | reqFrame: null,
55 | singleWaitTime: null
56 | });
57 | const defaultOption = {
58 | step: 1,
59 | limitMoveNum: 5,
60 | hoverStop: true,
61 | direction: 1,
62 | openTouch: true,
63 | singleHeight: 0,
64 | singleWidth: 0,
65 | waitTime: 1e3,
66 | switchOffset: 30,
67 | autoPlay: true,
68 | navigation: false,
69 | switchSingleStep: 134,
70 | switchDelay: 400,
71 | switchDisabledClass: "disabled",
72 | isSingleRemUnit: false
73 | };
74 | const emit = __emit;
75 | const Props = __props;
76 | onBeforeMount(() => {
77 | initData.ease = "ease-in";
78 | initData.isHover = false;
79 | initData.reqFrame = null;
80 | initData.singleWaitTime = null;
81 | });
82 | onMounted(() => {
83 | _initMove();
84 | });
85 | onBeforeUnmount(() => {
86 | _cancle();
87 | clearTimeout(initData.singleWaitTime);
88 | });
89 | const options = computed(() => {
90 | return { ...defaultOption, ...Props.classOptions };
91 | });
92 | const isHorizontal = computed(() => options.value.direction > 1).value;
93 | const float = computed(() => {
94 | let isFloat;
95 | if (isHorizontal) {
96 | isFloat = { float: "left", overflow: "hidden" };
97 | } else {
98 | isFloat = { overflow: "hidden" };
99 | }
100 | return isFloat;
101 | });
102 | const pos = computed(() => {
103 | return {
104 | transform: `translate(${initData.xPos}px,${initData.yPos}px)`,
105 | transition: `all ${initData.ease} ${initData.delay}ms`,
106 | overflow: "hidden"
107 | };
108 | });
109 | const navigation = computed(() => options.value.navigation).value;
110 | const autoPlay = computed(() => {
111 | if (navigation)
112 | return false;
113 | return options.value.autoPlay;
114 | });
115 | const scrollSwitch = computed(
116 | () => Props.dataList.length >= options.value.limitMoveNum
117 | );
118 | const hoverStopSwitch = computed(
119 | () => options.value.hoverStop && autoPlay.value && scrollSwitch.value
120 | );
121 | const baseFontSize = computed(
122 | () => options.value.isSingleRemUnit ? parseInt(window.getComputedStyle(document.documentElement, null).fontSize) : 1
123 | ).value;
124 | const realSingleStopWidth = computed(
125 | () => options.value.singleWidth * baseFontSize
126 | ).value;
127 | const realSingleStopHeight = computed(
128 | () => options.value.singleHeight * baseFontSize
129 | ).value;
130 | const gap = ref(1);
131 | const step = computed(() => {
132 | let singleStep;
133 | let step2 = options.value.step;
134 | if (isHorizontal) {
135 | singleStep = realSingleStopWidth;
136 | } else {
137 | singleStep = realSingleStopHeight;
138 | }
139 | if (singleStep > 0 && singleStep % step2 > 0) {
140 | console.error(
141 | "\u5982\u679C\u8BBE\u7F6E\u4E86\u5355\u6B65\u6EDA\u52A8,step\u9700\u662F\u5355\u6B65\u5927\u5C0F\u7684\u7EA6\u6570,\u5426\u5219\u65E0\u6CD5\u4FDD\u8BC1\u5355\u6B65\u6EDA\u52A8\u7ED3\u675F\u7684\u4F4D\u7F6E\u662F\u5426\u51C6\u786E~~~~~"
142 | );
143 | }
144 | return step2;
145 | }).value;
146 | watch(
147 | () => Props.dataList,
148 | (newValue, oldValue) => {
149 | _dataWarm(newValue);
150 | if (!arrayEqual(newValue, oldValue)) {
151 | reset();
152 | }
153 | }
154 | );
155 | watch(autoPlay, (newBol) => {
156 | if (newBol) {
157 | reset();
158 | } else {
159 | _stopMove();
160 | }
161 | });
162 | function reset() {
163 | _cancle();
164 | _initMove();
165 | }
166 | function changeEnter() {
167 | if (hoverStopSwitch.value)
168 | _stopMove();
169 | }
170 | function changeLeave() {
171 | if (hoverStopSwitch.value)
172 | _startMove();
173 | }
174 | function _dataWarm(data) {
175 | if (data.length > 100) {
176 | console.warn(
177 | `\u6570\u636E\u8FBE\u5230\u4E86${data.length}\u6761\u6709\u70B9\u591A\u54E6~,\u53EF\u80FD\u4F1A\u9020\u6210\u90E8\u5206\u8001\u65E7\u6D4F\u89C8\u5668\u5361\u987F\u3002`
178 | );
179 | }
180 | }
181 | async function _initMove() {
182 | await nextTick();
183 | const { switchDelay } = options.value;
184 | _dataWarm(Props.dataList);
185 | copyHtml.value = "";
186 | if (isHorizontal) {
187 | initData.height = wrap.value.offsetHeight;
188 | initData.width = wrap.value.offsetWidth;
189 | let slotListWidth = slotList.value.offsetWidth;
190 | if (autoPlay.value) {
191 | slotListWidth = slotListWidth * 2 + 1;
192 | }
193 | realBox.value.style.width = slotListWidth + "px";
194 | initData.realBoxWidth = slotListWidth;
195 | }
196 | if (autoPlay.value) {
197 | initData.ease = "ease-in";
198 | initData.delay = 0;
199 | } else {
200 | initData.ease = "linear";
201 | initData.delay = switchDelay;
202 | return;
203 | }
204 | if (scrollSwitch.value) {
205 | let initTimer = null;
206 | copyHtml.value = slotList.value.innerHTML;
207 | gap.value = Math.ceil(wrap.value.offsetHeight / slotList.value.offsetHeight)
208 | if (initTimer)
209 | clearTimeout(initTimer);
210 | initTimer = setTimeout(() => {
211 | initData.realBoxHeight = realBox.value.offsetHeight;
212 | _move();
213 | }, 0);
214 | } else {
215 | _cancle();
216 | initData.xPos = 0;
217 | initData.yPos = 0;
218 | }
219 | }
220 | function _move() {
221 | if (initData.isHover)
222 | return;
223 | _cancle();
224 | initData.reqFrame = requestAnimationFrame(function() {
225 | const h = initData.realBoxHeight / 2;
226 | const w = initData.realBoxWidth / 2;
227 | let { direction, waitTime } = options.value;
228 | if (direction === 1) {
229 | if (Math.abs(initData.yPos) >= h) {
230 | emit("ScrollEnd");
231 | initData.yPos = 0;
232 | }
233 | initData.yPos -= step;
234 | } else if (direction === 0) {
235 | if (initData.yPos >= 0) {
236 | emit("ScrollEnd");
237 | initData.yPos = h * -1;
238 | }
239 | initData.yPos += step;
240 | } else if (direction === 2) {
241 | if (Math.abs(initData.xPos) >= w) {
242 | emit("ScrollEnd");
243 | initData.xPos = 0;
244 | }
245 | initData.xPos -= step;
246 | } else if (direction === 3) {
247 | if (initData.xPos >= 0) {
248 | emit("ScrollEnd");
249 | initData.xPos = w * -1;
250 | }
251 | initData.xPos += step;
252 | }
253 | if (initData.singleWaitTime)
254 | clearTimeout(initData.singleWaitTime);
255 | if (realSingleStopHeight) {
256 | if (Math.abs(initData.yPos) % realSingleStopHeight < step) {
257 | initData.singleWaitTime = setTimeout(() => {
258 | _move();
259 | }, waitTime);
260 | } else {
261 | _move();
262 | }
263 | } else if (realSingleStopWidth) {
264 | if (Math.abs(initData.xPos) % realSingleStopWidth < step) {
265 | initData.singleWaitTime = setTimeout(() => {
266 | _move();
267 | }, waitTime);
268 | } else {
269 | _move();
270 | }
271 | } else {
272 | _move();
273 | }
274 | });
275 | }
276 | function _cancle() {
277 | cancelAnimationFrame(initData.reqFrame || null);
278 | }
279 | function _stopMove() {
280 | initData.isHover = true;
281 | if (initData.singleWaitTime)
282 | clearTimeout(initData.singleWaitTime);
283 | _cancle();
284 | }
285 | function _startMove() {
286 | initData.isHover = false;
287 | _move();
288 | }
289 | return (_ctx, _cache) => {
290 | return openBlock(), createElementBlock("div", {
291 | ref_key: "wrap",
292 | ref: wrap
293 | }, [
294 | createElementVNode("div", {
295 | onMouseenter: changeEnter,
296 | onMouseleave: changeLeave,
297 | ref_key: "realBox",
298 | ref: realBox,
299 | style: normalizeStyle(pos.value)
300 | }, [
301 | createElementVNode("div", {
302 | style: normalizeStyle(float.value),
303 | ref_key: "slotList",
304 | ref: slotList
305 | }, [
306 | renderSlot(_ctx.$slots, "default")
307 | ], 4),
308 | (openBlock(true), createElementBlock(Fragment, null, renderList(gap.value, (i) => {
309 | return openBlock(), createElementBlock("div", {
310 | style: normalizeStyle(float.value),
311 | innerHTML: unref(copyHtml),
312 | key: i
313 | }, null, 12, _hoisted_1);
314 | }), 128))
315 | ], 36)
316 | ], 512);
317 | };
318 | }
319 | });
320 | const entry = {
321 | install(app) {
322 | app.component(_sfc_main.name, _sfc_main);
323 | }
324 | };
325 | export {
326 | entry as default,
327 | _sfc_main as vue3ScrollSeamless
328 | };
329 |
--------------------------------------------------------------------------------
/packages/vue3-scroll-seamless/dist/vue3-scroll-seamless.umd.js:
--------------------------------------------------------------------------------
1 | (function(global, factory) {
2 | typeof exports === "object" && typeof module !== "undefined" ? factory(exports, require("vue")) : typeof define === "function" && define.amd ? define(["exports", "vue"], factory) : (global = typeof globalThis !== "undefined" ? globalThis : global || self, factory(global.vue3ScrollSeamless = {}, global.Vue));
3 | })(this, function(exports2, vue) {
4 | "use strict";
5 | function animationFrame() {
6 | window.cancelAnimationFrame = function() {
7 | return window.cancelAnimationFrame || window.webkitCancelAnimationFrame || window.mozCancelAnimationFrame || window.oCancelAnimationFrame || window.msCancelAnimationFrame || function(id) {
8 | return window.clearTimeout(id);
9 | };
10 | }();
11 | window.requestAnimationFrame = function() {
12 | return window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || window.oRequestAnimationFrame || window.msRequestAnimationFrame || function(callback) {
13 | return window.setTimeout(callback, 1e3 / 60);
14 | };
15 | }();
16 | }
17 | function arrayEqual(arr1, arr2) {
18 | if (arr1 === arr2)
19 | return true;
20 | if (arr1.length !== arr2.length)
21 | return false;
22 | for (var i = 0; i < arr1.length; ++i) {
23 | if (arr1[i] !== arr2[i])
24 | return false;
25 | }
26 | return true;
27 | }
28 | const _hoisted_1 = ["innerHTML"];
29 | const _sfc_main = /* @__PURE__ */ vue.defineComponent({
30 | __name: "seamlessScroll",
31 | props: {
32 | dataList: {
33 | type: Array,
34 | default: []
35 | },
36 | classOptions: {
37 | type: Object,
38 | default: {}
39 | }
40 | },
41 | emits: ["ScrollEnd"],
42 | setup(__props, { emit: __emit }) {
43 | animationFrame();
44 | const slotList = vue.ref(null);
45 | const wrap = vue.ref(null);
46 | const realBox = vue.ref(null);
47 | let copyHtml = vue.ref(), initData = vue.reactive({
48 | xPos: 0,
49 | yPos: 0,
50 | delay: 0,
51 | ease: "ease-in",
52 | height: 0,
53 | width: 0,
54 | realBoxWidth: 0,
55 | realBoxHeight: 0,
56 | isHover: false,
57 | reqFrame: null,
58 | singleWaitTime: null
59 | });
60 | const defaultOption = {
61 | step: 1,
62 | limitMoveNum: 5,
63 | hoverStop: true,
64 | direction: 1,
65 | openTouch: true,
66 | singleHeight: 0,
67 | singleWidth: 0,
68 | waitTime: 1e3,
69 | switchOffset: 30,
70 | autoPlay: true,
71 | navigation: false,
72 | switchSingleStep: 134,
73 | switchDelay: 400,
74 | switchDisabledClass: "disabled",
75 | isSingleRemUnit: false
76 | };
77 | const emit = __emit;
78 | const Props = __props;
79 | vue.onBeforeMount(() => {
80 | initData.ease = "ease-in";
81 | initData.isHover = false;
82 | initData.reqFrame = null;
83 | initData.singleWaitTime = null;
84 | });
85 | vue.onMounted(() => {
86 | _initMove();
87 | });
88 | vue.onBeforeUnmount(() => {
89 | _cancle();
90 | clearTimeout(initData.singleWaitTime);
91 | });
92 | const options = vue.computed(() => {
93 | return { ...defaultOption, ...Props.classOptions };
94 | });
95 | const isHorizontal = vue.computed(() => options.value.direction > 1).value;
96 | const float = vue.computed(() => {
97 | let isFloat;
98 | if (isHorizontal) {
99 | isFloat = { float: "left", overflow: "hidden" };
100 | } else {
101 | isFloat = { overflow: "hidden" };
102 | }
103 | return isFloat;
104 | });
105 | const pos = vue.computed(() => {
106 | return {
107 | transform: `translate(${initData.xPos}px,${initData.yPos}px)`,
108 | transition: `all ${initData.ease} ${initData.delay}ms`,
109 | overflow: "hidden"
110 | };
111 | });
112 | const navigation = vue.computed(() => options.value.navigation).value;
113 | const autoPlay = vue.computed(() => {
114 | if (navigation)
115 | return false;
116 | return options.value.autoPlay;
117 | });
118 | const scrollSwitch = vue.computed(
119 | () => Props.dataList.length >= options.value.limitMoveNum
120 | );
121 | const hoverStopSwitch = vue.computed(
122 | () => options.value.hoverStop && autoPlay.value && scrollSwitch.value
123 | );
124 | const baseFontSize = vue.computed(
125 | () => options.value.isSingleRemUnit ? parseInt(window.getComputedStyle(document.documentElement, null).fontSize) : 1
126 | ).value;
127 | const realSingleStopWidth = vue.computed(
128 | () => options.value.singleWidth * baseFontSize
129 | ).value;
130 | const realSingleStopHeight = vue.computed(
131 | () => options.value.singleHeight * baseFontSize
132 | ).value;
133 | const gap = vue.ref(1);
134 | const step = vue.computed(() => {
135 | let singleStep;
136 | let step2 = options.value.step;
137 | if (isHorizontal) {
138 | singleStep = realSingleStopWidth;
139 | } else {
140 | singleStep = realSingleStopHeight;
141 | }
142 | if (singleStep > 0 && singleStep % step2 > 0) {
143 | console.error(
144 | "\u5982\u679C\u8BBE\u7F6E\u4E86\u5355\u6B65\u6EDA\u52A8,step\u9700\u662F\u5355\u6B65\u5927\u5C0F\u7684\u7EA6\u6570,\u5426\u5219\u65E0\u6CD5\u4FDD\u8BC1\u5355\u6B65\u6EDA\u52A8\u7ED3\u675F\u7684\u4F4D\u7F6E\u662F\u5426\u51C6\u786E~~~~~"
145 | );
146 | }
147 | return step2;
148 | }).value;
149 | vue.watch(
150 | () => Props.dataList,
151 | (newValue, oldValue) => {
152 | _dataWarm(newValue);
153 | if (!arrayEqual(newValue, oldValue)) {
154 | reset();
155 | }
156 | }
157 | );
158 | vue.watch(autoPlay, (newBol) => {
159 | if (newBol) {
160 | reset();
161 | } else {
162 | _stopMove();
163 | }
164 | });
165 | function reset() {
166 | _cancle();
167 | _initMove();
168 | }
169 | function changeEnter() {
170 | if (hoverStopSwitch.value)
171 | _stopMove();
172 | }
173 | function changeLeave() {
174 | if (hoverStopSwitch.value)
175 | _startMove();
176 | }
177 | function _dataWarm(data) {
178 | if (data.length > 100) {
179 | console.warn(
180 | `\u6570\u636E\u8FBE\u5230\u4E86${data.length}\u6761\u6709\u70B9\u591A\u54E6~,\u53EF\u80FD\u4F1A\u9020\u6210\u90E8\u5206\u8001\u65E7\u6D4F\u89C8\u5668\u5361\u987F\u3002`
181 | );
182 | }
183 | }
184 | async function _initMove() {
185 | await vue.nextTick();
186 | const { switchDelay } = options.value;
187 | _dataWarm(Props.dataList);
188 | copyHtml.value = "";
189 | if (isHorizontal) {
190 | initData.height = wrap.value.offsetHeight;
191 | initData.width = wrap.value.offsetWidth;
192 | let slotListWidth = slotList.value.offsetWidth;
193 | if (autoPlay.value) {
194 | slotListWidth = slotListWidth * 2 + 1;
195 | }
196 | realBox.value.style.width = slotListWidth + "px";
197 | initData.realBoxWidth = slotListWidth;
198 | }
199 | if (autoPlay.value) {
200 | initData.ease = "ease-in";
201 | initData.delay = 0;
202 | } else {
203 | initData.ease = "linear";
204 | initData.delay = switchDelay;
205 | return;
206 | }
207 | if (scrollSwitch.value) {
208 | let initTimer = null;
209 | copyHtml.value = slotList.value.innerHTML;
210 | gap.value = Math.ceil(realBox.value.offsetHeight / copyHtml.value.clientHeight);
211 | if (initTimer)
212 | clearTimeout(initTimer);
213 | initTimer = setTimeout(() => {
214 | initData.realBoxHeight = realBox.value.offsetHeight;
215 | _move();
216 | }, 0);
217 | } else {
218 | _cancle();
219 | initData.xPos = 0;
220 | initData.yPos = 0;
221 | }
222 | }
223 | function _move() {
224 | if (initData.isHover)
225 | return;
226 | _cancle();
227 | initData.reqFrame = requestAnimationFrame(function() {
228 | const h = initData.realBoxHeight / 2;
229 | const w = initData.realBoxWidth / 2;
230 | let { direction, waitTime } = options.value;
231 | if (direction === 1) {
232 | if (Math.abs(initData.yPos) >= h) {
233 | emit("ScrollEnd");
234 | initData.yPos = 0;
235 | }
236 | initData.yPos -= step;
237 | } else if (direction === 0) {
238 | if (initData.yPos >= 0) {
239 | emit("ScrollEnd");
240 | initData.yPos = h * -1;
241 | }
242 | initData.yPos += step;
243 | } else if (direction === 2) {
244 | if (Math.abs(initData.xPos) >= w) {
245 | emit("ScrollEnd");
246 | initData.xPos = 0;
247 | }
248 | initData.xPos -= step;
249 | } else if (direction === 3) {
250 | if (initData.xPos >= 0) {
251 | emit("ScrollEnd");
252 | initData.xPos = w * -1;
253 | }
254 | initData.xPos += step;
255 | }
256 | if (initData.singleWaitTime)
257 | clearTimeout(initData.singleWaitTime);
258 | if (realSingleStopHeight) {
259 | if (Math.abs(initData.yPos) % realSingleStopHeight < step) {
260 | initData.singleWaitTime = setTimeout(() => {
261 | _move();
262 | }, waitTime);
263 | } else {
264 | _move();
265 | }
266 | } else if (realSingleStopWidth) {
267 | if (Math.abs(initData.xPos) % realSingleStopWidth < step) {
268 | initData.singleWaitTime = setTimeout(() => {
269 | _move();
270 | }, waitTime);
271 | } else {
272 | _move();
273 | }
274 | } else {
275 | _move();
276 | }
277 | });
278 | }
279 | function _cancle() {
280 | cancelAnimationFrame(initData.reqFrame || null);
281 | }
282 | function _stopMove() {
283 | initData.isHover = true;
284 | if (initData.singleWaitTime)
285 | clearTimeout(initData.singleWaitTime);
286 | _cancle();
287 | }
288 | function _startMove() {
289 | initData.isHover = false;
290 | _move();
291 | }
292 | return (_ctx, _cache) => {
293 | return vue.openBlock(), vue.createElementBlock("div", {
294 | ref_key: "wrap",
295 | ref: wrap
296 | }, [
297 | vue.createElementVNode("div", {
298 | onMouseenter: changeEnter,
299 | onMouseleave: changeLeave,
300 | ref_key: "realBox",
301 | ref: realBox,
302 | style: vue.normalizeStyle(pos.value)
303 | }, [
304 | vue.createElementVNode("div", {
305 | style: vue.normalizeStyle(float.value),
306 | ref_key: "slotList",
307 | ref: slotList
308 | }, [
309 | vue.renderSlot(_ctx.$slots, "default")
310 | ], 4),
311 | (vue.openBlock(true), vue.createElementBlock(vue.Fragment, null, vue.renderList(gap.value, (i) => {
312 | return vue.openBlock(), vue.createElementBlock("div", {
313 | style: vue.normalizeStyle(float.value),
314 | innerHTML: vue.unref(copyHtml),
315 | key: i
316 | }, null, 12, _hoisted_1);
317 | }), 128))
318 | ], 36)
319 | ], 512);
320 | };
321 | }
322 | });
323 | const entry = {
324 | install(app) {
325 | app.component(_sfc_main.name, _sfc_main);
326 | }
327 | };
328 | exports2.default = entry;
329 | exports2.vue3ScrollSeamless = _sfc_main;
330 | Object.defineProperties(exports2, { __esModule: { value: true }, [Symbol.toStringTag]: { value: "Module" } });
331 | });
332 |
--------------------------------------------------------------------------------
/packages/vue3-scroll-seamless/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Document
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/packages/vue3-scroll-seamless/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "vue3-scroll-seamless",
3 | "version": "1.0.7",
4 | "description": "",
5 | "main": "dist/vue3-scroll-seamless.mjs",
6 | "homepage": "https://xiaofulzm.github.io/vue3-scroll-seamless/",
7 | "files": [
8 | "dist"
9 | ],
10 | "exports":{
11 | ".":{
12 | "import":"./dist/vue3-scroll-seamless.mjs",
13 | "require": "./dist/vue3-scroll-seamless.iifejs",
14 | "types": "./dist/vue3-scroll-seamless.umdjs"
15 | }
16 | },
17 | "scripts": {
18 | "dev": "vite",
19 | "build": "vite build",
20 | "test": "vitest",
21 | "lint": "eslint --fix --ext .ts,.vue src",
22 | "format": "prettier --write \"src/**/*.ts\" \"src/**/*.vue\""
23 | },
24 | "keywords": [
25 | "vue3",
26 | "vuejs",
27 | "ui",
28 | "components",
29 | "Seamless",
30 | "scroll"
31 | ],
32 | "author": "",
33 | "license": "MIT",
34 | "devDependencies": {
35 | "@typescript-eslint/eslint-plugin": "^5.36.2",
36 | "@typescript-eslint/parser": "^5.36.2",
37 | "@vitejs/plugin-vue": "^3.1.0",
38 | "@vue/eslint-config-prettier": "^7.0.0",
39 | "@vue/test-utils": "2.0.2",
40 | "babel-eslint": "^10.1.0",
41 | "eslint": "^8.23.0",
42 | "eslint-formatter-pretty": "^4.1.0",
43 | "eslint-plugin-json": "^3.1.0",
44 | "eslint-plugin-prettier": "^4.2.1",
45 | "eslint-plugin-vue": "^9.4.0",
46 | "happy-dom": "6.0.4",
47 | "prettier": "^2.7.1",
48 | "vitest": "0.21.1"
49 | },
50 | "dependencies": {
51 | "typescript": "^4.8.4"
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/packages/vue3-scroll-seamless/src/_test_/seamlessScroll.spec.ts:
--------------------------------------------------------------------------------
1 | import vue3ScrollSeamless from "../components/seamlessScroll.vue";
2 |
3 | import { shallowMount } from "@vue/test-utils";
4 | import { describe, expect, test } from "vitest";
5 |
6 | //测试分组
7 | // 测试分组
8 | describe("无缝滚动组件配置参数测试", () => {
9 | test("测试limitMoveNum(开启无缝滚动的数据量)配置是否生效!", async () => {
10 | const wrapper = shallowMount(vue3ScrollSeamless, {
11 | props: {
12 | dataList: [1, 2, 3, 4, 5],
13 | classOptions: {
14 | limitMoveNum: 6,
15 | },
16 | },
17 | });
18 |
19 | // 断言
20 | expect(wrapper.vm.scrollSwitch).toBe(false);
21 | await wrapper.setProps({ dataList: [1, 2, 3, 4, 5, 6, 7] });
22 | expect(wrapper.vm.scrollSwitch).toBe(true);
23 | });
24 |
25 | test("测试hoverStop(是否启用鼠标hover控制)配置是否生效!", () => {
26 | const wrapper = shallowMount(vue3ScrollSeamless, {
27 | props: {
28 | dataList: [1, 2, 3, 4, 5, 6, 7],
29 | },
30 | });
31 | expect(wrapper.vm.initData.isHover).toBe(false);
32 | const main = wrapper.find({ ref: "realBox" });
33 | main.trigger("mouseenter");
34 | expect(wrapper.vm.initData.isHover).toBe(true);
35 | });
36 | });
37 |
--------------------------------------------------------------------------------
/packages/vue3-scroll-seamless/src/components/seamlessScroll.vue:
--------------------------------------------------------------------------------
1 |
347 |
348 |
349 |
360 |
361 |
362 |
--------------------------------------------------------------------------------
/packages/vue3-scroll-seamless/src/demo.vue:
--------------------------------------------------------------------------------
1 |
14 |
15 |
16 |
17 |
23 |
24 | -
25 |
{{ item.title }} {{ i }}
26 |
32 |
33 |
34 |
35 |
36 |
37 |
70 |
--------------------------------------------------------------------------------
/packages/vue3-scroll-seamless/src/entry.ts:
--------------------------------------------------------------------------------
1 | import vue3ScrollSeamless from "./components/seamlessScroll.vue";
2 |
3 | import { App } from "vue";
4 |
5 | // console.log(vue3ScrollSeamless);
6 |
7 | export { vue3ScrollSeamless };
8 |
9 | export default {
10 | install(app: App) {
11 | app.component(vue3ScrollSeamless.name, vue3ScrollSeamless);
12 | },
13 | };
14 |
--------------------------------------------------------------------------------
/packages/vue3-scroll-seamless/src/index.ts:
--------------------------------------------------------------------------------
1 | import { h,createApp } from "vue";
2 | import Demo from "./demo.vue";
3 |
4 | createApp({
5 | render: () => h(Demo),
6 | }).mount("#app");
7 |
--------------------------------------------------------------------------------
/packages/vue3-scroll-seamless/src/shims.vue.d.ts:
--------------------------------------------------------------------------------
1 | declare module "*.vue" {
2 | import { DefineComponent } from "vue";
3 | const component: DefineComponent<{}, {}, any>;
4 | export default component;
5 | }
6 |
--------------------------------------------------------------------------------
/packages/vue3-scroll-seamless/src/utils/index.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * @desc AnimationFrame简单兼容
3 | */
4 |
5 | export function animationFrame() {
6 | window.cancelAnimationFrame = (function () {
7 | // @ts-ignore
8 | return (
9 | window.cancelAnimationFrame ||
10 | window.webkitCancelAnimationFrame ||
11 | window.mozCancelAnimationFrame ||
12 | window.oCancelAnimationFrame ||
13 | window.msCancelAnimationFrame ||
14 | function (id) {
15 | return window.clearTimeout(id);
16 | }
17 | );
18 | })();
19 | window.requestAnimationFrame = (function () {
20 | // @ts-ignore
21 | return (
22 | window.requestAnimationFrame ||
23 | window.webkitRequestAnimationFrame ||
24 | window.mozRequestAnimationFrame ||
25 | window.oRequestAnimationFrame ||
26 | window.msRequestAnimationFrame ||
27 | function (callback) {
28 | return window.setTimeout(callback, 1000 / 60);
29 | }
30 | );
31 | })();
32 | }
33 |
34 | /**
35 | * @desc 判断数组是否相等
36 | * @param {arr1,arr2}
37 | * @return {Boolean}
38 | */
39 | export function arrayEqual(arr1: unknown[], arr2: unknown[]): boolean {
40 | if (arr1 === arr2) return true;
41 | if (arr1.length !== arr2.length) return false;
42 | for (var i = 0; i < arr1.length; ++i) {
43 | if (arr1[i] !== arr2[i]) return false;
44 | }
45 | return true;
46 | }
47 |
--------------------------------------------------------------------------------
/packages/vue3-scroll-seamless/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "declaration": true, /* 生成相关的 '.d.ts' 文件。 */
4 | "declarationDir": "./dist/types", /* '.d.ts' 文件输出目录 */
5 | "lib": [ "dom", "es5", "es2015.promise" ,"es2015", "es2017"],
6 | "jsx": "preserve",
7 | "isolatedModules": true,
8 | "noImplicitThis": true
9 | },
10 | "include": [
11 | "./src/**/*.*"
12 | ],
13 | "exclude": [
14 | "node_modules"
15 | ],
16 | "esModuleInterop": true,
17 | "allowSyntheticDefaultImports": "true"
18 | }
--------------------------------------------------------------------------------
/packages/vue3-scroll-seamless/vite.config.ts:
--------------------------------------------------------------------------------
1 | ///
2 | import { defineConfig } from "vite";
3 | import vue from "@vitejs/plugin-vue";
4 |
5 |
6 | const rollupOptions = {
7 | external: ["vue", "vue-router"],
8 | output: {
9 | globals: {
10 | vue: "Vue",
11 | },
12 | },
13 | };
14 |
15 | export default defineConfig({
16 | plugins: [vue()],
17 | build: {
18 | rollupOptions,
19 | minify:false,
20 | lib: {
21 | entry: "./src/entry.ts",
22 | name: "vue3ScrollSeamless",
23 | fileName: "vue3-scroll-seamless",
24 | // 导出模块格式
25 | formats: ["es", "umd","iife"],
26 | },
27 | },
28 | test: {
29 | globals: true,
30 | environment: 'happy-dom',
31 | }
32 | });
--------------------------------------------------------------------------------
/pnpm-workspace.yaml:
--------------------------------------------------------------------------------
1 | packages:
2 | # all packages in subdirs of packages/ and components/
3 | - 'packages/**'
--------------------------------------------------------------------------------
/scripts/preinstall.js:
--------------------------------------------------------------------------------
1 | if (!/pnpm/.test(process.env.npm_execpath || '')) {
2 | // console.log('不懂问然叔')
3 | console.warn(
4 | `\u001b[33mThis repository requires using pnpm as the package manager ` +
5 | ` for scripts to work properly.\u001b[39m\n`
6 | )
7 | process.exit(1)
8 | }
--------------------------------------------------------------------------------