├── .eslintignore
├── .gitattributes
├── .github
└── workflows
│ └── deploy.yml
├── .gitignore
├── .nojekyll
├── .npmignore
├── .npmrc
├── .prettierignore
├── .prettierrc
├── CHANGELOG.md
├── LICENSE
├── README.md
├── README_CN.md
├── README_KO.md
├── dist
├── components
│ ├── Swipe.svelte
│ ├── Swipe.svelte.d.ts
│ ├── SwipeItem.svelte
│ └── SwipeItem.svelte.d.ts
├── helpers
│ ├── SwipeSnap.d.ts
│ └── SwipeSnap.js
├── index.d.ts
└── index.js
├── jsconfig.json
├── package-lock.json
├── package.json
├── src
├── app.html
├── lib
│ ├── components
│ │ ├── Swipe.svelte
│ │ └── SwipeItem.svelte
│ ├── helpers
│ │ └── SwipeSnap.js
│ └── index.js
└── routes
│ ├── +layout.js
│ └── +page.svelte
├── static
├── favicon.png
└── images
│ ├── 1.jpg
│ ├── 2.jpg
│ ├── 3.jpg
│ ├── 4.jpg
│ ├── 5.jpg
│ ├── dy-1.jpg
│ ├── dy-2.jpg
│ ├── dy-3.jpg
│ ├── dy-4.jpg
│ ├── dy-5.jpg
│ └── url-code.png
├── svelte-swipe.png
├── svelte.config.js
└── vite.config.js
/.eslintignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | node_modules
3 | /build
4 | /.svelte-kit
5 | /package
6 | .env
7 | .env.*
8 | !.env.example
9 |
10 | # Ignore files for PNPM, NPM and YARN
11 | pnpm-lock.yaml
12 | package-lock.json
13 | yarn.lock
14 |
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | # Auto detect text files and perform LF normalization
2 | * text=auto
3 |
--------------------------------------------------------------------------------
/.github/workflows/deploy.yml:
--------------------------------------------------------------------------------
1 |
2 | name: Deploy to GitHub Pages
3 |
4 | on:
5 | push:
6 | branches: [ master ]
7 | workflow_dispatch:
8 |
9 | jobs:
10 | build:
11 | runs-on: ubuntu-latest
12 | steps:
13 | - uses: actions/checkout@v2
14 |
15 | - name: Install dependencies and build
16 | run: |
17 | npm install
18 | npm run build
19 |
20 | - name: Create .nojekyll file
21 | run: touch build/.nojekyll
22 |
23 | - name: Deploy to GitHub Pages
24 | uses: JamesIves/github-pages-deploy-action@4.1.5
25 | with:
26 | branch: gh-pages
27 | folder: build
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | node_modules
3 | /build
4 | /.svelte-kit
5 | /package
6 | .env
7 | .env.*
8 | !.env.example
9 | debug.log
10 |
--------------------------------------------------------------------------------
/.nojekyll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SharifClick/svelte-swipe/7a771f0a0724452116eca1600817cbe611d15a62/.nojekyll
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | dev
2 | docs
3 | node_modules
--------------------------------------------------------------------------------
/.npmrc:
--------------------------------------------------------------------------------
1 | engine-strict=true
2 |
--------------------------------------------------------------------------------
/.prettierignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | node_modules
3 | /build
4 | /.svelte-kit
5 | /package
6 | .env
7 | .env.*
8 | !.env.example
9 |
10 | # Ignore files for PNPM, NPM and YARN
11 | pnpm-lock.yaml
12 | package-lock.json
13 | yarn.lock
14 |
--------------------------------------------------------------------------------
/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "singleQuote": true,
3 | "trailingComma": "none",
4 | "printWidth": 100,
5 | "plugins": ["prettier-plugin-svelte"],
6 | "pluginSearchDirs": ["."],
7 | "overrides": [
8 | {
9 | "files": "*.svelte",
10 | "options": { "parser": "svelte" }
11 | },
12 | {
13 | "files": "*.md",
14 | "options": { "requirePragma": true }
15 | }
16 | ]
17 | }
18 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # svelte-swipe changelog
2 |
3 | ## 1.0.0
4 |
5 | * First release
6 | * Supports indicators
7 | * Supports autoplay
8 |
9 | ## 1.1.0
10 |
11 | * Fix indicators position
12 | * Fix transition duration
13 | * Fix autoplay
14 |
15 | ## 1.2.0
16 |
17 | * Swipe item wrapper
18 |
19 | ## 1.2.1
20 |
21 | * bug fixes
22 |
23 | ## 1.2.2
24 |
25 | * style bug fixes
26 | * doc fixes
27 | * license is MIT
28 |
29 | ## 1.3.0
30 |
31 | * fix issue #2
32 | * bug fixes #2
33 | * doc fixes
34 |
35 | ## 1.3.1
36 |
37 | * fix issue #4
38 |
39 | ## 1.3.2
40 |
41 | * fix issue #6
42 |
43 | ## 1.4.0
44 |
45 | * add feat of issue #7
46 |
47 | ## 1.5.0
48 |
49 | * add feat of issue #8
50 | * bug fixes
51 | * exposed `goTo` method from root component
52 |
53 | ## 1.6.0
54 |
55 | * fix `goTo` logic to navigate slide by given slide index
56 | * exposed `nextItem`, `prevItem` method to move slide prev/next from outside
57 | * new readonly prop `active_item` for tracking active slide index
58 | * bug fixes
59 | * doc fixes
60 |
61 | ## 1.6.1
62 |
63 | * merge PR #14
64 | * closed issue #10, #14
65 | * doc fixes
66 |
67 | ## 1.7.0
68 |
69 | * feature request issue #18 (added vertical swipe support)
70 | * doc fixes
71 |
72 | ## 1.7.1
73 |
74 | * merged pr #26
75 | * fixed issue #25
76 |
77 | ## 1.7.2
78 |
79 | * merged pr #31
80 | * fixed page scroll on swipe issue
81 |
82 | ## 1.8.0
83 |
84 | * merged pr #42
85 | * Fixed issue #27
86 | * Allow Swipe height automatically follow the SwipeItem children
87 |
88 | ## 1.8.1
89 |
90 | * merged pr #44
91 | * Fixed issue #43
92 | * Allow default scroll on touch (for mobile)
93 |
94 | ## 1.8.2
95 | * Fixed issue #51
96 |
97 |
98 | ## 1.9.0
99 | * issue #34 resolved
100 | * Sveltekit featuring dev environment
101 | * Publish gh-pages
102 | * bugfixes
103 |
104 | ## 1.9.1
105 | * Fix module import path
106 |
107 | ## 1.9.2
108 | * Issue #55 - fix initial item shift issue (Thanks to @gnuletik)
109 | * Fix infinite play
110 | * Fix exports path on package file
111 | * Demo fix
112 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | (The MIT License)
2 |
3 | Copyright (c) 2019 Sharif Ahmed me.sharifahmed@gmail.com sharifahmed.me
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining
6 | a copy of this software and associated documentation files (the
7 | 'Software'), to deal in the Software without restriction, including
8 | without limitation the rights to use, copy, modify, merge, publish,
9 | distribute, sublicense, and/or sell copies of the Software, and to
10 | permit persons to whom the Software is furnished to do so, subject to
11 | the following conditions:
12 |
13 | The above copyright notice and this permission notice shall be
14 | included in all copies or substantial portions of the Software.
15 |
16 | THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
19 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
20 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
21 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
22 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23 |
24 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |

