pure-swipe-slider 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +22 -0
- package/README.md +341 -0
- package/package.json +37 -0
- package/register.js +4 -0
- package/swipe-slider.css +24 -0
- package/swipe-slider.js +168 -0
- package/swipe3.js +476 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 kellas
|
|
4
|
+
Based on Swipe.js by Brad Birdsall (Copyright (c) 2013, MIT License)
|
|
5
|
+
|
|
6
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
7
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
8
|
+
in the Software without restriction, including without limitation the rights
|
|
9
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
10
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
11
|
+
furnished to do so, subject to the following conditions:
|
|
12
|
+
|
|
13
|
+
The above copyright notice and this permission notice shall be included in all
|
|
14
|
+
copies or substantial portions of the Software.
|
|
15
|
+
|
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
17
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
18
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
19
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
20
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
21
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
22
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,341 @@
|
|
|
1
|
+
# Pure Swipe Slider Web Component
|
|
2
|
+
|
|
3
|
+
[](https://github.com/alexstep/swipe-slider)
|
|
4
|
+
[](https://github.com/alexstep/swipe-slider)
|
|
5
|
+
[](./LICENSE)
|
|
6
|
+
[](https://www.npmjs.com/package/pure-swipe-slider)
|
|
7
|
+
|
|
8
|
+
[**Live Demo**](https://alexstep.github.io/swipe-slider/demo.html)
|
|
9
|
+
|
|
10
|
+
A tiny, zero-dependency swipe slider as a Web Component (< 4 KB gzipped).
|
|
11
|
+
Framework-agnostic, mobile-first, event-driven.
|
|
12
|
+
|
|
13
|
+
Built on top of Swipe.js with modernized internals, Pointer Events, GPU-accelerated transforms, and a clean Custom Events interface.
|
|
14
|
+
|
|
15
|
+
## Two ways to use
|
|
16
|
+
|
|
17
|
+
- `<swipe-slider>` — Web Component for 99% of cases
|
|
18
|
+
- `swipe3.js` — low-level engine for advanced / legacy layouts
|
|
19
|
+
|
|
20
|
+
## Features
|
|
21
|
+
|
|
22
|
+
- **Full API access**: call `next()`, `prev()`, `slide()` directly on the element
|
|
23
|
+
- **Unified input handling**: touch, mouse, wheel, pen via Pointer Events
|
|
24
|
+
- **Mobile-optimized**: smooth 60fps animations with iOS overscroll handling
|
|
25
|
+
- **Framework-ready**: standard HTML tag with Custom Events
|
|
26
|
+
- **Flexible content**: images, dates, complex layouts, and dynamic slides
|
|
27
|
+
- **Performance-first**: GPU acceleration and modern optimizations
|
|
28
|
+
- **No build step**: works directly in modern browsers
|
|
29
|
+
|
|
30
|
+
## Installation
|
|
31
|
+
|
|
32
|
+
### From NPM
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
npm install pure-swipe-slider
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
### Quick Start
|
|
39
|
+
|
|
40
|
+
Simply import the component with default tag registration:
|
|
41
|
+
|
|
42
|
+
```javascript
|
|
43
|
+
import 'pure-swipe-slider/register.js';
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
### Custom Tag Registration
|
|
47
|
+
|
|
48
|
+
Import the class separately to register with your own tag name:
|
|
49
|
+
|
|
50
|
+
```javascript
|
|
51
|
+
import SwipeSlider from 'pure-swipe-slider';
|
|
52
|
+
|
|
53
|
+
// Register with custom tag name
|
|
54
|
+
customElements.define('my-slider', SwipeSlider);
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
Then use it in HTML:
|
|
58
|
+
|
|
59
|
+
```html
|
|
60
|
+
<my-slider>
|
|
61
|
+
<div>Slide 1</div>
|
|
62
|
+
<div>Slide 2</div>
|
|
63
|
+
</my-slider>
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
## Class Usage
|
|
67
|
+
|
|
68
|
+
The `SwipeSlider` class can be extended for custom behavior:
|
|
69
|
+
|
|
70
|
+
```javascript
|
|
71
|
+
import SwipeSlider from './swipe-slider.js';
|
|
72
|
+
|
|
73
|
+
class MyCustomSlider extends SwipeSlider {
|
|
74
|
+
connectedCallback() {
|
|
75
|
+
super.connectedCallback();
|
|
76
|
+
// Custom initialization
|
|
77
|
+
this.style.border = '2px solid blue';
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// Register custom class
|
|
82
|
+
customElements.define('my-custom-slider', MyCustomSlider);
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
## Usage
|
|
86
|
+
|
|
87
|
+
### Basic Example
|
|
88
|
+
|
|
89
|
+
```html
|
|
90
|
+
<swipe-slider draggable mousewheel>
|
|
91
|
+
<div>Slide 1</div>
|
|
92
|
+
<div>Slide 2</div>
|
|
93
|
+
<div>Slide 3</div>
|
|
94
|
+
</swipe-slider>
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
With default tag registration:
|
|
98
|
+
|
|
99
|
+
```javascript
|
|
100
|
+
import './register.js';
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
### With Event Listeners
|
|
104
|
+
|
|
105
|
+
```javascript
|
|
106
|
+
const slider = document.querySelector('swipe-slider');
|
|
107
|
+
|
|
108
|
+
slider.addEventListener('swipe:change', (e) => {
|
|
109
|
+
const { index, element, direction } = e.detail;
|
|
110
|
+
console.log(`Swiped to index ${index} in direction ${direction}`);
|
|
111
|
+
});
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
## API Reference
|
|
115
|
+
|
|
116
|
+
### Attributes
|
|
117
|
+
|
|
118
|
+
Boolean attributes follow HTML semantics: presence = `true`, absence = `false`.
|
|
119
|
+
|
|
120
|
+
| Attribute | Type | Default | Description |
|
|
121
|
+
|-----------|------|---------|-------------|
|
|
122
|
+
| `start-slide` | Number | `0` | Initial slide index. |
|
|
123
|
+
| `speed` | Number | `400` | Transition speed in milliseconds. |
|
|
124
|
+
| `draggable` | Boolean | `false` | Enable mouse dragging. |
|
|
125
|
+
| `mousewheel` | Boolean | `true` | Enable mouse wheel navigation. Use `no-mousewheel` to disable. |
|
|
126
|
+
| `disable-scroll` | Boolean | `false` | Disable vertical scrolling while swiping. |
|
|
127
|
+
| `stop-propagation` | Boolean | `false` | Stop event propagation. |
|
|
128
|
+
| `passive-events` | Boolean | `false` | Use passive event listeners (may improve performance but breaks preventDefault). |
|
|
129
|
+
| `loop` | Boolean | `false` | Enable infinite circular loop (moves slides dynamically). |
|
|
130
|
+
| `auto-height` | Boolean \| Number | `false` | Enable automatic height adjustment. Optional value sets min-height. |
|
|
131
|
+
|
|
132
|
+
### Events
|
|
133
|
+
|
|
134
|
+
All events are dispatched as CustomEvent with `{ bubbles: true, composed: true }`.
|
|
135
|
+
|
|
136
|
+
| Event | Detail | Description |
|
|
137
|
+
|-------|--------|-------------|
|
|
138
|
+
| `swipe:change` | `{ index, element, direction }` | Fired when the active slide changes. |
|
|
139
|
+
| `swipe:transition-end` | `{ index, element }` | Fired when the transition finishes. |
|
|
140
|
+
| `swipe:drag-start` | `{ index, element }` | Fired when dragging starts. |
|
|
141
|
+
| `swipe:drag-end` | `{ index, element }` | Fired when dragging ends. |
|
|
142
|
+
| `swipe:move` | `none` | Fired during movement (high-frequency event, use sparingly). |
|
|
143
|
+
|
|
144
|
+
### Methods
|
|
145
|
+
|
|
146
|
+
All `swipe.js` methods are proxied:
|
|
147
|
+
|
|
148
|
+
- `slide(to, speed)`
|
|
149
|
+
- `prev()`
|
|
150
|
+
- `next()`
|
|
151
|
+
- `getPos()`
|
|
152
|
+
- `getNumSlides()`
|
|
153
|
+
- `kill()`
|
|
154
|
+
- `setup(options)`
|
|
155
|
+
- `appendSlide(element)`
|
|
156
|
+
- `prependSlide(element)`
|
|
157
|
+
- `adjustHeight(element)`
|
|
158
|
+
|
|
159
|
+
## Direct Usage (swipe3.js)
|
|
160
|
+
|
|
161
|
+
For advanced use cases or when you don't need the web component wrapper, you can use `swipe3.js` directly:
|
|
162
|
+
|
|
163
|
+
### Import
|
|
164
|
+
|
|
165
|
+
```javascript
|
|
166
|
+
import Swipe from './swipe3.js';
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
### HTML Structure
|
|
170
|
+
|
|
171
|
+
```html
|
|
172
|
+
<div id="slider-container">
|
|
173
|
+
<div class="slides">
|
|
174
|
+
<div>Slide 1</div>
|
|
175
|
+
<div>Slide 2</div>
|
|
176
|
+
<div>Slide 3</div>
|
|
177
|
+
</div>
|
|
178
|
+
</div>
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
### JavaScript Usage
|
|
182
|
+
|
|
183
|
+
```javascript
|
|
184
|
+
// Initialize
|
|
185
|
+
const container = document.getElementById('slider-container');
|
|
186
|
+
const swipeInstance = Swipe(container, {
|
|
187
|
+
speed: 400, // Transition speed in ms
|
|
188
|
+
startSlide: 0, // Initial slide index
|
|
189
|
+
draggable: true, // Enable mouse dragging
|
|
190
|
+
mousewheel: true, // Enable mouse wheel navigation
|
|
191
|
+
disableScroll: false, // Prevent vertical scrolling during swipe
|
|
192
|
+
stopPropagation: false, // Stop event propagation
|
|
193
|
+
passive: false, // Use passive event listeners
|
|
194
|
+
|
|
195
|
+
// Callbacks
|
|
196
|
+
callback(index, element, direction) {
|
|
197
|
+
console.log(`Swiped to slide ${index} in direction ${direction}`);
|
|
198
|
+
},
|
|
199
|
+
|
|
200
|
+
transitionEnd(index, element) {
|
|
201
|
+
console.log(`Transition ended at slide ${index}`);
|
|
202
|
+
},
|
|
203
|
+
|
|
204
|
+
dragStart(index, element) {
|
|
205
|
+
console.log(`Drag started at slide ${index}`);
|
|
206
|
+
},
|
|
207
|
+
|
|
208
|
+
dragEnd(index, element) {
|
|
209
|
+
console.log(`Drag ended at slide ${index}`);
|
|
210
|
+
},
|
|
211
|
+
|
|
212
|
+
runMove() {
|
|
213
|
+
console.log('Moving during drag');
|
|
214
|
+
}
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
// Use API methods
|
|
218
|
+
swipeInstance.next(); // Go to next slide
|
|
219
|
+
swipeInstance.prev(); // Go to previous slide
|
|
220
|
+
swipeInstance.slide(2); // Go to slide at index 2
|
|
221
|
+
swipeInstance.getPos(); // Get current slide index
|
|
222
|
+
swipeInstance.getNumSlides(); // Get total number of slides
|
|
223
|
+
|
|
224
|
+
// Add slides dynamically
|
|
225
|
+
const newSlide = document.createElement('div');
|
|
226
|
+
newSlide.textContent = 'New Slide';
|
|
227
|
+
swipeInstance.appendSlide(newSlide);
|
|
228
|
+
|
|
229
|
+
// Cleanup when done
|
|
230
|
+
swipeInstance.kill();
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
### Required CSS
|
|
234
|
+
|
|
235
|
+
When using directly, you'll need to add basic CSS:
|
|
236
|
+
|
|
237
|
+
```css
|
|
238
|
+
#slider-container {
|
|
239
|
+
position: relative;
|
|
240
|
+
overflow: hidden;
|
|
241
|
+
width: 100%;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
.slides {
|
|
245
|
+
position: relative;
|
|
246
|
+
white-space: nowrap;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
.slides > * {
|
|
250
|
+
display: inline-block;
|
|
251
|
+
vertical-align: top;
|
|
252
|
+
white-space: normal;
|
|
253
|
+
}
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
### Options Reference
|
|
257
|
+
|
|
258
|
+
| Option | Type | Default | Description |
|
|
259
|
+
|--------|------|---------|-------------|
|
|
260
|
+
| `speed` | Number | `400` | Transition speed in milliseconds |
|
|
261
|
+
| `startSlide` | Number | `0` | Initial slide index |
|
|
262
|
+
| `draggable` | Boolean | `false` | Enable mouse dragging |
|
|
263
|
+
| `mousewheel` | Boolean | `true` | Enable mouse wheel navigation |
|
|
264
|
+
| `disableScroll` | Boolean | `false` | Disable vertical scrolling while swiping |
|
|
265
|
+
| `stopPropagation` | Boolean | `false` | Stop event propagation |
|
|
266
|
+
| `ignore` | String | `null` | CSS selector for elements to ignore (e.g., "button, a") |
|
|
267
|
+
| `passive` | Boolean | `false` | Use passive event listeners (may improve performance but breaks preventDefault) |
|
|
268
|
+
|
|
269
|
+
### Callback Functions
|
|
270
|
+
|
|
271
|
+
All callbacks receive `(index, element)` parameters where:
|
|
272
|
+
- `index`: Current slide index (0-based)
|
|
273
|
+
- `element`: Current slide DOM element
|
|
274
|
+
|
|
275
|
+
| Callback | Parameters | Description |
|
|
276
|
+
|----------|------------|-------------|
|
|
277
|
+
| `callback` | `(index, element, direction)` | Fired when slide changes (direction: -1 for prev, 1 for next) |
|
|
278
|
+
| `transitionEnd` | `(index, element)` | Fired when transition animation completes |
|
|
279
|
+
| `dragStart` | `(index, element)` | Fired when dragging starts |
|
|
280
|
+
| `dragEnd` | `(index, element)` | Fired when dragging ends |
|
|
281
|
+
| `runMove` | `none` | Fired during dragging movement |
|
|
282
|
+
|
|
283
|
+
### API Methods
|
|
284
|
+
|
|
285
|
+
| Method | Parameters | Description |
|
|
286
|
+
|--------|------------|-------------|
|
|
287
|
+
| `slide(to, speed?)` | `to`: slide index, `speed?`: optional transition speed | Navigate to specific slide |
|
|
288
|
+
| `prev()` | none | Go to previous slide |
|
|
289
|
+
| `next()` | none | Go to next slide |
|
|
290
|
+
| `getPos()` | none | Get current slide index |
|
|
291
|
+
| `getNumSlides()` | none | Get total number of slides |
|
|
292
|
+
| `appendSlide(element)` | `element`: DOM element | Add slide at the end |
|
|
293
|
+
| `prependSlide(element)` | `element`: DOM element | Add slide at the beginning |
|
|
294
|
+
| `setup(options?)` | `options?`: new options object | Reinitialize with new options |
|
|
295
|
+
| `kill()` | none | Destroy instance and cleanup events/styles |
|
|
296
|
+
|
|
297
|
+
## CSS Customization
|
|
298
|
+
|
|
299
|
+
The component provides a minimal CSS structure with a built-in height transition (0.2s linear) for `auto-height` mode. You can customize the look using standard CSS:
|
|
300
|
+
|
|
301
|
+
```css
|
|
302
|
+
swipe-slider {
|
|
303
|
+
/* container styles */
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
.swipe-slider-wrapper > * {
|
|
307
|
+
/* slide styles */
|
|
308
|
+
}
|
|
309
|
+
```
|
|
310
|
+
|
|
311
|
+
## When this is not a good fit
|
|
312
|
+
|
|
313
|
+
- If you need virtualized slides (1000+ items)
|
|
314
|
+
- If you rely heavily on React/Vue-specific lifecycles
|
|
315
|
+
- If you need autoplay / pagination / thumbnails out of the box
|
|
316
|
+
- If you need complex state management or data binding
|
|
317
|
+
|
|
318
|
+
## License
|
|
319
|
+
|
|
320
|
+
MIT
|
|
321
|
+
|
|
322
|
+
## Performance Optimizations (swipe3.js)
|
|
323
|
+
|
|
324
|
+
The internal `swipe3.js` engine includes these optimizations for smooth mobile performance:
|
|
325
|
+
|
|
326
|
+
| Optimization | Description |
|
|
327
|
+
|-------------|-------------|
|
|
328
|
+
| GPU-accelerated transforms | `translate3d` for hardware acceleration instead of `translateX` |
|
|
329
|
+
| will-change hints | Browser hints to create compositor layers for smoother animations |
|
|
330
|
+
| Pointer Events API | Unified touch/mouse/pen input handling with fallbacks |
|
|
331
|
+
| Passive event listeners | Non-blocking touch listeners where possible (via `passive-events` attr) |
|
|
332
|
+
| Reduced layout thrashing | Batched DOM reads/writes to minimize reflows |
|
|
333
|
+
| Modern ES6+ syntax | Latest JavaScript features for better performance and maintainability |
|
|
334
|
+
| Size (Gzipped) | < 4 KB for the entire component (JS + CSS) |
|
|
335
|
+
|
|
336
|
+
## Size
|
|
337
|
+
|
|
338
|
+
| Metric | JavaScript | CSS | Total |
|
|
339
|
+
|--------|------------|-----|-------|
|
|
340
|
+
| Minified | 9.8 KB | 0.3 KB | **10.1 KB** |
|
|
341
|
+
| Minified + Gzip | 3.2 KB | 0.2 KB | **3.4 KB** |
|
package/package.json
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "pure-swipe-slider",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "A tiny, zero-dependency swipe slider web component (< 4 KB gzipped).",
|
|
5
|
+
"main": "swipe-slider.js",
|
|
6
|
+
"module": "swipe-slider.js",
|
|
7
|
+
"files": [
|
|
8
|
+
"swipe-slider.js",
|
|
9
|
+
"swipe3.js",
|
|
10
|
+
"swipe-slider.css",
|
|
11
|
+
"register.js",
|
|
12
|
+
"LICENSE",
|
|
13
|
+
"README.md"
|
|
14
|
+
],
|
|
15
|
+
"scripts": {
|
|
16
|
+
"test": "echo \"Error: no test specified\" && exit 1"
|
|
17
|
+
},
|
|
18
|
+
"repository": {
|
|
19
|
+
"type": "git",
|
|
20
|
+
"url": "git+https://github.com/alexstep/swipe-slider.git"
|
|
21
|
+
},
|
|
22
|
+
"keywords": [
|
|
23
|
+
"web-component",
|
|
24
|
+
"swipe",
|
|
25
|
+
"slider",
|
|
26
|
+
"carousel",
|
|
27
|
+
"touch",
|
|
28
|
+
"mobile-first",
|
|
29
|
+
"pointer-events"
|
|
30
|
+
],
|
|
31
|
+
"author": "alexstep",
|
|
32
|
+
"license": "MIT",
|
|
33
|
+
"bugs": {
|
|
34
|
+
"url": "https://github.com/alexstep/swipe-slider/issues"
|
|
35
|
+
},
|
|
36
|
+
"homepage": "https://github.com/alexstep/swipe-slider#readme"
|
|
37
|
+
}
|
package/register.js
ADDED
package/swipe-slider.css
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
swipe-slider {
|
|
2
|
+
display: block;
|
|
3
|
+
width: 100%;
|
|
4
|
+
min-width: 0;
|
|
5
|
+
overflow: hidden;
|
|
6
|
+
position: relative;
|
|
7
|
+
transition: height 0.2s linear;
|
|
8
|
+
will-change: height;
|
|
9
|
+
|
|
10
|
+
.swipe-slider-wrapper {
|
|
11
|
+
overflow: hidden;
|
|
12
|
+
position: relative;
|
|
13
|
+
height: 100%;
|
|
14
|
+
touch-action: pan-y;
|
|
15
|
+
|
|
16
|
+
& > * {
|
|
17
|
+
float: left;
|
|
18
|
+
width: 100%;
|
|
19
|
+
position: relative;
|
|
20
|
+
box-sizing: border-box;
|
|
21
|
+
will-change: transform;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
}
|
package/swipe-slider.js
ADDED
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
import Swipe from './swipe3.js'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Universal Swipe Slider Web Component
|
|
5
|
+
* Wrapper around swipe.js
|
|
6
|
+
*/
|
|
7
|
+
export default class SwipeSlider extends HTMLElement {
|
|
8
|
+
constructor() {
|
|
9
|
+
super()
|
|
10
|
+
this.swipe = null
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
connectedCallback() {
|
|
14
|
+
this.init()
|
|
15
|
+
}
|
|
16
|
+
disconnectedCallback() {
|
|
17
|
+
this.kill()
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
_emit(name, detail = {}) {
|
|
21
|
+
this.dispatchEvent(new CustomEvent(`swipe:${name}`, { detail, bubbles: true }))
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
_idx(el, i) {
|
|
25
|
+
const li = el?.dataset?.logicalIndex
|
|
26
|
+
return li !== undefined ? +li : i
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
init(options = {}) {
|
|
30
|
+
if (this.swipe) this.kill()
|
|
31
|
+
|
|
32
|
+
const get = a => this.getAttribute(a)
|
|
33
|
+
const has = a => this.hasAttribute(a)
|
|
34
|
+
const autoH = get('auto-height')
|
|
35
|
+
const isLoop = has('loop')
|
|
36
|
+
|
|
37
|
+
const sOpts = {
|
|
38
|
+
startSlide: +(get('start-slide') || 0),
|
|
39
|
+
speed: +(get('speed') || 400),
|
|
40
|
+
draggable: has('draggable'),
|
|
41
|
+
mousewheel: !has('no-mousewheel'),
|
|
42
|
+
disableScroll: has('disable-scroll'),
|
|
43
|
+
stopPropagation: has('stop-propagation'),
|
|
44
|
+
passive: has('passive-events'),
|
|
45
|
+
...options,
|
|
46
|
+
callback: (i, el, dir) => {
|
|
47
|
+
if (autoH !== null) this.adjustHeight(el)
|
|
48
|
+
|
|
49
|
+
if (isLoop && this.swipe) {
|
|
50
|
+
const total = this.swipe.getNumSlides()
|
|
51
|
+
const method = i === total - 1 ? 'appendSlide' : i === 0 ? 'prependSlide' : null
|
|
52
|
+
if (method) {
|
|
53
|
+
const wrapper = this.querySelector('.swipe-slider-wrapper')
|
|
54
|
+
const slide = method[0] === 'a' ? wrapper?.firstElementChild : wrapper?.lastElementChild
|
|
55
|
+
if (slide) this[method](slide)
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
this._emit('change', { index: this._idx(el, i), element: el, direction: dir })
|
|
60
|
+
options.callback?.(i, el, dir)
|
|
61
|
+
},
|
|
62
|
+
transitionEnd: (i, el) => {
|
|
63
|
+
this._emit('transition-end', { index: i, element: el })
|
|
64
|
+
options.transitionEnd?.(i, el)
|
|
65
|
+
},
|
|
66
|
+
dragStart: (i, el) => {
|
|
67
|
+
this._emit('drag-start', { index: i, element: el })
|
|
68
|
+
options.dragStart?.(i, el)
|
|
69
|
+
},
|
|
70
|
+
dragEnd: (i, el) => {
|
|
71
|
+
this._emit('drag-end', { index: i, element: el })
|
|
72
|
+
options.dragEnd?.(i, el)
|
|
73
|
+
},
|
|
74
|
+
runMove: () => {
|
|
75
|
+
this._emit('move')
|
|
76
|
+
options.runMove?.()
|
|
77
|
+
},
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
let wrapper = this.querySelector('.swipe-slider-wrapper')
|
|
81
|
+
if (!wrapper) {
|
|
82
|
+
wrapper = document.createElement('div')
|
|
83
|
+
wrapper.className = 'swipe-slider-wrapper'
|
|
84
|
+
let i = 0
|
|
85
|
+
while (this.firstChild) {
|
|
86
|
+
const child = this.firstChild
|
|
87
|
+
if (child instanceof HTMLElement) child.dataset.logicalIndex = String(i++)
|
|
88
|
+
wrapper.appendChild(child)
|
|
89
|
+
}
|
|
90
|
+
this.appendChild(wrapper)
|
|
91
|
+
} else {
|
|
92
|
+
;[...wrapper.children].forEach((c, i) => {
|
|
93
|
+
if (c instanceof HTMLElement && !c.dataset.logicalIndex) c.dataset.logicalIndex = String(i)
|
|
94
|
+
})
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
if (isLoop && wrapper.children.length > 1) {
|
|
98
|
+
const first = wrapper.firstElementChild
|
|
99
|
+
const last = wrapper.lastElementChild
|
|
100
|
+
if (sOpts.startSlide === 0 && last) {
|
|
101
|
+
wrapper.prepend(last)
|
|
102
|
+
sOpts.startSlide = 1
|
|
103
|
+
} else if (sOpts.startSlide === wrapper.children.length - 1 && first) {
|
|
104
|
+
wrapper.append(first)
|
|
105
|
+
sOpts.startSlide = wrapper.children.length - 2
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
requestAnimationFrame(() => {
|
|
110
|
+
// @ts-ignore
|
|
111
|
+
this.swipe = new Swipe(this, sOpts)
|
|
112
|
+
if (autoH !== null) {
|
|
113
|
+
const slides = this.querySelectorAll('.swipe-slider-wrapper > *')
|
|
114
|
+
const active = this.swipe?.getPos()
|
|
115
|
+
if (active !== undefined && slides[active]) this.adjustHeight(slides[active])
|
|
116
|
+
}
|
|
117
|
+
this.dataset.ready = 'true'
|
|
118
|
+
})
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
adjustHeight(el) {
|
|
122
|
+
if (!el) return
|
|
123
|
+
const min = +(this.getAttribute('auto-height') || 0)
|
|
124
|
+
this.style.height = Math.max(el.offsetHeight, min) + 'px'
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
slide(to, s) {
|
|
128
|
+
return this.swipe?.slide(to, s)
|
|
129
|
+
}
|
|
130
|
+
prev() {
|
|
131
|
+
if (this.hasAttribute('loop') && this.swipe?.getPos() === 0) {
|
|
132
|
+
const s = this.querySelector('.swipe-slider-wrapper')?.lastElementChild
|
|
133
|
+
if (s instanceof HTMLElement) this.prependSlide(s)
|
|
134
|
+
}
|
|
135
|
+
return this.swipe?.prev()
|
|
136
|
+
}
|
|
137
|
+
next() {
|
|
138
|
+
if (this.hasAttribute('loop') && this.swipe?.getPos() === this.swipe?.getNumSlides() - 1) {
|
|
139
|
+
const s = this.querySelector('.swipe-slider-wrapper')?.firstElementChild
|
|
140
|
+
if (s instanceof HTMLElement) this.appendSlide(s)
|
|
141
|
+
}
|
|
142
|
+
return this.swipe?.next()
|
|
143
|
+
}
|
|
144
|
+
getPos() {
|
|
145
|
+
const i = this.swipe ? this.swipe.getPos() : 0
|
|
146
|
+
const slides = this.querySelectorAll('.swipe-slider-wrapper > *')
|
|
147
|
+
return this._idx(slides[i], i)
|
|
148
|
+
}
|
|
149
|
+
getNumSlides() {
|
|
150
|
+
return this.swipe?.getNumSlides()
|
|
151
|
+
}
|
|
152
|
+
kill() {
|
|
153
|
+
if (this.swipe) {
|
|
154
|
+
this.swipe.kill()
|
|
155
|
+
this.swipe = null
|
|
156
|
+
}
|
|
157
|
+
delete this.dataset.ready
|
|
158
|
+
}
|
|
159
|
+
setup(o) {
|
|
160
|
+
this.swipe?.setup(o)
|
|
161
|
+
}
|
|
162
|
+
appendSlide(s) {
|
|
163
|
+
this.swipe?.appendSlide(s)
|
|
164
|
+
}
|
|
165
|
+
prependSlide(s) {
|
|
166
|
+
this.swipe?.prependSlide(s)
|
|
167
|
+
}
|
|
168
|
+
}
|
package/swipe3.js
ADDED
|
@@ -0,0 +1,476 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Swipe 3.0 - Optimized for mobile performance
|
|
3
|
+
* Based on https://github.com/thebird/Swipe
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const root = typeof self === 'object' && self.self === self ? self : typeof globalThis === 'object' ? globalThis : this
|
|
7
|
+
|
|
8
|
+
function debounce(fn, delay = 150) {
|
|
9
|
+
let timeoutId = null
|
|
10
|
+
|
|
11
|
+
function debounced(...args) {
|
|
12
|
+
if (timeoutId) clearTimeout(timeoutId)
|
|
13
|
+
timeoutId = setTimeout(() => {
|
|
14
|
+
timeoutId = null
|
|
15
|
+
fn.apply(this, args)
|
|
16
|
+
}, delay)
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
debounced.cancel = () => {
|
|
20
|
+
if (timeoutId) {
|
|
21
|
+
clearTimeout(timeoutId)
|
|
22
|
+
timeoutId = null
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
return debounced
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function isCancelable(event) {
|
|
30
|
+
return event && (typeof event.cancelable !== 'boolean' || event.cancelable)
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const supports = {
|
|
34
|
+
pointerEvents: 'PointerEvent' in root,
|
|
35
|
+
touch: 'ontouchstart' in root,
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const EASING = 'cubic-bezier(0.25, 0.46, 0.45, 0.94)'
|
|
39
|
+
|
|
40
|
+
function Swipe(container, options = {}) {
|
|
41
|
+
if (!container) return null
|
|
42
|
+
|
|
43
|
+
const config = {
|
|
44
|
+
speed: 400,
|
|
45
|
+
startSlide: 0,
|
|
46
|
+
draggable: false,
|
|
47
|
+
mousewheel: true,
|
|
48
|
+
disableScroll: false,
|
|
49
|
+
stopPropagation: false,
|
|
50
|
+
ignore: null,
|
|
51
|
+
...options,
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
let start = {}
|
|
55
|
+
let delta = {}
|
|
56
|
+
let isScrolling
|
|
57
|
+
let index = parseInt(config.startSlide, 10) || 0
|
|
58
|
+
|
|
59
|
+
const element = container.children[0]
|
|
60
|
+
if (!element) return null
|
|
61
|
+
|
|
62
|
+
let slides, slidePos, width, length
|
|
63
|
+
|
|
64
|
+
const slideDir = (() => {
|
|
65
|
+
const dir = root.getComputedStyle?.(container, null)?.getPropertyValue('direction')
|
|
66
|
+
return dir === 'rtl' ? 'right' : 'left'
|
|
67
|
+
})()
|
|
68
|
+
|
|
69
|
+
const debouncedSetup = debounce(setup, 150)
|
|
70
|
+
let wheelEndTimeout = null
|
|
71
|
+
|
|
72
|
+
const run = (name, ...args) => config[name]?.(...args)
|
|
73
|
+
|
|
74
|
+
// Unified event handlers
|
|
75
|
+
const events = {
|
|
76
|
+
handleEvent(event) {
|
|
77
|
+
switch (event.type) {
|
|
78
|
+
case 'pointerdown':
|
|
79
|
+
this.onStart(event, event.clientX, event.clientY, event.pointerId)
|
|
80
|
+
break
|
|
81
|
+
case 'pointermove':
|
|
82
|
+
if (event.isPrimary && event.pointerId === start.pointerId) {
|
|
83
|
+
this.onMove(event, event.clientX, event.clientY)
|
|
84
|
+
}
|
|
85
|
+
break
|
|
86
|
+
case 'pointerup':
|
|
87
|
+
case 'pointercancel':
|
|
88
|
+
case 'pointerleave':
|
|
89
|
+
this.onEnd(event, 'pointer')
|
|
90
|
+
break
|
|
91
|
+
|
|
92
|
+
case 'touchstart':
|
|
93
|
+
if (event.touches[0]) {
|
|
94
|
+
this.onStart(event, event.touches[0].pageX, event.touches[0].pageY)
|
|
95
|
+
}
|
|
96
|
+
break
|
|
97
|
+
case 'touchmove':
|
|
98
|
+
if (event.touches.length === 1 && !(event.scale && event.scale !== 1)) {
|
|
99
|
+
this.onMove(event, event.touches[0].pageX, event.touches[0].pageY)
|
|
100
|
+
}
|
|
101
|
+
break
|
|
102
|
+
case 'touchend':
|
|
103
|
+
this.onEnd(event, 'touch')
|
|
104
|
+
break
|
|
105
|
+
|
|
106
|
+
case 'mousedown':
|
|
107
|
+
event.preventDefault()
|
|
108
|
+
this.onStart(event, event.pageX, event.pageY)
|
|
109
|
+
break
|
|
110
|
+
case 'mousemove':
|
|
111
|
+
this.onMove(event, event.pageX, event.pageY)
|
|
112
|
+
break
|
|
113
|
+
case 'mouseup':
|
|
114
|
+
case 'mouseleave':
|
|
115
|
+
this.onEnd(event, 'mouse')
|
|
116
|
+
break
|
|
117
|
+
|
|
118
|
+
case 'transitionend':
|
|
119
|
+
if (parseInt(event.target.getAttribute('data-index'), 10) === index) {
|
|
120
|
+
run('transitionEnd', index, slides[index])
|
|
121
|
+
}
|
|
122
|
+
break
|
|
123
|
+
case 'resize':
|
|
124
|
+
debouncedSetup()
|
|
125
|
+
break
|
|
126
|
+
case 'wheel':
|
|
127
|
+
this.onWheel(event)
|
|
128
|
+
break
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
if (config.stopPropagation) event.stopPropagation()
|
|
132
|
+
},
|
|
133
|
+
|
|
134
|
+
captured: false,
|
|
135
|
+
|
|
136
|
+
onStart(event, x, y, pointerId) {
|
|
137
|
+
if (event.type === 'pointerdown' && !event.isPrimary) return
|
|
138
|
+
if (config.ignore && event.target.matches(config.ignore)) return
|
|
139
|
+
|
|
140
|
+
start = { x, y, time: Date.now(), pointerId }
|
|
141
|
+
isScrolling = undefined
|
|
142
|
+
delta = {}
|
|
143
|
+
this.captured = false
|
|
144
|
+
|
|
145
|
+
if (event.type === 'pointerdown') {
|
|
146
|
+
element.addEventListener('pointermove', this, { passive: false })
|
|
147
|
+
element.addEventListener('pointerup', this)
|
|
148
|
+
element.addEventListener('pointercancel', this)
|
|
149
|
+
element.addEventListener('pointerleave', this)
|
|
150
|
+
} else if (event.type === 'touchstart') {
|
|
151
|
+
element.addEventListener('touchmove', this, { passive: false })
|
|
152
|
+
element.addEventListener('touchend', this)
|
|
153
|
+
} else {
|
|
154
|
+
element.addEventListener('mousemove', this)
|
|
155
|
+
element.addEventListener('mouseup', this)
|
|
156
|
+
element.addEventListener('mouseleave', this)
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
run('dragStart', index, slides[index])
|
|
160
|
+
},
|
|
161
|
+
|
|
162
|
+
onMove(event, x, y) {
|
|
163
|
+
delta = { x: x - start.x, y: y - start.y }
|
|
164
|
+
|
|
165
|
+
if (isScrolling === undefined) {
|
|
166
|
+
const sensitivity = 10
|
|
167
|
+
isScrolling = Math.abs(delta.y) > Math.abs(delta.x) + sensitivity
|
|
168
|
+
|
|
169
|
+
if (!isScrolling && !this.captured && event.type === 'pointermove') {
|
|
170
|
+
element.setPointerCapture(event.pointerId)
|
|
171
|
+
this.captured = true
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
if (!isScrolling) {
|
|
176
|
+
if (isCancelable(event)) event.preventDefault()
|
|
177
|
+
run('runMove')
|
|
178
|
+
|
|
179
|
+
const resistedDelta = applyResistance(delta.x)
|
|
180
|
+
translate(index - 1, resistedDelta + slidePos[index - 1], 0)
|
|
181
|
+
translate(index, resistedDelta + slidePos[index], 0)
|
|
182
|
+
translate(index + 1, resistedDelta + slidePos[index + 1], 0)
|
|
183
|
+
} else if (config.disableScroll && isCancelable(event)) {
|
|
184
|
+
event.preventDefault()
|
|
185
|
+
}
|
|
186
|
+
},
|
|
187
|
+
|
|
188
|
+
onEnd(event, type) {
|
|
189
|
+
if (type === 'pointer' && !event.isPrimary) return
|
|
190
|
+
|
|
191
|
+
if (this.captured) {
|
|
192
|
+
element.releasePointerCapture(event.pointerId)
|
|
193
|
+
this.captured = false
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
if (type === 'pointer') {
|
|
197
|
+
element.removeEventListener('pointermove', this)
|
|
198
|
+
element.removeEventListener('pointerup', this)
|
|
199
|
+
element.removeEventListener('pointercancel', this)
|
|
200
|
+
element.removeEventListener('pointerleave', this)
|
|
201
|
+
} else if (type === 'touch') {
|
|
202
|
+
element.removeEventListener('touchmove', this)
|
|
203
|
+
element.removeEventListener('touchend', this)
|
|
204
|
+
} else {
|
|
205
|
+
element.removeEventListener('mousemove', this)
|
|
206
|
+
element.removeEventListener('mouseup', this)
|
|
207
|
+
element.removeEventListener('mouseleave', this)
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
this.finishSwipe()
|
|
211
|
+
},
|
|
212
|
+
|
|
213
|
+
onWheel(event) {
|
|
214
|
+
let deltaX = event.deltaX
|
|
215
|
+
let deltaY = event.deltaY
|
|
216
|
+
if (event.deltaMode === 1) {
|
|
217
|
+
deltaX *= 40
|
|
218
|
+
deltaY *= 40
|
|
219
|
+
} else if (event.deltaMode === 2) {
|
|
220
|
+
deltaX *= width
|
|
221
|
+
deltaY *= width
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
if (Math.abs(deltaX) < 3) return
|
|
225
|
+
if (isCancelable(event)) event.preventDefault()
|
|
226
|
+
|
|
227
|
+
if (!delta.x) start.time = Date.now()
|
|
228
|
+
|
|
229
|
+
let slower = 0.7
|
|
230
|
+
const absDelta = Math.abs(delta.x || 0)
|
|
231
|
+
if (absDelta > width * 0.5) slower = 0.3
|
|
232
|
+
if (absDelta > width) slower = 0.1
|
|
233
|
+
if (absDelta > width * 1.5) slower = 0.05
|
|
234
|
+
if (absDelta > width * 2) slower = 0.01
|
|
235
|
+
|
|
236
|
+
delta = {
|
|
237
|
+
x: (delta.x || 0) - deltaX * slower,
|
|
238
|
+
y: (delta.y || 0) - deltaY * 0.5,
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
const resistedDelta = applyResistance(delta.x)
|
|
242
|
+
translate(index - 1, resistedDelta + slidePos[index - 1], 0)
|
|
243
|
+
translate(index, resistedDelta + slidePos[index], 0)
|
|
244
|
+
translate(index + 1, resistedDelta + slidePos[index + 1], 0)
|
|
245
|
+
|
|
246
|
+
if (wheelEndTimeout) clearTimeout(wheelEndTimeout)
|
|
247
|
+
wheelEndTimeout = setTimeout(() => {
|
|
248
|
+
this.finishSwipe()
|
|
249
|
+
delta = {}
|
|
250
|
+
wheelEndTimeout = null
|
|
251
|
+
}, 50)
|
|
252
|
+
},
|
|
253
|
+
|
|
254
|
+
finishSwipe() {
|
|
255
|
+
const duration = Date.now() - start.time
|
|
256
|
+
const absX = Math.abs(delta.x || 0)
|
|
257
|
+
const isValidSlide = (duration < 250 && absX > 20) || absX > width / 2
|
|
258
|
+
const isPastBounds = (!index && delta.x > 0) || (index === slides.length - 1 && delta.x < 0)
|
|
259
|
+
const direction = delta.x ? Math.abs(delta.x) / delta.x : 0
|
|
260
|
+
|
|
261
|
+
if (!isScrolling && delta.x) {
|
|
262
|
+
if (isValidSlide && !isPastBounds) {
|
|
263
|
+
if (direction < 0) {
|
|
264
|
+
move(index - 1, -width, 0)
|
|
265
|
+
move(index, slidePos[index] - width, config.speed)
|
|
266
|
+
move(circle(index + 1), slidePos[circle(index + 1)] - width, config.speed)
|
|
267
|
+
index = circle(index + 1)
|
|
268
|
+
} else {
|
|
269
|
+
move(index + 1, width, 0)
|
|
270
|
+
move(index, slidePos[index] + width, config.speed)
|
|
271
|
+
move(circle(index - 1), slidePos[circle(index - 1)] + width, config.speed)
|
|
272
|
+
index = circle(index - 1)
|
|
273
|
+
}
|
|
274
|
+
run('callback', index, slides[index], direction)
|
|
275
|
+
} else {
|
|
276
|
+
move(index - 1, -width, config.speed)
|
|
277
|
+
move(index, 0, config.speed)
|
|
278
|
+
move(index + 1, width, config.speed)
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
run('dragEnd', index, slides[index])
|
|
283
|
+
},
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
function applyResistance(deltaX) {
|
|
287
|
+
const atStart = !index && deltaX > 0
|
|
288
|
+
const atEnd = index === slides.length - 1 && deltaX < 0
|
|
289
|
+
if (atStart || atEnd) {
|
|
290
|
+
return deltaX / (Math.abs(deltaX) / width + 1)
|
|
291
|
+
}
|
|
292
|
+
return deltaX
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
function circle(idx) {
|
|
296
|
+
return (slides.length + (idx % slides.length)) % slides.length
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
function move(idx, dist, speed) {
|
|
300
|
+
translate(idx, dist, speed)
|
|
301
|
+
slidePos[idx] = dist
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
function translate(idx, dist, speed) {
|
|
305
|
+
const slide = slides[idx]
|
|
306
|
+
if (!slide?.style) return
|
|
307
|
+
|
|
308
|
+
slide.style.transitionDuration = speed + 'ms'
|
|
309
|
+
slide.style.transitionTimingFunction = EASING
|
|
310
|
+
slide.style.transform = `translate3d(${dist}px, 0, 0)`
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
function setup(opts) {
|
|
314
|
+
if (opts) Object.assign(config, opts)
|
|
315
|
+
|
|
316
|
+
slides = element.children
|
|
317
|
+
length = slides.length
|
|
318
|
+
|
|
319
|
+
if (!length) return
|
|
320
|
+
|
|
321
|
+
width = container.getBoundingClientRect().width || container.offsetWidth
|
|
322
|
+
|
|
323
|
+
slidePos = new Array(length)
|
|
324
|
+
element.style.width = length * width * 2 + 'px'
|
|
325
|
+
|
|
326
|
+
for (let i = length - 1; i >= 0; i--) {
|
|
327
|
+
const slide = slides[i]
|
|
328
|
+
|
|
329
|
+
slide.style.willChange = 'transform'
|
|
330
|
+
slide.style.width = width + 'px'
|
|
331
|
+
slide.setAttribute('data-index', i)
|
|
332
|
+
slide.style[slideDir] = i * -width + 'px'
|
|
333
|
+
|
|
334
|
+
if (slideDir === 'right') {
|
|
335
|
+
slide.style.float = 'right'
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
const initialDist = index > i ? -width : index < i ? width : 0
|
|
339
|
+
move(i, initialDist, 0)
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
detachEvents()
|
|
343
|
+
attachEvents()
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
function attachEvents() {
|
|
347
|
+
if (supports.pointerEvents) {
|
|
348
|
+
element.addEventListener('pointerdown', events)
|
|
349
|
+
} else if (supports.touch) {
|
|
350
|
+
element.addEventListener('touchstart', events)
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
if (config.draggable && !supports.pointerEvents) {
|
|
354
|
+
element.addEventListener('mousedown', events)
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
if (config.mousewheel) {
|
|
358
|
+
element.addEventListener('wheel', events, { passive: false })
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
element.addEventListener('transitionend', events)
|
|
362
|
+
root.addEventListener('resize', events)
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
function detachEvents() {
|
|
366
|
+
if (supports.pointerEvents) {
|
|
367
|
+
element.removeEventListener('pointerdown', events)
|
|
368
|
+
element.removeEventListener('pointermove', events)
|
|
369
|
+
element.removeEventListener('pointerup', events)
|
|
370
|
+
element.removeEventListener('pointercancel', events)
|
|
371
|
+
} else {
|
|
372
|
+
element.removeEventListener('touchstart', events)
|
|
373
|
+
element.removeEventListener('touchmove', events)
|
|
374
|
+
element.removeEventListener('touchend', events)
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
element.removeEventListener('mousedown', events)
|
|
378
|
+
element.removeEventListener('mousemove', events)
|
|
379
|
+
element.removeEventListener('mouseup', events)
|
|
380
|
+
element.removeEventListener('mouseleave', events)
|
|
381
|
+
|
|
382
|
+
element.removeEventListener('wheel', events)
|
|
383
|
+
element.removeEventListener('transitionend', events)
|
|
384
|
+
root.removeEventListener('resize', events)
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
const getPos = () => index
|
|
388
|
+
|
|
389
|
+
function slideTo(to, slideSpeed) {
|
|
390
|
+
to = typeof to === 'number' ? to : parseInt(to, 10)
|
|
391
|
+
|
|
392
|
+
if (index === to) return
|
|
393
|
+
|
|
394
|
+
const direction = Math.abs(index - to) / (index - to)
|
|
395
|
+
let diff = Math.abs(index - to) - 1
|
|
396
|
+
|
|
397
|
+
while (diff--) {
|
|
398
|
+
move(circle((to > index ? to : index) - diff - 1), width * direction, 0)
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
to = circle(to)
|
|
402
|
+
|
|
403
|
+
move(index, width * direction, slideSpeed ?? config.speed)
|
|
404
|
+
move(to, 0, slideSpeed ?? config.speed)
|
|
405
|
+
|
|
406
|
+
index = to
|
|
407
|
+
|
|
408
|
+
requestAnimationFrame(() => {
|
|
409
|
+
run('callback', index, slides[index], direction)
|
|
410
|
+
})
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
function prev() {
|
|
414
|
+
slideTo(index - 1)
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
function next() {
|
|
418
|
+
if (index < slides.length - 1) {
|
|
419
|
+
slideTo(index + 1)
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
function kill() {
|
|
424
|
+
element.style.width = ''
|
|
425
|
+
element.style[slideDir] = ''
|
|
426
|
+
|
|
427
|
+
for (let i = slides.length - 1; i >= 0; i--) {
|
|
428
|
+
const slide = slides[i]
|
|
429
|
+
|
|
430
|
+
if (slide.getAttribute('data-cloned')) {
|
|
431
|
+
slide.parentElement?.removeChild(slide)
|
|
432
|
+
continue
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
slide.style.width = ''
|
|
436
|
+
slide.style[slideDir] = ''
|
|
437
|
+
slide.style.transitionDuration = ''
|
|
438
|
+
slide.style.transform = ''
|
|
439
|
+
slide.style.willChange = ''
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
detachEvents()
|
|
443
|
+
debouncedSetup.cancel()
|
|
444
|
+
|
|
445
|
+
if (wheelEndTimeout) {
|
|
446
|
+
clearTimeout(wheelEndTimeout)
|
|
447
|
+
wheelEndTimeout = null
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
setup()
|
|
452
|
+
|
|
453
|
+
return {
|
|
454
|
+
setup,
|
|
455
|
+
slide: slideTo,
|
|
456
|
+
prev,
|
|
457
|
+
next,
|
|
458
|
+
getPos,
|
|
459
|
+
getNumSlides: () => length,
|
|
460
|
+
setIndex: newIndex => {
|
|
461
|
+
index = newIndex
|
|
462
|
+
},
|
|
463
|
+
appendSlide: slide => {
|
|
464
|
+
element.appendChild(slide)
|
|
465
|
+
setup()
|
|
466
|
+
},
|
|
467
|
+
prependSlide: slide => {
|
|
468
|
+
element.prepend(slide)
|
|
469
|
+
index++
|
|
470
|
+
setup()
|
|
471
|
+
},
|
|
472
|
+
kill,
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
export default Swipe
|