diff --git a/README.md b/README.md
index 03fb7a5..21c32e6 100644
--- a/README.md
+++ b/README.md
@@ -42,7 +42,10 @@ npm install nativescript-ng2-carousel --save
3. To use the CarouselDirective, create a template that applies the directive as an attribute to a paragraph GridLayout element:
-<GridLayout [carousel]="images" carouselLabelOverlay="true" carouselSpeed="2000">
+<GridLayout [carousel]="images"
+ carouselLabelOverlay="true"
+ carouselSpeed="2000"
+ (selectedImageChange)="select($event)">
</GridLayout>
@@ -133,8 +136,11 @@ Currently directive supported attributes:
* **carousel** list of images as JSON or CarouselSlide class, accepted parameters (title?, url?, file?)
* **carouselSpeed** _(optional)_ defines the interval (number in ms) to wait before the next slide is shown
* **carouselAnimationSpeed** _(optional)_ defines the animation speed (number in ms)
+* **arrowsEnabled** _(optional)_ Show arrows to navigate the carousel (default false since swipe it's preferred)
* **carouselArrows** _(optional)_ arrow type, accepted values _none_, _small_, _normal_, _bold_ or _narrow_ (default _normal_)
* **carouselLabelOverlay** _(optional)_ silde title over image, accepted values _true_ or _false_ (default false)
+* **selectedImageChange** _(optional)_ get the title of the selected image (key)
+* **currentElementHiglhtColor** _(recommended)_ background color to apply to the current element
## Changelog
diff --git a/nativescript-ng2-carousel.ts b/nativescript-ng2-carousel-swipeable.ts
similarity index 67%
rename from nativescript-ng2-carousel.ts
rename to nativescript-ng2-carousel-swipeable.ts
index 4a133af..a79e496 100644
--- a/nativescript-ng2-carousel.ts
+++ b/nativescript-ng2-carousel-swipeable.ts
@@ -1,20 +1,17 @@
-import {Directive, ElementRef, AfterViewInit, Input} from '@angular/core';
-import {AnimationCurve} from "ui/enums";
+import {AfterViewInit, Directive, ElementRef, EventEmitter, Input, Output} from '@angular/core';
import {Image} from "ui/image";
import {StackLayout} from "ui/layouts/stack-layout";
import {GridLayout, ItemSpec} from "ui/layouts/grid-layout";
-import {GridUnitType} from "ui/layouts/grid-layout";
-import {HorizontalAlignment} from "ui/enums";
import {Label} from "ui/label";
-import {GestureTypes} from "ui/gestures";
+import {GestureTypes, SwipeGestureEventData} from "ui/gestures";
import {View} from "ui/core/view";
-import {Visibility} from "ui/enums";
-import {fromFile} from "image-source";
-import {fromResource} from "image-source";
+import {fromFile, fromResource} from "image-source";
@Directive({selector: '[carousel]'})
export class CarouselDirective implements AfterViewInit {
+ private static TRANSPARENT: string = "#FFFFFF";
+
private static animationSpeedDefault: number = 400; // in ms
private static autoPlaySpeedDefault: number = 0; // in ms
@@ -27,18 +24,23 @@ export class CarouselDirective implements AfterViewInit {
// Private control attributes
private direction: CarouselDirections = null;
- private currentImage: number = 0;
private movingImages: boolean = false;
- private indexMoveLeft: number = null;
- private indexMoveRight: number = null;
- private indexMoveCenter: number = null;
+
+ // My pointers
+ private currentImage: number = 0;
+ private nextImage: number = null;
+ private nextNextImage: number = null;
// Options
@Input() carousel: any;
@Input() carouselSpeed: number; // autoplay speed (ms)
@Input() carouselArrows: string; // arrows type
+ @Input() arrowsEnabled: boolean = false; // enable arrows [default to false]
@Input() carouselLabelOverlay: boolean; // title over image (bool)
@Input() carouselAnimationSpeed: number; // animation speed
+ @Input() currentElementHiglhtColor: string;
+
+ @Output() selectedImageChange: EventEmitter = new EventEmitter();
constructor(private elem: ElementRef) {
this.container = elem.nativeElement;
@@ -49,7 +51,10 @@ export class CarouselDirective implements AfterViewInit {
this.initContainer();
this.initImagesLayout();
this.initSlides();
- this.initControls();
+ // Prefer swipe over arrows tap
+ if (this.arrowsEnabled === true) {
+ this.initControls();
+ }
this.initAutoPlay();
}
@@ -100,10 +105,8 @@ export class CarouselDirective implements AfterViewInit {
* Init carousel layout
*/
private initContainer() {
- this.container.horizontalAlignment = "center";
+ this.container.horizontalAlignment = "left";
this.container.addRow(new ItemSpec(1, "auto"));
- this.container.addColumn(new ItemSpec(1, "star"));
- this.container.addColumn(new ItemSpec(1, "star"));
}
/**
@@ -112,10 +115,30 @@ export class CarouselDirective implements AfterViewInit {
private initImagesLayout() {
this.totalItems = this.carousel.length;
this.carouselSlides = new GridLayout();
- GridLayout.setColumnSpan(this.carouselSlides, 2);
+ GridLayout.setColumnSpan(this.carouselSlides, 3);
+ this.carouselSlides.addColumn(new ItemSpec(1, 'auto'));
+ this.carouselSlides.addColumn(new ItemSpec(1, 'auto'));
+ this.carouselSlides.addColumn(new ItemSpec(1, 'auto'));
this.container.addChild(this.carouselSlides);
}
+ private initVisibility(index: number) {
+ return index === 0 || index === 1 || index === 2 ? "visible" : "collapse";
+ }
+
+ private getInitColPos(index: number) {
+ switch (index) {
+ case 0:
+ return 0;
+ case 1:
+ return 1;
+ case 2:
+ return 2; // could be any value between 0-2 since would be collapse
+
+ }
+ }
+
+
/**
* Init carousel sliders provided in "carousel" directive attribute
*/
@@ -124,23 +147,59 @@ export class CarouselDirective implements AfterViewInit {
let gridLayout = new GridLayout();
gridLayout.addRow(new ItemSpec(1, "auto"));
- gridLayout.visibility = i == 0 ? "visible" : "collapse";
+ gridLayout.visibility = this.initVisibility(i);
+
+ if (i === 0 ) {
+ gridLayout.backgroundColor = this.currentElementHiglhtColor;
+ }
+ let image: Image;
if (slide.url) {
- let image: Image = CarouselDirective.generateImageSliderFromUrl(slide.url);
+ image = CarouselDirective.generateImageSliderFromUrl(slide.url);
+ image.on(GestureTypes.tap, () => {
+ this.selectedImageChange.emit( slide.title );
+ });
+ image.on(GestureTypes.swipe, (args: SwipeGestureEventData) => {
+ if (args.direction === 1) {
+ this.swipe(CarouselDirections.DIRECTION_LEFT);
+ } else if (args.direction === 2) {
+ this.swipe(CarouselDirections.DIRECTION_RIGHT);
+ }
+ });
gridLayout.addChild(image);
}
if (slide.file && slide.file.indexOf('res://') !== 0) {
- let image: Image = CarouselDirective.generateImageSliderFromFile(slide.file);
+ image = CarouselDirective.generateImageSliderFromFile(slide.file);
+ image.on(GestureTypes.tap, () => {
+ this.selectedImageChange.emit( slide.title );
+ });
+ image.on(GestureTypes.swipe, (args: SwipeGestureEventData) => {
+ if (args.direction === 1) {
+ this.swipe(CarouselDirections.DIRECTION_LEFT);
+ } else if (args.direction === 2) {
+ this.swipe(CarouselDirections.DIRECTION_RIGHT);
+ }
+ });
gridLayout.addChild(image);
}
if (slide.file && slide.file.indexOf('res://') === 0) {
- let image: Image = CarouselDirective.generateImageSliderFromResource(slide.file);
+ image = CarouselDirective.generateImageSliderFromResource(slide.file);
+ image.on(GestureTypes.tap, () => {
+ this.selectedImageChange.emit( slide.title );
+ });
+ image.on(GestureTypes.swipe, (args: SwipeGestureEventData) => {
+ if (args.direction === 1) {
+ this.swipe(CarouselDirections.DIRECTION_LEFT);
+ } else if (args.direction === 2) {
+ this.swipe(CarouselDirections.DIRECTION_RIGHT);
+ }
+ });
gridLayout.addChild(image);
}
+
if (slide.title) {
let title: Label = CarouselDirective.generateTitleSlider(slide.title);
if (this.carouselLabelOverlay) {
@@ -149,7 +208,11 @@ export class CarouselDirective implements AfterViewInit {
}
gridLayout.addChild(title);
}
+
this.carouselSlides.addChild(gridLayout);
+ if (gridLayout.visibility === 'visible') {
+ GridLayout.setColumn(gridLayout, this.getInitColPos(i));
+ }
});
}
@@ -252,6 +315,7 @@ export class CarouselDirective implements AfterViewInit {
private initAutoPlay() {
if (this.carouselSpeed && CarouselDirective.isNumeric(this.carouselSpeed)) {
clearInterval(this.autoPlayIntervalId);
+ // @ts-ignore
this.autoPlayIntervalId = setInterval(() => {
this.swipe(CarouselDirections.DIRECTION_RIGHT);
}, this.carouselSpeed + this.carouselAnimationSpeed);
@@ -265,6 +329,7 @@ export class CarouselDirective implements AfterViewInit {
if (this.autoPlayIntervalId) {
clearTimeout(this.autoPlayTimeoutId);
clearInterval(this.autoPlayIntervalId);
+ // @ts-ignore
this.autoPlayTimeoutId = setTimeout(() => {
this.swipe(CarouselDirections.DIRECTION_RIGHT);
this.initAutoPlay();
@@ -305,81 +370,29 @@ export class CarouselDirective implements AfterViewInit {
// Get element width + image visibility
let elementWidth = this.elem.nativeElement.getActualSize().width;
- view.visibility = [this.indexMoveCenter, this.indexMoveLeft, this.indexMoveRight].indexOf(i) > -1 ? "visible" : "collapse";
+ view.visibility = [this.currentImage, this.nextImage, this.nextNextImage].indexOf(i) > -1 ? "visible" : "collapse";
- // Perfrom animation
- this.checkCL(view, i, elementWidth);
- this.checkCR(view, i, elementWidth);
- this.checkRC(view, i, elementWidth);
- this.checkLC(view, i, elementWidth);
- }
- }
-
- /**
- * Move image center -> left
- * @param view
- * @param index
- * @param elementWidth
- */
- private checkCL(view: View, index: number, elementWidth: number) {
- if (this.indexMoveLeft == index) {
- view.translateX = 0;
- view.animate({
- translate: {x: elementWidth, y: 0},
- duration: this.carouselAnimationSpeed,
- curve: AnimationCurve.easeIn
- });
- }
- }
-
- /**
- * Move image right -> center
- * @param view
- * @param index
- * @param elementWidth
- */
- private checkRC(view: View, index: number, elementWidth: number) {
- if (this.indexMoveCenter == index && this.direction == CarouselDirections.DIRECTION_LEFT) {
- view.translateX = -elementWidth;
- view.animate({
- translate: {x: 0, y: 0},
- duration: this.carouselAnimationSpeed,
- curve: AnimationCurve.easeOut
- });
- }
- }
-
- /**
- * Move image center -> right
- * @param view
- * @param index
- * @param elementWidth
- */
- private checkCR(view: View, index: number, elementWidth: number) {
- if (this.indexMoveRight == index) {
- view.translateX = 0;
- view.animate({
- translate: {x: -elementWidth, y: 0},
- duration: this.carouselAnimationSpeed,
- curve: AnimationCurve.easeIn
- });
+ // Perfrom translation
+ if (view.visibility === 'visible'){
+ this.applySwipe(view, i, elementWidth);
+ }
}
}
- /**
- * Move image left -> center
- * @param view
- * @param index
- * @param elementWidth
- */
- private checkLC(view: View, index: number, elementWidth: number) {
- if (this.indexMoveCenter == index && this.direction == CarouselDirections.DIRECTION_RIGHT) {
- view.translateX = elementWidth;
- view.animate({
- translate: {x: 0, y: 0},
- duration: this.carouselAnimationSpeed,
- curve: AnimationCurve.easeOut
- });
+ private applySwipe (view: View, index: number, elementWidth: number) {
+ switch (index) {
+ case this.currentImage:
+ view.backgroundColor = this.currentElementHiglhtColor;
+ GridLayout.setColumn(view, 0);
+ break;
+ case this.nextImage:
+ view.backgroundColor = CarouselDirective.TRANSPARENT;
+ GridLayout.setColumn(view, 1);
+ break;
+ case this.nextNextImage:
+ view.backgroundColor = CarouselDirective.TRANSPARENT;
+ GridLayout.setColumn(view, 2);
+ break;
}
}
@@ -391,16 +404,16 @@ export class CarouselDirective implements AfterViewInit {
// right to left
case CarouselDirections.DIRECTION_LEFT:
- this.indexMoveLeft = this.currentImage;
- this.currentImage = ((this.currentImage == 0 ? this.totalItems : this.currentImage) - 1) % this.totalItems;
- this.indexMoveCenter = this.currentImage;
+ this.currentImage = ((this.currentImage === 0 ? this.totalItems : this.currentImage) - 1) % this.totalItems;
+ this.nextImage = ((this.currentImage === 0 ? this.totalItems : this.currentImage) - 1) % this.totalItems;
+ this.nextNextImage = ((this.nextImage === 0 ? this.totalItems : this.nextImage) - 1) % this.totalItems;
break;
// left to right
case CarouselDirections.DIRECTION_RIGHT:
- this.indexMoveRight = this.currentImage;
- this.currentImage = (this.currentImage + 1) % this.totalItems;
- this.indexMoveCenter = this.currentImage;
+ this.currentImage = ((this.currentImage === 0 ? this.totalItems : this.currentImage) + 1) % this.totalItems;
+ this.nextImage = (this.currentImage + 1) % this.totalItems;
+ this.nextNextImage = (this.nextImage + 1) % this.totalItems;
break;
}
}
@@ -409,9 +422,8 @@ export class CarouselDirective implements AfterViewInit {
* Reset values after animation
*/
private resetAnimationValues() {
- this.indexMoveLeft = null;
- this.indexMoveRight = null;
- this.indexMoveCenter = null;
+ this.nextImage = null;
+ this.nextNextImage = null;
this.movingImages = false;
}
diff --git a/package-lock.json b/package-lock.json
new file mode 100644
index 0000000..aa5c790
--- /dev/null
+++ b/package-lock.json
@@ -0,0 +1,47 @@
+{
+ "name": "nativescript-ng2-carousel",
+ "version": "0.1.0",
+ "lockfileVersion": 1,
+ "requires": true,
+ "dependencies": {
+ "@angular/core": {
+ "version": "6.1.3",
+ "resolved": "https://registry.npmjs.org/@angular/core/-/core-6.1.3.tgz",
+ "integrity": "sha512-pqRfQphqIEExhDWM3RRusvLY6gFN0zdITC7TqQy6Wof6VKgWOvfHiHPbiamw4kpEzflMekuOeNm0s6h6hIUnWA==",
+ "requires": {
+ "tslib": "^1.9.0"
+ }
+ },
+ "rxjs": {
+ "version": "6.2.2",
+ "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.2.2.tgz",
+ "integrity": "sha512-0MI8+mkKAXZUF9vMrEoPnaoHkfzBPP4IGwUYRJhIRJF6/w3uByO1e91bEHn8zd43RdkTMKiooYKmwz7RH6zfOQ==",
+ "requires": {
+ "tslib": "^1.9.0"
+ }
+ },
+ "tns-core-modules": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/tns-core-modules/-/tns-core-modules-4.2.0.tgz",
+ "integrity": "sha512-tEbQfeXVp6i9RJAmC+erd63mr6NYw5nX/gzYDVb1Va52/Jdj9ZeGbXKAPyjVXZ3BGI6O9QM1jQUem/Ow6v5FqQ==",
+ "requires": {
+ "tns-core-modules-widgets": "4.2.0"
+ }
+ },
+ "tns-core-modules-widgets": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/tns-core-modules-widgets/-/tns-core-modules-widgets-4.2.0.tgz",
+ "integrity": "sha512-0YlfWN1Wy2qFlnzwrwit910whA9TC9en1PxS/5Yx63lL9uXHyOB53C8POnKfM82YUFVPxifcpuYtCC7+DBXbag=="
+ },
+ "tslib": {
+ "version": "1.9.3",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.9.3.tgz",
+ "integrity": "sha512-4krF8scpejhaOgqzBEcGM7yDIEfi0/8+8zDRZhNZZ2kjmHJ4hv3zCbQWxoJGz1iw5U0Jl0nma13xzHXcncMavQ=="
+ },
+ "zone.js": {
+ "version": "0.8.26",
+ "resolved": "https://registry.npmjs.org/zone.js/-/zone.js-0.8.26.tgz",
+ "integrity": "sha512-W9Nj+UmBJG251wkCacIkETgra4QgBo/vgoEkb4a2uoLzpQG7qF9nzwoLXWU5xj3Fg2mxGvEDh47mg24vXccYjA=="
+ }
+ }
+}
diff --git a/package.json b/package.json
index 2172607..f3579fc 100644
--- a/package.json
+++ b/package.json
@@ -1,15 +1,15 @@
{
- "name": "nativescript-ng2-carousel",
- "version": "0.1.0",
+ "name": "nativescript-ng2-carousel-swipeable",
+ "version": "0.1.2",
"description": "A simple NativeScript + Angular images carousel for iOS and Android",
"license": "MIT",
- "main": "nativescript-ng2-carousel",
- "homepage": "https://github.com/alcoceba/ngCarouselDirective#readme",
+ "main": "nativescript-ng2-carousel-swipeable",
+ "homepage": "https://github.com/fdiazaguirre/ngCarouselDirective#readme",
"repository": {
"type": "git",
- "url": "git+https://github.com/alcoceba/ngCarouselDirective.git"
+ "url": "git+https://github.com/fdiazaguirre/ngCarouselDirective.git"
},
- "typings": "nativescript-ng2-carousel.ts",
+ "typings": "nativescript-ng2-carousel-swipeable.ts",
"keywords": [
"angular",
"nativescript",
@@ -44,8 +44,9 @@
"zone.js": "^0.8.0"
},
"author": "Manel Alcoceba ",
+ "contributors": ["Federico Diaz "],
"bugs": {
- "url": "https://github.com/alcoceba/ngCarouselDirective/issues"
+ "url": "https://github.com/fdiazaguirre/ngCarouselDirective/issues"
},
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
diff --git a/tsconfig.json b/tsconfig.json
index 2c8cfed..fab8e17 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -20,7 +20,7 @@
}
},
"files": [
- "nativescript-ng2-carousel.ts"
+ "nativescript-ng2-carousel-swipeable.ts"
],
"exclude": [
"node_modules",