1/** 2 * GSAP Slider x Hamak 1.0.0 3 * AUTHORS: Quentin Lequenne & Sounthida Kong 4 * DECLARATION 5 * const slide = new GSapSlider( selector, options) 6 * 7 * STRUCTURE 8 * .gsap-slider 9 * -- .gsap-wrapper 10 * ---- .gsap-slide 11 * ---- .gsap-slide 12 * ---- ... 13 * 14 * OPTIONS 15 * option; type; default => fonction 16 * slidesToShow; int; 1 => Slides à afficher 17 * spaceBetweenSlides; int; 0 => Padding horizontal ajouté à chaque slide 18 * sameHeightSlide; bool; false => Ajuste les tailles de slide à la plus haute 19 * speed; float; 0.5 => Vitesse de transition (en secondes) 20 * ease; string; power1.inOut => Type de transition 21 * fade; bool; false => Transition en fondu (une seule slide) 22 * arrows; bool; false => Instancie les flèches 23 * arrowPrev; string; false => Sélecteur de la flèche Précédent (arrows doit être à true) 24 * arrowNext; string; false => Sélecteur de la flèche Suivant (arrows doit être à true) 25 * pauseOnHover; bool; false => Interrompt les animations au hover 26 * swipe; bool; false => Permet de swiper pour passer aux slides précédentes/suivantes 27 * repeat; bool; false => Slider infini si true 28 * center; bool; false => La slide active est centrée 29 * paused; bool; true => Défilement smooth continu si false (Attention: pas un autoplay) 30 * responsive; array; none => Modification d'options aux breakpoints (sauf fade) 31 * autoplay; bool; false => Lecture automatique 32 * duration; int; 4000 => Temps de break en lecture auto (en ms) 33 * locomotiveSpeed; int; 200 => Vitesse de locomotive (en px/s, couplé à paused: false) 34 * delayedContent; bool; false => Applique un délai d'apparition au contenu de la slide ayant la classe "delayed-content" 35 * delayedContentY; int; 0 => Point d'origine du contenu délayé en Y 36 * delayedContentX; int; 0 => Point d'origine du contenu délayé en X 37 * whiteSpace; bool; true => Empêche la mise à la ligne de phrases si false et recalcule leur largeur 38 * 39 */ 40 41 42class GSapSlider { 43 44 /////////////////////////////////////////////////////////////////////////// 45 // VARIABLES 46 /////////////////////////////////////////////////////////////////////////// 47 sliders; 48 sliderConfig; 49 mainConfig; 50 loopConfig; 51 responsiveOptions; 52 responsiveCount; 53 responsiveBreakpoints; 54 55 /////////////////////////////////////////////////////////////////////////// 56 // CONSTRUCTOR 57 /////////////////////////////////////////////////////////////////////////// 58 constructor ( selector, sliderConfig ){ 59 this.sliders = $(selector); 60 this.sliderConfig = sliderConfig; 61 this.mainConfig = this.setConfig( 'slide' ); 62 this.loopConfig = this.setConfig( 'loop' ); 63 this.responsiveOptions = this.sliderConfig.responsive; 64 this.responsiveBreakpoints = []; 65 this.responsiveCount = this.countResponsive( this.responsiveOptions ); 66 this.loopTimeline = this.initLoopTimeline(); 67 this.init(); 68 } 69 70 /////////////////////////////////////////////////////////////////////////// 71 // INIT FUNCTION 72 /////////////////////////////////////////////////////////////////////////// 73 74 initLoopTimeline(){ 75 var loop = false; 76 const self = this; 77 this.sliders.each(function(){ 78 const container = $(this); 79 const boxes = gsap.utils.toArray($(this).find('.gsap-slide')); 80 self.updateSliderConfig( container, boxes ); 81 if( !self.mainConfig.fade && boxes.length > self.mainConfig.slidesToShow || !self.mainConfig.fade){ 82 loop = self.horizontalLoop(boxes, self.loopConfig); 83 84 console.log(self.loopConfig); 85 86 if(self.mainConfig.arrows == true){ 87 if(boxes.length <= self.mainConfig.slidesToShow){ 88 let previous = document.querySelector(self.mainConfig.arrowPrev); 89 previous.closest('.arrows').style.display = 'none'; 90 }else{ 91 let previous = document.querySelector(self.mainConfig.arrowPrev); 92 previous.closest('.arrows').style.display = 'flex'; 93 } 94 } 95 } 96 }) 97 return{ 98 loop 99 } 100 } 101 init = () => { 102 const self = this; 103 104 this.sliders.each(function(){ 105 const container = $(this); 106 const boxes = gsap.utils.toArray($(this).find('.gsap-slide')); 107 108 self.setEnvironment( container ); 109 110 self.updateSliderConfig( container, boxes ); 111 112 window.addEventListener("resize", () => { 113 self.updateSliderConfig( container, boxes ); 114 115 if ( self.mainConfig.whiteSpace == false && !self.mainConfig.fade ) { 116 self.noWhiteSpace( boxes ); 117 } 118 }); 119 120 var loop = false; 121 122 if( !self.mainConfig.fade && boxes.length > self.mainConfig.slidesToShow ){
123 loop = self.loopTimeline.loop; 124 125 $(this).hover( 126 function(){ 127 if( self.mainConfig.pauseOnHover && !self.loopConfig.paused && !self.mainConfig.fade ){ 128 loop.pause(); 129 } 130 }, 131 function(){ 132 if( self.mainConfig.pauseOnHover && !self.loopConfig.paused && !self.mainConfig.fade ){ 133 loop.play(); 134 } 135 } 136 ) 137 } 138 139 if(self.mainConfig.slideToClickedSlide){ 140 boxes.forEach((box, i) => box.addEventListener("click", () => loop.toIndex(i, {duration: 0.8, ease: "power1.inOut"}))); 141 } 142 143 if(self.mainConfig.arrows){ 144 if( self.mainConfig.arrowPrev ){ 145 document.querySelector(self.mainConfig.arrowPrev).addEventListener("click", () => { 146 if( self.mainConfig.arrows && self.mainConfig.arrowPrev && !self.mainConfig.fade ){ 147 loop.previous({duration: self.mainConfig.speed, ease: self.mainConfig.ease}) 148 } 149 else if( self.mainConfig.arrows && self.mainConfig.arrowPrev && self.mainConfig.fade ){ 150 self.fadeSlide(container, 'prev'); 151 } 152 }); 153 } 154 if( self.mainConfig.arrowNext ){ 155 document.querySelector(self.mainConfig.arrowNext).addEventListener("click", () => { 156 if( self.mainConfig.arrows && self.mainConfig.arrowNext && !self.mainConfig.fade ){ 157 loop.next({duration: self.mainConfig.speed, ease: self.mainConfig.ease}) 158 } 159 else if( self.mainConfig.arrows && self.mainConfig.arrowNext && self.mainConfig.fade ){ 160 self.fadeSlide(container, 'next'); 161 } 162 }); 163 } 164 165 } 166 167 if(self.mainConfig.autoplay){ 168 169 const autoplay = new IntervalTimer(() => { 170 if( loop != false & !self.mainConfig.fade ){ 171 loop.next({duration: self.mainConfig.speed, ease: self.mainConfig.ease}) 172 } 173 else if( self.mainConfig.fade ){ 174 self.fadeSlide(container, 'next'); 175 } 176 }, self.mainConfig.duration); 177 178 autoplay.start(); 179 180 if(self.mainConfig.arrows){ 181 document.querySelector(self.mainConfig.arrowPrev).addEventListener("click", () => { 182 autoplay.pause(); 183 setTimeout(() => { 184 autoplay.start(); 185 }, self.mainConfig.duration); 186 }) 187 188 document.querySelector(self.mainConfig.arrowNext).addEventListener("click", () => { 189 autoplay.pause(); 190 setTimeout(() => { 191 autoplay.start(); 192 }, self.mainConfig.duration); 193 }) 194 195 } 196 197 /* 198 if(self.mainConfig.pauseOnHover == true){ 199 $(this).hover( 200 function(){ 201 autoplay.pause(); 202 }, 203 function(){ 204 autoplay.resume(); 205 } 206 ) 207 } 208 */ 209 } 210 211 if( self.mainConfig.swipe ){ 212 var startX; 213 var startY; 214 var threshold = 30; 215 216 this.addEventListener('touchstart', function(event) { 217 startX = event.touches[0].clientX; 218 startY = event.touches[0].clientY; 219 }); 220 221 this.addEventListener('touchmove', function(event) { 222 event.preventDefault(); 223 }); 224 225 this.addEventListener('touchend', function(event) { 226 var endX = event.changedTouches[0].clientX; 227 var endY = event.changedTouches[0].clientY; 228 229 var deltaX = startX - endX; 230 var deltaY = startY - endY; 231 232 if (Math.abs(deltaX) > Math.abs(deltaY) && Math.abs(deltaX) > threshold) { 233 if (deltaX > 0) { 234 if( !self.mainConfig.fade ){ 235 loop.previous({duration: self.mainConfig.speed, ease: self.mainConfig.ease}) 236 } 237 else if( self.mainConfig.fade ){ 238 self.fadeSlide(container, 'prev'); 239 } 240 } else { 241 if( !self.mainConfig.fade ){ 242 loop.next({duration: self.mainConfig.speed, ease: self.mainConfig.ease}) 243 } 244 else if( self.mainConfig.fade ){ 245 self.fadeSlide(container, 'next'); 246 } 247 } 248 } 249 }); 250 } 251 252 if( self.mainConfig.adaptiveHeight ) { 253 let startHeight = container.find('.slide-active').innerHeight(); 254 container.css('height', startHeight); 255 } 256 257 }) 258 } 259
260 /////////////////////////////////////////////////////////////////////////// 261 // SET CONFIG ON FIRST INITIALIZATION 262 /////////////////////////////////////////////////////////////////////////// 263 setConfig = ( target ) => { 264 const self = this; 265 let activeElement; 266 267 if(self.sliderConfig.slidesToShow == 'autoWidth' && self.sliderConfig.draggable == true){ 268 // SET DRAGGABLE ONLY IF DIV IS OVERFLOWING 269 let container_width = this.sliders.outerWidth(); 270 let width_slides = this.sliders.find('.gsap-slide:first-child').outerWidth(); 271 let total_slides = this.sliders.find('.gsap-slide').length; 272 let container_scroll_width = width_slides * total_slides; 273 274 if(container_scroll_width > container_width){ 275 this.sliders.find('.gsap-wrapper').css('justify-content', 'flex-start'); 276 self.sliderConfig.draggable = true; 277 }else{ 278 this.sliders.find('.gsap-wrapper').css('justify-content', 'center'); 279 self.sliderConfig.draggable = false; 280 } 281 } 282 283 if( target == 'slide' ){ 284 return { 285 slidesToShow: self.sliderConfig.slidesToShow != null ? self.sliderConfig.slidesToShow : 1, 286 spaceBetweenSlides: self.sliderConfig.spaceBetweenSlides != null ? self.sliderConfig.spaceBetweenSlides / 2 : 0, 287 sameHeightSlides: self.sliderConfig.sameHeightSlides != null ? self.sliderConfig.sameHeightSlides : false, 288 speed: self.sliderConfig.speed != null ? self.sliderConfig.speed : 0.5, 289 ease: self.sliderConfig.ease != null ? self.sliderConfig.ease : "power1.inOut", 290 fade: self.sliderConfig.fade != null ? self.sliderConfig.fade : false, 291 arrows: self.sliderConfig.arrows != null ? self.sliderConfig.arrows : true, 292 arrowPrev: self.sliderConfig.arrows != null && self.sliderConfig.arrowPrev != null ? self.sliderConfig.arrowPrev : false, 293 arrowNext: self.sliderConfig.arrows != null && self.sliderConfig.arrowNext != null ? self.sliderConfig.arrowNext : false, 294 pauseOnHover: self.sliderConfig.pauseOnHover != null ? self.sliderConfig.pauseOnHover : true, 295 swipe: self.sliderConfig.swipe != null ? self.sliderConfig.swipe : false, 296 autoplay: self.sliderConfig.autoplay != null ? self.sliderConfig.autoplay : false, 297 duration: self.sliderConfig.duration != null ? self.sliderConfig.duration : 4000, 298 delayedContent: self.sliderConfig.delayedContent != null ? self.sliderConfig.delayedContent : false, 299 delayedContentY: self.sliderConfig.delayedContentY != null ? self.sliderConfig.delayedContentY : 0, 300 delayedContentX: self.sliderConfig.delayedContentX != null ? self.sliderConfig.delayedContentX : 0, 301 adaptiveHeight: self.sliderConfig.adaptiveHeight != null ? self.sliderConfig.adaptiveHeight : false, 302 whiteSpace: self.sliderConfig.whiteSpace != null ? self.sliderConfig.whiteSpace : true, 303 slideToClickedSlide: self.sliderConfig.slideToClickedSlide != null ? self.sliderConfig.slideToClickedSlide : true, 304 } 305 } 306 else if ( target == 'loop' ){ 307 return { 308 onChange: (element, index) => { // when the active element changes, this function gets called. 309 activeElement && activeElement.classList.remove("slide-active"); 310 element.classList.add("slide-active"); 311 activeElement = element; 312 }, 313 reversed: self.sliderConfig.reversed != null ? self.sliderConfig.reversed : false, 314 draggable: self.sliderConfig.draggable != null ? self.sliderConfig.draggable : false, 315 repeat: self.sliderConfig.repeat != null && self.sliderConfig.repeat == true ? -1 : false, 316 center: self.sliderConfig.center != null ? self.sliderConfig.center : false, 317 paused: self.sliderConfig.paused != null ? self.sliderConfig.paused : true, 318 speed: self.sliderConfig.locomotiveSpeed != null ? self.sliderConfig.locomotiveSpeed : 0.1, 319 } 320 } 321 } 322
323 /////////////////////////////////////////////////////////////////////////// 324 // UPDATE CONFIG (RESIZE, QUERIES) 325 /////////////////////////////////////////////////////////////////////////// 326 updateConfig = ( target, breakpoint = false ) => { 327 const self = this; 328 let activeElement; 329 330 if( target == 'slide' ){ 331 332 var backupOptions = this.mainConfig != null ? this.mainConfig : this.sliderConfig; 333 334 335 if( breakpoint ) { 336 return { 337 slidesToShow: self.responsiveOptions[breakpoint].slidesToShow != null ? self.responsiveOptions[breakpoint].slidesToShow : backupOptions.slidesToShow, 338 spaceBetweenSlides: self.responsiveOptions[breakpoint].spaceBetweenSlides != null ? self.responsiveOptions[breakpoint].spaceBetweenSlides / 2 : backupOptions.spaceBetweenSlides, 339 sameHeightSlides: self.responsiveOptions[breakpoint].sameHeightSlides != null ? self.responsiveOptions[breakpoint].sameHeightSlides : backupOptions.sameHeightSlides, 340 speed: self.responsiveOptions[breakpoint].speed != null ? self.responsiveOptions[breakpoint].speed : backupOptions.speed, 341 ease: self.responsiveOptions[breakpoint].ease != null ? self.responsiveOptions[breakpoint].ease : backupOptions.ease, 342 arrows: self.responsiveOptions[breakpoint].arrows != null ? self.responsiveOptions[breakpoint].arrows : backupOptions.arrows, 343 arrowPrev: self.responsiveOptions[breakpoint].arrows != null && self.responsiveOptions[breakpoint].arrowPrev != null ? self.responsiveOptions[breakpoint].arrowPrev : backupOptions.arrowPrev, 344 arrowNext: self.responsiveOptions[breakpoint].arrows != null && self.responsiveOptions[breakpoint].arrowNext != null ? self.responsiveOptions[breakpoint].arrowNext : backupOptions.arrowNext, 345 pauseOnHover: self.responsiveOptions[breakpoint].pauseOnHover != null ? self.responsiveOptions[breakpoint].pauseOnHover : backupOptions.pauseOnHover, 346 swipe: self.responsiveOptions[breakpoint].swipe != null ? self.responsiveOptions[breakpoint].swipe : backupOptions.swipe, 347 autoplay: self.responsiveOptions[breakpoint].autoplay != null ? self.responsiveOptions[breakpoint].autoplay : backupOptions.autoplay, 348 duration: self.responsiveOptions[breakpoint].duration != null ? self.responsiveOptions[breakpoint].duration : backupOptions.duration, 349 delayedContent: self.responsiveOptions[breakpoint].delayedContent != null ? self.responsiveOptions[breakpoint].delayedContent : backupOptions.delayedContent, 350 delayedContentY: self.responsiveOptions[breakpoint].delayedContentY != null ? self.responsiveOptions[breakpoint].delayedContentY : backupOptions.delayedContentY, 351 delayedContentX: self.responsiveOptions[breakpoint].delayedContentX != null ? self.responsiveOptions[breakpoint].delayedContentX : backupOptions.delayedContentX, 352 adaptiveHeight: self.responsiveOptions[breakpoint].adaptiveHeight != null ? self.responsiveOptions[breakpoint].adaptiveHeight : backupOptions.adaptiveHeight, 353 whiteSpace: self.responsiveOptions[breakpoint].whiteSpace != null ? self.responsiveOptions[breakpoint].whiteSpace : backupOptions.whiteSpace, 354 fade: backupOptions.fade, 355 } 356 } 357 else{ 358 return { 359 slidesToShow: self.sliderConfig.slidesToShow != null ? self.sliderConfig.slidesToShow : 1, 360 spaceBetweenSlides: self.sliderConfig.spaceBetweenSlides != null ? self.sliderConfig.spaceBetweenSlides / 2 : 0, 361 sameHeightSlides: self.sliderConfig.sameHeightSlides != null ? self.sliderConfig.sameHeightSlides : false, 362 speed: self.sliderConfig.speed != null ? self.sliderConfig.speed : 0.5, 363 ease: self.sliderConfig.ease != null ? self.sliderConfig.ease : "power1.inOut", 364 arrows: self.sliderConfig.arrows != null ? self.sliderConfig.arrows : true, 365 arrowPrev: self.sliderConfig.arrows != null && self.sliderConfig.arrowPrev != null ? self.sliderConfig.arrowPrev : false, 366 arrowNext: self.sliderConfig.arrows != null && self.sliderConfig.arrowNext != null ? self.sliderConfig.arrowNext : false, 367 pauseOnHover: self.sliderConfig.pauseOnHover != null ? self.sliderConfig.pauseOnHover : true, 368 swipe: self.sliderConfig.swipe != null ? self.sliderConfig.swipe : false, 369 fade: self.sliderConfig.fade != null ? self.sliderConfig.fade : false,
370 autoplay: self.sliderConfig.autoplay != null ? self.sliderConfig.autoplay : false, 371 duration: self.sliderConfig.duration != null ? self.sliderConfig.duration : 4000, 372 delayedContent: self.sliderConfig.delayedContent != null ? self.sliderConfig.delayedContent : false, 373 delayedContentY: self.sliderConfig.delayedContentY != null ? self.sliderConfig.delayedContentY : 0, 374 delayedContentX: self.sliderConfig.delayedContentX != null ? self.sliderConfig.delayedContentX : 0, 375 adaptiveHeight: self.sliderConfig.adaptiveHeight != null ? self.sliderConfig.adaptiveHeight : false, 376 whiteSpace: self.sliderConfig.whiteSpace != null ? self.sliderConfig.whiteSpace : true, 377 } 378 } 379 } 380 else if ( target == 'loop' ){ 381 var backupOptions = this.loopConfig != null ? this.loopConfig : this.sliderConfig; 382 383 if ( breakpoint ) { 384 return { 385 onChange: (element, index) => { // when the active element changes, this function gets called. 386 activeElement && activeElement.classList.remove("slide-active"); 387 element.classList.add("slide-active"); 388 activeElement = element; 389 }, 390 reversed: self.sliderConfig.reversed != null ? self.sliderConfig.reversed : false, 391 draggable: self.responsiveOptions[breakpoint].draggable != null ? self.responsiveOptions[breakpoint].draggable : backupOptions.draggable, 392 center: self.responsiveOptions[breakpoint].center != null ? self.responsiveOptions[breakpoint].center : backupOptions.center, 393 paused: self.responsiveOptions[breakpoint].paused != null ? self.responsiveOptions[breakpoint].paused : backupOptions.paused, 394 repeat: self.responsiveOptions[breakpoint].repeat != null && self.responsiveOptions[breakpoint].repeat ? -1 : backupOptions.repeat, 395 speed: self.responsiveOptions[breakpoint].locomotiveSpeed != null ? self.responsiveOptions[breakpoint].locomotiveSpeed : backupOptions.speed, 396 } 397 } 398 else{ 399 return { 400 onChange: (element, index) => { // when the active element changes, this function gets called. 401 activeElement && activeElement.classList.remove("slide-active"); 402 element.classList.add("slide-active"); 403 activeElement = element; 404 }, 405 reversed: self.sliderConfig.reversed != null ? self.sliderConfig.reversed : false, 406 draggable: self.sliderConfig.draggable != null ? self.sliderConfig.draggable : false, 407 center: self.sliderConfig.center != null ? self.sliderConfig.center : false, 408 paused: self.sliderConfig.paused != null ? self.sliderConfig.paused : true, 409 repeat: self.sliderConfig.repeat != null && self.sliderConfig.repeat ? -1 : false, 410 speed: self.sliderConfig.locomotiveSpeed != null ? self.sliderConfig.locomotiveSpeed : 0.5, 411 } 412 } 413 } 414 } 415 416 /////////////////////////////////////////////////////////////////////////// 417 // SET SLIDER CONFIG (RESIZE, QUERIES) 418 /////////////////////////////////////////////////////////////////////////// 419 updateSliderConfig = ( container, boxes ) => { 420 421 const self = this; 422 423 const resp = self.responsiveBreakpoints.sort(function(a, b) { return b - a; }); 424 425 if(resp.length > 0){ 426 if( window.matchMedia('(min-width: ' + resp[0] + 'px)').matches ){ 427 self.mainConfig = self.updateConfig( 'slide' ); 428 self.loopConfig = self.updateConfig( 'loop' ); 429 } 430 else if( 431 window.matchMedia('(max-width: ' + resp[0] + 'px)').matches && 432 window.matchMedia('(min-width: ' + resp[resp.length - 1] + 'px)').matches 433 ){ 434 for(var i = 0; i < resp.length - 1; i++){ 435 if( 436 window.matchMedia('(max-width: ' + resp[i] + 'px)').matches && 437 window.matchMedia('(min-width: ' + resp[i + 1] + 'px)').matches 438 ){ 439 self.mainConfig = self.updateConfig( 'slide', resp[i] ); 440 self.loopConfig = self.updateConfig( 'loop', resp[i] ); 441 } 442 } 443 } 444 else{ 445 self.mainConfig = self.updateConfig( 'slide', resp[resp.length - 1] ); 446 self.loopConfig = self.updateConfig( 'loop', resp[resp.length - 1] ); 447 } 448 449 this.sliders.each(function(){ 450 const container = $(this); 451 const boxes = gsap.utils.toArray($(this).find('.gsap-slide')); 452 if( !self.mainConfig.fade && boxes.length > self.mainConfig.slidesToShow || !self.mainConfig.fade){ 453 if(self.mainConfig.arrows == true){ 454 if(boxes.length <= self.mainConfig.slidesToShow){ 455 let previous = document.querySelector(self.mainConfig.arrowPrev); 456 previous.closest('.arrows').style.display = 'none'; 457 }else{ 458 let previous = document.querySelector(self.mainConfig.arrowPrev); 459 previous.closest('.arrows').style.display = 'flex'; 460 } 461 } 462 } 463 }) 464 } 465 466 const config = self.mainConfig; 467 468 const wrapper = container.find('.gsap-wrapper');
469 const wrapperWidth = wrapper.outerWidth(); 470 var higherSlide = 0; 471 var indexSlide = 0; 472 var wrapperAlign = config.sameHeightSlides == true ? 'stretch' : 'center'; 473 474 wrapper.css({ 475 'display': 'flex', 476 'align-items': wrapperAlign, 477 'position': 'relative' 478 }); 479 480 if( config.spaceBetweenSlides != 0 && config.fade == false ){ 481 482 wrapper.css('margin', '0 -' + config.spaceBetweenSlides + 'px'); 483 } 484 boxes.forEach((box, i) => { 485 if(config.whiteSpace !== false){ 486 $(box).attr('data-gsap-slide-index', indexSlide).css({ 487 'flex-shrink': '0', 488 }); 489 } 490 491 if(config.fade == true){ 492 let slideHeight = $(box).outerHeight(); 493 let opacity = indexSlide == 0 ? 1 : 0; 494 let visibility = indexSlide == 0 ? 'visible' : 'hidden'; 495 let active = indexSlide == 0 ? 'slide-active' : ''; 496 497 $(box).addClass(active).css({ 498 'width': '100%', 499 'position': 'absolute', 500 'top': 0, 501 'left' : 0, 502 'opacity': opacity, 503 'visibility': visibility, 504 }) 505 506 higherSlide = slideHeight > higherSlide ? slideHeight : higherSlide; 507 } 508 else{ 509 $(box).css({ 510 'position': 'relative', 511 'top': 'auto', 512 'left' : 'auto', 513 'opacity': 1, 514 'visibility': 'visible', 515 }) 516 if( config.spaceBetweenSlides != 0 && config.whiteSpace !== false ) { 517 var slideWidth = (wrapperWidth / config.slidesToShow) + ((config.spaceBetweenSlides * 2) / config.slidesToShow); 518 519 box.style.width = slideWidth + 'px'; 520 box.style.paddingLeft = config.spaceBetweenSlides + 'px'; 521 box.style.paddingRight = config.spaceBetweenSlides + 'px'; 522 } 523 else if( config.whiteSpace == false ) { 524 var boxWidth = $(box).outerWidth(); 525 $(box).find('p').css({'white-space': 'nowrap'}); 526 $(box).css('width', boxWidth + 'px'); 527 } 528 else{ 529 var slideWidth = wrapperWidth / config.slidesToShow; 530 $(box).css('width', slideWidth + 'px') 531 } 532 } 533 534 indexSlide++; 535 }) 536 537 if( config.fade == true ) { 538 wrapper.css('height', higherSlide); 539 } 540 else{ 541 wrapper.css('height', 'auto'); 542 } 543 } 544 545 /////////////////////////////////////////////////////////////////////////// 546 // HORIZONTAL LOOP CODE 547 /////////////////////////////////////////////////////////////////////////// 548 /* 549 horizontalLoop = (items, config) => { 550 items = gsap.utils.toArray(items); 551 config = config || {}; 552 let tl = gsap.timeline({repeat: config.repeat, paused: config.paused, defaults: {ease: "none"}, onReverseComplete: () => tl.totalTime(tl.rawTime() + tl.duration() * 100)}), 553 length = items.length, 554 startX = items[0].offsetLeft, 555 times = [], 556 widths = [], 557 xPercents = [], 558 curIndex = 0, 559 pixelsPerSecond = (config.speed || 1) * 100, 560 snap = config.snap === false ? v => v : gsap.utils.snap(config.snap || 1), // some browsers shift by a pixel to accommodate flex layouts, so for example if width is 20% the first element's width might be 242px, and the next 243px, alternating back and forth. So we snap to 5 percentage points to make things look more natural 561 populateWidths = () => items.forEach((el, i) => { 562 widths[i] = parseFloat(gsap.getProperty(el, "width", "px")); 563 xPercents[i] = snap(parseFloat(gsap.getProperty(el, "x", "px")) / widths[i] * 100 + gsap.getProperty(el, "xPercent"));
564 }), 565 getTotalWidth = () => items[length-1].offsetLeft + xPercents[length-1] / 100 * widths[length-1] - startX + items[length-1].offsetWidth * gsap.getProperty(items[length-1], "scaleX") + (parseFloat(config.paddingRight) || 0), 566 totalWidth, curX, distanceToStart, distanceToLoop, item, i; 567 populateWidths(); 568 gsap.set(items, { // convert "x" to "xPercent" to make things responsive, and populate the widths/xPercents Arrays to make lookups faster. 569 xPercent: i => xPercents[i] 570 }); 571 gsap.set(items, {x: 0}); 572 totalWidth = getTotalWidth(); 573 for (i = 0; i < length; i++) { 574 item = items[i]; 575 curX = xPercents[i] / 100 * widths[i]; 576 distanceToStart = item.offsetLeft + curX - startX; 577 distanceToLoop = distanceToStart + widths[i] * gsap.getProperty(item, "scaleX"); 578 tl.to(item, {xPercent: snap((curX - distanceToLoop) / widths[i] * 100), duration: distanceToLoop / pixelsPerSecond}, 0) 579 .fromTo(item, {xPercent: snap((curX - distanceToLoop + totalWidth) / widths[i] * 100)}, {xPercent: xPercents[i], duration: (curX - distanceToLoop + totalWidth - curX) / pixelsPerSecond, immediateRender: false}, distanceToLoop / pixelsPerSecond) 580 .add("label" + i, distanceToStart / pixelsPerSecond); 581 times[i] = distanceToStart / pixelsPerSecond; 582 } 583 function updateActive(index){ 584 items.forEach((el, i) => { 585 if($(el).attr('data-gsap-slide-index') == index){ 586 $(el).addClass('slide-active'); 587 }else{ 588 $(el).removeClass('slide-active'); 589 } 590 }); 591 } 592 function toIndex(index, vars) { 593 vars = vars || {}; 594 (Math.abs(index - curIndex) > length / 2) && (index += index > curIndex ? -length : length); // always go in the shortest direction 595 let newIndex = gsap.utils.wrap(0, length, index), 596 time = times[newIndex]; 597 if (time > tl.time() !== index > curIndex) { // if we're wrapping the timeline's playhead, make the proper adjustments 598 vars.modifiers = {time: gsap.utils.wrap(0, tl.duration())}; 599 time += tl.duration() * (index > curIndex ? 1 : -1); 600 } 601 curIndex = newIndex; 602 vars.overwrite = true; 603 604 updateActive(curIndex); 605 return tl.tweenTo(time, vars); 606 } 607 tl.next = vars => toIndex(curIndex+1, vars); 608 tl.previous = vars => toIndex(curIndex-1, vars); 609 tl.current = () => curIndex; 610 tl.toIndex = (index, vars) => toIndex(index, vars); 611 tl.updateIndex = () => curIndex = Math.round(tl.progress() * items.length); 612 tl.times = times; 613 tl.progress(1, true).progress(0, true); // pre-render for performance 614 if (config.reversed) { 615 tl.vars.onReverseComplete(); 616 tl.reverse(); 617 } 618 if (config.draggable && typeof(Draggable) === "function") { 619 let proxy = document.createElement("div"), 620 wrap = gsap.utils.wrap(0, 1), 621 ratio, startProgress, draggable, dragSnap, roundFactor, 622 align = () => tl.progress(wrap(startProgress + (draggable.startX - draggable.x) * ratio)), 623 syncIndex = () => tl.updateIndex(); 624 typeof(InertiaPlugin) === "undefined" && console.warn("InertiaPlugin required for momentum-based scrolling and snapping. https://greensock.com/club"); 625 draggable = Draggable.create(proxy, { 626 trigger: items[0].parentNode, 627 type: "x", 628 onPress() { 629 startProgress = tl.progress(); 630 tl.progress(0); 631 populateWidths(); 632 totalWidth = getTotalWidth(); 633 ratio = 1 / totalWidth; 634 dragSnap = totalWidth / items.length; 635 roundFactor = Math.pow(10, ((dragSnap + "").split(".")[1] || "").length); 636 tl.progress(startProgress); 637 }, 638 onDrag: align, 639 onThrowUpdate: align, 640 inertia: true, 641 snap: value => { 642 let n = Math.round(parseFloat(value) / dragSnap) * dragSnap * roundFactor; 643 return (n - n % 1) / roundFactor; 644 }, 645 onRelease: syncIndex, 646 onThrowComplete: () => gsap.set(proxy, {x: 0}) && syncIndex() 647 })[0]; 648 649 tl.draggable = draggable; 650 } 651 652 653 return tl; 654 } 655 */ 656 657 658 horizontalLoop(items, config) { 659 items = gsap.utils.toArray(items); 660 config = config || {}; 661 console.log(config); 662 let onChange = config.onChange, 663 lastIndex = 0, 664 tl = gsap.timeline({repeat: config.repeat, onUpdate: onChange && function() { 665 let i = tl.closestIndex(); 666 if (lastIndex !== i) { 667 lastIndex = i; 668 onChange(items[i], i); 669 } 670 }, paused: config.paused, defaults: {ease: "none"}, onReverseComplete: () => tl.totalTime(tl.rawTime() + tl.duration() * 100)}), 671 length = items.length, 672 startX = items[0].offsetLeft, 673 times = [], 674 widths = [], 675 spaceBefore = [], 676 xPercents = [], 677 curIndex = 0, 678 indexIsDirty = false, 679 center = config.center, 680 pixelsPerSecond = (config.speed || 1) * 100, 681 snap = config.snap === false ? v => v : gsap.utils.snap(config.snap || 1), // some browsers shift by a pixel to accommodate flex layouts, so for example if width is 20% the first element's width might be 242px, and the next 243px, alternating back and forth. So we snap to 5 percentage points to make things look more natural 682 timeOffset = 0, 683 container = center === true ? items[0].parentNode : gsap.utils.toArray(center)[0] || items[0].parentNode, 684 totalWidth, 685 getTotalWidth = () => items[length-1].offsetLeft + xPercents[length-1] / 100 * widths[length-1] - startX + spaceBefore[0] + items[length-1].offsetWidth * gsap.getProperty(items[length-1], "scaleX") + (parseFloat(config.paddingRight) || 0),
686 populateWidths = () => { 687 let b1 = container.getBoundingClientRect(), b2; 688 items.forEach((el, i) => { 689 widths[i] = parseFloat(gsap.getProperty(el, "width", "px")); 690 xPercents[i] = snap(parseFloat(gsap.getProperty(el, "x", "px")) / widths[i] * 100 + gsap.getProperty(el, "xPercent")); 691 b2 = el.getBoundingClientRect(); 692 spaceBefore[i] = b2.left - (i ? b1.right : b1.left); 693 b1 = b2; 694 }); 695 gsap.set(items, { // convert "x" to "xPercent" to make things responsive, and populate the widths/xPercents Arrays to make lookups faster. 696 xPercent: i => xPercents[i] 697 }); 698 totalWidth = getTotalWidth(); 699 }, 700 timeWrap, 701 populateOffsets = () => { 702 timeOffset = center ? tl.duration() * (container.offsetWidth / 2) / totalWidth : 0; 703 center && times.forEach((t, i) => { 704 times[i] = timeWrap(tl.labels["label" + i] + tl.duration() * widths[i] / 2 / totalWidth - timeOffset); 705 }); 706 }, 707 getClosest = (values, value, wrap) => { 708 let i = values.length, 709 closest = 1e10, 710 index = 0, d; 711 while (i--) { 712 d = Math.abs(values[i] - value); 713 if (d > wrap / 2) { 714 d = wrap - d; 715 } 716 if (d < closest) { 717 closest = d; 718 index = i; 719 } 720 } 721 return index; 722 }, 723 populateTimeline = () => { 724 let i, item, curX, distanceToStart, distanceToLoop; 725 tl.clear(); 726 for (i = 0; i < length; i++) { 727 item = items[i]; 728 curX = xPercents[i] / 100 * widths[i]; 729 distanceToStart = item.offsetLeft + curX - startX + spaceBefore[0]; 730 distanceToLoop = distanceToStart + widths[i] * gsap.getProperty(item, "scaleX"); 731 tl.to(item, {xPercent: snap((curX - distanceToLoop) / widths[i] * 100), duration: distanceToLoop / pixelsPerSecond}, 0) 732 .fromTo(item, {xPercent: snap((curX - distanceToLoop + totalWidth) / widths[i] * 100)}, {xPercent: xPercents[i], duration: (curX - distanceToLoop + totalWidth - curX) / pixelsPerSecond, immediateRender: false}, distanceToLoop / pixelsPerSecond) 733 .add("label" + i, distanceToStart / pixelsPerSecond); 734 times[i] = distanceToStart / pixelsPerSecond; 735 } 736 timeWrap = gsap.utils.wrap(0, tl.duration()); 737 }, 738 refresh = (deep) => { 739 let progress = tl.progress(); 740 tl.progress(0, true); 741 populateWidths(); 742 deep && populateTimeline(); 743 populateOffsets(); 744 deep && tl.draggable ? tl.time(times[curIndex], true) : tl.progress(progress, true); 745 }, 746 proxy; 747 gsap.set(items, {x: 0}); 748 populateWidths(); 749 populateTimeline(); 750 populateOffsets(); 751 //window.addEventListener("resize", () => refresh(true)); 752 function toIndex(index, vars) { 753 vars = vars || {}; 754 (Math.abs(index - curIndex) > length / 2) && (index += index > curIndex ? -length : length); // always go in the shortest direction 755 let newIndex = gsap.utils.wrap(0, length, index), 756 time = times[newIndex]; 757 if (time > tl.time() !== index > curIndex && index !== curIndex) { // if we're wrapping the timeline's playhead, make the proper adjustments 758 time += tl.duration() * (index > curIndex ? 1 : -1); 759 } 760 if (time < 0 || time > tl.duration()) { 761 vars.modifiers = {time: timeWrap};
762 } 763 curIndex = newIndex; 764 vars.overwrite = true; 765 gsap.killTweensOf(proxy); 766 return vars.duration === 0 ? tl.time(timeWrap(time)) : tl.tweenTo(time, vars); 767 } 768 tl.toIndex = (index, vars) => toIndex(index, vars); 769 tl.closestIndex = setCurrent => { 770 let index = getClosest(times, tl.time(), tl.duration()); 771 if (setCurrent) { 772 curIndex = index; 773 indexIsDirty = false; 774 } 775 return index; 776 }; 777 tl.current = () => indexIsDirty ? tl.closestIndex(true) : curIndex; 778 tl.next = vars => toIndex(tl.current()+1, vars); 779 tl.previous = vars => toIndex(tl.current()-1, vars); 780 tl.times = times; 781 tl.progress(1, true).progress(0, true); // pre-render for performance 782 if (config.reversed) { 783 tl.vars.onReverseComplete(); 784 tl.reverse(); 785 } 786 if (config.draggable && typeof(Draggable) === "function") { 787 proxy = document.createElement("div") 788 let wrap = gsap.utils.wrap(0, 1), 789 ratio, startProgress, draggable, dragSnap, lastSnap, initChangeX, 790 align = () => tl.progress(wrap(startProgress + (draggable.startX - draggable.x) * ratio)), 791 syncIndex = () => tl.closestIndex(true); 792 typeof(InertiaPlugin) === "undefined" && console.warn("InertiaPlugin required for momentum-based scrolling and snapping. https://greensock.com/club"); 793 draggable = Draggable.create(proxy, { 794 trigger: items[0].parentNode, 795 type: "x", 796 onPressInit() { 797 let x = this.x; 798 gsap.killTweensOf(tl); 799 startProgress = tl.progress(); 800 refresh(); 801 ratio = 1 / totalWidth; 802 initChangeX = (startProgress / -ratio) - x; 803 gsap.set(proxy, {x: startProgress / -ratio}); 804 }, 805 onDrag: align, 806 onThrowUpdate: align, 807 overshootTolerance: 0, 808 inertia: true, 809 snap(value) { 810 //note: if the user presses and releases in the middle of a throw, due to the sudden correction of proxy.x in the onPressInit(), the velocity could be very large, throwing off the snap. So sense that condition and adjust for it. We also need to set overshootTolerance to 0 to prevent the inertia from causing it to shoot past and come back 811 if (Math.abs(startProgress / -ratio - this.x) < 10) { 812 return lastSnap + initChangeX 813 } 814 let time = -(value * ratio) * tl.duration(), 815 wrappedTime = timeWrap(time), 816 snapTime = times[getClosest(times, wrappedTime, tl.duration())], 817 dif = snapTime - wrappedTime; 818 Math.abs(dif) > tl.duration() / 2 && (dif += dif < 0 ? tl.duration() : -tl.duration()); 819 lastSnap = (time + dif) / tl.duration() / -ratio; 820 return lastSnap; 821 }, 822 onRelease() { 823 syncIndex(); 824 draggable.isThrowing && (indexIsDirty = true); 825 }, 826 onThrowComplete: syncIndex 827 })[0]; 828 tl.draggable = draggable; 829 } 830 tl.closestIndex(true); 831 lastIndex = curIndex; 832 onChange && onChange(items[curIndex], curIndex); 833 return tl; 834 } 835 836 837 838 /////////////////////////////////////////////////////////////////////////// 839 // FADE SLIDES 840 /////////////////////////////////////////////////////////////////////////// 841 fadeSlide = ( container, direction ) => { 842 const self = this; 843 const parent = container; 844 845 var currentSlide = parent.find('.slide-active'); 846 var slideIndex = currentSlide.data('gsap-slide-index'); 847 var slideCount = parent.find('.gsap-slide').length; 848 var nextIndex = 0; 849 var prevIndex = 0; 850 851 if( slideIndex + 1 >= slideCount){ 852 if( self.sliderConfig.repeat ){ 853 nextIndex = 0; 854 } 855 else{ 856 nextIndex = slideCount - 1; 857 } 858 } 859 else{ 860 nextIndex = slideIndex + 1; 861 } 862 863 if( slideIndex - 1 <= 0){ 864 if( self.sliderConfig.repeat ){ 865 prevIndex = slideCount - 1; 866 } 867 else{ 868 prevIndex = 0; 869 } 870 } 871 else{ 872 prevIndex = slideIndex - 1; 873 } 874 875 var nextSlide = parent.find('.gsap-slide[data-gsap-slide-index=' + nextIndex + ']'); 876 var prevSlide = parent.find('.gsap-slide[data-gsap-slide-index=' + prevIndex + ']'); 877 878 const fadeTimeline = gsap.timeline({ 879 defaults: { 880 ease: self.mainConfig.ease, 881 duration: self.mainConfig.speed 882 } 883 }) 884 885 if( direction == 'prev' ) {
886 var nextHeight = prevSlide.innerHeight(); 887 888 if( self.mainConfig.delayedContent && self.mainConfig.adaptiveHeight ) { 889 fadeTimeline 890 .to( currentSlide.find('.delayed-content'), {autoAlpha: 0, x: self.mainConfig.delayedContentX, y: self.mainConfig.delayedContentY}) 891 .to( currentSlide, {autoAlpha: 0}) 892 .to( parent, { height: nextHeight }, '<' ) 893 .to( prevSlide, {autoAlpha: 1}, '<') 894 .to( prevSlide.find('.delayed-content'), {autoAlpha: 1, y: 0, x: 0}) 895 .add(function(){ 896 currentSlide.removeClass('slide-active'); 897 prevSlide.addClass('slide-active'); 898 }) 899 } 900 else if( self.mainConfig.delayedContent ) { 901 fadeTimeline 902 .to( currentSlide.find('.delayed-content'), {autoAlpha: 0, x: self.mainConfig.delayedContentX, y: self.mainConfig.delayedContentY}) 903 .to( currentSlide, {autoAlpha: 0}) 904 .to( prevSlide, {autoAlpha: 1}, '<') 905 .to( prevSlide.find('.delayed-content'), {autoAlpha: 1, y: 0, x: 0}) 906 .add(function(){ 907 currentSlide.removeClass('slide-active'); 908 prevSlide.addClass('slide-active'); 909 }) 910 911 } 912 else if( self.mainConfig.adaptiveHeight ) { 913 fadeTimeline 914 .to( currentSlide, {autoAlpha: 0}) 915 .to( parent, { height: nextHeight }, '<' ) 916 .to( prevSlide, {autoAlpha: 1}, '<') 917 .add(function(){ 918 currentSlide.removeClass('slide-active'); 919 prevSlide.addClass('slide-active'); 920 }) 921 922 } 923 else{ 924 fadeTimeline 925 .to( currentSlide, {autoAlpha: 0}) 926 .to( prevSlide, {autoAlpha: 1}, '<') 927 .add(function(){ 928 currentSlide.removeClass('slide-active'); 929 prevSlide.addClass('slide-active'); 930 }) 931 } 932 } 933 else if( direction == 'next' ){ 934 var nextHeight = nextSlide.innerHeight(); 935 936 if( self.mainConfig.delayedContent && self.mainConfig.adaptiveHeight ) { 937 fadeTimeline 938 .to( currentSlide.find('.delayed-content'), {autoAlpha: 0, x: self.mainConfig.delayedContentX, y: self.mainConfig.delayedContentY}) 939 .to( currentSlide, {autoAlpha: 0}) 940 .to( parent, { height: nextHeight }, '<' ) 941 .to( nextSlide, {autoAlpha: 1}, '<') 942 .to( nextSlide.find('.delayed-content'), {autoAlpha: 1, y: 0, x: 0}) 943 .add(function(){ 944 currentSlide.removeClass('slide-active'); 945 nextSlide.addClass('slide-active'); 946 }) 947 } 948 else if( self.mainConfig.delayedContent ) { 949 fadeTimeline 950 .to( currentSlide.find('.delayed-content'), {autoAlpha: 0, x: self.mainConfig.delayedContentX, y: self.mainConfig.delayedContentY}) 951 .to( currentSlide, {autoAlpha: 0}) 952 .to( nextSlide, {autoAlpha: 1}, '<') 953 .to( nextSlide.find('.delayed-content'), {autoAlpha: 1, y: 0, x: 0}) 954 .add(function(){ 955 currentSlide.removeClass('slide-active'); 956 nextSlide.addClass('slide-active'); 957 }) 958 959 } 960 else if( self.mainConfig.adaptiveHeight ) { 961 fadeTimeline 962 .to( currentSlide, {autoAlpha: 0}) 963 .to( parent, { height: nextHeight }, '<' ) 964 .to( nextSlide, {autoAlpha: 1}, '<') 965 .add(function(){ 966 currentSlide.removeClass('slide-active'); 967 nextSlide.addClass('slide-active'); 968 }) 969 970 } 971 else{ 972 fadeTimeline 973 .to( currentSlide, {autoAlpha: 0}) 974 .to( nextSlide, {autoAlpha: 1}, '<') 975 .add(function(){ 976 currentSlide.removeClass('slide-active'); 977 nextSlide.addClass('slide-active'); 978 }) 979 } 980 } 981 } 982
983 /////////////////////////////////////////////////////////////////////////// 984 // COUNT RESPONSIVE BREAKPOINTS 985 /////////////////////////////////////////////////////////////////////////// 986 countResponsive = ( obj ) => { 987 let counter = 0; 988 const self = this; 989 990 for (var prop in obj) { 991 if(obj.hasOwnProperty(prop)){ 992 self.responsiveBreakpoints.push( prop ); 993 counter++; 994 } 995 } 996 997 return counter; 998 } 999 1000 /////////////////////////////////////////////////////////////////////////// 1001 // COUNT RESPONSIVE BREAKPOINTS 1002 /////////////////////////////////////////////////////////////////////////// 1003 setEnvironment = ( container ) => { 1004 container.find('.gsap-wrapper').css({ 1005 'width': '100%', 1006 'position': 'relative', 1007 'display': 'flex' 1008 }) 1009 } 1010 1011 /////////////////////////////////////////////////////////////////////////// 1012 // CALC NO WHITESPACE 1013 /////////////////////////////////////////////////////////////////////////// 1014 noWhiteSpace = ( boxes ) => { 1015 const config = self.mainConfig; 1016 1017 boxes.forEach((box, i) => { 1018 $(box).css('width', 'fit-content'); 1019 var boxWidth = $(box).outerWidth(); 1020 1021 $(box).find('p').css({'white-space': 'nowrap'}) 1022 $(box).css('width', boxWidth + 'px'); 1023 }) 1024 } 1025} 1026 1027class IntervalTimer { 1028 callbackStartTime; 1029 remaining = 0; 1030 paused = false; 1031 timerId = null; 1032 callback; 1033 delay; 1034 1035 constructor(callback, delay) { 1036 this.callback = callback; 1037 this.delay = delay; 1038 } 1039 1040 pause() { 1041 if (!this.paused) { 1042 this.clear(); 1043 this.remaining = new Date().getTime() - this.callbackStartTime; 1044 this.paused = true; 1045 } 1046 } 1047 1048 resume() { 1049 if (this.paused) { 1050 if (this.remaining) { 1051 setTimeout(() => { 1052 this.run(); 1053 this.paused = false; 1054 this.start(); 1055 }, this.remaining); 1056 } else { 1057 this.paused = false; 1058 this.start(); 1059 } 1060 } 1061 } 1062 1063 clear() { 1064 clearInterval(this.timerId); 1065 } 1066 1067 start() { 1068 this.clear(); 1069 this.timerId = setInterval(() => { 1070 this.run(); 1071 }, this.delay); 1072 } 1073 1074 run() { 1075 this.callbackStartTime = new Date().getTime(); 1076 this.callback(); 1077 } 1078} 1079 1080// HOW TO USE FUNCTION: 1081/* 1082 1.Wrap slider in a variable; 1083 2.Yourslider.slideToIndex(i); 1084*/ 1085GSapSlider.prototype.slideToIndex = function(index, duration, ease) { 1086 this.loopTimeline.loop.toIndex(index, {duration: duration ? duration : 0.8, ease: ease ? ease : "power1.inOut"}); 1087}
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.