Skip to content

Commit

Permalink
Add swipe to images and set as default method
Browse files Browse the repository at this point in the history
  • Loading branch information
fdiazaguirre committed Aug 28, 2018
1 parent 4dd9773 commit ca5e335
Show file tree
Hide file tree
Showing 5 changed files with 177 additions and 110 deletions.
8 changes: 7 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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:
<pre>
&lt;GridLayout [carousel]="images" carouselLabelOverlay="true" carouselSpeed="2000"&gt;
&lt;GridLayout [carousel]="images"
carouselLabelOverlay="true"
carouselSpeed="2000"
(selectedImageChange)="select($event)"&gt;

&lt;/GridLayout&gt;
</pre>
Expand Down Expand Up @@ -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

Expand Down
215 changes: 114 additions & 101 deletions nativescript-ng2-carousel.ts → nativescript-ng2-carousel-swipeable.ts
Original file line number Diff line number Diff line change
@@ -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

Expand All @@ -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<string> = new EventEmitter<string>();

constructor(private elem: ElementRef) {
this.container = elem.nativeElement;
Expand All @@ -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}`)
}

/**
Expand Down Expand Up @@ -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"));
}

/**
Expand All @@ -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
*/
Expand All @@ -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) {
Expand All @@ -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));
}
});
}

Expand Down Expand Up @@ -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);
Expand All @@ -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();
Expand Down Expand Up @@ -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;
}
}

Expand All @@ -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;
}
}
Expand All @@ -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;
}

Expand Down
Loading

0 comments on commit ca5e335

Please sign in to comment.