Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions src/slider/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@
<body>
<main>
<!--Slider that slides horizontally with responsive settings.-->
<tp-slider swipe-threshold="210" flexible-height="yes" infinite="yes" swipe="yes" responsive='[{"media":"(min-width: 600px)","flexible-height":"yes","infinite":"no","swipe":"yes","auto-slide-interval":2000,"per-view":2,"step":2},{"media":"(min-width: 300px)","flexible-height":"yes","infinite":"no","swipe":"yes","behaviour":"fade","auto-slide-interval":2000,"per-view":1,"step":1}]'>
<tp-slider flexible-height="yes" infinite="yes" responsive='[{"media":"(min-width: 600px)","flexible-height":"yes","infinite":"no","auto-slide-interval":2000,"per-view":2,"step":2},{"media":"(min-width: 300px)","flexible-height":"yes","infinite":"no","behaviour":"fade","auto-slide-interval":2000,"per-view":1,"step":1}]'>
<tp-slider-arrow direction="previous"><button>&laquo; Previous</button></tp-slider-arrow>
<tp-slider-arrow direction="next"><button>Next &raquo;</button></tp-slider-arrow>
<tp-slider-track>
Expand Down Expand Up @@ -63,7 +63,7 @@
<br>

<!--Slider with 'fade in' effect and flexible height-->
<tp-slider flexible-height="yes" infinite="yes" swipe="yes" behaviour="fade">
<tp-slider flexible-height="yes" infinite="yes" behaviour="fade">
<tp-slider-arrow direction="previous"><button>&laquo; Previous</button></tp-slider-arrow>
<tp-slider-arrow direction="next"><button>Next &raquo;</button></tp-slider-arrow>
<tp-slider-track>
Expand Down Expand Up @@ -92,7 +92,7 @@
<br>

<!--Slider with 'fade in' effect and not flexible height-->
<tp-slider behaviour="fade" infinite="yes" swipe="yes">
<tp-slider behaviour="fade" infinite="yes">
<tp-slider-arrow direction="previous"><button>&laquo; Previous</button></tp-slider-arrow>
<tp-slider-arrow direction="next"><button>Next &raquo;</button></tp-slider-arrow>
<tp-slider-track>
Expand Down Expand Up @@ -162,14 +162,14 @@
</tp-slider>

