Skip to content

Commit f499496

Browse files
Mandurobrandyscarney
authored andcommitted
feat(slides): add swiper controller (#9983)
1 parent e35a3b1 commit f499496

File tree

7 files changed

+235
-2
lines changed

7 files changed

+235
-2
lines changed

src/components/slides/slides.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,22 @@ export class Slides extends Ion {
161161
}
162162
private _autoplayMs: number;
163163

164+
/**
165+
* @input {Slides} Pass another Slides instance or array of Slides instances
166+
* that should be controlled by this Slides instance.
167+
* Default: `null`.
168+
*/
169+
@Input()
170+
get control() {
171+
return this._control;
172+
}
173+
set control(val: Slides | Slides[]) {
174+
if (val instanceof Slides || Array.isArray(val)) {
175+
this._control = val;
176+
}
177+
}
178+
private _control: Slides | Slides[] = null;
179+
164180
/**
165181
* @input {string} Could be `slide`, `fade`, `cube`, `coverflow` or `flip`.
166182
* Default: `slide`.
@@ -551,6 +567,10 @@ export class Slides extends Ion {
551567
/** @private */
552568
runCallbacksOnInit = true;
553569

570+
// Controller
571+
controlBy = 'slide';
572+
controlInverse = false;
573+
554574
// Keyboard
555575
/**
556576
* @private
@@ -827,6 +847,8 @@ export class Slides extends Ion {
827847
/** @internal */
828848
_slidesSizesGrid: any;
829849
/** @internal */
850+
_spline: any;
851+
/** @internal */
830852
_supportTouch: boolean;
831853
/** @internal */
832854
_supportGestures: boolean;
Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
import { Slides } from '../slides';
2+
import { Platform } from '../../../platform/platform';
3+
import { fixLoop, onTransitionEnd, onTransitionStart } from './swiper';
4+
import { updateActiveIndex } from './swiper-index';
5+
import { isHorizontal, maxTranslate, minTranslate } from './swiper-utils';
6+
import { updateProgress } from './swiper-progress';
7+
import { setWrapperTransition, setWrapperTranslate } from './swiper-transition';
8+
9+
10+
/*=========================
11+
Controller
12+
===========================*/
13+
export const SWIPER_CONTROLLER = {
14+
LinearSpline: function (s: Slides, plt: Platform, x: any, y: any) {
15+
this.x = x;
16+
this.y = y;
17+
this.lastIndex = x.length - 1;
18+
// Given an x value (x2), return the expected y2 value:
19+
// (x1,y1) is the known point before given value,
20+
// (x3,y3) is the known point after given value.
21+
var i1, i3;
22+
23+
this.interpolate = function (x2) {
24+
if (!x2) return 0;
25+
26+
// Get the indexes of x1 and x3 (the array indexes before and after given x2):
27+
i3 = binarySearch(this.x, x2);
28+
i1 = i3 - 1;
29+
30+
// We have our indexes i1 & i3, so we can calculate already:
31+
// y2 := ((x2−x1) × (y3−y1)) ÷ (x3−x1) + y1
32+
return ((x2 - this.x[i1]) * (this.y[i3] - this.y[i1])) / (this.x[i3] - this.x[i1]) + this.y[i1];
33+
};
34+
35+
var binarySearch = (function () {
36+
var maxIndex, minIndex, guess;
37+
return function (array, val) {
38+
minIndex = -1;
39+
maxIndex = array.length;
40+
while (maxIndex - minIndex > 1)
41+
if (array[guess = maxIndex + minIndex >> 1] <= val) {
42+
minIndex = guess;
43+
} else {
44+
maxIndex = guess;
45+
}
46+
return maxIndex;
47+
};
48+
})();
49+
},
50+
// xxx: for now i will just save one spline function to to
51+
getInterpolateFunction: function (s: Slides, plt: Platform, c: Slides) {
52+
if (!s._spline) s._spline = s.loop ?
53+
new SWIPER_CONTROLLER.LinearSpline(s, plt, s._slidesGrid, c._slidesGrid) :
54+
new SWIPER_CONTROLLER.LinearSpline(s, plt, s._snapGrid, c._snapGrid);
55+
},
56+
setTranslate: function (s: Slides, plt: Platform, translate: number, byController: Slides) {
57+
var controlled = s.control;
58+
var multiplier, controlledTranslate;
59+
function setControlledTranslate(c: Slides) {
60+
// this will create an Interpolate function based on the snapGrids
61+
// x is the Grid of the scrolled scroller and y will be the controlled scroller
62+
// it makes sense to create this only once and recall it for the interpolation
63+
// the function does a lot of value caching for performance
64+
translate = c._rtl && isHorizontal(c) ? -s._translate : s._translate;
65+
if (s.controlBy === 'slide') {
66+
SWIPER_CONTROLLER.getInterpolateFunction(s, plt, c);
67+
// i am not sure why the values have to be multiplicated this way, tried to invert the snapGrid
68+
// but it did not work out
69+
controlledTranslate = -s._spline.interpolate(-translate);
70+
}
71+
72+
if (!controlledTranslate || s.controlBy === 'container') {
73+
multiplier = (maxTranslate(c) - minTranslate(c)) / (maxTranslate(s) - minTranslate(s));
74+
controlledTranslate = (translate - minTranslate(s)) * multiplier + minTranslate(c);
75+
}
76+
77+
if (s.controlInverse) {
78+
controlledTranslate = maxTranslate(c) - controlledTranslate;
79+
}
80+
updateProgress(c, controlledTranslate);
81+
setWrapperTranslate(c, plt, controlledTranslate, false, s);
82+
updateActiveIndex(c);
83+
}
84+
if (Array.isArray(controlled)) {
85+
for (var i = 0; i < controlled.length; i++) {
86+
if (controlled[i] !== byController) {
87+
setControlledTranslate(controlled[i]);
88+
}
89+
}
90+
} else if (byController !== controlled) {
91+
setControlledTranslate(controlled);
92+
}
93+
},
94+
setTransition: function (s: Slides, plt: Platform, duration: number, byController: Slides) {
95+
var controlled = s.control;
96+
var i;
97+
function setControlledTransition(c: Slides) {
98+
setWrapperTransition(c, plt, duration, s);
99+
if (duration !== 0) {
100+
onTransitionStart(c);
101+
plt.transitionEnd(c._wrapper, () => {
102+
if (!controlled) return;
103+
if (c.loop && s.controlBy === 'slide') {
104+
fixLoop(c, plt);
105+
}
106+
onTransitionEnd(c, plt);
107+
});
108+
}
109+
}
110+
if (Array.isArray(controlled)) {
111+
for (i = 0; i < controlled.length; i++) {
112+
if (controlled[i] !== byController) {
113+
setControlledTransition(controlled[i]);
114+
}
115+
}
116+
} else if (byController !== controlled) {
117+
setControlledTransition(controlled);
118+
}
119+
}
120+
};

src/components/slides/swiper/swiper-events.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -832,6 +832,10 @@ function onResize(s: Slides, plt: Platform, forceUpdatePagination: boolean) {
832832
updatePagination(s);
833833
}
834834

835+
if (s._spline) {
836+
s._spline = undefined;
837+
}
838+
835839
var slideChangedBySlideTo = false;
836840
if (s.freeMode) {
837841
var newTranslate = Math.min(Math.max(s._translate, maxTranslate(s)), minTranslate(s));

src/components/slides/swiper/swiper-transition.ts

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,11 @@ import { parallaxSetTransition, parallaxSetTranslate } from './swiper-parallax';
44
import { Platform } from '../../../platform/platform';
55
import { updateProgress } from './swiper-progress';
66
import { updateActiveIndex } from './swiper-index';
7+
import { SWIPER_CONTROLLER } from './swiper-controller';
78
import { SWIPER_EFFECTS } from './swiper-effects';
89

910

10-
export function setWrapperTranslate(s: Slides, plt: Platform, translate: any, shouldUpdateActiveIndex?: boolean, byController?: any) {
11+
export function setWrapperTranslate(s: Slides, plt: Platform, translate: any, shouldUpdateActiveIndex?: boolean, byController?: Slides) {
1112
var x = 0, y = 0, z = 0;
1213
if (isHorizontal(s)) {
1314
x = s._rtl ? -translate : translate;
@@ -51,6 +52,10 @@ export function setWrapperTranslate(s: Slides, plt: Platform, translate: any, sh
5152
if (s.parallax) {
5253
parallaxSetTranslate(s);
5354
}
55+
56+
if (s.control) {
57+
SWIPER_CONTROLLER.setTranslate(s, plt, s._translate, byController);
58+
}
5459
}
5560

5661

@@ -127,7 +132,7 @@ export function getWrapperTranslate(s: Slides, plt: Platform, axis?: any) {
127132
return getTranslate(s, plt, s._wrapper, axis);
128133
}
129134

130-
export function setWrapperTransition(s: Slides, plt: Platform, duration: number, byController?: any) {
135+
export function setWrapperTransition(s: Slides, plt: Platform, duration: number, byController?: Slides) {
131136
transition(s._wrapper, duration);
132137

133138
if (s.effect !== 'slide' && SWIPER_EFFECTS[s.effect]) {
@@ -137,4 +142,8 @@ export function setWrapperTransition(s: Slides, plt: Platform, duration: number,
137142
if (s.parallax) {
138143
parallaxSetTransition(s, duration);
139144
}
145+
146+
if (s.control) {
147+
SWIPER_CONTROLLER.setTransition(s, plt, duration, byController);
148+
}
140149
}

src/components/slides/swiper/swiper.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -618,6 +618,9 @@ export function update(s: Slides, plt: Platform, updateTranslate?: boolean) {
618618
}
619619

620620
if (updateTranslate) {
621+
if (s._spline) {
622+
s._spline = undefined;
623+
}
621624
if (s.freeMode) {
622625
forceSetTranslate();
623626
if (s.autoHeight) {
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import { Component, ViewChild, NgModule } from '@angular/core';
2+
import { IonicApp, IonicModule, Slides } from '../../../..';
3+
4+
5+
@Component({
6+
templateUrl: 'main.html'
7+
})
8+
export class MyPage {
9+
@ViewChild('firstSlider') slider1: Slides;
10+
@ViewChild('secondSlider') slider2: Slides;
11+
}
12+
13+
14+
@Component({
15+
template: `<ion-nav [root]="root"></ion-nav>`
16+
})
17+
export class E2EApp {
18+
root: any = MyPage;
19+
}
20+
21+
@NgModule({
22+
declarations: [
23+
E2EApp,
24+
MyPage
25+
],
26+
imports: [
27+
IonicModule.forRoot(E2EApp)
28+
],
29+
bootstrap: [IonicApp],
30+
entryComponents: [
31+
E2EApp,
32+
MyPage
33+
]
34+
})
35+
export class AppModule {}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
2+
<ion-content no-bounce>
3+
4+
<div style="height: 200px; background-color: #eee;">
5+
<ion-slides class="my-slides" #firstSlider [control]="secondSlider">
6+
7+
<ion-slide padding>
8+
<h1>Slide 1</h1>
9+
</ion-slide>
10+
11+
<ion-slide padding>
12+
<h1>Slide 2</h1>
13+
</ion-slide>
14+
15+
<ion-slide padding>
16+
<h1>Slide 3</h1>
17+
</ion-slide>
18+
19+
</ion-slides>
20+
</div>
21+
22+
<div style="height: 200px; background-color: #ddd;">
23+
<ion-slides class="my-slides" #secondSlider [control]="firstSlider">
24+
25+
<ion-slide padding>
26+
<h1>Slide 1</h1>
27+
</ion-slide>
28+
29+
<ion-slide padding>
30+
<h1>Slide 2</h1>
31+
</ion-slide>
32+
33+
<ion-slide padding>
34+
<h1>Slide 3</h1>
35+
</ion-slide>
36+
37+
</ion-slides>
38+
</div>
39+
40+
</ion-content>

0 commit comments

Comments
 (0)