├── .github
└── workflows
│ └── auto-deploy.yml
├── .gitignore
├── .vscode
└── extensions.json
├── LICENSE
├── README.md
├── carousel
├── __tests__
│ └── carousel.spec.ts
├── index.ts
└── src
│ ├── carousel.scss
│ ├── carousel.tsx
│ ├── carousel.type.ts
│ ├── carousel.util.ts
│ ├── components
│ ├── arrow-left.tsx
│ ├── arrow-right.tsx
│ ├── carousel-indicator.scss
│ ├── carousel-indicator.tsx
│ ├── carousel-next.scss
│ ├── carousel-next.tsx
│ ├── carousel-prev.scss
│ └── carousel-prev.tsx
│ └── composables
│ ├── use-autoplay.ts
│ └── use-page.ts
├── docs
├── .vitepress
│ ├── config.mts
│ └── theme
│ │ ├── index.scss
│ │ ├── index.ts
│ │ └── register-components.js
├── features
│ ├── autoplay
│ │ └── index.md
│ ├── basic
│ │ └── index.md
│ ├── bilibili-events
│ │ └── index.md
│ ├── collapse-card
│ │ └── index.md
│ ├── custom-indicator
│ │ └── index.md
│ ├── custom-pagination
│ │ └── index.md
│ ├── huawei-events
│ │ └── index.md
│ ├── indicator-position
│ │ └── index.md
│ ├── juejin-events
│ │ └── index.md
│ ├── leetcode-card
│ │ └── index.md
│ ├── pagination-position
│ │ └── index.md
│ └── qqmusic
│ │ └── index.md
├── index.md
├── public
│ └── assets
│ │ ├── juejin1.png
│ │ └── juejin2.png
└── vite.config.ts
├── index.html
├── package-lock.json
├── package.json
├── public
├── 1-default.gif
├── 10-collapse-card.gif
├── 2-juejin.gif
├── 3-indicator-position.gif
├── 4-custom-indicator.gif
├── 5-pagination-position.gif
├── 6-custom-pagination.gif
├── 7-huawei.gif
├── 8-qqmusic.gif
├── 9-bilibili.gif
└── favicon.ico
├── src
├── App.vue
├── assets
│ └── logo.png
├── components
│ └── HelloWorld.vue
├── env.d.ts
└── main.ts
├── tsconfig.json
├── vite.config.lib.ts
└── vite.config.ts
/.github/workflows/auto-deploy.yml:
--------------------------------------------------------------------------------
1 | name: Deploy
2 |
3 | on:
4 | push:
5 | branches: [main]
6 |
7 | permissions:
8 | contents: read
9 | pages: write
10 | id-token: write
11 |
12 | concurrency:
13 | group: pages
14 | cancel-in-progress: false
15 |
16 | jobs:
17 | # Build job
18 | build:
19 | runs-on: ubuntu-latest
20 | steps:
21 | - name: Checkout
22 | uses: actions/checkout@v4
23 | with:
24 | fetch-depth: 0
25 | - name: Setup Node
26 | uses: actions/setup-node@v4
27 | with:
28 | node-version: 20
29 | cache: npm
30 | - name: Setup Pages
31 | uses: actions/configure-pages@v4
32 | - name: Install dependencies
33 | run: npm i
34 | - name: Build with VitePress
35 | run: npm run docs:build
36 | - name: Upload artifact
37 | uses: actions/upload-pages-artifact@v3
38 | with:
39 | path: docs/.vitepress/dist
40 |
41 | # Deployment job
42 | deploy:
43 | environment:
44 | name: github-pages
45 | url: ${{ steps.deployment.outputs.page_url }}
46 | needs: build
47 | runs-on: ubuntu-latest
48 | name: Deploy
49 | steps:
50 | - name: Deploy to GitHub Pages
51 | id: deployment
52 | uses: actions/deploy-pages@v4
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | .DS_Store
3 | dist
4 | dist-ssr
5 | *.local
6 | cache
7 |
--------------------------------------------------------------------------------
/.vscode/extensions.json:
--------------------------------------------------------------------------------
1 | {
2 | "recommendations": ["johnsoncodehk.volar"]
3 | }
4 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2022 Kagol
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 | # Vue Carousel
2 |
3 | 一个简单、灵活的`Vue3`走马灯组件,非常轻量,只有`5kB`。
4 |
5 | 预览地址:
6 | [https://kagol.github.io/vue-carousel/](https://kagol.github.io/vue-carousel/)
7 |
8 | ## 快速开始
9 |
10 | 创建一个vite工程:
11 |
12 | ```
13 | npm create vite vite-demo --template vue-ts
14 | ```
15 |
16 | 安装`Carousel`:
17 | ```
18 | npm install @kagol/vue-carousel
19 | ```
20 |
21 | 在`main.ts`中引入`Carousel`:
22 | ```
23 | import Carousel from '@kagol/vue-carousel'
24 | import '@kagol/vue-carousel/dist/style.css'
25 |
26 | createApp(App)
27 | .use(Carousel)
28 | .mount('#app')
29 | ```
30 |
31 | 在`App.vue`中使用:
32 |
33 | ```
34 |
35 | page 1
36 | page 2
37 | page 3
38 |
39 | ```
40 |
41 | ## 效果动图
42 |
43 | 默认效果:
44 |
45 | 
46 |
47 | 掘金活动:
48 |
49 | 
50 |
51 | 指示器位置:
52 |
53 | 
54 |
55 | 自定义指示器:
56 |
57 | 
58 |
59 | 分页器位置:
60 |
61 | 
62 |
63 | 自定义分页器:
64 |
65 | 
66 |
67 | 华为官网:
68 |
69 | 
70 |
71 | QQ音乐:
72 |
73 | 
74 |
75 | B站:
76 |
77 | 
78 |
79 | 手风琴式折叠卡片:
80 |
81 | 
82 |
83 | ## API
84 |
85 | ### Carousel 组件
86 |
87 | props
88 |
89 | | 属性 | 类型 | 默认 | 说明 |
90 | | ------- | ------ | ---- | -------------- |
91 | | v-model | Number | 1 | 可选,当前页码 |
92 | | autoplay | Boolean | true | 可选,是否自动播放 |
93 | | interval | Number | 3000 | 可选,自动播放的时间间隔,单位是毫秒 |
94 |
95 | 插槽
96 |
97 | | 属性 | 类型 | 默认 | 说明 |
98 | | ------- | ------ | ---- | -------------- |
99 | | default | -- | -- | 必选,默认插槽 |
100 | | indicator | -- | -- | 可选,指示器插槽 |
101 | | pagination | -- | -- | 可选,分页器插槽 |
102 |
103 | ### CarouselIndicator 组件
104 |
105 | props
106 |
107 | | 属性 | 类型 | 默认 | 说明 |
108 | | ------- | ------ | ---- | -------------- |
109 | | v-model | Number | 1 | 可选,当前页码 |
110 | | count | Number | -- | 可选,指示器元素数量 |
111 |
112 | 插槽
113 |
114 | | 属性 | 类型 | 默认 | 说明 |
115 | | ------- | ------ | ---- | -------------- |
116 | | default | ({ pageIndex, setPageIndex }) => {} | -- | 可选,默认插槽 |
117 |
118 | ### CarouselPrev 组件
119 |
120 | 插槽
121 |
122 | | 属性 | 类型 | 默认 | 说明 |
123 | | ------- | ------ | ---- | -------------- |
124 | | default | -- | -- | 可选,默认插槽 |
125 |
126 | ### CarouselNext 组件
127 |
128 | 插槽
129 |
130 | | 属性 | 类型 | 默认 | 说明 |
131 | | ------- | ------ | ---- | -------------- |
132 | | default | -- | -- | 可选,默认插槽 |
133 |
134 | 参考:
135 |
136 | [用积木理论设计的Carousel组件都有哪些有趣的玩法?](https://juejin.cn/post/7056193763810476063/)
137 |
--------------------------------------------------------------------------------
/carousel/__tests__/carousel.spec.ts:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kagol/vue-carousel/bad1b2ade8b317312af96a89d3ba2ce6ed5c4c88/carousel/__tests__/carousel.spec.ts
--------------------------------------------------------------------------------
/carousel/index.ts:
--------------------------------------------------------------------------------
1 | import { App } from 'vue'
2 | // import { usePage } from 'vueuse-components'
3 | import usePage from './src/composables/use-page'
4 | import Carousel from './src/carousel'
5 | import CarouselIndicator from './src/components/carousel-indicator'
6 | import CarouselPrev from './src/components/carousel-prev'
7 | import CarouselNext from './src/components/carousel-next'
8 |
9 | export { Carousel, CarouselIndicator, CarouselPrev, CarouselNext, usePage }
10 |
11 | export default {
12 | install(app: App) {
13 | app.component(Carousel.name, Carousel)
14 | app.component(CarouselIndicator.name, CarouselIndicator)
15 | app.component(CarouselPrev.name, CarouselPrev)
16 | app.component(CarouselNext.name, CarouselNext)
17 | app.config.globalProperties.usePage = usePage
18 | }
19 | }
--------------------------------------------------------------------------------
/carousel/src/carousel.scss:
--------------------------------------------------------------------------------
1 | .xui-carousel {
2 | position: relative;
3 | overflow: hidden;
4 |
5 | .xui-carousel-indicator {
6 | position: absolute;
7 | }
8 | }
9 |
10 | .xui-carousel-item-container {
11 | display: flex;
12 | position: relative;
13 | transition: left 500ms ease 0s; // 内容切换时的动效
14 |
15 | & > * {
16 | flex: 1;
17 | }
18 | }
19 |
20 | .xui-arrow {
21 | position: absolute;
22 | top: 50%;
23 | margin-top: -18px;
24 | cursor: pointer;
25 | width: 36px;
26 | height: 36px;
27 | border-radius: 18px;
28 | background: var(--xui-highlight-overlay, rgba(255, 255, 255, .8));
29 | box-shadow: var(--xui-shadow-length-hover, 0 4px 16px 0) var(--xui-light-shadow, rgba(0, 0, 0, .1));
30 | display: inline-flex;
31 | align-items: center;
32 | justify-content: center;
33 | transition: background-color var(--xui-animation-duration-slow, .3s) var(--xui-animation-ease-in-out-smooth, cubic-bezier(.645, .045, .355, 1));
34 |
35 | &:hover {
36 | background: var(--xui-area, #f8f8f8);
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/carousel/src/carousel.tsx:
--------------------------------------------------------------------------------
1 | import { defineComponent, renderSlot, useSlots, watch, toRefs, ref } from 'vue'
2 | // import { usePage } from 'vueuse-components'
3 | import usePage from './composables/use-page'
4 |
5 | // Components
6 | import CarouselIndicator from './components/carousel-indicator'
7 | import CarouselPrev from './components/carousel-prev'
8 | import CarouselNext from './components/carousel-next'
9 |
10 | // Composables
11 | import useAutoplay from './composables/use-autoplay'
12 |
13 | // Util
14 | import { formatPageIndex } from './carousel.util'
15 |
16 | // Props/Types
17 | import { carouselProps, CarouselProps } from './carousel.type'
18 |
19 | // SCSS
20 | import './carousel.scss'
21 |
22 | export default defineComponent({
23 | name: 'Carousel',
24 | components: {
25 | CarouselIndicator,
26 | CarouselPrev,
27 | CarouselNext,
28 | },
29 | props: carouselProps,
30 | emits: ['update:modelValue'],
31 | setup(props: CarouselProps, { slots, emit }) {
32 | const { modelValue, autoplay, interval } = toRefs(props)
33 |
34 | const { pageIndex, prevPage, nextPage, setPageIndex } = usePage(modelValue.value)
35 | const { startPlay, stopPlay } = useAutoplay(nextPage, interval.value)
36 |
37 | const getCarouselItems = () => {
38 | let carouselItems = []
39 | useSlots().default().forEach(item => {
40 | if (typeof item.type !== 'symbol') {
41 | carouselItems.push(item)
42 | } else {
43 | if (Symbol.keyFor(item.type) === 'v-fgt') {
44 | carouselItems = [...carouselItems, ...item.children]
45 | } else if(Symbol.keyFor(item.type) === 'v-txt') {
46 | console.warn('不支持文本节点,需要包裹一层元素标签,比如:item 改成
item
')
47 | }
48 | }
49 | })
50 | return carouselItems
51 | }
52 |
53 | const count = getCarouselItems().length
54 | const defaultFormattedPageIndex = formatPageIndex(pageIndex.value, count)
55 | const formattedPageIndex = ref(defaultFormattedPageIndex)
56 |
57 | const launchTimer = (autoplay) => {
58 | if (autoplay) {
59 | startPlay()
60 | } else {
61 | stopPlay()
62 | }
63 | }
64 |
65 | launchTimer(autoplay.value)
66 |
67 | watch(autoplay, (newVal) => {
68 | launchTimer(newVal)
69 | })
70 |
71 | watch(modelValue, (newVal: number) => {
72 | pageIndex.value = newVal
73 | })
74 |
75 | watch(pageIndex, (newVal: number) => {
76 | emit('update:modelValue', newVal)
77 | formattedPageIndex.value = formatPageIndex(pageIndex.value, count)
78 | })
79 |
80 | watch(formattedPageIndex, (newVal: number) => {
81 | pageIndex.value = newVal
82 | })
83 |
84 | return () => {
85 | return (
86 |
87 |
94 | {renderSlot(useSlots(), 'default')}
95 |
96 | {
97 | slots.pagination
98 | ? renderSlot(useSlots(), 'pagination', {
99 | prevPage, nextPage
100 | }) : <>
101 |
{
102 | emit('update:modelValue', props.modelValue-1)
103 | prevPage()
104 | }} />
105 | {
106 | emit('update:modelValue', props.modelValue+1)
107 | nextPage()
108 | }} />
109 | >
110 | }
111 | {slots.indicator ? (
112 | slots.indicator({
113 | count,
114 | pageIndex: formattedPageIndex.value,
115 | setPageIndex
116 | })
117 | ) : (
118 |
122 | )}
123 |
124 | )
125 | }
126 | },
127 | })
128 |
--------------------------------------------------------------------------------
/carousel/src/carousel.type.ts:
--------------------------------------------------------------------------------
1 | import { extractPropTypes } from 'vue'
2 |
3 | export const carouselProps = {
4 | modelValue: {
5 | type: Number,
6 | },
7 | autoplay: {
8 | type: Boolean,
9 | default: true,
10 | },
11 | interval: {
12 | type: Number,
13 | default: 3000,
14 | }
15 | }
16 |
17 | export type CarouselProps = extractPropTypes
18 |
--------------------------------------------------------------------------------
/carousel/src/carousel.util.ts:
--------------------------------------------------------------------------------
1 | export const formatPageIndex = (current, count) => {
2 | if (current <= 0) {
3 | return current + count * (Math.floor(-current / count) + 1)
4 | } else {
5 | return current % count === 0 ? count : current % count
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/carousel/src/components/arrow-left.tsx:
--------------------------------------------------------------------------------
1 | export default () => (
2 |
24 | )
25 |
--------------------------------------------------------------------------------
/carousel/src/components/arrow-right.tsx:
--------------------------------------------------------------------------------
1 | export default () => (
2 |
25 | )
26 |
--------------------------------------------------------------------------------
/carousel/src/components/carousel-indicator.scss:
--------------------------------------------------------------------------------
1 | .xui-carousel-indicator {
2 | display: flex;
3 | position: relative;
4 | bottom: 12px;
5 | justify-content: center;
6 | width: 100%;
7 |
8 | .xui-carousel-indicator-item {
9 | cursor: pointer;
10 | width: 6px;
11 | height: 6px;
12 | border-radius: 3px;
13 | margin-right: 8px;
14 | background: var(--xui-icon-fill, #d3d5d9);
15 |
16 | &.active {
17 | width: 24px;
18 | background: var(--xui-list-item-active-bg, #5e7ce0);
19 | transition: all var(--xui-animation-duration-slow, .3s) var(--xui-animation-ease-in-smooth, cubic-bezier(.645, .045, .355, 1));
20 | }
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/carousel/src/components/carousel-indicator.tsx:
--------------------------------------------------------------------------------
1 | import { defineComponent, toRefs, watch } from 'vue'
2 | // import { usePage } from 'vueuse-components'
3 | import usePage from '../composables/use-page'
4 | import './carousel-indicator.scss'
5 |
6 | export default defineComponent({
7 | name: 'CarouselIndicator',
8 | props: {
9 | modelValue: {
10 | type: Number,
11 | },
12 | count: {
13 | type: Number,
14 | }
15 | },
16 | emits: ['update:modelValue'],
17 | setup(props, { emit, slots }) {
18 | const { modelValue } = toRefs(props)
19 | const { pageIndex, setPageIndex } = usePage(modelValue.value)
20 | const indicatorArr = Array.from(new Array(props.count).keys())
21 |
22 | watch(modelValue, (newVal: number) => {
23 | pageIndex.value = newVal
24 | })
25 |
26 | watch(pageIndex, (newVal: number) => {
27 | emit('update:modelValue', newVal)
28 | })
29 |
30 | return () => {
31 | return
32 | {
33 | slots.default
34 | ? slots.default({
35 | pageIndex: pageIndex.value,
36 | setPageIndex
37 | })
38 | : indicatorArr.map((item, index) => {
39 | return
setPageIndex(index + 1)}>
40 | })
41 | }
42 |
43 | }
44 | }
45 | })
--------------------------------------------------------------------------------
/carousel/src/components/carousel-next.scss:
--------------------------------------------------------------------------------
1 | .xui-arrow-right {
2 | right: 20px;
3 | }
4 |
--------------------------------------------------------------------------------
/carousel/src/components/carousel-next.tsx:
--------------------------------------------------------------------------------
1 | import { defineComponent, renderSlot } from 'vue'
2 | import DArrowRight from './arrow-right'
3 | import './carousel-next.scss'
4 |
5 | export default defineComponent({
6 | name: 'CarouselNext',
7 | setup(props, { slots, attrs }) {
8 | return () => {
9 | return <>
10 | {
11 | slots.default
12 | ? renderSlot(slots, 'default')
13 | : <>
14 |
15 |
16 |
17 | >
18 | }
19 | >
20 | }
21 | }
22 | })
23 |
--------------------------------------------------------------------------------
/carousel/src/components/carousel-prev.scss:
--------------------------------------------------------------------------------
1 | .xui-arrow-left {
2 | left: 20px;
3 | }
4 |
--------------------------------------------------------------------------------
/carousel/src/components/carousel-prev.tsx:
--------------------------------------------------------------------------------
1 | import { defineComponent, renderSlot } from 'vue'
2 | import DArrowLeft from './arrow-left'
3 | import './carousel-prev.scss'
4 |
5 | export default defineComponent({
6 | name: 'CarouselPrev',
7 | setup(props, { slots, attrs }) {
8 | return () => {
9 | return <>
10 | {
11 | slots.default
12 | ? renderSlot(slots, 'default')
13 | : <>
14 |
15 |
16 |
17 | >
18 | }
19 | >
20 | }
21 | }
22 | })
23 |
--------------------------------------------------------------------------------
/carousel/src/composables/use-autoplay.ts:
--------------------------------------------------------------------------------
1 | import { onUnmounted } from 'vue'
2 |
3 | export default function useAutoplay(next, interval) {
4 | let timerId
5 |
6 | const startPlay = () => {
7 | stopPlay()
8 |
9 | timerId = setInterval(next, interval)
10 | }
11 |
12 | const stopPlay = () => {
13 | if (timerId) {
14 | clearInterval(timerId)
15 | timerId = null
16 | }
17 | }
18 |
19 | onUnmounted(() => {
20 | stopPlay()
21 | })
22 |
23 | return {
24 | startPlay, stopPlay
25 | }
26 | }
--------------------------------------------------------------------------------
/carousel/src/composables/use-page.ts:
--------------------------------------------------------------------------------
1 | import { ref } from 'vue'
2 |
3 | export default function usePage(defaultPageIndex = 1) {
4 | const pageIndex = ref(defaultPageIndex)
5 |
6 | const setPageIndex = (current: number) => {
7 | pageIndex.value = current
8 | }
9 |
10 | const jumpPage = (page: number) => {
11 | pageIndex.value += page
12 | }
13 |
14 | const prevPage = () => jumpPage(-1)
15 |
16 | const nextPage = () => jumpPage(1)
17 |
18 | return { pageIndex, setPageIndex, jumpPage, prevPage, nextPage }
19 | }
20 |
--------------------------------------------------------------------------------
/docs/.vitepress/config.mts:
--------------------------------------------------------------------------------
1 | import { demoBlockPlugin, demoblockVitePlugin } from 'vitepress-theme-demoblock'
2 |
3 | const sidebar = [
4 | { text: "快速开始", link: "/" },
5 | {
6 | text: "特性", items: [
7 | { text: "基本用法", link: "/features/basic/" },
8 | { text: "自动播放", link: "/features/autoplay/" },
9 | { text: "掘金活动", link: "/features/juejin-events/" },
10 | { text: "指示器位置", link: "/features/indicator-position/" },
11 | { text: "自定义指示器", link: "/features/custom-indicator/" },
12 | { text: "分页器位置", link: "/features/pagination-position/" },
13 | { text: "自定义分页器", link: "/features/custom-pagination/" },
14 | { text: "手风琴式折叠卡片", link: "/features/collapse-card/" },
15 | { text: "华为", link: "/features/huawei-events/" },
16 | { text: "B站", link: "/features/bilibili-events/" },
17 | { text: "QQ音乐", link: "/features/qqmusic/" },
18 | { text: "LeetCode", link: "/features/leetcode-card/" },
19 | ]
20 | }
21 | ]
22 |
23 | const nav = [
24 | { text: 'Github', link: 'https://github.com/kagol/vue-carousel' }
25 | ]
26 |
27 | const config = {
28 | base: '/vue-carousel/',
29 | title: 'Vue Carousel',
30 | head: [
31 | ['link', { rel: 'icon', type: 'image/svg+xml', href: '/assets/logo.svg' }],
32 | ],
33 | themeConfig: {
34 | sidebar,
35 | nav,
36 | logo: '/assets/logo.svg',
37 | },
38 | markdown: {
39 | config: (md) => {
40 | // 这里可以使用 markdown-it 插件,vitepress-theme-demoblock就是基于此开发的
41 | md.use(demoBlockPlugin, {
42 | cssPreprocessor: 'scss'
43 | })
44 | }
45 | },
46 | vite: {
47 | plugins: [demoblockVitePlugin()]
48 | }
49 | }
50 |
51 | export default config
52 |
--------------------------------------------------------------------------------
/docs/.vitepress/theme/index.scss:
--------------------------------------------------------------------------------
1 | .debug {
2 | display: none;
3 | }
4 |
5 | .demoblock-view {
6 | overflow: initial !important;
7 | }
8 |
--------------------------------------------------------------------------------
/docs/.vitepress/theme/index.ts:
--------------------------------------------------------------------------------
1 | import Theme from 'vitepress/dist/client/theme-default/index'
2 | import Carousel from '../../../carousel'
3 |
4 | import { registerComponents } from './register-components.js'
5 | import 'vitepress-theme-demoblock/dist/theme/styles/index.css'
6 | import './index.scss'
7 |
8 | export default {
9 | ...Theme,
10 | enhanceApp({ app }) {
11 | app.use(Carousel)
12 | registerComponents(app)
13 | },
14 | };
15 |
--------------------------------------------------------------------------------
/docs/.vitepress/theme/register-components.js:
--------------------------------------------------------------------------------
1 | import Demo from 'vitepress-theme-demoblock/dist/client/components/Demo.vue'
2 | import DemoBlock from 'vitepress-theme-demoblock/dist/client/components/DemoBlock.vue'
3 | export function registerComponents(app) {
4 | app.component('Demo', Demo)
5 | app.component('DemoBlock', DemoBlock)
6 | }
7 |
--------------------------------------------------------------------------------
/docs/features/autoplay/index.md:
--------------------------------------------------------------------------------
1 | # 自动播放
2 |
3 | `autoplay`可以设置是否需要自动播放,默认值为`true`,默认每隔3000毫秒切换一次,可以通过设置`interval`自定义自动播放的间隔时间。
4 |
5 | :::demo
6 |
7 |
8 | ```vue
9 |
10 |
11 |
12 |
13 | page 1
14 | page 2
15 | page 3
16 |
17 |
18 |
38 |
45 | ```
46 |
47 | :::
48 |
--------------------------------------------------------------------------------
/docs/features/basic/index.md:
--------------------------------------------------------------------------------
1 | # 基本用法
2 |
3 |
4 | `Carousel`组件提供了默认插槽,直接将元素放在``即可实现轮播效果。
5 |
6 | :::demo
7 |
8 |
9 | ```vue
10 |
11 |
12 | page 1
13 | page 2
14 | page 3
15 |
16 |
17 |
24 | ```
25 |
26 | :::
27 |
--------------------------------------------------------------------------------
/docs/features/bilibili-events/index.md:
--------------------------------------------------------------------------------
1 |
2 | ### B站
3 |
4 | :::demo
5 |
6 | ```vue
7 |
8 |
9 |
10 |
11 |

12 |
13 |
15 |
16 |
17 |
18 |
19 |

20 |
21 |
22 |
23 |
24 |
25 |
26 |

27 |
28 |
30 |
31 |
32 |
33 |
34 |

35 |
36 |
38 |
39 |
40 |
41 |
42 |

43 |
44 |
45 |
46 |
47 |
48 |
49 |

50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
77 |
78 |
79 |
80 |
177 | ```
178 |
179 | :::
--------------------------------------------------------------------------------
/docs/features/collapse-card/index.md:
--------------------------------------------------------------------------------
1 | ### 手风琴式折叠卡片
2 |
3 | `CarouselIndicator`指示器组件提供了默认插槽,可以单独使用它实现自定义指示器效果,比如手风琴式折叠卡片。
4 |
5 | :::demo
6 |
7 | ```vue
8 |
9 |
10 |
11 |
12 |
13 |
Explore The World
14 |
15 |
16 |
Wild Forest
17 |
18 |
19 |
Sunny Beach
20 |
21 |
22 |
City on Winter
23 |
24 |
25 |
Mountains - Clouds
26 |
27 |
28 |
29 |
30 |
31 |
85 | ```
86 |
87 | :::
--------------------------------------------------------------------------------
/docs/features/custom-indicator/index.md:
--------------------------------------------------------------------------------
1 | # 自定义指示器
2 |
3 | 如果`Carousel`内置的`CarouselIndicator`指示器不满足你的要求,还可以定制自己的指示器。
4 |
5 | :::demo
6 |
7 | ```vue
8 |
9 |
10 | page 1
11 | page 2
12 | page 3
13 |
14 |
15 |
21 |
22 |
23 |
24 |
25 |
36 |
64 | ```
65 |
66 | :::
--------------------------------------------------------------------------------
/docs/features/custom-pagination/index.md:
--------------------------------------------------------------------------------
1 | ### 自定义分页器
2 |
3 | :::demo
4 |
5 | ```vue
6 |
7 |
8 | page 1
9 | page 2
10 | page 3
11 |
12 |
20 |
21 |
22 |
23 |
52 | ```
53 |
54 | :::
--------------------------------------------------------------------------------
/docs/features/huawei-events/index.md:
--------------------------------------------------------------------------------
1 | ### 华为
2 |
3 | :::demo
4 |
5 | ```vue
6 |
7 |
8 |
9 |
10 |

11 |
12 |
前行不辍,未来可期
13 |
华为轮值董事长郭平新年致辞
14 |
了解更多
15 |
16 |
17 |
18 |
19 |
20 |
21 |
30 |
31 |
32 |
33 |
102 | ```
103 |
104 | :::
--------------------------------------------------------------------------------
/docs/features/indicator-position/index.md:
--------------------------------------------------------------------------------
1 | # 指示器位置
2 |
3 | ``组件将其中的指示器子组件暴露出来,并提供了`indicator`插槽,因此可以随意调整`CarouselIndicator`的位置,比如放在左下角。
4 |
5 | :::demo
6 |
7 | ```vue
8 |
9 |
10 | page 1
11 | page 2
12 | page 3
13 |
14 |
15 |
16 |
17 |
18 |
25 | ```
26 |
27 | :::
--------------------------------------------------------------------------------
/docs/features/juejin-events/index.md:
--------------------------------------------------------------------------------
1 | # 掘金活动
2 |
3 | ``元素里面可以放任意元素,比如放上两张图片就是掘金活动的效果。
4 |
5 | :::demo
6 |
7 | ```vue
8 |
9 |
10 |
14 |
18 |
19 |
20 | ```
21 |
22 | :::
23 |
--------------------------------------------------------------------------------
/docs/features/leetcode-card/index.md:
--------------------------------------------------------------------------------
1 | # LeetCode
2 |
3 | ### 首页轮播
4 |
5 | `Carousel`和`CarouselIndicator`组合使用,可以很方便地实现 LeetCode 的卡片轮播效果。
6 |
7 | :::demo
8 |
9 | ```vue
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
53 | ```
54 |
55 | :::
56 |
57 | ### 求职页轮播
58 |
59 | :::demo
60 |
61 | ```vue
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
105 | ```
106 |
107 | :::
108 |
109 | ### 题库页
110 |
111 | :::demo
112 |
113 | ```vue
114 |
115 |
116 |
117 |
学习计划
118 |
126 |
127 |
128 |
160 |
168 |
169 |
170 |
171 |
172 |
173 |
187 |
254 | ```
255 |
256 | :::
257 |
--------------------------------------------------------------------------------
/docs/features/pagination-position/index.md:
--------------------------------------------------------------------------------
1 | ### 分页器位置
2 |
3 | :::demo
4 |
5 | ```vue
6 |
7 |
8 | page 1
9 | page 2
10 | page 3
11 |
12 |
13 |
14 |
15 |
16 |
17 |
24 | ```
25 |
26 | :::
--------------------------------------------------------------------------------
/docs/features/qqmusic/index.md:
--------------------------------------------------------------------------------
1 | # QQ音乐
2 |
3 | :::demo
4 |
5 | ```vue
6 |
7 |
8 |
9 |
23 |
37 |
38 |
39 |
40 |
143 | ```
144 |
145 | :::
146 |
--------------------------------------------------------------------------------
/docs/index.md:
--------------------------------------------------------------------------------
1 | # 快速开始
2 |
3 | 创建一个vite工程:
4 |
5 | ```
6 | yarn create vite vite-demo --template vue-ts
7 | ```
8 |
9 | 安装`Carousel`:
10 | ```
11 | yarn add @kagol/vue-carousel
12 | ```
13 |
14 | 在`main.ts`中引入`Carousel`:
15 | ```
16 | import Carousel from '@kagol/vue-carousel'
17 | import '@kagol/vue-carousel/dist/style.css'
18 |
19 | createApp(App)
20 | .use(Carousel)
21 | .mount('#app')
22 | ```
23 |
24 | 在`App.vue`中使用:
25 |
26 | ```vue
27 |
28 |
29 | page 1
30 | page 2
31 | page 3
32 |
33 |
34 |
41 | ```
42 |
43 | ### API
44 |
45 |
46 |
47 | #### Carousel 组件
48 |
49 | props
50 |
51 | | 属性 | 类型 | 默认 | 说明 |
52 | | ------- | ------ | ---- | -------------- |
53 | | v-model | Number | 1 | 可选,当前页码 |
54 | | autoplay | Boolean | true | 可选,是否自动播放 |
55 | | interval | Number | 3000 | 可选,自动播放的时间间隔,单位是毫秒 |
56 |
57 | 插槽
58 |
59 | | 属性 | 类型 | 默认 | 说明 |
60 | | ------- | ------ | ---- | -------------- |
61 | | default | -- | -- | 必选,默认插槽 |
62 | | indicator | -- | -- | 可选,指示器插槽 |
63 | | pagination | -- | -- | 可选,分页器插槽 |
64 |
65 | #### CarouselIndicator 组件
66 |
67 | props
68 |
69 | | 属性 | 类型 | 默认 | 说明 |
70 | | ------- | ------ | ---- | -------------- |
71 | | v-model | Number | 1 | 可选,当前页码 |
72 | | count | Number | -- | 可选,指示器元素数量 |
73 |
74 | 插槽
75 |
76 | | 属性 | 类型 | 默认 | 说明 |
77 | | ------- | ------ | ---- | -------------- |
78 | | default | ({ pageIndex, setPageIndex }) => {} | -- | 可选,默认插槽 |
79 |
80 | #### CarouselPrev 组件
81 |
82 | 插槽
83 |
84 | | 属性 | 类型 | 默认 | 说明 |
85 | | ------- | ------ | ---- | -------------- |
86 | | default | -- | -- | 可选,默认插槽 |
87 |
88 | #### CarouselNext 组件
89 |
90 | 插槽
91 |
92 | | 属性 | 类型 | 默认 | 说明 |
93 | | ------- | ------ | ---- | -------------- |
94 | | default | -- | -- | 可选,默认插槽 |
95 |
--------------------------------------------------------------------------------
/docs/public/assets/juejin1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kagol/vue-carousel/bad1b2ade8b317312af96a89d3ba2ce6ed5c4c88/docs/public/assets/juejin1.png
--------------------------------------------------------------------------------
/docs/public/assets/juejin2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kagol/vue-carousel/bad1b2ade8b317312af96a89d3ba2ce6ed5c4c88/docs/public/assets/juejin2.png
--------------------------------------------------------------------------------
/docs/vite.config.ts:
--------------------------------------------------------------------------------
1 | import { defineConfig } from 'vite'
2 | import vueJsx from '@vitejs/plugin-vue-jsx'
3 |
4 | // https://vitejs.dev/config/
5 | export default defineConfig({
6 | plugins: [vueJsx()],
7 | css: {
8 | preprocessorOptions: {
9 | scss: {
10 | charset: false
11 | }
12 | }
13 | }
14 | })
15 |
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Vite App
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@kagol/vue-carousel",
3 | "version": "0.1.2",
4 | "description": "Vue3 Carousel: A simple and flexible carousel component for Vue3, extremely lightweight with only 5kB.",
5 | "homepage": "https://kagol.github.io/vue-carousel/",
6 | "files": [
7 | "dist"
8 | ],
9 | "main": "./dist/vue-carousel.umd.js",
10 | "module": "./dist/vue-carousel.es.js",
11 | "scripts": {
12 | "dev": "vite",
13 | "build": "vue-tsc --noEmit && vite build",
14 | "build:lib": "vite build --config vite.config.lib.ts",
15 | "preview": "vite preview",
16 | "docs:dev": "vitepress dev docs",
17 | "docs:build": "vitepress build docs",
18 | "docs:preview": "vitepress preview docs",
19 | "register:components": "vitepress-rc",
20 | "test": "jest"
21 | },
22 | "author": "Kagol ",
23 | "license": "MIT",
24 | "dependencies": {
25 | "vue": "^3.4.21",
26 | "vueuse-components": "^0.0.1"
27 | },
28 | "devDependencies": {
29 | "@vitejs/plugin-vue": "^5.0.4",
30 | "@vitejs/plugin-vue-jsx": "^3.1.0",
31 | "@vue/test-utils": "^2.4.5",
32 | "jest": "^29.7.0",
33 | "sass": "^1.74.1",
34 | "typescript": "^5.4.4",
35 | "vite": "^5.2.8",
36 | "vitepress": "^1.0.2",
37 | "vitepress-theme-demoblock": "^3.0.7",
38 | "vue-tsc": "^2.0.11"
39 | },
40 | "optionalDependencies": {
41 | "@rollup/rollup-linux-x64-gnu": "4.14.1",
42 | "@rollup/rollup-darwin-x64": "4.14.1"
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/public/1-default.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kagol/vue-carousel/bad1b2ade8b317312af96a89d3ba2ce6ed5c4c88/public/1-default.gif
--------------------------------------------------------------------------------
/public/10-collapse-card.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kagol/vue-carousel/bad1b2ade8b317312af96a89d3ba2ce6ed5c4c88/public/10-collapse-card.gif
--------------------------------------------------------------------------------
/public/2-juejin.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kagol/vue-carousel/bad1b2ade8b317312af96a89d3ba2ce6ed5c4c88/public/2-juejin.gif
--------------------------------------------------------------------------------
/public/3-indicator-position.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kagol/vue-carousel/bad1b2ade8b317312af96a89d3ba2ce6ed5c4c88/public/3-indicator-position.gif
--------------------------------------------------------------------------------
/public/4-custom-indicator.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kagol/vue-carousel/bad1b2ade8b317312af96a89d3ba2ce6ed5c4c88/public/4-custom-indicator.gif
--------------------------------------------------------------------------------
/public/5-pagination-position.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kagol/vue-carousel/bad1b2ade8b317312af96a89d3ba2ce6ed5c4c88/public/5-pagination-position.gif
--------------------------------------------------------------------------------
/public/6-custom-pagination.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kagol/vue-carousel/bad1b2ade8b317312af96a89d3ba2ce6ed5c4c88/public/6-custom-pagination.gif
--------------------------------------------------------------------------------
/public/7-huawei.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kagol/vue-carousel/bad1b2ade8b317312af96a89d3ba2ce6ed5c4c88/public/7-huawei.gif
--------------------------------------------------------------------------------
/public/8-qqmusic.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kagol/vue-carousel/bad1b2ade8b317312af96a89d3ba2ce6ed5c4c88/public/8-qqmusic.gif
--------------------------------------------------------------------------------
/public/9-bilibili.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kagol/vue-carousel/bad1b2ade8b317312af96a89d3ba2ce6ed5c4c88/public/9-bilibili.gif
--------------------------------------------------------------------------------
/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kagol/vue-carousel/bad1b2ade8b317312af96a89d3ba2ce6ed5c4c88/public/favicon.ico
--------------------------------------------------------------------------------
/src/App.vue:
--------------------------------------------------------------------------------
1 |
21 |
22 |
23 |
24 |

25 |
26 |
27 | item 1
28 | item 2
29 | {{ item.thumbnail }}
30 |
31 |
32 |
33 |
34 |
35 |
50 |
--------------------------------------------------------------------------------
/src/assets/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kagol/vue-carousel/bad1b2ade8b317312af96a89d3ba2ce6ed5c4c88/src/assets/logo.png
--------------------------------------------------------------------------------
/src/components/HelloWorld.vue:
--------------------------------------------------------------------------------
1 |
8 |
9 |
10 | {{ msg }}
11 |
12 |
13 | Recommended IDE setup:
14 | VSCode
15 | +
16 | Volar
17 |
18 |
19 | See README.md
for more information.
20 |
21 |
22 |
23 | Vite Docs
24 |
25 | |
26 | Vue 3 Docs
27 |
28 |
29 |
30 |
31 | Edit
32 | components/HelloWorld.vue
to test hot module replacement.
33 |
34 |
35 |
36 |
53 |
--------------------------------------------------------------------------------
/src/env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
3 | declare module '*.vue' {
4 | import { DefineComponent } from 'vue'
5 | // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/ban-types
6 | const component: DefineComponent<{}, {}, any>
7 | export default component
8 | }
9 |
--------------------------------------------------------------------------------
/src/main.ts:
--------------------------------------------------------------------------------
1 | import { createApp } from 'vue'
2 | import App from './App.vue'
3 | // import Carousel from '../dist/vue-carousel.es'
4 | // import '../dist/style.css'
5 | import Carousel from '@kagol/vue-carousel'
6 |
7 | createApp(App)
8 | .use(Carousel)
9 | .mount('#app')
10 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "esnext",
4 | "useDefineForClassFields": true,
5 | "module": "esnext",
6 | "moduleResolution": "node",
7 | "strict": true,
8 | "jsx": "preserve",
9 | "sourceMap": true,
10 | "resolveJsonModule": true,
11 | "esModuleInterop": true,
12 | "lib": ["esnext", "dom"]
13 | },
14 | "include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue"]
15 | }
16 |
--------------------------------------------------------------------------------
/vite.config.lib.ts:
--------------------------------------------------------------------------------
1 | import { defineConfig } from 'vite'
2 | import vue from '@vitejs/plugin-vue'
3 | import vueJsx from '@vitejs/plugin-vue-jsx'
4 | import path from 'path'
5 |
6 | // https://vitejs.dev/config/
7 | export default defineConfig({
8 | publicDir: false,
9 | build: {
10 | lib: {
11 | entry: path.resolve(__dirname, 'carousel/index.ts'),
12 | name: 'VueCarousel',
13 | fileName: (format) => `vue-carousel.${format}.js`
14 | },
15 | rollupOptions: {
16 | // 确保外部化处理那些你不想打包进库的依赖
17 | external: ['vue'],
18 | output: {
19 | // 在 UMD 构建模式下为这些外部化的依赖提供一个全局变量
20 | globals: {
21 | vue: 'Vue'
22 | }
23 | }
24 | }
25 | },
26 | plugins: [vue(), vueJsx()]
27 | })
28 |
--------------------------------------------------------------------------------
/vite.config.ts:
--------------------------------------------------------------------------------
1 | import { defineConfig } from 'vite'
2 | import vue from '@vitejs/plugin-vue'
3 | import vueJsx from '@vitejs/plugin-vue-jsx'
4 | import path from 'path'
5 |
6 | // https://vitejs.dev/config/
7 | export default defineConfig({
8 | plugins: [vue(), vueJsx()],
9 | resolve: {
10 | alias: [
11 | { find: '@kagol/vue-carousel', replacement: path.resolve(__dirname, 'carousel') }
12 | ]
13 | }
14 | })
15 |
--------------------------------------------------------------------------------