mirror of
https://github.com/ionic-team/ionic-framework.git
synced 2025-08-20 04:14:21 +08:00
541 lines
13 KiB
TypeScript
541 lines
13 KiB
TypeScript
import { Component, h, Ionic, Prop } from '../index';
|
|
import { Swiper } from '../../vendor/swiper';
|
|
|
|
/**
|
|
* @name Slides
|
|
* @description
|
|
* The Slides component is a multi-section container. Each section can be swiped
|
|
* or dragged between. It contains any number of [Slide](../Slide) components.
|
|
*
|
|
*
|
|
* Adopted from Swiper.js:
|
|
* The most modern mobile touch slider and framework with
|
|
* hardware accelerated transitions.
|
|
*
|
|
* http://www.idangero.us/swiper/
|
|
*
|
|
* Copyright 2016, Vladimir Kharlampidi
|
|
* The iDangero.us
|
|
* http://www.idangero.us/
|
|
*
|
|
* Licensed under MIT
|
|
*/
|
|
|
|
|
|
@Component({
|
|
tag: 'ion-slides',
|
|
styleUrls: {
|
|
default: 'slides.scss'
|
|
},
|
|
shadow: false
|
|
})
|
|
export class Slides {
|
|
swiper: any;
|
|
$el: HTMLElement;
|
|
|
|
/**
|
|
* @input {string} The animation effect of the slides.
|
|
* Possible values are: `slide`, `fade`, `cube`, `coverflow` or `flip`.
|
|
* Default: `slide`.
|
|
*/
|
|
@Prop() effect: string = 'slide';
|
|
|
|
/**
|
|
* @input {number} Delay between transitions (in milliseconds). If this
|
|
* parameter is not passed, autoplay is disabled. Default does
|
|
* not have a value and does not autoplay.
|
|
* Default: `null`.
|
|
*/
|
|
@Prop() autoplay: number;
|
|
|
|
/**
|
|
* @input {Slides} Pass another Slides instance or array of Slides instances
|
|
* that should be controlled by this Slides instance.
|
|
* Default: `null`.
|
|
*/
|
|
@Prop() control: Slides | Slides[] = null;
|
|
|
|
/**
|
|
* @input {string} Swipe direction: 'horizontal' or 'vertical'.
|
|
* Default: `horizontal`.
|
|
*/
|
|
@Prop() direction: 'horizontal' | 'vertical' = 'horizontal';
|
|
|
|
/**
|
|
* @input {number} Index number of initial slide. Default: `0`.
|
|
*/
|
|
@Prop() initialSlide: number = 0;
|
|
|
|
/**
|
|
* @input {boolean} If true, continuously loop from the last slide to the
|
|
* first slide.
|
|
*/
|
|
@Prop() loop: boolean = false;
|
|
|
|
/**
|
|
* @input {boolean} If true, show the pager.
|
|
*/
|
|
@Prop() pager: boolean;
|
|
|
|
/**
|
|
* @input {string} Type of pagination. Possible values are:
|
|
* `bullets`, `fraction`, `progress`. Default: `bullets`.
|
|
* (Note that the pager will not show unless `pager` input
|
|
* is set to true).
|
|
*/
|
|
@Prop() paginationType: string = 'bullets';
|
|
|
|
|
|
/**
|
|
* @input {boolean} If true, allows you to use "parallaxed" elements inside of
|
|
* slider.
|
|
*/
|
|
@Prop() parallax: boolean = false;
|
|
|
|
/**
|
|
* @input {number} Slides per view. Slides visible at the same time. Default: `1`.
|
|
*/
|
|
@Prop() slidesPerView: number | 'auto' = 1;
|
|
|
|
/**
|
|
* @input {number} Distance between slides in px. Default: `0`.
|
|
*/
|
|
@Prop() spaceBetween: number = 0;
|
|
|
|
/**
|
|
* @input {number} Duration of transition between slides
|
|
* (in milliseconds). Default: `300`.
|
|
*/
|
|
@Prop() speed: number = 300;
|
|
|
|
|
|
/**
|
|
* @input {boolean} If true, enables zooming functionality.
|
|
*/
|
|
@Prop() zoom: boolean;
|
|
|
|
/**
|
|
* @input {boolean} If true, enables keyboard control
|
|
*/
|
|
@Prop() keyboardControl: boolean;
|
|
|
|
|
|
render() {
|
|
return h(this,
|
|
h('div', {
|
|
class: {
|
|
'swiper-container': true
|
|
},
|
|
'data-dir': 'rtl'
|
|
},
|
|
[
|
|
h('div', {
|
|
class: {
|
|
'swiper-wrapper': true
|
|
}
|
|
},
|
|
h('slot')
|
|
),
|
|
h('div', {
|
|
class: {
|
|
'swiper-pagination': true,
|
|
'hide': !this.pager
|
|
}
|
|
})
|
|
]
|
|
)
|
|
);
|
|
}
|
|
|
|
/**
|
|
* @hidden
|
|
* Height of container.
|
|
*/
|
|
height: number;
|
|
|
|
/**
|
|
* @hidden
|
|
* Width of container.
|
|
*/
|
|
width: number;
|
|
|
|
/**
|
|
* @hidden
|
|
* Enabled this option and swiper will be operated as usual except it will
|
|
* not move, real translate values on wrapper will not be set. Useful when
|
|
* you may need to create custom slide transition.
|
|
*/
|
|
virtualTranslate = false;
|
|
|
|
/**
|
|
* @hidden
|
|
* Set to true to round values of slides width and height to prevent blurry
|
|
* texts on usual resolution screens (if you have such)
|
|
*/
|
|
roundLengths = false;
|
|
|
|
// Slides grid
|
|
|
|
/**
|
|
* @hidden
|
|
*/
|
|
originalEvent: any;
|
|
|
|
emitEvent(eventName: string) {
|
|
return (data: any) => {
|
|
Ionic.emit(this, eventName, data);
|
|
};
|
|
}
|
|
|
|
|
|
/**
|
|
* Private properties only useful to this class.
|
|
* ------------------------------------
|
|
*/
|
|
private _init: boolean;
|
|
private _tmr: number;
|
|
|
|
/**
|
|
* Properties that are exposed publically but no docs.
|
|
* ------------------------------------
|
|
*/
|
|
/** @hidden */
|
|
container: HTMLElement;
|
|
/** @hidden */
|
|
id: number;
|
|
/** @hidden */
|
|
renderedHeight: number;
|
|
/** @hidden */
|
|
renderedWidth: number;
|
|
/** @hidden */
|
|
slideId: string;
|
|
/** @hidden */
|
|
swipeDirection: string;
|
|
/** @hidden */
|
|
velocity: number;
|
|
|
|
|
|
/**
|
|
* Properties which are for internal use only
|
|
* and not exposed to the public
|
|
* ------------------------------------
|
|
*/
|
|
|
|
/** @hidden */
|
|
nextButton: HTMLElement;
|
|
/** @hidden */
|
|
prevButton: HTMLElement;
|
|
|
|
|
|
|
|
constructor(
|
|
) {
|
|
this.id = ++slidesId;
|
|
this.slideId = 'slides-' + this.id;
|
|
}
|
|
|
|
private _initSlides() {
|
|
if (!this._init) {
|
|
console.debug(`ion-slides, init`);
|
|
|
|
this.container = <HTMLElement>this.$el.children[0];
|
|
|
|
var swiperOptions = {
|
|
height: this.height,
|
|
width: this.width,
|
|
virtualTranslate: this.virtualTranslate,
|
|
roundLengths: this.roundLengths,
|
|
originalEvent: this.originalEvent,
|
|
autoplay: this.autoplay,
|
|
direction: this.direction,
|
|
initialSlide: this.initialSlide,
|
|
loop: this.loop,
|
|
pager: this.pager,
|
|
paginationType: this.paginationType,
|
|
parallax: this.parallax,
|
|
slidesPerView: this.slidesPerView,
|
|
spaceBetween: this.spaceBetween,
|
|
speed: this.speed,
|
|
zoom: this.zoom,
|
|
slidesPerColumn: 1,
|
|
slidesPerColumnFill: 'column',
|
|
slidesPerGroup: 1,
|
|
centeredSlides: false,
|
|
slidesOffsetBefore: 0,
|
|
slidesOffsetAfter: 0,
|
|
touchEventsTarget: 'container',
|
|
autoplayDisableOnInteraction: true,
|
|
autoplayStopOnLast: false,
|
|
freeMode: false,
|
|
freeModeMomentum: true,
|
|
freeModeMomentumRatio: 1,
|
|
freeModeMomentumBounce: true,
|
|
freeModeMomentumBounceRatio: 1,
|
|
freeModeMomentumVelocityRatio: 1,
|
|
freeModeSticky: false,
|
|
freeModeMinimumVelocity: 0.02,
|
|
autoHeight: false,
|
|
setWrapperSize: false,
|
|
zoomMax: 3,
|
|
zoomMin: 1,
|
|
zoomToggle: true,
|
|
touchRatio: 1,
|
|
touchAngle: 45,
|
|
simulateTouch: true,
|
|
shortSwipes: true,
|
|
longSwipes: true,
|
|
longSwipesRatio: 0.5,
|
|
longSwipesMs: 300,
|
|
followFinger: true,
|
|
onlyExternal: false,
|
|
threshold: 0,
|
|
touchMoveStopPropagation: true,
|
|
touchReleaseOnEdges: false,
|
|
iOSEdgeSwipeDetection: false,
|
|
iOSEdgeSwipeThreshold: 20,
|
|
paginationClickable: false,
|
|
paginationHide: false,
|
|
resistance: true,
|
|
resistanceRatio: 0.85,
|
|
watchSlidesProgress: false,
|
|
watchSlidesVisibility: false,
|
|
preventClicks: true,
|
|
preventClicksPropagation: true,
|
|
slideToClickedSlide: false,
|
|
loopAdditionalSlides: 0,
|
|
noSwiping: true,
|
|
runCallbacksOnInit: true,
|
|
controlBy: 'slide',
|
|
controlInverse: false,
|
|
keyboardControl: true,
|
|
coverflow: {
|
|
rotate: 50,
|
|
stretch: 0,
|
|
depth: 100,
|
|
modifier: 1,
|
|
slideShadows: true
|
|
},
|
|
flip: {
|
|
slideShadows: true,
|
|
limitRotation: true
|
|
},
|
|
cube: {
|
|
slideShadows: true,
|
|
shadow: true,
|
|
shadowOffset: 20,
|
|
shadowScale: 0.94
|
|
},
|
|
fade: {
|
|
crossFade: false
|
|
},
|
|
prevSlideMessage: 'Previous slide',
|
|
nextSlideMessage: 'Next slide',
|
|
firstSlideMessage: 'This is the first slide',
|
|
lastSlideMessage: 'This is the last slide',
|
|
onSlideChangeStart: this.emitEvent('ionSlideWillChange'),
|
|
onSlideChangeEnd: this.emitEvent('ionSlideDidChange'),
|
|
onAutoplay: this.emitEvent('ionSlideAutoplay'),
|
|
onAutoplayStart: this.emitEvent('ionSlideAutoplayStart'),
|
|
onAutoplayStop: this.emitEvent('ionSlideAutoplayStop'),
|
|
onSlideNextStart: this.emitEvent('ionSlideNextStarto'),
|
|
onSlidePrevStart: this.emitEvent('ionSlidePrevStart'),
|
|
onSlideNextEnd: this.emitEvent('ionSlideNextEnd'),
|
|
onSlidePrevEnd: this.emitEvent('ionSlidePrevEnd'),
|
|
onTransitionStart: this.emitEvent('ionSlideTransitionStart'),
|
|
onTransitionEnd: this.emitEvent('ionSlideTransitionEnd'),
|
|
onTap: this.emitEvent('ionSlideTap'),
|
|
onDoubleTap: this.emitEvent('ionSlideDoubleTap'),
|
|
onProgress: this.emitEvent('ionSlideProgress'),
|
|
onSliderMove: this.emitEvent('ionSlideDrag'),
|
|
onReachBeginning: this.emitEvent('ionSlideReachStart'),
|
|
onReachEnd: this.emitEvent('ionSlideReachEnd'),
|
|
onTouchStart: this.emitEvent('ionSlideTouchStart'),
|
|
onTouchEnd: this.emitEvent('ionSlideTouchEnd')
|
|
};
|
|
|
|
// init swiper core
|
|
this.swiper = new Swiper(this.container, swiperOptions);
|
|
|
|
if (this.keyboardControl) {
|
|
// init keyboard event listeners
|
|
this.enableKeyboardControl(true);
|
|
}
|
|
|
|
this._init = true;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @hidden
|
|
*/
|
|
ionViewDidLoad() {
|
|
/**
|
|
* TODO: This should change because currently ionViewDidLoad fires independent of whether the
|
|
* child components are ready.
|
|
*/
|
|
setTimeout(() => {
|
|
this._initSlides();
|
|
}, 10);
|
|
}
|
|
|
|
/**
|
|
* Update the underlying slider implementation. Call this if you've added or removed
|
|
* child slides.
|
|
*/
|
|
update(debounce = 300) {
|
|
if (this._init) {
|
|
window.clearTimeout(this._tmr);
|
|
this._tmr = window.setTimeout(() => {
|
|
this.swiper.update();
|
|
|
|
// Don't allow pager to show with > 10 slides
|
|
if (this.length() > 10) {
|
|
this.paginationType = undefined;
|
|
}
|
|
}, debounce);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Transition to the specified slide.
|
|
*
|
|
* @param {number} index The index number of the slide.
|
|
* @param {number} [speed] Transition duration (in ms).
|
|
* @param {boolean} [runCallbacks] Whether or not to emit the `ionSlideWillChange`/`ionSlideDidChange` events. Default true.
|
|
*/
|
|
slideTo(index: number, speed?: number, runCallbacks?: boolean) {
|
|
this.swiper.slideTo(index, speed, runCallbacks);
|
|
}
|
|
|
|
/**
|
|
* Transition to the next slide.
|
|
*
|
|
* @param {number} [speed] Transition duration (in ms).
|
|
* @param {boolean} [runCallbacks] Whether or not to emit the `ionSlideWillChange`/`ionSlideDidChange` events. Default true.
|
|
*/
|
|
slideNext(speed?: number, runCallbacks?: boolean) {
|
|
this.swiper.slideNext(runCallbacks, speed);
|
|
}
|
|
|
|
/**
|
|
* Transition to the previous slide.
|
|
*
|
|
* @param {number} [speed] Transition duration (in ms).
|
|
* @param {boolean} [runCallbacks] Whether or not to emit the `ionSlideWillChange`/`ionSlideDidChange` events. Default true.
|
|
*/
|
|
slidePrev(speed?: number, runCallbacks?: boolean) {
|
|
this.swiper.slidePrev(runCallbacks, speed);
|
|
}
|
|
|
|
/**
|
|
* Get the index of the active slide.
|
|
*
|
|
* @returns {number} The index number of the current slide.
|
|
*/
|
|
getActiveIndex(): number {
|
|
return this.swiper.activeIndex;
|
|
}
|
|
|
|
/**
|
|
* Get the index of the previous slide.
|
|
*
|
|
* @returns {number} The index number of the previous slide.
|
|
*/
|
|
getPreviousIndex(): number {
|
|
return this.swiper.previousIndex;
|
|
}
|
|
|
|
/**
|
|
* Get the total number of slides.
|
|
*
|
|
* @returns {number} The total number of slides.
|
|
*/
|
|
length(): number {
|
|
return this.swiper.slides.length;
|
|
}
|
|
|
|
/**
|
|
* Get whether or not the current slide is the last slide.
|
|
*
|
|
* @returns {boolean} If the slide is the last slide or not.
|
|
*/
|
|
isEnd(): boolean {
|
|
return this.isEnd();
|
|
}
|
|
|
|
/**
|
|
* Get whether or not the current slide is the first slide.
|
|
*
|
|
* @returns {boolean} If the slide is the first slide or not.
|
|
*/
|
|
isBeginning(): boolean {
|
|
return this.isBeginning();
|
|
}
|
|
|
|
/**
|
|
* Start auto play.
|
|
*/
|
|
startAutoplay() {
|
|
this.swiper.startAutoplay();
|
|
}
|
|
|
|
/**
|
|
* Stop auto play.
|
|
*/
|
|
stopAutoplay() {
|
|
this.swiper.stopAutoplay();
|
|
}
|
|
|
|
/**
|
|
* Lock or unlock the ability to slide to the next slides.
|
|
*/
|
|
lockSwipeToNext(shouldLockSwipeToNext: boolean) {
|
|
if (shouldLockSwipeToNext) {
|
|
return this.swiper.lockSwipeToNext();
|
|
}
|
|
this.swiper.unlockSwipeToNext();
|
|
}
|
|
|
|
/**
|
|
* Lock or unlock the ability to slide to the previous slides.
|
|
*/
|
|
lockSwipeToPrev(shouldLockSwipeToPrev: boolean) {
|
|
if (shouldLockSwipeToPrev) {
|
|
return this.swiper.lockSwipeToPrev();
|
|
}
|
|
this.swiper.unlockSwipeToPrev();
|
|
}
|
|
|
|
/**
|
|
* Lock or unlock the ability to slide to change slides.
|
|
*/
|
|
lockSwipes(shouldLockSwipes: boolean) {
|
|
if (shouldLockSwipes) {
|
|
return this.swiper.lockSwipes();
|
|
}
|
|
this.swiper.unlockSwipes();
|
|
}
|
|
|
|
/**
|
|
* Enable or disable keyboard control.
|
|
*/
|
|
enableKeyboardControl(shouldEnableKeyboard: boolean) {
|
|
if (shouldEnableKeyboard) {
|
|
return this.swiper.enableKeyboardControl();
|
|
}
|
|
this.swiper.disableKeyboardControl();
|
|
}
|
|
|
|
/**
|
|
* @hidden
|
|
*/
|
|
ionViewDidUnload() {
|
|
this._init = false;
|
|
|
|
this.swiper.destroy(true, true);
|
|
this.enableKeyboardControl(false);
|
|
}
|
|
}
|
|
|
|
let slidesId = -1;
|