diff --git a/src/components/slides/slides.ts b/src/components/slides/slides.ts index 12ec6ce699..43aea71959 100644 --- a/src/components/slides/slides.ts +++ b/src/components/slides/slides.ts @@ -161,6 +161,22 @@ export class Slides extends Ion { } private _autoplayMs: number; + /** + * @input {Slides} Pass another Slides instance or array of Slides instances + * that should be controlled by this Slides instance. + * Default: `null`. + */ + @Input() + get control() { + return this._control; + } + set control(val: Slides | Slides[]) { + if (val instanceof Slides || Array.isArray(val)) { + this._control = val; + } + } + private _control: Slides | Slides[] = null; + /** * @input {string} Could be `slide`, `fade`, `cube`, `coverflow` or `flip`. * Default: `slide`. @@ -551,6 +567,10 @@ export class Slides extends Ion { /** @private */ runCallbacksOnInit = true; + // Controller + controlBy = 'slide'; + controlInverse = false; + // Keyboard /** * @private @@ -827,6 +847,8 @@ export class Slides extends Ion { /** @internal */ _slidesSizesGrid: any; /** @internal */ + _spline: any; + /** @internal */ _supportTouch: boolean; /** @internal */ _supportGestures: boolean; diff --git a/src/components/slides/swiper/swiper-controller.ts b/src/components/slides/swiper/swiper-controller.ts new file mode 100644 index 0000000000..4340c451ce --- /dev/null +++ b/src/components/slides/swiper/swiper-controller.ts @@ -0,0 +1,120 @@ +import { Slides } from '../slides'; +import { Platform } from '../../../platform/platform'; +import { fixLoop, onTransitionEnd, onTransitionStart } from './swiper'; +import { updateActiveIndex } from './swiper-index'; +import { isHorizontal, maxTranslate, minTranslate } from './swiper-utils'; +import { updateProgress } from './swiper-progress'; +import { setWrapperTransition, setWrapperTranslate } from './swiper-transition'; + + +/*========================= + Controller + ===========================*/ +export const SWIPER_CONTROLLER = { + LinearSpline: function (s: Slides, plt: Platform, x: any, y: any) { + 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. + var i1, i3; + + this.interpolate = function (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]; + }; + + var binarySearch = (function () { + var maxIndex, minIndex, guess; + return function (array, val) { + minIndex = -1; + maxIndex = array.length; + while (maxIndex - minIndex > 1) + if (array[guess = maxIndex + minIndex >> 1] <= val) { + minIndex = guess; + } else { + maxIndex = guess; + } + return maxIndex; + }; + })(); + }, + // xxx: for now i will just save one spline function to to + getInterpolateFunction: function (s: Slides, plt: Platform, c: Slides) { + if (!s._spline) s._spline = s.loop ? + new SWIPER_CONTROLLER.LinearSpline(s, plt, s._slidesGrid, c._slidesGrid) : + new SWIPER_CONTROLLER.LinearSpline(s, plt, s._snapGrid, c._snapGrid); + }, + setTranslate: function (s: Slides, plt: Platform, translate: number, byController: Slides) { + var controlled = s.control; + var multiplier, controlledTranslate; + function setControlledTranslate(c: Slides) { + // 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 + translate = c._rtl && isHorizontal(c) ? -s._translate : s._translate; + if (s.controlBy === 'slide') { + SWIPER_CONTROLLER.getInterpolateFunction(s, plt, 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 = -s._spline.interpolate(-translate); + } + + if (!controlledTranslate || s.controlBy === 'container') { + multiplier = (maxTranslate(c) - minTranslate(c)) / (maxTranslate(s) - minTranslate(s)); + controlledTranslate = (translate - minTranslate(s)) * multiplier + minTranslate(c); + } + + if (s.controlInverse) { + controlledTranslate = maxTranslate(c) - controlledTranslate; + } + updateProgress(c, controlledTranslate); + setWrapperTranslate(c, plt, controlledTranslate, false, s); + updateActiveIndex(c); + } + if (Array.isArray(controlled)) { + for (var i = 0; i < controlled.length; i++) { + if (controlled[i] !== byController) { + setControlledTranslate(controlled[i]); + } + } + } else if (byController !== controlled) { + setControlledTranslate(controlled); + } + }, + setTransition: function (s: Slides, plt: Platform, duration: number, byController: Slides) { + var controlled = s.control; + var i; + function setControlledTransition(c: Slides) { + setWrapperTransition(c, plt, duration, s); + if (duration !== 0) { + onTransitionStart(c); + plt.transitionEnd(c._wrapper, () => { + if (!controlled) return; + if (c.loop && s.controlBy === 'slide') { + fixLoop(c, plt); + } + onTransitionEnd(c, plt); + }); + } + } + if (Array.isArray(controlled)) { + for (i = 0; i < controlled.length; i++) { + if (controlled[i] !== byController) { + setControlledTransition(controlled[i]); + } + } + } else if (byController !== controlled) { + setControlledTransition(controlled); + } + } +}; diff --git a/src/components/slides/swiper/swiper-events.ts b/src/components/slides/swiper/swiper-events.ts index aaa04f8ad8..66e644c71b 100644 --- a/src/components/slides/swiper/swiper-events.ts +++ b/src/components/slides/swiper/swiper-events.ts @@ -832,6 +832,10 @@ function onResize(s: Slides, plt: Platform, forceUpdatePagination: boolean) { updatePagination(s); } + if (s._spline) { + s._spline = undefined; + } + var slideChangedBySlideTo = false; if (s.freeMode) { var newTranslate = Math.min(Math.max(s._translate, maxTranslate(s)), minTranslate(s)); diff --git a/src/components/slides/swiper/swiper-transition.ts b/src/components/slides/swiper/swiper-transition.ts index c9d54365f9..0caf9debd9 100644 --- a/src/components/slides/swiper/swiper-transition.ts +++ b/src/components/slides/swiper/swiper-transition.ts @@ -4,10 +4,11 @@ import { parallaxSetTransition, parallaxSetTranslate } from './swiper-parallax'; import { Platform } from '../../../platform/platform'; import { updateProgress } from './swiper-progress'; import { updateActiveIndex } from './swiper-index'; +import { SWIPER_CONTROLLER } from './swiper-controller'; import { SWIPER_EFFECTS } from './swiper-effects'; -export function setWrapperTranslate(s: Slides, plt: Platform, translate: any, shouldUpdateActiveIndex?: boolean, byController?: any) { +export function setWrapperTranslate(s: Slides, plt: Platform, translate: any, shouldUpdateActiveIndex?: boolean, byController?: Slides) { var x = 0, y = 0, z = 0; if (isHorizontal(s)) { x = s._rtl ? -translate : translate; @@ -51,6 +52,10 @@ export function setWrapperTranslate(s: Slides, plt: Platform, translate: any, sh if (s.parallax) { parallaxSetTranslate(s); } + + if (s.control) { + SWIPER_CONTROLLER.setTranslate(s, plt, s._translate, byController); + } } @@ -127,7 +132,7 @@ export function getWrapperTranslate(s: Slides, plt: Platform, axis?: any) { return getTranslate(s, plt, s._wrapper, axis); } -export function setWrapperTransition(s: Slides, plt: Platform, duration: number, byController?: any) { +export function setWrapperTransition(s: Slides, plt: Platform, duration: number, byController?: Slides) { transition(s._wrapper, duration); if (s.effect !== 'slide' && SWIPER_EFFECTS[s.effect]) { @@ -137,4 +142,8 @@ export function setWrapperTransition(s: Slides, plt: Platform, duration: number, if (s.parallax) { parallaxSetTransition(s, duration); } + + if (s.control) { + SWIPER_CONTROLLER.setTransition(s, plt, duration, byController); + } } diff --git a/src/components/slides/swiper/swiper.ts b/src/components/slides/swiper/swiper.ts index 2bb765ab98..21fc1f1499 100644 --- a/src/components/slides/swiper/swiper.ts +++ b/src/components/slides/swiper/swiper.ts @@ -618,6 +618,9 @@ export function update(s: Slides, plt: Platform, updateTranslate?: boolean) { } if (updateTranslate) { + if (s._spline) { + s._spline = undefined; + } if (s.freeMode) { forceSetTranslate(); if (s.autoHeight) { diff --git a/src/components/slides/test/control/app-module.ts b/src/components/slides/test/control/app-module.ts new file mode 100644 index 0000000000..0b6dd4a38a --- /dev/null +++ b/src/components/slides/test/control/app-module.ts @@ -0,0 +1,35 @@ +import { Component, ViewChild, NgModule } from '@angular/core'; +import { IonicApp, IonicModule, Slides } from '../../../..'; + + +@Component({ + templateUrl: 'main.html' +}) +export class MyPage { + @ViewChild('firstSlider') slider1: Slides; + @ViewChild('secondSlider') slider2: Slides; +} + + +@Component({ + template: `` +}) +export class E2EApp { + root: any = MyPage; +} + +@NgModule({ + declarations: [ + E2EApp, + MyPage + ], + imports: [ + IonicModule.forRoot(E2EApp) + ], + bootstrap: [IonicApp], + entryComponents: [ + E2EApp, + MyPage + ] +}) +export class AppModule {} diff --git a/src/components/slides/test/control/main.html b/src/components/slides/test/control/main.html new file mode 100644 index 0000000000..a4b5121123 --- /dev/null +++ b/src/components/slides/test/control/main.html @@ -0,0 +1,40 @@ + + + +
+ + + +

Slide 1

+
+ + +

Slide 2

+
+ + +

Slide 3

+
+ +
+
+ +
+ + + +

Slide 1

+
+ + +

Slide 2

+
+ + +

Slide 3

+
+ +
+
+ +