Files
2017-05-17 22:34:29 +02:00

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;