183 lines
		
	
	
		
			6.3 KiB
		
	
	
	
		
			JavaScript
		
	
	
			
		
		
	
	
			183 lines
		
	
	
		
			6.3 KiB
		
	
	
	
		
			JavaScript
		
	
	
| /* eslint no-bitwise: ["error", { "allow": [">>"] }] */
 | ||
| import { elementTransitionEnd, nextTick } from '../../shared/utils.js';
 | ||
| export default function Controller({
 | ||
|   swiper,
 | ||
|   extendParams,
 | ||
|   on
 | ||
| }) {
 | ||
|   extendParams({
 | ||
|     controller: {
 | ||
|       control: undefined,
 | ||
|       inverse: false,
 | ||
|       by: 'slide' // or 'container'
 | ||
|     }
 | ||
|   });
 | ||
| 
 | ||
|   swiper.controller = {
 | ||
|     control: undefined
 | ||
|   };
 | ||
|   function LinearSpline(x, y) {
 | ||
|     const binarySearch = function search() {
 | ||
|       let maxIndex;
 | ||
|       let minIndex;
 | ||
|       let guess;
 | ||
|       return (array, val) => {
 | ||
|         minIndex = -1;
 | ||
|         maxIndex = array.length;
 | ||
|         while (maxIndex - minIndex > 1) {
 | ||
|           guess = maxIndex + minIndex >> 1;
 | ||
|           if (array[guess] <= val) {
 | ||
|             minIndex = guess;
 | ||
|           } else {
 | ||
|             maxIndex = guess;
 | ||
|           }
 | ||
|         }
 | ||
|         return maxIndex;
 | ||
|       };
 | ||
|     }();
 | ||
|     this.x = x;
 | ||
|     this.y = y;
 | ||
|     this.lastIndex = x.length - 1;
 | ||
|     // Given an x value (x2), return the expected y2 value:
 | ||
|     // (x1,y1) is the known point before given value,
 | ||
|     // (x3,y3) is the known point after given value.
 | ||
|     let i1;
 | ||
|     let i3;
 | ||
|     this.interpolate = function interpolate(x2) {
 | ||
|       if (!x2) return 0;
 | ||
| 
 | ||
|       // Get the indexes of x1 and x3 (the array indexes before and after given x2):
 | ||
|       i3 = binarySearch(this.x, x2);
 | ||
|       i1 = i3 - 1;
 | ||
| 
 | ||
|       // We have our indexes i1 & i3, so we can calculate already:
 | ||
|       // y2 := ((x2−x1) × (y3−y1)) ÷ (x3−x1) + y1
 | ||
|       return (x2 - this.x[i1]) * (this.y[i3] - this.y[i1]) / (this.x[i3] - this.x[i1]) + this.y[i1];
 | ||
|     };
 | ||
|     return this;
 | ||
|   }
 | ||
|   function getInterpolateFunction(c) {
 | ||
|     swiper.controller.spline = swiper.params.loop ? new LinearSpline(swiper.slidesGrid, c.slidesGrid) : new LinearSpline(swiper.snapGrid, c.snapGrid);
 | ||
|   }
 | ||
|   function setTranslate(_t, byController) {
 | ||
|     const controlled = swiper.controller.control;
 | ||
|     let multiplier;
 | ||
|     let controlledTranslate;
 | ||
|     const Swiper = swiper.constructor;
 | ||
|     function setControlledTranslate(c) {
 | ||
|       if (c.destroyed) return;
 | ||
| 
 | ||
|       // this will create an Interpolate function based on the snapGrids
 | ||
|       // x is the Grid of the scrolled scroller and y will be the controlled scroller
 | ||
|       // it makes sense to create this only once and recall it for the interpolation
 | ||
|       // the function does a lot of value caching for performance
 | ||
|       const translate = swiper.rtlTranslate ? -swiper.translate : swiper.translate;
 | ||
|       if (swiper.params.controller.by === 'slide') {
 | ||
|         getInterpolateFunction(c);
 | ||
|         // i am not sure why the values have to be multiplicated this way, tried to invert the snapGrid
 | ||
|         // but it did not work out
 | ||
|         controlledTranslate = -swiper.controller.spline.interpolate(-translate);
 | ||
|       }
 | ||
|       if (!controlledTranslate || swiper.params.controller.by === 'container') {
 | ||
|         multiplier = (c.maxTranslate() - c.minTranslate()) / (swiper.maxTranslate() - swiper.minTranslate());
 | ||
|         if (Number.isNaN(multiplier) || !Number.isFinite(multiplier)) {
 | ||
|           multiplier = 1;
 | ||
|         }
 | ||
|         controlledTranslate = (translate - swiper.minTranslate()) * multiplier + c.minTranslate();
 | ||
|       }
 | ||
|       if (swiper.params.controller.inverse) {
 | ||
|         controlledTranslate = c.maxTranslate() - controlledTranslate;
 | ||
|       }
 | ||
|       c.updateProgress(controlledTranslate);
 | ||
|       c.setTranslate(controlledTranslate, swiper);
 | ||
|       c.updateActiveIndex();
 | ||
|       c.updateSlidesClasses();
 | ||
|     }
 | ||
|     if (Array.isArray(controlled)) {
 | ||
|       for (let i = 0; i < controlled.length; i += 1) {
 | ||
|         if (controlled[i] !== byController && controlled[i] instanceof Swiper) {
 | ||
|           setControlledTranslate(controlled[i]);
 | ||
|         }
 | ||
|       }
 | ||
|     } else if (controlled instanceof Swiper && byController !== controlled) {
 | ||
|       setControlledTranslate(controlled);
 | ||
|     }
 | ||
|   }
 | ||
|   function setTransition(duration, byController) {
 | ||
|     const Swiper = swiper.constructor;
 | ||
|     const controlled = swiper.controller.control;
 | ||
|     let i;
 | ||
|     function setControlledTransition(c) {
 | ||
|       if (c.destroyed) return;
 | ||
|       c.setTransition(duration, swiper);
 | ||
|       if (duration !== 0) {
 | ||
|         c.transitionStart();
 | ||
|         if (c.params.autoHeight) {
 | ||
|           nextTick(() => {
 | ||
|             c.updateAutoHeight();
 | ||
|           });
 | ||
|         }
 | ||
|         elementTransitionEnd(c.wrapperEl, () => {
 | ||
|           if (!controlled) return;
 | ||
|           c.transitionEnd();
 | ||
|         });
 | ||
|       }
 | ||
|     }
 | ||
|     if (Array.isArray(controlled)) {
 | ||
|       for (i = 0; i < controlled.length; i += 1) {
 | ||
|         if (controlled[i] !== byController && controlled[i] instanceof Swiper) {
 | ||
|           setControlledTransition(controlled[i]);
 | ||
|         }
 | ||
|       }
 | ||
|     } else if (controlled instanceof Swiper && byController !== controlled) {
 | ||
|       setControlledTransition(controlled);
 | ||
|     }
 | ||
|   }
 | ||
|   function removeSpline() {
 | ||
|     if (!swiper.controller.control) return;
 | ||
|     if (swiper.controller.spline) {
 | ||
|       swiper.controller.spline = undefined;
 | ||
|       delete swiper.controller.spline;
 | ||
|     }
 | ||
|   }
 | ||
|   on('beforeInit', () => {
 | ||
|     if (typeof window !== 'undefined' && (
 | ||
|     // eslint-disable-line
 | ||
|     typeof swiper.params.controller.control === 'string' || swiper.params.controller.control instanceof HTMLElement)) {
 | ||
|       const controlElement = document.querySelector(swiper.params.controller.control);
 | ||
|       if (controlElement && controlElement.swiper) {
 | ||
|         swiper.controller.control = controlElement.swiper;
 | ||
|       } else if (controlElement) {
 | ||
|         const onControllerSwiper = e => {
 | ||
|           swiper.controller.control = e.detail[0];
 | ||
|           swiper.update();
 | ||
|           controlElement.removeEventListener('init', onControllerSwiper);
 | ||
|         };
 | ||
|         controlElement.addEventListener('init', onControllerSwiper);
 | ||
|       }
 | ||
|       return;
 | ||
|     }
 | ||
|     swiper.controller.control = swiper.params.controller.control;
 | ||
|   });
 | ||
|   on('update', () => {
 | ||
|     removeSpline();
 | ||
|   });
 | ||
|   on('resize', () => {
 | ||
|     removeSpline();
 | ||
|   });
 | ||
|   on('observerUpdate', () => {
 | ||
|     removeSpline();
 | ||
|   });
 | ||
|   on('setTranslate', (_s, translate, byController) => {
 | ||
|     if (!swiper.controller.control || swiper.controller.control.destroyed) return;
 | ||
|     swiper.controller.setTranslate(translate, byController);
 | ||
|   });
 | ||
|   on('setTransition', (_s, duration, byController) => {
 | ||
|     if (!swiper.controller.control || swiper.controller.control.destroyed) return;
 | ||
|     swiper.controller.setTransition(duration, byController);
 | ||
|   });
 | ||
|   Object.assign(swiper.controller, {
 | ||
|     setTranslate,
 | ||
|     setTransition
 | ||
|   });
 | ||
| } |