mirror of
https://github.com/ionic-team/ionic-framework.git
synced 2025-08-22 13:32:54 +08:00
feat(slides): add swiper controller (#9983)
This commit is contained in:
@ -161,6 +161,22 @@ export class Slides extends Ion {
|
|||||||
}
|
}
|
||||||
private _autoplayMs: number;
|
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`.
|
* @input {string} Could be `slide`, `fade`, `cube`, `coverflow` or `flip`.
|
||||||
* Default: `slide`.
|
* Default: `slide`.
|
||||||
@ -551,6 +567,10 @@ export class Slides extends Ion {
|
|||||||
/** @private */
|
/** @private */
|
||||||
runCallbacksOnInit = true;
|
runCallbacksOnInit = true;
|
||||||
|
|
||||||
|
// Controller
|
||||||
|
controlBy = 'slide';
|
||||||
|
controlInverse = false;
|
||||||
|
|
||||||
// Keyboard
|
// Keyboard
|
||||||
/**
|
/**
|
||||||
* @private
|
* @private
|
||||||
@ -827,6 +847,8 @@ export class Slides extends Ion {
|
|||||||
/** @internal */
|
/** @internal */
|
||||||
_slidesSizesGrid: any;
|
_slidesSizesGrid: any;
|
||||||
/** @internal */
|
/** @internal */
|
||||||
|
_spline: any;
|
||||||
|
/** @internal */
|
||||||
_supportTouch: boolean;
|
_supportTouch: boolean;
|
||||||
/** @internal */
|
/** @internal */
|
||||||
_supportGestures: boolean;
|
_supportGestures: boolean;
|
||||||
|
120
src/components/slides/swiper/swiper-controller.ts
Normal file
120
src/components/slides/swiper/swiper-controller.ts
Normal file
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
@ -832,6 +832,10 @@ function onResize(s: Slides, plt: Platform, forceUpdatePagination: boolean) {
|
|||||||
updatePagination(s);
|
updatePagination(s);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (s._spline) {
|
||||||
|
s._spline = undefined;
|
||||||
|
}
|
||||||
|
|
||||||
var slideChangedBySlideTo = false;
|
var slideChangedBySlideTo = false;
|
||||||
if (s.freeMode) {
|
if (s.freeMode) {
|
||||||
var newTranslate = Math.min(Math.max(s._translate, maxTranslate(s)), minTranslate(s));
|
var newTranslate = Math.min(Math.max(s._translate, maxTranslate(s)), minTranslate(s));
|
||||||
|
@ -4,10 +4,11 @@ import { parallaxSetTransition, parallaxSetTranslate } from './swiper-parallax';
|
|||||||
import { Platform } from '../../../platform/platform';
|
import { Platform } from '../../../platform/platform';
|
||||||
import { updateProgress } from './swiper-progress';
|
import { updateProgress } from './swiper-progress';
|
||||||
import { updateActiveIndex } from './swiper-index';
|
import { updateActiveIndex } from './swiper-index';
|
||||||
|
import { SWIPER_CONTROLLER } from './swiper-controller';
|
||||||
import { SWIPER_EFFECTS } from './swiper-effects';
|
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;
|
var x = 0, y = 0, z = 0;
|
||||||
if (isHorizontal(s)) {
|
if (isHorizontal(s)) {
|
||||||
x = s._rtl ? -translate : translate;
|
x = s._rtl ? -translate : translate;
|
||||||
@ -51,6 +52,10 @@ export function setWrapperTranslate(s: Slides, plt: Platform, translate: any, sh
|
|||||||
if (s.parallax) {
|
if (s.parallax) {
|
||||||
parallaxSetTranslate(s);
|
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);
|
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);
|
transition(s._wrapper, duration);
|
||||||
|
|
||||||
if (s.effect !== 'slide' && SWIPER_EFFECTS[s.effect]) {
|
if (s.effect !== 'slide' && SWIPER_EFFECTS[s.effect]) {
|
||||||
@ -137,4 +142,8 @@ export function setWrapperTransition(s: Slides, plt: Platform, duration: number,
|
|||||||
if (s.parallax) {
|
if (s.parallax) {
|
||||||
parallaxSetTransition(s, duration);
|
parallaxSetTransition(s, duration);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (s.control) {
|
||||||
|
SWIPER_CONTROLLER.setTransition(s, plt, duration, byController);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -618,6 +618,9 @@ export function update(s: Slides, plt: Platform, updateTranslate?: boolean) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (updateTranslate) {
|
if (updateTranslate) {
|
||||||
|
if (s._spline) {
|
||||||
|
s._spline = undefined;
|
||||||
|
}
|
||||||
if (s.freeMode) {
|
if (s.freeMode) {
|
||||||
forceSetTranslate();
|
forceSetTranslate();
|
||||||
if (s.autoHeight) {
|
if (s.autoHeight) {
|
||||||
|
35
src/components/slides/test/control/app-module.ts
Normal file
35
src/components/slides/test/control/app-module.ts
Normal file
@ -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: `<ion-nav [root]="root"></ion-nav>`
|
||||||
|
})
|
||||||
|
export class E2EApp {
|
||||||
|
root: any = MyPage;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
declarations: [
|
||||||
|
E2EApp,
|
||||||
|
MyPage
|
||||||
|
],
|
||||||
|
imports: [
|
||||||
|
IonicModule.forRoot(E2EApp)
|
||||||
|
],
|
||||||
|
bootstrap: [IonicApp],
|
||||||
|
entryComponents: [
|
||||||
|
E2EApp,
|
||||||
|
MyPage
|
||||||
|
]
|
||||||
|
})
|
||||||
|
export class AppModule {}
|
40
src/components/slides/test/control/main.html
Normal file
40
src/components/slides/test/control/main.html
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
|
||||||
|
<ion-content no-bounce>
|
||||||
|
|
||||||
|
<div style="height: 200px; background-color: #eee;">
|
||||||
|
<ion-slides class="my-slides" #firstSlider [control]="secondSlider">
|
||||||
|
|
||||||
|
<ion-slide padding>
|
||||||
|
<h1>Slide 1</h1>
|
||||||
|
</ion-slide>
|
||||||
|
|
||||||
|
<ion-slide padding>
|
||||||
|
<h1>Slide 2</h1>
|
||||||
|
</ion-slide>
|
||||||
|
|
||||||
|
<ion-slide padding>
|
||||||
|
<h1>Slide 3</h1>
|
||||||
|
</ion-slide>
|
||||||
|
|
||||||
|
</ion-slides>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div style="height: 200px; background-color: #ddd;">
|
||||||
|
<ion-slides class="my-slides" #secondSlider [control]="firstSlider">
|
||||||
|
|
||||||
|
<ion-slide padding>
|
||||||
|
<h1>Slide 1</h1>
|
||||||
|
</ion-slide>
|
||||||
|
|
||||||
|
<ion-slide padding>
|
||||||
|
<h1>Slide 2</h1>
|
||||||
|
</ion-slide>
|
||||||
|
|
||||||
|
<ion-slide padding>
|
||||||
|
<h1>Slide 3</h1>
|
||||||
|
</ion-slide>
|
||||||
|
|
||||||
|
</ion-slides>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</ion-content>
|
Reference in New Issue
Block a user