From ca5e335c6789443e6f8a9e93fe95049646fb522f Mon Sep 17 00:00:00 2001 From: fdiazaguirre Date: Wed, 22 Aug 2018 14:29:26 +0200 Subject: [PATCH 1/3] Add swipe to images and set as default method --- README.md | 8 +- ... => nativescript-ng2-carousel-swipeable.ts | 215 ++++++++++-------- package-lock.json | 47 ++++ package.json | 15 +- tsconfig.json | 2 +- 5 files changed, 177 insertions(+), 110 deletions(-) rename nativescript-ng2-carousel.ts => nativescript-ng2-carousel-swipeable.ts (67%) create mode 100644 package-lock.json 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..084bcdd 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,8 +51,12 @@ 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(); + console.log(`${this.currentElementHiglhtColor}`) } /** @@ -100,10 +106,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 +116,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 +148,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 +209,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 +316,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 +330,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 +371,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 +405,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 +423,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..b369fed 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.1", "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", From e3827ef0224ccb67e27195e6acc5f6ed72dc4fa7 Mon Sep 17 00:00:00 2001 From: fdiazaguirre Date: Tue, 28 Aug 2018 17:30:36 +0200 Subject: [PATCH 2/3] fix version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index b369fed..f3579fc 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "nativescript-ng2-carousel-swipeable", - "version": "0.1.1", + "version": "0.1.2", "description": "A simple NativeScript + Angular images carousel for iOS and Android", "license": "MIT", "main": "nativescript-ng2-carousel-swipeable", From 9a5a3b221cf411482b26f0e41ec4375494fed941 Mon Sep 17 00:00:00 2001 From: fdiazaguirre Date: Tue, 4 Sep 2018 21:35:22 +0200 Subject: [PATCH 3/3] Remove comment --- nativescript-ng2-carousel-swipeable.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/nativescript-ng2-carousel-swipeable.ts b/nativescript-ng2-carousel-swipeable.ts index 084bcdd..a79e496 100644 --- a/nativescript-ng2-carousel-swipeable.ts +++ b/nativescript-ng2-carousel-swipeable.ts @@ -56,7 +56,6 @@ export class CarouselDirective implements AfterViewInit { this.initControls(); } this.initAutoPlay(); - console.log(`${this.currentElementHiglhtColor}`) } /**