Gist
I am creating an image slider with Angular 6, that can be moved by dragging. The position is changed via transform: translateX(percentage)
.When the user doesn't click anymore I am animating the x-position.
Issue(s)
Problem is showcased in this video.
(1) The animation works but isn't changing the transform
style of the element. (so the translateX value is wrong after the animation finished)
(2) After animating once, the dragging does not work anymore (only when destroying the animation, which results in weird behavior)
Code
Html:
<div
class="slider"
(mousedown)="onMouseDown($event)"
(touchstart)="onTouchStart($event)"
(mouseup)="onMouseUp($event)"
(touchend)="onTouchEnd($event)"
(mousemove)="onMouseMove($event)"
(touchmove)="onTouchMove($event)"
(mouseleave)="onMouseLeave($event)"
#slider
>
<div
class="vis-container"
[style.transform]="translateXValue"
[class.grabbing]="grabbing"
[class.grab]="!grabbing"
#container
>
<div
class="item"
*ngFor="let i of images; let j = index"
[style.left]="j * 100 + '%'"
>
<img
dyImagePositioning
src="{{i}}"
>
</div>
</div>
<div>{{translateXValue}}</div>
</div>
Typescript:
images: string[] = [
'https://images.unsplash.com/photo-1533147722845-4078740ae714?ixlib=rb-0.3.5&ixid=eyJhcHBfaWQiOjEyMDd9&s=c8e866ca41f36ad560c435c1bb51e0fd&auto=format&fit=crop&w=1350&q=80',
'https://images.unsplash.com/photo-1504415724575-56cef5e3da72?ixlib=rb-0.3.5&s=89c39a767a764693bcc2e2c2ecad59ce&auto=format&fit=crop&w=1350&q=80',
'https://s3.eu-central-1.amazonaws.com/drakery-4/product-files/5b5c8d96823eba53e45ba5ac/5b5c8d9a823eba53e45ba5ad-normal.webp',
];
grabbing: boolean = false;
startX: number;
startXPerc: number;
translateX: number = 0;
animationDurance: number = 300;
@ViewChild('slider') slider;
@ViewChild('container') container;
constructor(
private builder: AnimationBuilder,
) { }
ngOnInit(): void { }
start(pageX: number): void {
this.grabbing = true;
this.startX = pageX - this.slider.nativeElement.offsetLeft;
this.startXPerc = this.translateX;
}
onMouseDown(event: MouseEvent): void {
this.start(event.pageX);
}
onTouchStart(event: TouchEvent): void {
this.start(event.changedTouches[0].pageX);
}
end(): void {
this.grabbing = false;
const endX = Math.round(this.translateX / 100) * 100;
const animationFactory = this.builder.build(this.translate(
this.translateX,
endX,
));
const animationPlayer: AnimationPlayer = animationFactory.create(this.container.nativeElement);
animationPlayer.play();
// Destroying the animation makes it able
// to drag again, but results in weird behaviour
/* animationPlayer.onDone(() => {
animationPlayer.destroy();
}) */
}
onMouseUp(event: MouseEvent): void {
this.end();
}
onTouchEnd(event: TouchEvent): void {
this.end();
}
move(pageX: number): void {
if (!this.grabbing) return;
event.preventDefault();
const x = pageX - this.slider.nativeElement.offsetLeft;
const diff = this.startX - x;
const diffPerc = 100 * diff / this.slider.nativeElement.offsetWidth;
this.translateX = this.startXPerc - diffPerc;
}
onMouseMove(event: MouseEvent): void {
this.move(event.pageX);
}
onTouchMove(event: TouchEvent): void {
this.move(event.changedTouches[0].pageX);
}
onMouseLeave(event: MouseEvent): void {
if (!this.grabbing) return;
this.grabbing = false;
this.end();
}
get translateXValue(): string {
return `translateX(${this.translateX}%)`
}
private translate(startX: number, endX: number): AnimationMetadata[] {
return [
style({ transform: `translateX(${startX}%)` }),
animate(`${this.animationDurance}ms ease-out`, style({ transform: `translateX(${endX}%)` })),
];
}
In your case, I would dump the angular animation and simply use a css animation instead. So in your case it would something like this:
.slider
...
transition-duration: 500ms
transition-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1)