3 |
4 |
19 |
20 | ____
21 |
22 | [English](README.md) / [Korean](README_KO.md) / [简体中文](README_CN.md)
23 |
24 | Swipable items wrapper component for Svelte :fire: :boom: (zero dependencies)
25 |
26 | ## 🚀[See it in Action](https://sharifclick.github.io/svelte-swipe/)
27 |
28 |
29 | ## Installation
30 |
31 | ```bash
32 | npm i -D svelte-swipe
33 | ```
34 |
35 | ## Usage
36 |
37 | ```html
38 |
50 |
51 |
61 |
62 |
81 |
82 | ```
83 |
84 | ### Supports Dynamic height (from child) 🔥
85 |
86 | ```html
87 |
88 |
96 |
97 |
98 |
99 | {#each items as item, i}
100 |
104 | ....
105 |
106 | {/each}
107 |
108 |
109 |
110 | ```
111 | ### Supports Infinite swipe 🔥
112 |
113 | ```html
114 |
115 |
116 |
117 |
118 | {#each items as item, i}
119 |
122 | ....
123 |
124 | {/each}
125 |
126 |
127 |
128 | ```
129 | ### Vertical Swipe 🔥
130 |
131 | ```html
132 |
133 |
134 |
135 |
136 | ...
137 |
138 | ...
139 |
140 |
141 | ```
142 |
143 | ### Pointer event inside Swipe Item
144 |
145 | ```html
146 |
153 |
154 |
155 |
156 |
157 |
158 |
159 |
160 |
161 | ...
162 |
163 |
164 |
165 | ```
166 |
167 |
168 | ### Programmatically change slides
169 |
170 | ```html
171 |
172 |
184 |
185 |
186 | ....
187 | ...
188 |
189 |
190 |
191 |
192 |
193 |
194 | ```
195 |
196 | ### Supports custom thumbnail
197 | ## 🚀[See example with custom thumbnail](https://svelte.dev/repl/be477862ac8b4dfea4c8e454e556ef2c?version=3.20.1)
198 | ```html
199 |
200 |
208 |
209 |
210 | ....
211 | ...
212 |
213 |
214 |
215 | ```
216 |
217 | ## Default css custom properties
218 |
219 | ```css
220 |
221 | :root{
222 | --sv-swipe-panel-height: inherit;
223 | --sv-swipe-panel-width: inherit;
224 | --sv-swipe-panel-wrapper-index: 2;
225 | --sv-swipe-indicator-active-color: grey;
226 | --sv-swipe-handler-top: 0px;
227 | }
228 |
229 | ```
230 |
231 | ## Props
232 |
233 | | Name | Type | Description | Required | Default |
234 | | --- | --- | --- | --- | --- |
235 | | `is_vertical` | `Boolean` | allow swipe items vertically | No | `false` |
236 | | `autoplay` | `Boolean` | Play items as slide | No | `false` |
237 | | `showIndicators` | `Boolean` | appears clickable circle indicators bottom center of item | No | `false` |
238 | | `transitionDuration` | `Number` | staying duration of per slide/swipe item | No | `200` *ms |
239 | | `delay` | `Number` | transition delay | No | `1000` *ms |
240 | | `defaultIndex` | `Number` | initial item index | No |`0` |
241 | | `allow_dynamic_height` | `Boolean` | allow firing height change event `on:swipe_item_height_change` | No |`false` |
242 | | `allow_infinite_swipe` | `Boolean` | allow swipe items infinitely | No |`false` |
243 | | `active` | `Boolean` | fire height change event | No |`false` |
244 |
245 | ## Events
246 |
247 | | Name | Description | Component |
248 | | --- | --- | --- |
249 | | `on:change` | fires on swipe-end with with holding detail `active_item`, `swipe_direction` and `active_element` | `Swipe` |
250 | | `on:swipe_item_height_change` | fires on swipe-end with holding child's current height detail | `SwipeItem` |
251 |
252 |
253 | ## NPM Statistics
254 |
255 | Download stats for this NPM package
256 |
257 | [](https://nodei.co/npm/svelte-swipe/)
258 |
259 | ### Scan qr code to see url in your device
260 |
261 | 
262 |
--------------------------------------------------------------------------------
/README_CN.md:
--------------------------------------------------------------------------------
1 | # Svelte Swipe
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 | Svelte 轮播组件 :fire: :boom: (无依赖文件 - 压缩后仅 3.37 KB )
19 |
20 | ## 🚀[查看示例](https://sharifclick.github.io/svelte-swipe/)
21 |
22 |
23 | ## 如何安装
24 |
25 | ```bash
26 | npm i -D svelte-swipe
27 | ```
28 |
29 | ## 使用方法
30 |
31 | ```html
32 |
44 |
45 |
55 |
56 |
75 |
76 | ```
77 | ### 垂直轮播 🔥
78 |
79 | ```html
80 |
81 |
82 |
83 |
84 | ...
85 |
86 | ...
87 |
88 |
89 | ```
90 |
91 | ### 轮播项的点击事件
92 |
93 | ```html
94 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 | ...
110 |
111 |
112 |
113 | ```
114 |
115 |
116 | ### 自定义的轮播方式
117 |
118 | ```html
119 |
120 |
132 |
133 |
134 | ....
135 | ...
136 |
137 |
138 |
139 |
140 |
141 |
142 | ```
143 |
144 | ### 支持自定义轮播缩略图
145 | ## 🚀[查看带有自定义轮播图的示例](https://svelte.dev/repl/be477862ac8b4dfea4c8e454e556ef2c?version=3.20.1)
146 | ```html
147 |
148 |
156 |
157 |
158 | ....
159 | ...
160 |
161 |
162 |
163 | ```
164 |
165 | ## 默认的css自定义属性
166 |
167 | ```css
168 |
169 | :root{
170 | --sv-swipe-panel-height: inherit;
171 | --sv-swipe-panel-width: inherit;
172 | --sv-swipe-panel-wrapper-index: 2;
173 | --sv-swipe-indicator-active-color: grey;
174 | --sv-swipe-handler-top: 0px;
175 | }
176 |
177 | ```
178 |
179 | ## 配置项
180 |
181 | | 名称 | 类型 | 描述 | 必填 | 默认值 |
182 | | --- | --- | --- | --- | --- |
183 | | `is_vertical` | `Boolean` | 允许轮播图垂直滑动 | No | `false` |
184 | | `autoplay` | `Boolean` | 允许轮播图自动滚动 | No | `false` |
185 | | `showIndicators` | `Boolean` | 在轮播图下方显示默认轮播指示器 | No | `false` |
186 | | `transitionDuration` | `Number` | 轮播图动画过渡时间 | No | `200` 毫秒 |
187 | | `delay` | `Number` | 轮播图自动滚动等待时间 | No | `1000` 毫秒 |
188 | | `defaultIndex` | `Number` | 轮播图初始位置索引 | No |`0` |
189 |
190 | ## NPM 统计
191 |
192 | 此 NPM 包的统计信息
193 |
194 | [](https://nodei.co/npm/svelte-swipe/)
195 |
196 | ### 扫描二维码在手机端查看示例效果
197 |
198 | 
199 |
--------------------------------------------------------------------------------
/README_KO.md:
--------------------------------------------------------------------------------
1 | # Svelte Swipe
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 | Svelte의 스와이프 가능한 wrapper 컴포넌트 :fire: :boom:
19 |
20 | ## 🚀[동작 예시 보기](https://sharifclick.github.io/svelte-swipe/)
21 |
22 | ## 설치
23 |
24 | ```bash
25 | npm i -D svelte-swipe
26 | ```
27 |
28 | ## 사용
29 |
30 | ```html
31 |
42 |
43 |
53 |
54 |
73 | ```
74 |
75 | ### 수직 스와이프 🔥
76 |
77 | ```html
78 |
79 |
80 |
81 | ...
82 |
83 | ...
84 |
85 |
86 | ```
87 |
88 | ### 스와이프 항목 내부의 포인터 이벤트
89 |
90 | ```html
91 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 | ...
105 |
106 |
107 | ```
108 |
109 | ### 코드로 슬라이드 바꾸기
110 |
111 | ```html
112 |
123 |
124 |
125 | ....
126 | ...
127 |
128 |
129 |
130 |
131 |
132 |
133 | ```
134 |
135 | ## 기본 css 커스텀 값들
136 |
137 | ```css
138 | :root {
139 | --sv-swipe-panel-height: inherit;
140 | --sv-swipe-panel-width: inherit;
141 | --sv-swipe-panel-wrapper-index: 2;
142 | --sv-swipe-indicator-active-color: grey;
143 | --sv-swipe-handler-top: 0px;
144 | }
145 | ```
146 |
147 | ## Config에 넘길 수 있는 인자
148 |
149 | | Name | Type | Description | Required | Default |
150 | | -------------------- | --------- | ------------------------------------------------------- | -------- | ----------- |
151 | | `is_vertical` | `Boolean` | 수직 방향으로 swipe 여부 | No | `false` |
152 | | `autoplay` | `Boolean` | 항목 자동 슬라이드 여부 | No | `false` |
153 | | `showIndicators` | `Boolean` | 중앙 하단에 클릭 가능한 원형의 인디케이터 표시 여부 | No | `false` |
154 | | `transitionDuration` | `Number` | 각 스와이프 아이템으로 넘어가는 데 걸리는 시간 | No | `200` \*ms |
155 | | `delay` | `Number` | autoplay 활성화 시 다음 아이템으로 스와이프 하는 딜레이 | No | `1000` \*ms |
156 | | `defaultIndex` | `Number` | 가장 먼저 표시할 기본 항목의 인덱스 | No | `0` |
157 |
158 | ## NPM 통계
159 |
160 | 이 NPM 패키지에 대한 통계 다운로드
161 |
162 | [](https://nodei.co/npm/svelte-swipe/)
163 |
164 | ### QR 코드를 스캔하여 URL 확인
165 |
166 | 
167 |
--------------------------------------------------------------------------------
/dist/components/Swipe.svelte:
--------------------------------------------------------------------------------
1 |
159 |
160 |
161 |
166 |
167 |
173 | {#if showIndicators}
174 |
175 | {#each indicators as x, i}
176 | {
182 | changeItem(i);
183 | }}
184 | on:keydown={(event) => {
185 | if (event.key === 'Enter') {
186 | changeItem(i);
187 | }
188 | }}
189 | />
190 | {/each}
191 |
192 | {/if}
193 |
194 |
195 |
247 |
--------------------------------------------------------------------------------
/dist/components/Swipe.svelte.d.ts:
--------------------------------------------------------------------------------
1 | /** @typedef {typeof __propDef.props} SwipeProps */
2 | /** @typedef {typeof __propDef.events} SwipeEvents */
3 | /** @typedef {typeof __propDef.slots} SwipeSlots */
4 | export default class Swipe extends SvelteComponentTyped<{
5 | is_vertical?: boolean | undefined;
6 | active_item?: number | undefined;
7 | allow_infinite_swipe?: boolean | undefined;
8 | goTo?: ((step: any) => void) | undefined;
9 | nextItem?: (() => void) | undefined;
10 | prevItem?: (() => void) | undefined;
11 | transitionDuration?: number | undefined;
12 | showIndicators?: boolean | undefined;
13 | autoplay?: boolean | undefined;
14 | delay?: number | undefined;
15 | defaultIndex?: number | undefined;
16 | pause_on_hover?: boolean | undefined;
17 | }, {
18 | change: CustomEvent;
19 | } & {
20 | [evt: string]: CustomEvent;
21 | }, {
22 | default: {};
23 | }> {
24 | get active_item(): number;
25 | get pause_on_hover(): NonNullable;
26 | get goTo(): (step: any) => void;
27 | get prevItem(): () => void;
28 | get nextItem(): () => void;
29 | }
30 | export type SwipeProps = typeof __propDef.props;
31 | export type SwipeEvents = typeof __propDef.events;
32 | export type SwipeSlots = typeof __propDef.slots;
33 | import { SvelteComponentTyped } from "svelte";
34 | declare const __propDef: {
35 | props: {
36 | is_vertical?: boolean | undefined;
37 | active_item?: number | undefined;
38 | allow_infinite_swipe?: boolean | undefined;
39 | goTo?: ((step: any) => void) | undefined;
40 | nextItem?: (() => void) | undefined;
41 | prevItem?: (() => void) | undefined;
42 | transitionDuration?: number | undefined;
43 | showIndicators?: boolean | undefined;
44 | autoplay?: boolean | undefined;
45 | delay?: number | undefined;
46 | defaultIndex?: number | undefined;
47 | pause_on_hover?: boolean | undefined;
48 | };
49 | events: {
50 | change: CustomEvent;
51 | } & {
52 | [evt: string]: CustomEvent;
53 | };
54 | slots: {
55 | default: {};
56 | };
57 | };
58 | export {};
59 |
--------------------------------------------------------------------------------
/dist/components/SwipeItem.svelte:
--------------------------------------------------------------------------------
1 |
29 |
30 |
39 |
40 |
56 |
--------------------------------------------------------------------------------
/dist/components/SwipeItem.svelte.d.ts:
--------------------------------------------------------------------------------
1 | /** @typedef {typeof __propDef.props} SwipeItemProps */
2 | /** @typedef {typeof __propDef.events} SwipeItemEvents */
3 | /** @typedef {typeof __propDef.slots} SwipeItemSlots */
4 | export default class SwipeItem extends SvelteComponentTyped<{
5 | style?: string | undefined;
6 | active?: boolean | undefined;
7 | classes?: string | undefined;
8 | allow_dynamic_height?: boolean | undefined;
9 | }, {
10 | swipe_item_height_change: CustomEvent;
11 | } & {
12 | [evt: string]: CustomEvent;
13 | }, {
14 | default: {};
15 | }> {
16 | }
17 | export type SwipeItemProps = typeof __propDef.props;
18 | export type SwipeItemEvents = typeof __propDef.events;
19 | export type SwipeItemSlots = typeof __propDef.slots;
20 | import { SvelteComponentTyped } from "svelte";
21 | declare const __propDef: {
22 | props: {
23 | style?: string | undefined;
24 | active?: boolean | undefined;
25 | classes?: string | undefined;
26 | allow_dynamic_height?: boolean | undefined;
27 | };
28 | events: {
29 | swipe_item_height_change: CustomEvent;
30 | } & {
31 | [evt: string]: CustomEvent;
32 | };
33 | slots: {
34 | default: {};
35 | };
36 | };
37 | export {};
38 |
--------------------------------------------------------------------------------
/dist/helpers/SwipeSnap.d.ts:
--------------------------------------------------------------------------------
1 | export default SwipeSnap;
2 | /**
3 | * A class for creating a swipeable carousel with snapping behavior.
4 | * @class
5 | */
6 | declare class SwipeSnap {
7 | /**
8 | * Creates an instance of SwipeSnap.
9 | * @constructor
10 | * @param {Object} [options={}] - Options for configuring the SwipeSnap carousel.
11 | * @param {HTMLElement} [options.element] - The HTML element that contains the carousel.
12 | * @param {boolean} [options.is_vertical=false] - Whether the carousel is vertical (true) or horizontal (false).
13 | * @param {number} [options.transition_duration=300] - The duration of the transition animation in milliseconds.
14 | * @param {boolean} [options.allow_infinite_swipe=false] - Whether to allow infinite looping of carousel items.
15 | */
16 | constructor(options?: {
17 | element?: HTMLElement | undefined;
18 | is_vertical?: boolean | undefined;
19 | transition_duration?: number | undefined;
20 | allow_infinite_swipe?: boolean | undefined;
21 | } | undefined);
22 | element: HTMLElement | undefined;
23 | wrapper: Element | null;
24 | handler: Element | null;
25 | elements: NodeListOf;
26 | elements_count: number;
27 | is_vertical: boolean | undefined;
28 | transition_duration: number | undefined;
29 | allow_infinite_swipe: boolean | undefined;
30 | pos_axis: number;
31 | page_axis: string;
32 | axis: number;
33 | long_touch: boolean;
34 | last_axis_pos: number;
35 | default_index: number;
36 | active_indicator: number;
37 | active_item: number;
38 | touch_active: boolean;
39 | SWIPE: (event: any) => void;
40 | SWIPE_START: (event: any) => void;
41 | SWIPE_END: (event: any) => void;
42 | /**
43 | * Prevents the default behavior of an event.
44 | * @param {Event} event - The event to prevent.
45 | */
46 | prevent(event: Event): void;
47 | update(): void;
48 | available_space: any;
49 | available_distance: any;
50 | available_measure: number | undefined;
51 | setInfiniteSwipe(): void;
52 | setElementsPosition({ elems, available_space, pos_axis, has_infinite_loop, distance, moving, init, end, reset }: {
53 | elems?: any[] | undefined;
54 | available_space?: number | undefined;
55 | pos_axis?: number | undefined;
56 | has_infinite_loop?: boolean | undefined;
57 | distance?: number | undefined;
58 | moving?: boolean | undefined;
59 | init?: boolean | undefined;
60 | end?: boolean | undefined;
61 | reset?: boolean | undefined;
62 | }): void;
63 | generateTranslateValue(value: any): string;
64 | generateTouchPosCss(value: any, touch_end?: boolean): string;
65 | swipeStart(event: any): void;
66 | swipe(event: any): void;
67 | swipeEnd(event: any): void;
68 | eventDelegate(type: any): void;
69 | changeItem(item: any): void;
70 | goTo(step: any): void;
71 | prevItem(): void;
72 | nextItem(): void;
73 | /**
74 | * Retrieves the current properties of the carousel.
75 | * @returns {Object} Carousel properties object.
76 | * @property {number} elements_count - The total number of carousel items.
77 | * @property {number} active_item - The index of the currently active carousel item.
78 | * @property {HTMLElement} active_element - The currently active carousel item element.
79 | */
80 | getProps(): Object;
81 | }
82 |
--------------------------------------------------------------------------------
/dist/helpers/SwipeSnap.js:
--------------------------------------------------------------------------------
1 | // @ts-nocheck
2 | /**
3 | * A class for creating a swipeable carousel with snapping behavior.
4 | * @class
5 | */
6 |
7 | class SwipeSnap {
8 | /**
9 | * Creates an instance of SwipeSnap.
10 | * @constructor
11 | * @param {Object} [options={}] - Options for configuring the SwipeSnap carousel.
12 | * @param {HTMLElement} [options.element] - The HTML element that contains the carousel.
13 | * @param {boolean} [options.is_vertical=false] - Whether the carousel is vertical (true) or horizontal (false).
14 | * @param {number} [options.transition_duration=300] - The duration of the transition animation in milliseconds.
15 | * @param {boolean} [options.allow_infinite_swipe=false] - Whether to allow infinite looping of carousel items.
16 | */
17 |
18 | constructor(options = {}) {
19 | this.element = options.element;
20 | this.wrapper = this.element.querySelector('.swipeable-slot-wrapper');
21 | this.handler = this.element.querySelector('.swipe-handler');
22 | this.elements = this.wrapper.querySelectorAll('.swipeable-item');
23 | this.elements_count = this.elements.length;
24 |
25 | this.is_vertical = options.is_vertical;
26 | this.transition_duration = options.transition_duration;
27 | this.allow_infinite_swipe = options.allow_infinite_swipe;
28 |
29 | this.pos_axis = 0;
30 | this.page_axis = options.is_vertical ? 'pageY' : 'pageX';
31 | this.axis = 0;
32 | this.long_touch = false;
33 | this.last_axis_pos = 0;
34 | this.default_index = 0;
35 | this.active_indicator = 0;
36 | this.active_item = 0;
37 | this.touch_active = false;
38 |
39 | if (options.allow_infinite_swipe) {
40 | this.setInfiniteSwipe();
41 | }
42 |
43 | this.SWIPE = this.swipe.bind(this);
44 | this.SWIPE_START = this.swipeStart.bind(this);
45 | this.SWIPE_END = this.swipeEnd.bind(this);
46 |
47 | this.handler.addEventListener('touchstart', this.SWIPE_START);
48 | this.handler.addEventListener('mousedown', this.SWIPE_START);
49 | }
50 |
51 | /**
52 | * Prevents the default behavior of an event.
53 | * @param {Event} event - The event to prevent.
54 | */
55 |
56 | prevent(event) {
57 | if (event && event.cancelable) {
58 | event.preventDefault();
59 | }
60 | event && event.stopImmediatePropagation();
61 | event && event.stopPropagation();
62 | }
63 |
64 | update() {
65 | let { offsetWidth, offsetHeight } = this.wrapper;
66 | this.available_space = this.is_vertical ? offsetHeight : offsetWidth;
67 |
68 | this.setElementsPosition({
69 | init: true,
70 | elems: [...this.elements],
71 | available_space: this.available_space,
72 | has_infinite_loop: this.allow_infinite_swipe
73 | });
74 |
75 | this.available_distance = 0;
76 | this.available_measure = this.available_space * (this.elements_count - 1);
77 | if (this.default_index) {
78 | this.changeItem(this.default_index);
79 | }
80 | }
81 |
82 | setInfiniteSwipe() {
83 | this.wrapper.prepend(this.elements[this.elements_count - 1].cloneNode(true));
84 | this.wrapper.append(this.elements[0].cloneNode(true));
85 | this.elements = this.element.querySelectorAll('.swipeable-item');
86 | }
87 |
88 | setElementsPosition({
89 | elems = [],
90 | available_space = 0,
91 | pos_axis = 0,
92 | has_infinite_loop = false,
93 | distance = 0,
94 | moving = false,
95 | init = false,
96 | end = false,
97 | reset = false
98 | }) {
99 | elems.forEach((element, i) => {
100 | let idx = has_infinite_loop ? i - 1 : i;
101 | if (init) {
102 | element.style.transform = this.generateTranslateValue(available_space * idx);
103 | element.classList.remove('is-item-hidden');
104 | }
105 | if (moving) {
106 | element.style.cssText = this.generateTouchPosCss(available_space * idx - distance);
107 | }
108 | if (end) {
109 | element.style.cssText = this.generateTouchPosCss(available_space * idx - pos_axis, true);
110 | }
111 | if (reset) {
112 | element.style.cssText = this.generateTouchPosCss(available_space * idx - pos_axis);
113 | }
114 | });
115 | }
116 |
117 | generateTranslateValue(value) {
118 | return this.is_vertical ? `translate3d(0, ${value}px, 0)` : `translate3d(${value}px, 0, 0)`;
119 | }
120 |
121 | generateTouchPosCss(value, touch_end = false) {
122 | let transform_string = this.generateTranslateValue(value);
123 | let _css = `
124 | -webkit-transition-duration: ${touch_end ? this.transition_duration : '0'}ms;
125 | transition-duration: ${touch_end ? this.transition_duration : '0'}ms;
126 | -webkit-transform: ${transform_string};
127 | -ms-transform: ${transform_string};`;
128 | return _css;
129 | }
130 |
131 | swipeStart(event) {
132 | this.prevent(event);
133 | this.touch_active = true;
134 | this.long_touch = false;
135 | setTimeout(() => {
136 | this.long_touch = true;
137 | }, 250);
138 | this.axis = event.touches ? event.touches[0][this.page_axis] : event[this.page_axis];
139 | this.eventDelegate('add');
140 | }
141 |
142 | swipe(event) {
143 | if (this.touch_active) {
144 | this.prevent(event);
145 | let axis = event.touches ? event.touches[0][this.page_axis] : event[this.page_axis];
146 | let distance = this.axis - axis + this.pos_axis;
147 | if (!this.allow_infinite_swipe) {
148 | if (
149 | (this.pos_axis == 0 && this.axis < axis) ||
150 | (this.pos_axis == this.available_measure && this.axis > axis)
151 | ) {
152 | return;
153 | }
154 | }
155 | event.preventDefault();
156 |
157 | // if (distance <= availableMeasure && distance >= 0) {
158 | // }
159 | this.setElementsPosition({
160 | moving: true,
161 | elems: [...this.elements],
162 | available_space: this.available_space,
163 | has_infinite_loop: this.allow_infinite_swipe,
164 | distance
165 | });
166 | this.available_distance = distance;
167 | this.last_axis_pos = axis;
168 | }
169 | }
170 |
171 | swipeEnd(event) {
172 | this.prevent(event);
173 | let direction = this.axis < this.last_axis_pos;
174 | this.touch_active = false;
175 | let available_space = this.available_space;
176 | let accidental_touch =
177 | Math.round(this.available_space / 50) > Math.abs(this.axis - this.last_axis_pos);
178 | if (this.long_touch || accidental_touch) {
179 | this.available_distance =
180 | Math.round(this.available_distance / available_space) * available_space;
181 | } else {
182 | this.available_distance = direction
183 | ? Math.floor(this.available_distance / available_space) * available_space
184 | : Math.ceil(this.available_distance / available_space) * available_space;
185 | }
186 | this.axis = null;
187 | this.last_axis_pos = null;
188 | this.pos_axis = this.available_distance;
189 | this.active_indicator = this.available_distance / available_space;
190 | this.active_item = this.active_indicator;
191 | this.default_index = this.active_item;
192 |
193 | this.setElementsPosition({
194 | end: true,
195 | elems: [...this.elements],
196 | available_space: available_space,
197 | pos_axis: this.pos_axis,
198 | has_infinite_loop: this.allow_infinite_swipe
199 | });
200 |
201 | if (this.allow_infinite_swipe) {
202 | if (this.active_item === -1) {
203 | this.pos_axis = available_space * (this.elements_count - 1);
204 | }
205 | if (this.active_item === this.elements_count) {
206 | this.pos_axis = 0;
207 | }
208 | this.active_indicator = this.pos_axis / available_space;
209 | this.active_item = this.active_indicator;
210 | this.default_index = this.active_item;
211 |
212 | setTimeout(() => {
213 | this.setElementsPosition({
214 | reset: true,
215 | elems: [...this.elements],
216 | available_space: available_space,
217 | pos_axis: this.pos_axis,
218 | has_infinite_loop: this.allow_infinite_swipe
219 | });
220 | }, this.transition_duration);
221 | }
222 | let swipe_direction = direction ? 'right' : 'left';
223 | this.eventDelegate('remove');
224 |
225 | const customEvent = new CustomEvent('swipe_end', {
226 | detail: {
227 | active_item: this.active_item,
228 | swipe_direction,
229 | active_element: this.elements[this.active_item]
230 | }
231 | });
232 |
233 | this.element.dispatchEvent(customEvent);
234 | }
235 |
236 | eventDelegate(type) {
237 | let delegationTypes = {
238 | add: 'addEventListener',
239 | remove: 'removeEventListener'
240 | };
241 | if (typeof window !== 'undefined') {
242 | window[delegationTypes[type]]('mousemove', this.SWIPE);
243 | window[delegationTypes[type]]('mouseup', this.SWIPE_END);
244 | window[delegationTypes[type]]('touchmove', this.SWIPE, { passive: false });
245 | window[delegationTypes[type]]('touchend', this.SWIPE_END, { passive: false });
246 | }
247 | }
248 |
249 | changeItem(item) {
250 | let max = this.available_space;
251 | this.available_distance = max * item;
252 | this.active_indicator = item;
253 | this.swipeEnd();
254 | }
255 |
256 | goTo(step) {
257 | let item = this.allow_infinite_swipe
258 | ? step
259 | : Math.max(0, Math.min(step, this.elements_count - 1));
260 | this.changeItem(item);
261 | }
262 |
263 | prevItem() {
264 | let step = this.active_indicator - 1;
265 | this.goTo(step);
266 | }
267 |
268 | nextItem() {
269 | let step = this.active_indicator + 1;
270 | this.goTo(step);
271 | }
272 |
273 | /**
274 | * Retrieves the current properties of the carousel.
275 | * @returns {Object} Carousel properties object.
276 | * @property {number} elements_count - The total number of carousel items.
277 | * @property {number} active_item - The index of the currently active carousel item.
278 | * @property {HTMLElement} active_element - The currently active carousel item element.
279 | */
280 |
281 | getProps() {
282 | return {
283 | elements_count: this.elements_count,
284 | active_item: this.active_item,
285 | active_element: this.elements !== null ? this.elements[this.active_item] : null
286 | };
287 | }
288 | }
289 |
290 | export default SwipeSnap;
291 |
--------------------------------------------------------------------------------
/dist/index.d.ts:
--------------------------------------------------------------------------------
1 | export { default as Swipe } from "./components/Swipe.svelte";
2 | export { default as SwipeItem } from "./components/SwipeItem.svelte";
3 |
--------------------------------------------------------------------------------
/dist/index.js:
--------------------------------------------------------------------------------
1 | export { default as Swipe } from './components/Swipe.svelte';
2 | export { default as SwipeItem } from './components/SwipeItem.svelte';
3 |
--------------------------------------------------------------------------------
/jsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "allowJs": true,
4 | "checkJs": true,
5 | "esModuleInterop": true,
6 | "forceConsistentCasingInFileNames": true,
7 | "resolveJsonModule": true,
8 | "skipLibCheck": true,
9 | "sourceMap": true,
10 | "strict": true
11 | }
12 | // Path aliases are handled by https://kit.svelte.dev/docs/configuration#alias and https://kit.svelte.dev/docs/configuration#files
13 | //
14 | // If you want to overwrite includes/excludes, make sure to copy over the relevant includes/excludes
15 | // from the referenced tsconfig.json - TypeScript does not merge them in
16 | }
17 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "svelte-swipe",
3 | "version": "2.0.4",
4 | "description": "A Svelte component to swipe elements just like a snap",
5 | "homepage": "https://github.com/SharifClick/svelte-swipe",
6 | "repository": {
7 | "type": "git",
8 | "url": "https://github.com/SharifClick/svelte-swipe"
9 | },
10 | "publishConfig": {
11 | "registry": "https://registry.npmjs.org/"
12 | },
13 | "bugs": {
14 | "url": "https://github.com/SharifClick/svelte-swipe/issues"
15 | },
16 | "author": {
17 | "name": "Sharif Ahmed",
18 | "email": "me.sharifahmed@gmail.com",
19 | "url": "http://sharifahmed.me"
20 | },
21 | "license": "MIT",
22 | "scripts": {
23 | "dev": "vite dev --force",
24 | "build": "vite build && npm run package",
25 | "package": "svelte-kit sync && svelte-package",
26 | "check": "svelte-kit sync && svelte-check --tsconfig ./jsconfig.json",
27 | "check:watch": "svelte-kit sync && svelte-check --tsconfig ./jsconfig.json --watch",
28 | "lint": "prettier --plugin-search-dir . --check . && eslint .",
29 | "format": "prettier --plugin-search-dir . --write .",
30 | "deploy": "npm run build && touch build/.nojekyll && gh-pages -d build -t true"
31 | },
32 | "devDependencies": {
33 | "@sveltejs/adapter-auto": "2.1.0",
34 | "@sveltejs/adapter-static": "^2.0.3",
35 | "@sveltejs/kit": "^1.24.1",
36 | "@sveltejs/package": "^2.2.2",
37 | "gh-pages": "^6.0.0",
38 | "eslint": "^8.48.0",
39 | "eslint-config-prettier": "^9.0.0",
40 | "eslint-plugin-svelte": "^2.33.0",
41 | "prettier": "^3.0.3",
42 | "prettier-plugin-svelte": "^3.0.3",
43 | "svelte": "^4.2.0",
44 | "svelte-check": "^3.5.1",
45 | "svelte-preprocess": "^5.0.4",
46 | "vite": "^4.4.9"
47 | },
48 | "peerDependencies": {
49 | "svelte": "^3.54.0 || ^4.0.0"
50 | },
51 | "exports": {
52 | ".": {
53 | "types": "./dist/index.d.ts",
54 | "svelte": "./dist/index.js"
55 | }
56 | },
57 | "files": [
58 | "dist"
59 | ],
60 | "main": "./dist/index.js",
61 | "type": "module",
62 | "keywords": [
63 | "svelte",
64 | "svelte.js",
65 | "sveltejs",
66 | "svelte-swipe",
67 | "svelte-slider",
68 | "swipe",
69 | "slider",
70 | "carousel",
71 | "svelte-carousel"
72 | ]
73 | }
74 |
--------------------------------------------------------------------------------
/src/app.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
10 | %sveltekit.head%
11 |
12 |
13 |
14 | %sveltekit.body%
15 |
16 |
17 |
--------------------------------------------------------------------------------
/src/lib/components/Swipe.svelte:
--------------------------------------------------------------------------------
1 |
159 |
160 |
161 |
166 |
167 |
173 | {#if showIndicators}
174 |
175 | {#each indicators as x, i}
176 | {
182 | changeItem(i);
183 | }}
184 | on:keydown={(event) => {
185 | if (event.key === 'Enter') {
186 | changeItem(i);
187 | }
188 | }}
189 | />
190 | {/each}
191 |
192 | {/if}
193 |
194 |
195 |
247 |
--------------------------------------------------------------------------------
/src/lib/components/SwipeItem.svelte:
--------------------------------------------------------------------------------
1 |
29 |
30 |
39 |
40 |
56 |
--------------------------------------------------------------------------------
/src/lib/helpers/SwipeSnap.js:
--------------------------------------------------------------------------------
1 | // @ts-nocheck
2 | /**
3 | * A class for creating a swipeable carousel with snapping behavior.
4 | * @class
5 | */
6 |
7 | class SwipeSnap {
8 | /**
9 | * Creates an instance of SwipeSnap.
10 | * @constructor
11 | * @param {Object} [options={}] - Options for configuring the SwipeSnap carousel.
12 | * @param {HTMLElement} [options.element] - The HTML element that contains the carousel.
13 | * @param {boolean} [options.is_vertical=false] - Whether the carousel is vertical (true) or horizontal (false).
14 | * @param {number} [options.transition_duration=300] - The duration of the transition animation in milliseconds.
15 | * @param {boolean} [options.allow_infinite_swipe=false] - Whether to allow infinite looping of carousel items.
16 | */
17 |
18 | constructor(options = {}) {
19 | this.element = options.element;
20 | this.wrapper = this.element.querySelector('.swipeable-slot-wrapper');
21 | this.handler = this.element.querySelector('.swipe-handler');
22 | this.elements = this.wrapper.querySelectorAll('.swipeable-item');
23 | this.elements_count = this.elements.length;
24 |
25 | this.is_vertical = options.is_vertical;
26 | this.transition_duration = options.transition_duration;
27 | this.allow_infinite_swipe = options.allow_infinite_swipe;
28 |
29 | this.pos_axis = 0;
30 | this.page_axis = options.is_vertical ? 'pageY' : 'pageX';
31 | this.axis = 0;
32 | this.long_touch = false;
33 | this.last_axis_pos = 0;
34 | this.default_index = 0;
35 | this.active_indicator = 0;
36 | this.active_item = 0;
37 | this.touch_active = false;
38 |
39 | if (options.allow_infinite_swipe) {
40 | this.setInfiniteSwipe();
41 | }
42 |
43 | this.SWIPE = this.swipe.bind(this);
44 | this.SWIPE_START = this.swipeStart.bind(this);
45 | this.SWIPE_END = this.swipeEnd.bind(this);
46 |
47 | this.handler.addEventListener('touchstart', this.SWIPE_START);
48 | this.handler.addEventListener('mousedown', this.SWIPE_START);
49 | }
50 |
51 | /**
52 | * Prevents the default behavior of an event.
53 | * @param {Event} event - The event to prevent.
54 | */
55 |
56 | prevent(event) {
57 | if (event && event.cancelable) {
58 | event.preventDefault();
59 | }
60 | event && event.stopImmediatePropagation();
61 | event && event.stopPropagation();
62 | }
63 |
64 | update() {
65 | let { offsetWidth, offsetHeight } = this.wrapper;
66 | this.available_space = this.is_vertical ? offsetHeight : offsetWidth;
67 |
68 | this.setElementsPosition({
69 | init: true,
70 | elems: [...this.elements],
71 | available_space: this.available_space,
72 | has_infinite_loop: this.allow_infinite_swipe
73 | });
74 |
75 | this.available_distance = 0;
76 | this.available_measure = this.available_space * (this.elements_count - 1);
77 | if (this.default_index) {
78 | this.changeItem(this.default_index);
79 | }
80 | }
81 |
82 | setInfiniteSwipe() {
83 | this.wrapper.prepend(this.elements[this.elements_count - 1].cloneNode(true));
84 | this.wrapper.append(this.elements[0].cloneNode(true));
85 | this.elements = this.element.querySelectorAll('.swipeable-item');
86 | }
87 |
88 | setElementsPosition({
89 | elems = [],
90 | available_space = 0,
91 | pos_axis = 0,
92 | has_infinite_loop = false,
93 | distance = 0,
94 | moving = false,
95 | init = false,
96 | end = false,
97 | reset = false
98 | }) {
99 | elems.forEach((element, i) => {
100 | let idx = has_infinite_loop ? i - 1 : i;
101 | if (init) {
102 | element.style.transform = this.generateTranslateValue(available_space * idx);
103 | element.classList.remove('is-item-hidden');
104 | }
105 | if (moving) {
106 | element.style.cssText = this.generateTouchPosCss(available_space * idx - distance);
107 | }
108 | if (end) {
109 | element.style.cssText = this.generateTouchPosCss(available_space * idx - pos_axis, true);
110 | }
111 | if (reset) {
112 | element.style.cssText = this.generateTouchPosCss(available_space * idx - pos_axis);
113 | }
114 | });
115 | }
116 |
117 | generateTranslateValue(value) {
118 | return this.is_vertical ? `translate3d(0, ${value}px, 0)` : `translate3d(${value}px, 0, 0)`;
119 | }
120 |
121 | generateTouchPosCss(value, touch_end = false) {
122 | let transform_string = this.generateTranslateValue(value);
123 | let _css = `
124 | -webkit-transition-duration: ${touch_end ? this.transition_duration : '0'}ms;
125 | transition-duration: ${touch_end ? this.transition_duration : '0'}ms;
126 | -webkit-transform: ${transform_string};
127 | -ms-transform: ${transform_string};`;
128 | return _css;
129 | }
130 |
131 | swipeStart(event) {
132 | this.prevent(event);
133 | this.touch_active = true;
134 | this.long_touch = false;
135 | setTimeout(() => {
136 | this.long_touch = true;
137 | }, 250);
138 | this.axis = event.touches ? event.touches[0][this.page_axis] : event[this.page_axis];
139 | this.eventDelegate('add');
140 | }
141 |
142 | swipe(event) {
143 | if (this.touch_active) {
144 | this.prevent(event);
145 | let axis = event.touches ? event.touches[0][this.page_axis] : event[this.page_axis];
146 | let distance = this.axis - axis + this.pos_axis;
147 | if (!this.allow_infinite_swipe) {
148 | if (
149 | (this.pos_axis == 0 && this.axis < axis) ||
150 | (this.pos_axis == this.available_measure && this.axis > axis)
151 | ) {
152 | return;
153 | }
154 | }
155 | event.preventDefault();
156 |
157 | // if (distance <= availableMeasure && distance >= 0) {
158 | // }
159 | this.setElementsPosition({
160 | moving: true,
161 | elems: [...this.elements],
162 | available_space: this.available_space,
163 | has_infinite_loop: this.allow_infinite_swipe,
164 | distance
165 | });
166 | this.available_distance = distance;
167 | this.last_axis_pos = axis;
168 | }
169 | }
170 |
171 | swipeEnd(event) {
172 | this.prevent(event);
173 | let direction = this.axis < this.last_axis_pos;
174 | this.touch_active = false;
175 | let available_space = this.available_space;
176 | let accidental_touch =
177 | Math.round(this.available_space / 50) > Math.abs(this.axis - this.last_axis_pos);
178 | if (this.long_touch || accidental_touch) {
179 | this.available_distance =
180 | Math.round(this.available_distance / available_space) * available_space;
181 | } else {
182 | this.available_distance = direction
183 | ? Math.floor(this.available_distance / available_space) * available_space
184 | : Math.ceil(this.available_distance / available_space) * available_space;
185 | }
186 | this.axis = null;
187 | this.last_axis_pos = null;
188 | this.pos_axis = this.available_distance;
189 | this.active_indicator = this.available_distance / available_space;
190 | this.active_item = this.active_indicator;
191 | this.default_index = this.active_item;
192 |
193 | this.setElementsPosition({
194 | end: true,
195 | elems: [...this.elements],
196 | available_space: available_space,
197 | pos_axis: this.pos_axis,
198 | has_infinite_loop: this.allow_infinite_swipe
199 | });
200 |
201 | if (this.allow_infinite_swipe) {
202 | if (this.active_item === -1) {
203 | this.pos_axis = available_space * (this.elements_count - 1);
204 | }
205 | if (this.active_item === this.elements_count) {
206 | this.pos_axis = 0;
207 | }
208 | this.active_indicator = this.pos_axis / available_space;
209 | this.active_item = this.active_indicator;
210 | this.default_index = this.active_item;
211 |
212 | setTimeout(() => {
213 | this.setElementsPosition({
214 | reset: true,
215 | elems: [...this.elements],
216 | available_space: available_space,
217 | pos_axis: this.pos_axis,
218 | has_infinite_loop: this.allow_infinite_swipe
219 | });
220 | }, this.transition_duration);
221 | }
222 | let swipe_direction = direction ? 'right' : 'left';
223 | this.eventDelegate('remove');
224 |
225 | const customEvent = new CustomEvent('swipe_end', {
226 | detail: {
227 | active_item: this.active_item,
228 | swipe_direction,
229 | active_element: this.elements[this.active_item]
230 | }
231 | });
232 |
233 | this.element.dispatchEvent(customEvent);
234 | }
235 |
236 | eventDelegate(type) {
237 | let delegationTypes = {
238 | add: 'addEventListener',
239 | remove: 'removeEventListener'
240 | };
241 | if (typeof window !== 'undefined') {
242 | window[delegationTypes[type]]('mousemove', this.SWIPE);
243 | window[delegationTypes[type]]('mouseup', this.SWIPE_END);
244 | window[delegationTypes[type]]('touchmove', this.SWIPE, { passive: false });
245 | window[delegationTypes[type]]('touchend', this.SWIPE_END, { passive: false });
246 | }
247 | }
248 |
249 | changeItem(item) {
250 | let max = this.available_space;
251 | this.available_distance = max * item;
252 | this.active_indicator = item;
253 | this.swipeEnd();
254 | }
255 |
256 | goTo(step) {
257 | let item = this.allow_infinite_swipe
258 | ? step
259 | : Math.max(0, Math.min(step, this.elements_count - 1));
260 | this.changeItem(item);
261 | }
262 |
263 | prevItem() {
264 | let step = this.active_indicator - 1;
265 | this.goTo(step);
266 | }
267 |
268 | nextItem() {
269 | let step = this.active_indicator + 1;
270 | this.goTo(step);
271 | }
272 |
273 | /**
274 | * Retrieves the current properties of the carousel.
275 | * @returns {Object} Carousel properties object.
276 | * @property {number} elements_count - The total number of carousel items.
277 | * @property {number} active_item - The index of the currently active carousel item.
278 | * @property {HTMLElement} active_element - The currently active carousel item element.
279 | */
280 |
281 | getProps() {
282 | return {
283 | elements_count: this.elements_count,
284 | active_item: this.active_item,
285 | active_element: this.elements !== null ? this.elements[this.active_item] : null
286 | };
287 | }
288 | }
289 |
290 | export default SwipeSnap;
291 |
--------------------------------------------------------------------------------
/src/lib/index.js:
--------------------------------------------------------------------------------
1 | export { default as Swipe } from './components/Swipe.svelte';
2 | export { default as SwipeItem } from './components/SwipeItem.svelte';
3 |
--------------------------------------------------------------------------------
/src/routes/+layout.js:
--------------------------------------------------------------------------------
1 | export const prerender = true;
2 | //
3 |
--------------------------------------------------------------------------------
/src/routes/+page.svelte:
--------------------------------------------------------------------------------
1 |
118 |
119 |
124 |
154 |
155 |
156 |
157 |
158 |
Svelte Swipe
159 |
Swipable items wrapper component for Svelte
160 |
161 |
162 |
163 |
164 |
165 |
181 |
182 |
183 |
184 |
185 | {#each images as image}
186 |
187 |
188 |
189 | {/each}
190 |
191 |
192 |
193 |
194 |
195 |
196 |
202 |
203 | {#if customThumbnail}
204 |
205 |
206 | {#each images as image, i}
207 |
![]()
changeSlide(i)}
210 | style="height:30px; width:30px; cursor:pointer"
211 | src={base + '/' + image}
212 | alt=""
213 | />
214 | {/each}
215 |
216 |
217 | {/if}
218 |
219 |
220 |
223 |
226 |
227 |
228 |
229 |
230 |
231 |
232 |
233 |
234 |
235 |
Allow pointer events inside Swipe Item
236 |
237 |
238 |
239 |
240 |
241 |
242 |
243 |
244 |
245 |
246 |
247 |
248 |
249 |
250 |
251 |
252 |
253 |
254 |
255 |
256 |
257 |
258 |
259 |
260 |
261 |
262 |
263 |
264 |
265 |
266 |
267 |
268 |
269 |
Dynamic Height
270 |
271 |
Dynamic height with children
272 |
273 |
Item Height: {swipe_holder_height}
274 |
275 |
276 |
277 |
278 | {#each dy_images as image, i}
279 |
284 |
285 |

286 |
287 |
288 | {/each}
289 |
290 |
291 |
292 |
293 |
294 |
295 |
296 |
Infinite swipe
297 |
313 |
314 |
315 |
316 |
324 | {#each images as image}
325 |
326 |
327 |
328 | {/each}
329 |
330 |
331 |
332 |
333 |
334 |
335 |
341 |
342 | {#if customThumbnail}
343 |
344 |
345 | {#each images as image, i}
346 |
![]()
changeSlideInf(i)}
349 | style="height:30px; width:30px; cursor:pointer"
350 | src={base + '/' + image}
351 | alt=""
352 | />
353 | {/each}
354 |
355 |
356 | {/if}
357 |
358 |
359 |
362 |
365 |
366 |
367 |
368 |
369 |
370 |
371 |
372 |
373 |
Unsplash Images
374 |
375 |
376 |
377 |
Item Height: {swipe_holder_height_unplash}
378 |
379 |
380 |
381 |
382 | {#each up_images as image, i}
383 |
384 |
385 |

386 |
387 |
388 | {/each}
389 |
390 |
391 |
392 |
393 |
394 |
395 |
396 |
Vertical Swipe 🔥
397 |
398 |
399 | {#each images as image}
400 |
401 |
402 |
403 | {/each}
404 |
405 |
406 |
407 |
408 |
409 |
410 |
475 |
--------------------------------------------------------------------------------
/static/favicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SharifClick/svelte-swipe/7a771f0a0724452116eca1600817cbe611d15a62/static/favicon.png
--------------------------------------------------------------------------------
/static/images/1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SharifClick/svelte-swipe/7a771f0a0724452116eca1600817cbe611d15a62/static/images/1.jpg
--------------------------------------------------------------------------------
/static/images/2.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SharifClick/svelte-swipe/7a771f0a0724452116eca1600817cbe611d15a62/static/images/2.jpg
--------------------------------------------------------------------------------
/static/images/3.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SharifClick/svelte-swipe/7a771f0a0724452116eca1600817cbe611d15a62/static/images/3.jpg
--------------------------------------------------------------------------------
/static/images/4.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SharifClick/svelte-swipe/7a771f0a0724452116eca1600817cbe611d15a62/static/images/4.jpg
--------------------------------------------------------------------------------
/static/images/5.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SharifClick/svelte-swipe/7a771f0a0724452116eca1600817cbe611d15a62/static/images/5.jpg
--------------------------------------------------------------------------------
/static/images/dy-1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SharifClick/svelte-swipe/7a771f0a0724452116eca1600817cbe611d15a62/static/images/dy-1.jpg
--------------------------------------------------------------------------------
/static/images/dy-2.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SharifClick/svelte-swipe/7a771f0a0724452116eca1600817cbe611d15a62/static/images/dy-2.jpg
--------------------------------------------------------------------------------
/static/images/dy-3.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SharifClick/svelte-swipe/7a771f0a0724452116eca1600817cbe611d15a62/static/images/dy-3.jpg
--------------------------------------------------------------------------------
/static/images/dy-4.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SharifClick/svelte-swipe/7a771f0a0724452116eca1600817cbe611d15a62/static/images/dy-4.jpg
--------------------------------------------------------------------------------
/static/images/dy-5.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SharifClick/svelte-swipe/7a771f0a0724452116eca1600817cbe611d15a62/static/images/dy-5.jpg
--------------------------------------------------------------------------------
/static/images/url-code.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SharifClick/svelte-swipe/7a771f0a0724452116eca1600817cbe611d15a62/static/images/url-code.png
--------------------------------------------------------------------------------
/svelte-swipe.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SharifClick/svelte-swipe/7a771f0a0724452116eca1600817cbe611d15a62/svelte-swipe.png
--------------------------------------------------------------------------------
/svelte.config.js:
--------------------------------------------------------------------------------
1 | import adapter from '@sveltejs/adapter-static';
2 |
3 | /** @type {import('@sveltejs/kit').Config} */
4 | const config = {
5 | kit: {
6 | paths: {
7 | base: '/svelte-swipe'
8 | },
9 | adapter: adapter()
10 | }
11 | };
12 |
13 | export default config;
14 |
--------------------------------------------------------------------------------
/vite.config.js:
--------------------------------------------------------------------------------
1 | // @ts-nocheck
2 | import { sveltekit } from "@sveltejs/kit/vite";
3 |
4 | /** @type {import('vite').UserConfig} */
5 | const config = {
6 | plugins: [sveltekit()],
7 | server: {
8 | port: 5000,
9 | },
10 |
11 | preview: {
12 | port: 5000,
13 | },
14 | };
15 |
16 | export default config;
17 |
18 |
--------------------------------------------------------------------------------