<!--Nested sliders-->
<tp-slider flexible-height="yes" swipe="yes" behaviour="fade">
<tp-slider flexible-height="yes" behaviour="fade">
<tp-slider-arrow direction="previous"><button>&laquo; Previous</button></tp-slider-arrow>
<tp-slider-arrow direction="next"><button>Next &raquo;</button></tp-slider-arrow>
<tp-slider-track>
<tp-slider-slides>
<tp-slider-slide><img src="https://picsum.photos/600/300" width="600" height="300" alt=""></tp-slider-slide>
<tp-slider-slide>
<tp-slider flexible-height="yes" infinite="yes" swipe="yes">
<tp-slider flexible-height="yes" infinite="yes">
<tp-slider-arrow direction="previous"><button>&laquo; Previous</button></tp-slider-arrow>
<tp-slider-arrow direction="next"><button>Next &raquo;</button></tp-slider-arrow>
<tp-slider-track>
Expand Down
4 changes: 4 additions & 0 deletions src/slider/style.scss
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@ tp-slider-slides {
position: relative;
display: flex;
align-items: flex-start;
overflow-y: visible;
overflow-x: auto;
scroll-snap-type: x mandatory;
scrollbar-width: none;

tp-slider:not([resizing="yes"]) & {
transition-duration: 0.6s;
Expand Down
149 changes: 80 additions & 69 deletions src/slider/tp-slider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,8 @@ export class TPSliderElement extends HTMLElement {
/**
* Properties.
*/
protected touchStartX: number = 0;
protected touchStartY: number = 0;
protected swipeThreshold: number = 200;
protected slidesScrollContainer: TPSliderSlidesElement | null;
protected slidesScrollContainerRect: DOMRect | {};
protected responsiveSettings: { [ key: string ]: any };
protected allowedResponsiveKeys: string[] = [
'flexible-height',
Expand All @@ -29,22 +28,25 @@ export class TPSliderElement extends HTMLElement {
'step',
'responsive',
];
protected isProgramaticScroll: boolean = false;

/**
* Constructor.
*/
constructor() {
// Initialize parent.
super();
this.slidesScrollContainer = this.querySelector( 'tp-slider-slides' );
this.slidesScrollContainerRect = this.slidesScrollContainer?.getBoundingClientRect() ?? {};

// Add event listener to handle current slide attribute on scroll.
this.slidesScrollContainer?.addEventListener( 'scroll', this.handleCurrentSlideOnScroll.bind( this ) );

// Set current slide.
if ( ! this.getAttribute( 'current-slide' ) ) {
this.setAttribute( 'current-slide', '1' );
}

// Threshold Setting.
this.swipeThreshold = Number( this?.getAttribute( 'swipe-threshold' ) ?? '200' );

// Initialize slider.
this.slide();
this.autoSlide();
Expand All @@ -65,10 +67,18 @@ export class TPSliderElement extends HTMLElement {
window.addEventListener( 'resize', this.handleResize.bind( this ) );
document.fonts.ready.then( () => this.handleResize() );
}
}

// Touch listeners.
this.addEventListener( 'touchstart', this.handleTouchStart.bind( this ), { passive: true } );
this.addEventListener( 'touchend', this.handleTouchEnd.bind( this ) );
/**
* Flag programmatic scroll to prevent scroll handling.
*/
protected flagProgramaticScroll(): void {
// Flag programmatic scroll.
this.isProgramaticScroll = true;
setTimeout( () => {
// Unflag programmatic scroll.
this.isProgramaticScroll = false;
}, 500 );
}

/**
Expand All @@ -92,7 +102,7 @@ export class TPSliderElement extends HTMLElement {
*/
static get observedAttributes(): string[] {
// Observed attributes.
return [ 'current-slide', 'flexible-height', 'infinite', 'swipe', 'per-view', 'step' ];
return [ 'current-slide', 'flexible-height', 'infinite', 'per-view', 'step' ];
}

/**
Expand Down Expand Up @@ -331,6 +341,9 @@ export class TPSliderElement extends HTMLElement {
* @protected
*/
protected slide(): void {
// Flag programmatic scroll.
this.flagProgramaticScroll();

// Check if slider is disabled.
if ( 'yes' === this.getAttribute( 'disabled' ) ) {
// Yes, it is. So stop.
Expand Down Expand Up @@ -358,7 +371,10 @@ export class TPSliderElement extends HTMLElement {
// Check if behaviour is set to fade and slide on the current slide index is present in the slides array.
if ( 'fade' !== behaviour && slides[ this.currentSlideIndex - 1 ] ) {
// Yes, it is. So slide to the current slide.
slidesContainer.style.left = `-${ slides[ this.currentSlideIndex - 1 ].offsetLeft }px`;
slidesContainer.scroll( {
left: slides[ this.currentSlideIndex - 1 ].offsetLeft - slides[ 0 ].offsetLeft,
behavior: 'smooth',
} );
}
}

Expand Down Expand Up @@ -487,6 +503,59 @@ export class TPSliderElement extends HTMLElement {
}
}

/**
* Handle current slide attribute on scroll.
*
* This is to handle the case when the user scrolls the slides track
* and we need to update the current slide index based on the scroll position.
*/
handleCurrentSlideOnScroll() {
// Check if this is a programmatic scroll.
if ( this.isProgramaticScroll ) {
// Early return to prevent handling programmatic scroll.
return;
}

// Check if we have scroll container.
if ( ! this.slidesScrollContainer ) {
// No scroll container, early return.
return;
}

// Get all the slides.
const slides : NodeListOf<TPSliderSlideElement> | null = this.querySelectorAll( 'tp-slider-slide' );

// If no slides.
if ( 0 === slides.length ) {
// Early return.
return;
}

// Check if scroll position is at right end.
const isAtRightEnd = Math.abs( this.slidesScrollContainer.scrollLeft + this.slidesScrollContainer.clientWidth - this.slidesScrollContainer.scrollWidth ) < 1;

// Conditionally set the current slide index based on the scroll position.
if ( isAtRightEnd ) {
// If the current slide index is equal to the total number of slides, set it to the last slide.
this.setCurrentSlide( slides.length );
} else {
// Loop through all slides and check which slide is intersecting with the left edge of the scroll container.
slides.forEach( ( slide: TPSliderSlideElement, index: number ) => {
// Get the bounding rectangle of the slide.
const slideRect = slide.getBoundingClientRect();

// Check if the slide is aligned with the left edge of the scroll container.
const isAlignedWithLeftEdge = Math.abs( slideRect?.left - ( this.slidesScrollContainerRect as DOMRect ).left ) < 1;

// Check if the slide is intersecting with the left edge of the scroll container.
if ( isAlignedWithLeftEdge ) {
// Yes, it is. So set the current slide index.
this.setCurrentSlide( index + 1 );
}
} );
}
}

/**
* Update the height of the slider based on current slide.
*/
Expand Down Expand Up @@ -624,64 +693,6 @@ export class TPSliderElement extends HTMLElement {
} );
}

/**
* Detect touch start event, and store the starting location.
*
* @param {Event} e Touch event.
*
* @protected
*/
protected handleTouchStart( e: TouchEvent ): void {
// initialize touch start coordinates
if ( 'yes' === this.getAttribute( 'swipe' ) ) {
this.touchStartX = e.touches[ 0 ].clientX;
this.touchStartY = e.touches[ 0 ].clientY;
}
}

/**
* Detect touch end event, and check if it was a left or right swipe.
*
* @param {Event} e Touch event.
*
* @protected
*/
protected handleTouchEnd( e: TouchEvent ): void {
// Early return if swipe is not enabled.
if ( 'yes' !== this.getAttribute( 'swipe' ) ) {
// Early return.
return;
}

// Calculate the horizontal and vertical distance moved.
const touchEndX: number = e.changedTouches[ 0 ].clientX;
const touchEndY: number = e.changedTouches[ 0 ].clientY;
const swipeDistanceX: number = touchEndX - this.touchStartX;
const swipeDistanceY: number = touchEndY - this.touchStartY;

// Determine if the swipe is predominantly horizontal or vertical.
const isHorizontalSwipe: boolean = Math.abs( swipeDistanceX ) > Math.abs( swipeDistanceY );

// If it's not horizontal swipe, return
if ( ! isHorizontalSwipe ) {
// Early return.
return;
}

// Check if it's a right or left swipe.
if ( swipeDistanceX > 0 ) {
// Right-Swipe: Check if horizontal swipe distance is less than the threshold.
if ( swipeDistanceX < this.swipeThreshold ) {
this.previous();
}
} else if ( swipeDistanceX < 0 ) {
// Left-Swipe: Check if horizontal swipe distance is less than the threshold.
if ( swipeDistanceX > -this.swipeThreshold ) {
this.next();
}
}
}

/**
* Auto slide.
*/
Expand Down