lazer-slider 1.1.1 → 1.1.3
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/README.md +325 -73
- package/dist/index.cjs +326 -109
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +61 -6
- package/dist/index.d.ts +61 -6
- package/dist/index.js +322 -108
- package/dist/index.js.map +1 -1
- package/dist/lazer-slider.css +263 -0
- package/package.json +5 -2
package/dist/index.cjs
CHANGED
|
@@ -26,7 +26,10 @@ __export(index_exports, {
|
|
|
26
26
|
easeOutExpo: () => easeOutExpo,
|
|
27
27
|
easeOutQuad: () => easeOutQuad,
|
|
28
28
|
generateBullets: () => generateBullets,
|
|
29
|
-
|
|
29
|
+
generateThumbs: () => generateThumbs,
|
|
30
|
+
injectStyles: () => injectStyles,
|
|
31
|
+
linear: () => linear,
|
|
32
|
+
removeStyles: () => removeStyles
|
|
30
33
|
});
|
|
31
34
|
module.exports = __toCommonJS(index_exports);
|
|
32
35
|
|
|
@@ -117,16 +120,19 @@ var updateActiveThumb = (thumbs, currentIndex, activeClass = "active") => {
|
|
|
117
120
|
thumb.setAttribute("aria-selected", isActive.toString());
|
|
118
121
|
});
|
|
119
122
|
};
|
|
120
|
-
var setupKeyboardNavigation = (feed, onPrev, onNext, abortSignal) => {
|
|
123
|
+
var setupKeyboardNavigation = (feed, onPrev, onNext, abortSignal, direction = "horizontal") => {
|
|
124
|
+
const isVertical = direction === "vertical";
|
|
125
|
+
const prevKey = isVertical ? "ArrowUp" : "ArrowLeft";
|
|
126
|
+
const nextKey = isVertical ? "ArrowDown" : "ArrowRight";
|
|
121
127
|
feed.addEventListener(
|
|
122
128
|
"keydown",
|
|
123
129
|
(event) => {
|
|
124
130
|
switch (event.key) {
|
|
125
|
-
case
|
|
131
|
+
case prevKey:
|
|
126
132
|
event.preventDefault();
|
|
127
133
|
onPrev();
|
|
128
134
|
break;
|
|
129
|
-
case
|
|
135
|
+
case nextKey:
|
|
130
136
|
event.preventDefault();
|
|
131
137
|
onNext();
|
|
132
138
|
break;
|
|
@@ -143,21 +149,25 @@ var setupKeyboardNavigation = (feed, onPrev, onNext, abortSignal) => {
|
|
|
143
149
|
var createDragState = () => ({
|
|
144
150
|
isDragging: false,
|
|
145
151
|
startX: 0,
|
|
152
|
+
startY: 0,
|
|
146
153
|
startScrollLeft: 0,
|
|
154
|
+
startScrollTop: 0,
|
|
147
155
|
velocity: 0,
|
|
148
156
|
lastX: 0,
|
|
157
|
+
lastY: 0,
|
|
149
158
|
lastTime: 0,
|
|
150
159
|
momentumId: null
|
|
151
160
|
});
|
|
152
|
-
var findNearestSlide = (feed, slides) => {
|
|
161
|
+
var findNearestSlide = (feed, slides, direction = "horizontal") => {
|
|
153
162
|
const feedRect = feed.getBoundingClientRect();
|
|
154
|
-
const
|
|
163
|
+
const isVertical = direction === "vertical";
|
|
164
|
+
const feedCenter = isVertical ? feedRect.top + feedRect.height / 2 : feedRect.left + feedRect.width / 2;
|
|
155
165
|
let nearestSlide = null;
|
|
156
166
|
let minDistance = Infinity;
|
|
157
167
|
for (const slide of slides) {
|
|
158
168
|
if (slide.offsetParent === null) continue;
|
|
159
169
|
const slideRect = slide.getBoundingClientRect();
|
|
160
|
-
const slideCenter = slideRect.left + slideRect.width / 2;
|
|
170
|
+
const slideCenter = isVertical ? slideRect.top + slideRect.height / 2 : slideRect.left + slideRect.width / 2;
|
|
161
171
|
const distance = Math.abs(feedCenter - slideCenter);
|
|
162
172
|
if (distance < minDistance) {
|
|
163
173
|
minDistance = distance;
|
|
@@ -166,20 +176,26 @@ var findNearestSlide = (feed, slides) => {
|
|
|
166
176
|
}
|
|
167
177
|
return nearestSlide;
|
|
168
178
|
};
|
|
169
|
-
var applyMomentum = (state, feed, slides, smoothScrollTo, onDragEnd) => {
|
|
179
|
+
var applyMomentum = (state, feed, slides, smoothScrollTo, onDragEnd, direction = "horizontal") => {
|
|
170
180
|
const friction = 0.95;
|
|
171
181
|
const minVelocity = 0.5;
|
|
182
|
+
const isVertical = direction === "vertical";
|
|
172
183
|
const animate = () => {
|
|
173
184
|
if (Math.abs(state.velocity) < minVelocity) {
|
|
174
185
|
state.momentumId = null;
|
|
175
|
-
const nearestSlide = findNearestSlide(feed, slides);
|
|
186
|
+
const nearestSlide = findNearestSlide(feed, slides, direction);
|
|
176
187
|
if (nearestSlide) {
|
|
177
|
-
|
|
188
|
+
const targetPosition = isVertical ? nearestSlide.offsetTop : nearestSlide.offsetLeft;
|
|
189
|
+
smoothScrollTo(targetPosition, easeOutCubic);
|
|
178
190
|
}
|
|
179
191
|
onDragEnd?.();
|
|
180
192
|
return;
|
|
181
193
|
}
|
|
182
|
-
|
|
194
|
+
if (isVertical) {
|
|
195
|
+
feed.scrollTop += state.velocity;
|
|
196
|
+
} else {
|
|
197
|
+
feed.scrollLeft += state.velocity;
|
|
198
|
+
}
|
|
183
199
|
state.velocity *= friction;
|
|
184
200
|
state.momentumId = requestAnimationFrame(animate);
|
|
185
201
|
};
|
|
@@ -191,9 +207,16 @@ var getEventX = (event) => {
|
|
|
191
207
|
}
|
|
192
208
|
return event.clientX;
|
|
193
209
|
};
|
|
210
|
+
var getEventY = (event) => {
|
|
211
|
+
if ("touches" in event) {
|
|
212
|
+
return event.touches[0]?.clientY ?? 0;
|
|
213
|
+
}
|
|
214
|
+
return event.clientY;
|
|
215
|
+
};
|
|
194
216
|
var setupDragToScroll = (config) => {
|
|
195
|
-
const { feed, slides, abortSignal, smoothScrollTo, onDragEnd,
|
|
217
|
+
const { feed, slides, abortSignal, smoothScrollTo, onDragEnd, direction = "horizontal" } = config;
|
|
196
218
|
const state = createDragState();
|
|
219
|
+
const isVertical = direction === "vertical";
|
|
197
220
|
const handleDragStart = (event) => {
|
|
198
221
|
if (state.momentumId !== null) {
|
|
199
222
|
cancelAnimationFrame(state.momentumId);
|
|
@@ -201,11 +224,13 @@ var setupDragToScroll = (config) => {
|
|
|
201
224
|
}
|
|
202
225
|
state.isDragging = true;
|
|
203
226
|
state.startX = getEventX(event);
|
|
227
|
+
state.startY = getEventY(event);
|
|
204
228
|
state.startScrollLeft = feed.scrollLeft;
|
|
229
|
+
state.startScrollTop = feed.scrollTop;
|
|
205
230
|
state.velocity = 0;
|
|
206
231
|
state.lastX = state.startX;
|
|
232
|
+
state.lastY = state.startY;
|
|
207
233
|
state.lastTime = performance.now();
|
|
208
|
-
feed.style.cursor = "grabbing";
|
|
209
234
|
feed.style.userSelect = "none";
|
|
210
235
|
if (event.type === "mousedown") {
|
|
211
236
|
event.preventDefault();
|
|
@@ -214,14 +239,24 @@ var setupDragToScroll = (config) => {
|
|
|
214
239
|
const handleDragMove = (event) => {
|
|
215
240
|
if (!state.isDragging) return;
|
|
216
241
|
const currentX = getEventX(event);
|
|
242
|
+
const currentY = getEventY(event);
|
|
217
243
|
const currentTime = performance.now();
|
|
218
|
-
const deltaX = state.startX - currentX;
|
|
219
244
|
const deltaTime = currentTime - state.lastTime;
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
245
|
+
if (isVertical) {
|
|
246
|
+
const deltaY = state.startY - currentY;
|
|
247
|
+
feed.scrollTop = state.startScrollTop + deltaY;
|
|
248
|
+
if (deltaTime > 0) {
|
|
249
|
+
state.velocity = (state.lastY - currentY) / deltaTime * 16;
|
|
250
|
+
}
|
|
251
|
+
state.lastY = currentY;
|
|
252
|
+
} else {
|
|
253
|
+
const deltaX = state.startX - currentX;
|
|
254
|
+
feed.scrollLeft = state.startScrollLeft + deltaX;
|
|
255
|
+
if (deltaTime > 0) {
|
|
256
|
+
state.velocity = (state.lastX - currentX) / deltaTime * 16;
|
|
257
|
+
}
|
|
258
|
+
state.lastX = currentX;
|
|
223
259
|
}
|
|
224
|
-
state.lastX = currentX;
|
|
225
260
|
state.lastTime = currentTime;
|
|
226
261
|
if (event.type === "touchmove") {
|
|
227
262
|
event.preventDefault();
|
|
@@ -230,41 +265,18 @@ var setupDragToScroll = (config) => {
|
|
|
230
265
|
const handleDragEnd = () => {
|
|
231
266
|
if (!state.isDragging) return;
|
|
232
267
|
state.isDragging = false;
|
|
233
|
-
feed.style.cursor = "grab";
|
|
234
268
|
feed.style.userSelect = "";
|
|
235
|
-
if (
|
|
236
|
-
|
|
237
|
-
applyMomentum(state, feed, slides, smoothScrollTo, onDragEnd);
|
|
238
|
-
} else {
|
|
239
|
-
const nearestSlide = findNearestSlide(feed, slides);
|
|
240
|
-
if (nearestSlide) {
|
|
241
|
-
smoothScrollTo(nearestSlide.offsetLeft, easeOutCubic);
|
|
242
|
-
}
|
|
243
|
-
onDragEnd?.();
|
|
244
|
-
}
|
|
269
|
+
if (Math.abs(state.velocity) > 1) {
|
|
270
|
+
applyMomentum(state, feed, slides, smoothScrollTo, onDragEnd, direction);
|
|
245
271
|
} else {
|
|
246
|
-
const
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
const visibleSlides = slides.filter((slide) => slide.offsetParent !== null);
|
|
251
|
-
const visibleIndex = visibleSlides.indexOf(nearestSlide);
|
|
252
|
-
let targetSlide = null;
|
|
253
|
-
if (swipeDistance > 0 && visibleIndex < visibleSlides.length - 1) {
|
|
254
|
-
targetSlide = visibleSlides[visibleIndex + 1] ?? nearestSlide;
|
|
255
|
-
} else if (swipeDistance < 0 && visibleIndex > 0) {
|
|
256
|
-
targetSlide = visibleSlides[visibleIndex - 1] ?? nearestSlide;
|
|
257
|
-
} else {
|
|
258
|
-
targetSlide = nearestSlide;
|
|
259
|
-
}
|
|
260
|
-
smoothScrollTo(targetSlide.offsetLeft, easeOutCubic);
|
|
261
|
-
} else if (nearestSlide) {
|
|
262
|
-
smoothScrollTo(nearestSlide.offsetLeft, easeOutCubic);
|
|
272
|
+
const nearestSlide = findNearestSlide(feed, slides, direction);
|
|
273
|
+
if (nearestSlide) {
|
|
274
|
+
const targetPosition = isVertical ? nearestSlide.offsetTop : nearestSlide.offsetLeft;
|
|
275
|
+
smoothScrollTo(targetPosition, easeOutCubic);
|
|
263
276
|
}
|
|
264
277
|
onDragEnd?.();
|
|
265
278
|
}
|
|
266
279
|
};
|
|
267
|
-
feed.style.cursor = "grab";
|
|
268
280
|
feed.addEventListener("mousedown", handleDragStart, { signal: abortSignal });
|
|
269
281
|
document.addEventListener("mousemove", handleDragMove, { signal: abortSignal });
|
|
270
282
|
document.addEventListener("mouseup", handleDragEnd, { signal: abortSignal });
|
|
@@ -333,6 +345,67 @@ var generateBullets = ({
|
|
|
333
345
|
return bullets;
|
|
334
346
|
};
|
|
335
347
|
|
|
348
|
+
// src/core/thumbs.ts
|
|
349
|
+
var generateThumbs = ({
|
|
350
|
+
thumbsContainer,
|
|
351
|
+
slides,
|
|
352
|
+
thumbClass,
|
|
353
|
+
thumbActiveClass,
|
|
354
|
+
feedId,
|
|
355
|
+
thumbImageSelector = "img",
|
|
356
|
+
thumbSize
|
|
357
|
+
}) => {
|
|
358
|
+
if (!thumbsContainer || !(thumbsContainer instanceof HTMLElement)) {
|
|
359
|
+
throw new Error("Invalid thumbsContainer: must be a valid HTMLElement");
|
|
360
|
+
}
|
|
361
|
+
if (!Array.isArray(slides) || slides.length === 0) {
|
|
362
|
+
throw new Error("Invalid slides: must be a non-empty array");
|
|
363
|
+
}
|
|
364
|
+
if (!feedId || typeof feedId !== "string") {
|
|
365
|
+
throw new Error("Invalid feedId: must be a non-empty string");
|
|
366
|
+
}
|
|
367
|
+
if (!thumbClass || typeof thumbClass !== "string") {
|
|
368
|
+
throw new Error("Invalid thumbClass: must be a non-empty string");
|
|
369
|
+
}
|
|
370
|
+
thumbsContainer.innerHTML = "";
|
|
371
|
+
thumbsContainer.setAttribute("role", "tablist");
|
|
372
|
+
thumbsContainer.setAttribute("aria-label", "Slide thumbnails");
|
|
373
|
+
const visibleSlides = slides.map((slide, originalIndex) => ({ slide, originalIndex })).filter(({ slide }) => slide.offsetParent !== null);
|
|
374
|
+
if (visibleSlides.length === 0) {
|
|
375
|
+
console.warn("No visible slides found");
|
|
376
|
+
return [];
|
|
377
|
+
}
|
|
378
|
+
const thumbs = visibleSlides.map(({ slide, originalIndex }, visibleIndex) => {
|
|
379
|
+
const thumb = document.createElement("button");
|
|
380
|
+
thumb.type = "button";
|
|
381
|
+
thumb.classList.add(thumbClass);
|
|
382
|
+
if (visibleIndex === 0) {
|
|
383
|
+
thumb.classList.add(thumbActiveClass);
|
|
384
|
+
}
|
|
385
|
+
const sourceImage = slide.querySelector(thumbImageSelector);
|
|
386
|
+
if (sourceImage?.src) {
|
|
387
|
+
const thumbImg = document.createElement("img");
|
|
388
|
+
thumbImg.src = sourceImage.src;
|
|
389
|
+
thumbImg.alt = sourceImage.alt || `Slide ${visibleIndex + 1} thumbnail`;
|
|
390
|
+
thumbImg.draggable = false;
|
|
391
|
+
if (thumbSize) {
|
|
392
|
+
thumbImg.style.width = `${thumbSize.width}px`;
|
|
393
|
+
thumbImg.style.height = `${thumbSize.height}px`;
|
|
394
|
+
thumbImg.style.objectFit = "cover";
|
|
395
|
+
}
|
|
396
|
+
thumb.appendChild(thumbImg);
|
|
397
|
+
}
|
|
398
|
+
thumb.setAttribute("role", "tab");
|
|
399
|
+
thumb.setAttribute("aria-selected", visibleIndex === 0 ? "true" : "false");
|
|
400
|
+
thumb.setAttribute("aria-controls", feedId);
|
|
401
|
+
thumb.setAttribute("aria-label", `Go to slide ${visibleIndex + 1}`);
|
|
402
|
+
thumb.setAttribute("data-slide-index", String(visibleIndex));
|
|
403
|
+
thumbsContainer.appendChild(thumb);
|
|
404
|
+
return thumb;
|
|
405
|
+
});
|
|
406
|
+
return thumbs;
|
|
407
|
+
};
|
|
408
|
+
|
|
336
409
|
// src/core/marquee.ts
|
|
337
410
|
var createMarqueeState = () => ({
|
|
338
411
|
initialized: false,
|
|
@@ -447,6 +520,72 @@ var attachMarqueeEventListeners = (settings, state, signal) => {
|
|
|
447
520
|
);
|
|
448
521
|
};
|
|
449
522
|
|
|
523
|
+
// src/core/styles.ts
|
|
524
|
+
var CRITICAL_STYLES = `
|
|
525
|
+
/* Lazer Slider - Critical Styles (Auto-injected) */
|
|
526
|
+
.lazer-feed {
|
|
527
|
+
position: relative;
|
|
528
|
+
display: flex;
|
|
529
|
+
overflow-x: auto;
|
|
530
|
+
overflow-y: hidden;
|
|
531
|
+
-webkit-overflow-scrolling: touch;
|
|
532
|
+
scrollbar-width: none;
|
|
533
|
+
scroll-snap-type: x mandatory;
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
.lazer-feed::-webkit-scrollbar {
|
|
537
|
+
display: none;
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
.lazer-feed.lazer-vertical {
|
|
541
|
+
flex-direction: column;
|
|
542
|
+
overflow-x: hidden;
|
|
543
|
+
overflow-y: auto;
|
|
544
|
+
scroll-snap-type: y mandatory;
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
.lazer-slide {
|
|
548
|
+
scroll-snap-align: start;
|
|
549
|
+
flex-shrink: 0;
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
.lazer-feed.is-dragging {
|
|
553
|
+
cursor: grabbing;
|
|
554
|
+
user-select: none;
|
|
555
|
+
scroll-snap-type: none;
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
.lazer-feed:not(.is-dragging) {
|
|
559
|
+
cursor: grab;
|
|
560
|
+
}
|
|
561
|
+
`;
|
|
562
|
+
var stylesInjected = false;
|
|
563
|
+
var STYLE_ID = "lazer-slider-critical-styles";
|
|
564
|
+
var injectStyles = () => {
|
|
565
|
+
if (stylesInjected || typeof document === "undefined") return;
|
|
566
|
+
const existingStyle = document.getElementById(STYLE_ID);
|
|
567
|
+
if (existingStyle) {
|
|
568
|
+
stylesInjected = true;
|
|
569
|
+
return;
|
|
570
|
+
}
|
|
571
|
+
const style = document.createElement("style");
|
|
572
|
+
style.id = STYLE_ID;
|
|
573
|
+
style.textContent = CRITICAL_STYLES;
|
|
574
|
+
document.head.appendChild(style);
|
|
575
|
+
stylesInjected = true;
|
|
576
|
+
};
|
|
577
|
+
var injectStylesOnce = () => {
|
|
578
|
+
injectStyles();
|
|
579
|
+
};
|
|
580
|
+
var removeStyles = () => {
|
|
581
|
+
if (typeof document === "undefined") return;
|
|
582
|
+
const style = document.getElementById(STYLE_ID);
|
|
583
|
+
if (style) {
|
|
584
|
+
style.remove();
|
|
585
|
+
stylesInjected = false;
|
|
586
|
+
}
|
|
587
|
+
};
|
|
588
|
+
|
|
450
589
|
// src/core/slider.ts
|
|
451
590
|
var ANIMATION = {
|
|
452
591
|
MIN_DURATION: 400,
|
|
@@ -457,6 +596,7 @@ var ANIMATION = {
|
|
|
457
596
|
};
|
|
458
597
|
var DESKTOP_BREAKPOINT = "(min-width: 64rem)";
|
|
459
598
|
var createSlider = (settings) => {
|
|
599
|
+
injectStylesOnce();
|
|
460
600
|
if (!settings.feed) {
|
|
461
601
|
throw new Error("lazer-slider: feed element is required");
|
|
462
602
|
}
|
|
@@ -466,16 +606,40 @@ var createSlider = (settings) => {
|
|
|
466
606
|
if (!settings.feed.id) {
|
|
467
607
|
settings.feed.id = `lazer-slider-feed-${Math.random().toString(36).substr(2, 9)}`;
|
|
468
608
|
}
|
|
609
|
+
settings.feed.classList.add("lazer-feed");
|
|
610
|
+
settings.slides.forEach((slide) => {
|
|
611
|
+
slide.classList.add("lazer-slide");
|
|
612
|
+
});
|
|
469
613
|
if (settings.bulletsContainer && !settings.thumbs) {
|
|
470
614
|
const bullets = generateBullets({
|
|
471
615
|
bulletsContainer: settings.bulletsContainer,
|
|
472
616
|
slides: settings.slides,
|
|
473
|
-
bulletClass: settings.bulletsClass ?? "
|
|
617
|
+
bulletClass: settings.bulletsClass ?? "lazer-bullet",
|
|
474
618
|
bulletActiveClass: settings.bulletsActiveClass ?? "active",
|
|
475
619
|
feedId: settings.feed.id
|
|
476
620
|
});
|
|
477
621
|
settings.thumbs = bullets;
|
|
478
622
|
}
|
|
623
|
+
if (settings.thumbsContainer && !settings.thumbs) {
|
|
624
|
+
const thumbs = generateThumbs({
|
|
625
|
+
thumbsContainer: settings.thumbsContainer,
|
|
626
|
+
slides: settings.slides,
|
|
627
|
+
thumbClass: settings.thumbsClass ?? "lazer-thumb",
|
|
628
|
+
thumbActiveClass: settings.thumbsActiveClass ?? "active",
|
|
629
|
+
feedId: settings.feed.id,
|
|
630
|
+
thumbImageSelector: settings.thumbImageSelector ?? "img",
|
|
631
|
+
thumbSize: settings.thumbSize
|
|
632
|
+
});
|
|
633
|
+
settings.thumbs = thumbs;
|
|
634
|
+
}
|
|
635
|
+
const direction = settings.direction ?? "horizontal";
|
|
636
|
+
const isVertical = direction === "vertical";
|
|
637
|
+
if (isVertical) {
|
|
638
|
+
settings.feed.classList.add("lazer-vertical");
|
|
639
|
+
}
|
|
640
|
+
if (settings.marquee) {
|
|
641
|
+
settings.feed.classList.add("lazer-marquee");
|
|
642
|
+
}
|
|
479
643
|
const state = {
|
|
480
644
|
currentSlideIndex: 0,
|
|
481
645
|
isScrolling: false,
|
|
@@ -484,7 +648,6 @@ var createSlider = (settings) => {
|
|
|
484
648
|
lastWidth: 0,
|
|
485
649
|
updateThumbTimeout: null,
|
|
486
650
|
scrollEndTimeout: null,
|
|
487
|
-
resizeTimeout: null,
|
|
488
651
|
abortController: new AbortController(),
|
|
489
652
|
autoplayIntervalId: null,
|
|
490
653
|
autoplayPaused: false,
|
|
@@ -503,10 +666,10 @@ var createSlider = (settings) => {
|
|
|
503
666
|
const marqueeState = createMarqueeState();
|
|
504
667
|
const easing = settings.easing ?? easeOutExpo;
|
|
505
668
|
const getFeedRect = () => {
|
|
506
|
-
const
|
|
507
|
-
if (!state.cachedFeedRect || state.lastWidth !==
|
|
669
|
+
const currentSize = isVertical ? settings.feed.clientHeight : settings.feed.clientWidth;
|
|
670
|
+
if (!state.cachedFeedRect || state.lastWidth !== currentSize) {
|
|
508
671
|
state.cachedFeedRect = settings.feed.getBoundingClientRect();
|
|
509
|
-
state.lastWidth =
|
|
672
|
+
state.lastWidth = currentSize;
|
|
510
673
|
}
|
|
511
674
|
return state.cachedFeedRect;
|
|
512
675
|
};
|
|
@@ -549,26 +712,38 @@ var createSlider = (settings) => {
|
|
|
549
712
|
requestAnimationFrame(() => {
|
|
550
713
|
const firstRealSlide = realSlides[0];
|
|
551
714
|
if (firstRealSlide) {
|
|
552
|
-
|
|
715
|
+
if (isVertical) {
|
|
716
|
+
settings.feed.scrollTop = firstRealSlide.offsetTop;
|
|
717
|
+
} else {
|
|
718
|
+
settings.feed.scrollLeft = firstRealSlide.offsetLeft;
|
|
719
|
+
}
|
|
553
720
|
}
|
|
554
721
|
});
|
|
555
722
|
loopState.initialized = true;
|
|
556
723
|
};
|
|
557
|
-
const handleLoopReposition = (
|
|
724
|
+
const handleLoopReposition = (navDirection) => {
|
|
558
725
|
if (!settings.loop || !loopState.initialized) return;
|
|
559
726
|
state.isLoopRepositioning = true;
|
|
560
727
|
const realSlides = loopState.realSlides;
|
|
561
728
|
const totalRealSlides = realSlides.length;
|
|
562
|
-
if (
|
|
729
|
+
if (navDirection === "next") {
|
|
563
730
|
const firstRealSlide = realSlides[0];
|
|
564
731
|
if (firstRealSlide) {
|
|
565
|
-
|
|
732
|
+
if (isVertical) {
|
|
733
|
+
settings.feed.scrollTop = firstRealSlide.offsetTop;
|
|
734
|
+
} else {
|
|
735
|
+
settings.feed.scrollLeft = firstRealSlide.offsetLeft;
|
|
736
|
+
}
|
|
566
737
|
}
|
|
567
738
|
state.currentSlideIndex = 0;
|
|
568
739
|
} else {
|
|
569
740
|
const lastRealSlide = realSlides[totalRealSlides - 1];
|
|
570
741
|
if (lastRealSlide) {
|
|
571
|
-
|
|
742
|
+
if (isVertical) {
|
|
743
|
+
settings.feed.scrollTop = lastRealSlide.offsetTop;
|
|
744
|
+
} else {
|
|
745
|
+
settings.feed.scrollLeft = lastRealSlide.offsetLeft;
|
|
746
|
+
}
|
|
572
747
|
}
|
|
573
748
|
state.currentSlideIndex = totalRealSlides - 1;
|
|
574
749
|
}
|
|
@@ -598,39 +773,74 @@ var createSlider = (settings) => {
|
|
|
598
773
|
settings.slides.forEach((slide) => {
|
|
599
774
|
slide.style.flex = "";
|
|
600
775
|
slide.style.minWidth = "";
|
|
776
|
+
slide.style.minHeight = "";
|
|
601
777
|
});
|
|
602
778
|
return;
|
|
603
779
|
}
|
|
604
|
-
const
|
|
605
|
-
const
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
780
|
+
const totalGapSize = gap * (perView - 1);
|
|
781
|
+
const slideSize = `calc((100% - ${totalGapSize}px) / ${perView})`;
|
|
782
|
+
if (isVertical) {
|
|
783
|
+
settings.slides.forEach((slide) => {
|
|
784
|
+
slide.style.flex = `0 0 ${slideSize}`;
|
|
785
|
+
slide.style.minHeight = slideSize;
|
|
786
|
+
slide.style.minWidth = "";
|
|
787
|
+
});
|
|
788
|
+
} else {
|
|
789
|
+
settings.slides.forEach((slide) => {
|
|
790
|
+
slide.style.flex = `0 0 ${slideSize}`;
|
|
791
|
+
slide.style.minWidth = slideSize;
|
|
792
|
+
slide.style.minHeight = "";
|
|
793
|
+
});
|
|
794
|
+
}
|
|
610
795
|
};
|
|
611
796
|
const updateScrollbar = () => {
|
|
612
797
|
if (!settings.scrollbarThumb) return;
|
|
613
798
|
const feedRect = getFeedRect();
|
|
614
|
-
|
|
615
|
-
|
|
799
|
+
if (isVertical) {
|
|
800
|
+
const thumbHeight = feedRect.height / settings.feed.scrollHeight * 100;
|
|
801
|
+
settings.scrollbarThumb.style.height = `${thumbHeight}%`;
|
|
802
|
+
settings.scrollbarThumb.style.width = "";
|
|
803
|
+
} else {
|
|
804
|
+
const thumbWidth = feedRect.width / settings.feed.scrollWidth * 100;
|
|
805
|
+
settings.scrollbarThumb.style.width = `${thumbWidth}%`;
|
|
806
|
+
settings.scrollbarThumb.style.height = "";
|
|
807
|
+
}
|
|
616
808
|
};
|
|
617
809
|
const updateScrollbarPosition = () => {
|
|
618
810
|
if (!settings.scrollbarThumb || !settings.scrollbarTrack) return;
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
811
|
+
if (isVertical) {
|
|
812
|
+
const trackHeight = settings.scrollbarTrack.getBoundingClientRect().height;
|
|
813
|
+
const thumbHeight = settings.scrollbarThumb.getBoundingClientRect().height;
|
|
814
|
+
const totalTransform = trackHeight - thumbHeight;
|
|
815
|
+
const maxScroll = settings.feed.scrollHeight - settings.feed.clientHeight;
|
|
816
|
+
const scrollProgress = maxScroll > 0 ? settings.feed.scrollTop / maxScroll : 0;
|
|
817
|
+
settings.scrollbarThumb.style.transform = `translateY(${totalTransform * scrollProgress}px)`;
|
|
818
|
+
} else {
|
|
819
|
+
const trackWidth = settings.scrollbarTrack.getBoundingClientRect().width;
|
|
820
|
+
const thumbWidth = settings.scrollbarThumb.getBoundingClientRect().width;
|
|
821
|
+
const totalTransform = trackWidth - thumbWidth;
|
|
822
|
+
const maxScroll = settings.feed.scrollWidth - settings.feed.clientWidth;
|
|
823
|
+
const scrollProgress = maxScroll > 0 ? settings.feed.scrollLeft / maxScroll : 0;
|
|
824
|
+
settings.scrollbarThumb.style.transform = `translateX(${totalTransform * scrollProgress}px)`;
|
|
825
|
+
}
|
|
625
826
|
};
|
|
626
827
|
const updateControlsVisibility = () => {
|
|
627
828
|
if (state.isLoopRepositioning) {
|
|
628
829
|
return;
|
|
629
830
|
}
|
|
630
831
|
const feedRect = getFeedRect();
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
832
|
+
let isAtStart;
|
|
833
|
+
let isAtEnd;
|
|
834
|
+
let shouldHideScrollbar;
|
|
835
|
+
if (isVertical) {
|
|
836
|
+
isAtStart = settings.feed.scrollTop <= 1;
|
|
837
|
+
isAtEnd = settings.feed.scrollTop + feedRect.height >= settings.feed.scrollHeight - 1;
|
|
838
|
+
shouldHideScrollbar = settings.feed.scrollHeight <= feedRect.height;
|
|
839
|
+
} else {
|
|
840
|
+
isAtStart = settings.feed.scrollLeft <= 1;
|
|
841
|
+
isAtEnd = settings.feed.scrollLeft + feedRect.width >= settings.feed.scrollWidth - 1;
|
|
842
|
+
shouldHideScrollbar = settings.feed.scrollWidth <= feedRect.width;
|
|
843
|
+
}
|
|
634
844
|
if (settings.scrollbarTrack) {
|
|
635
845
|
settings.scrollbarTrack.style.display = shouldHideScrollbar ? "none" : "block";
|
|
636
846
|
}
|
|
@@ -664,21 +874,25 @@ var createSlider = (settings) => {
|
|
|
664
874
|
const viewportVisibleSlides = slidesToCheck.filter((slide) => {
|
|
665
875
|
const slideRect = slide.getBoundingClientRect();
|
|
666
876
|
const tolerance = 20;
|
|
877
|
+
if (isVertical) {
|
|
878
|
+
return slideRect.bottom > feedRect.top + tolerance && slideRect.top < feedRect.bottom - tolerance;
|
|
879
|
+
}
|
|
667
880
|
return slideRect.right > feedRect.left + tolerance && slideRect.left < feedRect.right - tolerance;
|
|
668
881
|
});
|
|
669
882
|
if (viewportVisibleSlides.length && viewportVisibleSlides[0]) {
|
|
670
883
|
const newIndex = slidesToCheck.indexOf(viewportVisibleSlides[0]);
|
|
671
884
|
if (newIndex !== -1) {
|
|
672
885
|
state.currentSlideIndex = newIndex;
|
|
886
|
+
const currentScroll = isVertical ? settings.feed.scrollTop : settings.feed.scrollLeft;
|
|
673
887
|
settings.onScroll?.({
|
|
674
|
-
currentScroll
|
|
888
|
+
currentScroll,
|
|
675
889
|
currentSlideIndex: state.currentSlideIndex
|
|
676
890
|
});
|
|
677
891
|
}
|
|
678
892
|
}
|
|
679
893
|
};
|
|
680
894
|
const smoothScrollTo = (target, customEasing = easing, onComplete) => {
|
|
681
|
-
const start = settings.feed.scrollLeft;
|
|
895
|
+
const start = isVertical ? settings.feed.scrollTop : settings.feed.scrollLeft;
|
|
682
896
|
const distance = Math.abs(target - start);
|
|
683
897
|
const duration = Math.min(
|
|
684
898
|
ANIMATION.MAX_DURATION,
|
|
@@ -689,11 +903,20 @@ var createSlider = (settings) => {
|
|
|
689
903
|
const elapsed = (currentTime - startTime) / duration;
|
|
690
904
|
const progress = Math.min(elapsed, 1);
|
|
691
905
|
const ease = customEasing(progress);
|
|
692
|
-
|
|
906
|
+
const scrollPosition = start + (target - start) * ease;
|
|
907
|
+
if (isVertical) {
|
|
908
|
+
settings.feed.scrollTop = scrollPosition;
|
|
909
|
+
} else {
|
|
910
|
+
settings.feed.scrollLeft = scrollPosition;
|
|
911
|
+
}
|
|
693
912
|
if (progress < 1) {
|
|
694
913
|
requestAnimationFrame(animateScroll);
|
|
695
914
|
} else {
|
|
696
|
-
|
|
915
|
+
if (isVertical) {
|
|
916
|
+
settings.feed.scrollTop = target;
|
|
917
|
+
} else {
|
|
918
|
+
settings.feed.scrollLeft = target;
|
|
919
|
+
}
|
|
697
920
|
onComplete?.();
|
|
698
921
|
}
|
|
699
922
|
};
|
|
@@ -712,16 +935,17 @@ var createSlider = (settings) => {
|
|
|
712
935
|
state.updateThumbTimeout = setTimeout(() => {
|
|
713
936
|
state.isScrolling = false;
|
|
714
937
|
}, ANIMATION.THUMB_UPDATE_DELAY);
|
|
715
|
-
|
|
938
|
+
const targetPosition = isVertical ? settings.slides[index].offsetTop : settings.slides[index].offsetLeft;
|
|
939
|
+
smoothScrollTo(targetPosition);
|
|
716
940
|
};
|
|
717
|
-
const handleNavButtonClick = (
|
|
941
|
+
const handleNavButtonClick = (navDirection) => {
|
|
718
942
|
const realSlides = loopState.initialized ? loopState.realSlides : getVisibleSlides();
|
|
719
943
|
const slidesToScroll = isDesktop() ? settings.desktopSlidesPerScroll ?? 1 : settings.mobileSlidesPerScroll ?? 1;
|
|
720
944
|
const totalRealSlides = realSlides.length;
|
|
721
945
|
updateCurrentSlideIndex();
|
|
722
946
|
let targetSlide;
|
|
723
947
|
let needsReposition = false;
|
|
724
|
-
if (
|
|
948
|
+
if (navDirection === "prev") {
|
|
725
949
|
if (settings.loop && loopState.initialized && state.currentSlideIndex === 0) {
|
|
726
950
|
const prependedClones = loopState.clonedSlides.filter(
|
|
727
951
|
(clone) => clone.getAttribute("data-lazer-clone") === "prepend"
|
|
@@ -748,17 +972,19 @@ var createSlider = (settings) => {
|
|
|
748
972
|
}
|
|
749
973
|
}
|
|
750
974
|
if (!targetSlide) return;
|
|
975
|
+
const currentScroll = isVertical ? settings.feed.scrollTop : settings.feed.scrollLeft;
|
|
751
976
|
settings.onScrollStart?.({
|
|
752
|
-
currentScroll
|
|
977
|
+
currentScroll,
|
|
753
978
|
target: targetSlide,
|
|
754
|
-
direction
|
|
979
|
+
direction: navDirection
|
|
755
980
|
});
|
|
981
|
+
const targetPosition = isVertical ? targetSlide.offsetTop : targetSlide.offsetLeft;
|
|
756
982
|
if (needsReposition) {
|
|
757
|
-
smoothScrollTo(
|
|
758
|
-
handleLoopReposition(
|
|
983
|
+
smoothScrollTo(targetPosition, easing, () => {
|
|
984
|
+
handleLoopReposition(navDirection);
|
|
759
985
|
});
|
|
760
986
|
} else {
|
|
761
|
-
smoothScrollTo(
|
|
987
|
+
smoothScrollTo(targetPosition);
|
|
762
988
|
}
|
|
763
989
|
};
|
|
764
990
|
const updateScrollPosition = () => {
|
|
@@ -773,8 +999,9 @@ var createSlider = (settings) => {
|
|
|
773
999
|
}
|
|
774
1000
|
state.scrollEndTimeout = setTimeout(() => {
|
|
775
1001
|
state.isScrolling = false;
|
|
1002
|
+
const currentScroll = isVertical ? settings.feed.scrollTop : settings.feed.scrollLeft;
|
|
776
1003
|
settings.onScrollEnd?.({
|
|
777
|
-
currentScroll
|
|
1004
|
+
currentScroll,
|
|
778
1005
|
currentSlideIndex: state.currentSlideIndex
|
|
779
1006
|
});
|
|
780
1007
|
}, ANIMATION.SCROLL_END_DELAY);
|
|
@@ -789,20 +1016,8 @@ var createSlider = (settings) => {
|
|
|
789
1016
|
}
|
|
790
1017
|
};
|
|
791
1018
|
const handleWindowResize = () => {
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
}
|
|
795
|
-
state.resizeTimeout = setTimeout(() => {
|
|
796
|
-
state.cachedFeedRect = null;
|
|
797
|
-
updateCurrentSlideIndex();
|
|
798
|
-
const currentIndex = state.currentSlideIndex;
|
|
799
|
-
refresh();
|
|
800
|
-
const slides = loopState.initialized ? loopState.realSlides : getVisibleSlides();
|
|
801
|
-
const targetSlide = slides[currentIndex];
|
|
802
|
-
if (targetSlide) {
|
|
803
|
-
settings.feed.scrollLeft = targetSlide.offsetLeft;
|
|
804
|
-
}
|
|
805
|
-
}, 150);
|
|
1019
|
+
state.cachedFeedRect = null;
|
|
1020
|
+
refresh();
|
|
806
1021
|
};
|
|
807
1022
|
const attachEventListeners = () => {
|
|
808
1023
|
const { signal } = state.abortController;
|
|
@@ -835,9 +1050,10 @@ var createSlider = (settings) => {
|
|
|
835
1050
|
settings.feed,
|
|
836
1051
|
() => handleNavButtonClick("prev"),
|
|
837
1052
|
() => handleNavButtonClick("next"),
|
|
838
|
-
signal
|
|
1053
|
+
signal,
|
|
1054
|
+
direction
|
|
839
1055
|
);
|
|
840
|
-
if (settings.enableDragToScroll) {
|
|
1056
|
+
if (settings.enableDragToScroll !== false) {
|
|
841
1057
|
dragState = setupDragToScroll({
|
|
842
1058
|
feed: settings.feed,
|
|
843
1059
|
slides: settings.slides,
|
|
@@ -847,7 +1063,7 @@ var createSlider = (settings) => {
|
|
|
847
1063
|
updateCurrentSlideIndex();
|
|
848
1064
|
updateActiveThumb(settings.thumbs, state.currentSlideIndex);
|
|
849
1065
|
},
|
|
850
|
-
|
|
1066
|
+
direction
|
|
851
1067
|
});
|
|
852
1068
|
}
|
|
853
1069
|
if (settings.autoplay && settings.pauseOnHover !== false) {
|
|
@@ -902,7 +1118,8 @@ var createSlider = (settings) => {
|
|
|
902
1118
|
if (!targetSlide) return;
|
|
903
1119
|
state.currentSlideIndex = safeIndex;
|
|
904
1120
|
updateActiveThumb(settings.thumbs, safeIndex);
|
|
905
|
-
|
|
1121
|
+
const targetPosition = isVertical ? targetSlide.offsetTop : targetSlide.offsetLeft;
|
|
1122
|
+
smoothScrollTo(targetPosition);
|
|
906
1123
|
};
|
|
907
1124
|
const refresh = () => {
|
|
908
1125
|
state.cachedFeedRect = null;
|
|
@@ -924,9 +1141,6 @@ var createSlider = (settings) => {
|
|
|
924
1141
|
if (state.scrollEndTimeout) {
|
|
925
1142
|
clearTimeout(state.scrollEndTimeout);
|
|
926
1143
|
}
|
|
927
|
-
if (state.resizeTimeout) {
|
|
928
|
-
clearTimeout(state.resizeTimeout);
|
|
929
|
-
}
|
|
930
1144
|
if (dragState) {
|
|
931
1145
|
cleanupDrag(dragState);
|
|
932
1146
|
}
|
|
@@ -983,6 +1197,9 @@ var createSlider = (settings) => {
|
|
|
983
1197
|
easeOutExpo,
|
|
984
1198
|
easeOutQuad,
|
|
985
1199
|
generateBullets,
|
|
986
|
-
|
|
1200
|
+
generateThumbs,
|
|
1201
|
+
injectStyles,
|
|
1202
|
+
linear,
|
|
1203
|
+
removeStyles
|
|
987
1204
|
});
|
|
988
1205
|
//# sourceMappingURL=index.cjs.map
|