controller.js 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183
  1. /* eslint no-bitwise: ["error", { "allow": [">>"] }] */
  2. import { elementTransitionEnd, nextTick } from '../../shared/utils.js';
  3. export default function Controller({
  4. swiper,
  5. extendParams,
  6. on
  7. }) {
  8. extendParams({
  9. controller: {
  10. control: undefined,
  11. inverse: false,
  12. by: 'slide' // or 'container'
  13. }
  14. });
  15. swiper.controller = {
  16. control: undefined
  17. };
  18. function LinearSpline(x, y) {
  19. const binarySearch = function search() {
  20. let maxIndex;
  21. let minIndex;
  22. let guess;
  23. return (array, val) => {
  24. minIndex = -1;
  25. maxIndex = array.length;
  26. while (maxIndex - minIndex > 1) {
  27. guess = maxIndex + minIndex >> 1;
  28. if (array[guess] <= val) {
  29. minIndex = guess;
  30. } else {
  31. maxIndex = guess;
  32. }
  33. }
  34. return maxIndex;
  35. };
  36. }();
  37. this.x = x;
  38. this.y = y;
  39. this.lastIndex = x.length - 1;
  40. // Given an x value (x2), return the expected y2 value:
  41. // (x1,y1) is the known point before given value,
  42. // (x3,y3) is the known point after given value.
  43. let i1;
  44. let i3;
  45. this.interpolate = function interpolate(x2) {
  46. if (!x2) return 0;
  47. // Get the indexes of x1 and x3 (the array indexes before and after given x2):
  48. i3 = binarySearch(this.x, x2);
  49. i1 = i3 - 1;
  50. // We have our indexes i1 & i3, so we can calculate already:
  51. // y2 := ((x2−x1) × (y3−y1)) ÷ (x3−x1) + y1
  52. return (x2 - this.x[i1]) * (this.y[i3] - this.y[i1]) / (this.x[i3] - this.x[i1]) + this.y[i1];
  53. };
  54. return this;
  55. }
  56. function getInterpolateFunction(c) {
  57. swiper.controller.spline = swiper.params.loop ? new LinearSpline(swiper.slidesGrid, c.slidesGrid) : new LinearSpline(swiper.snapGrid, c.snapGrid);
  58. }
  59. function setTranslate(_t, byController) {
  60. const controlled = swiper.controller.control;
  61. let multiplier;
  62. let controlledTranslate;
  63. const Swiper = swiper.constructor;
  64. function setControlledTranslate(c) {
  65. if (c.destroyed) return;
  66. // this will create an Interpolate function based on the snapGrids
  67. // x is the Grid of the scrolled scroller and y will be the controlled scroller
  68. // it makes sense to create this only once and recall it for the interpolation
  69. // the function does a lot of value caching for performance
  70. const translate = swiper.rtlTranslate ? -swiper.translate : swiper.translate;
  71. if (swiper.params.controller.by === 'slide') {
  72. getInterpolateFunction(c);
  73. // i am not sure why the values have to be multiplicated this way, tried to invert the snapGrid
  74. // but it did not work out
  75. controlledTranslate = -swiper.controller.spline.interpolate(-translate);
  76. }
  77. if (!controlledTranslate || swiper.params.controller.by === 'container') {
  78. multiplier = (c.maxTranslate() - c.minTranslate()) / (swiper.maxTranslate() - swiper.minTranslate());
  79. if (Number.isNaN(multiplier) || !Number.isFinite(multiplier)) {
  80. multiplier = 1;
  81. }
  82. controlledTranslate = (translate - swiper.minTranslate()) * multiplier + c.minTranslate();
  83. }
  84. if (swiper.params.controller.inverse) {
  85. controlledTranslate = c.maxTranslate() - controlledTranslate;
  86. }
  87. c.updateProgress(controlledTranslate);
  88. c.setTranslate(controlledTranslate, swiper);
  89. c.updateActiveIndex();
  90. c.updateSlidesClasses();
  91. }
  92. if (Array.isArray(controlled)) {
  93. for (let i = 0; i < controlled.length; i += 1) {
  94. if (controlled[i] !== byController && controlled[i] instanceof Swiper) {
  95. setControlledTranslate(controlled[i]);
  96. }
  97. }
  98. } else if (controlled instanceof Swiper && byController !== controlled) {
  99. setControlledTranslate(controlled);
  100. }
  101. }
  102. function setTransition(duration, byController) {
  103. const Swiper = swiper.constructor;
  104. const controlled = swiper.controller.control;
  105. let i;
  106. function setControlledTransition(c) {
  107. if (c.destroyed) return;
  108. c.setTransition(duration, swiper);
  109. if (duration !== 0) {
  110. c.transitionStart();
  111. if (c.params.autoHeight) {
  112. nextTick(() => {
  113. c.updateAutoHeight();
  114. });
  115. }
  116. elementTransitionEnd(c.wrapperEl, () => {
  117. if (!controlled) return;
  118. c.transitionEnd();
  119. });
  120. }
  121. }
  122. if (Array.isArray(controlled)) {
  123. for (i = 0; i < controlled.length; i += 1) {
  124. if (controlled[i] !== byController && controlled[i] instanceof Swiper) {
  125. setControlledTransition(controlled[i]);
  126. }
  127. }
  128. } else if (controlled instanceof Swiper && byController !== controlled) {
  129. setControlledTransition(controlled);
  130. }
  131. }
  132. function removeSpline() {
  133. if (!swiper.controller.control) return;
  134. if (swiper.controller.spline) {
  135. swiper.controller.spline = undefined;
  136. delete swiper.controller.spline;
  137. }
  138. }
  139. on('beforeInit', () => {
  140. if (typeof window !== 'undefined' && (
  141. // eslint-disable-line
  142. typeof swiper.params.controller.control === 'string' || swiper.params.controller.control instanceof HTMLElement)) {
  143. const controlElement = document.querySelector(swiper.params.controller.control);
  144. if (controlElement && controlElement.swiper) {
  145. swiper.controller.control = controlElement.swiper;
  146. } else if (controlElement) {
  147. const onControllerSwiper = e => {
  148. swiper.controller.control = e.detail[0];
  149. swiper.update();
  150. controlElement.removeEventListener('init', onControllerSwiper);
  151. };
  152. controlElement.addEventListener('init', onControllerSwiper);
  153. }
  154. return;
  155. }
  156. swiper.controller.control = swiper.params.controller.control;
  157. });
  158. on('update', () => {
  159. removeSpline();
  160. });
  161. on('resize', () => {
  162. removeSpline();
  163. });
  164. on('observerUpdate', () => {
  165. removeSpline();
  166. });
  167. on('setTranslate', (_s, translate, byController) => {
  168. if (!swiper.controller.control || swiper.controller.control.destroyed) return;
  169. swiper.controller.setTranslate(translate, byController);
  170. });
  171. on('setTransition', (_s, duration, byController) => {
  172. if (!swiper.controller.control || swiper.controller.control.destroyed) return;
  173. swiper.controller.setTransition(duration, byController);
  174. });
  175. Object.assign(swiper.controller, {
  176. setTranslate,
  177. setTransition
  178. });
  179. }