Skip to content

Instantly share code, notes, and snippets.

@ruffiem
Last active February 29, 2020 08:10
Show Gist options
  • Save ruffiem/2a62243ffbe1e0b78a6d1afa6b240a99 to your computer and use it in GitHub Desktop.
Save ruffiem/2a62243ffbe1e0b78a6d1afa6b240a99 to your computer and use it in GitHub Desktop.
before-after image comparison in Angular
<div class="before" #before>
<img [src]="beforeImage">
</div>
<img class="after" [src]="afterImage">
<span class="handler" #handler></span>
:host {
width: 100%;
position: relative;
height: 240px;
display: block;
img {
width: 100%;
}
.before {
width: 100%;
height: 100%;
position: absolute;
overflow: hidden;
background: red;
}
.after {
width: 100%;
height: 100%;
background: blue;
}
.handler {
width: .25rem;
height: 100%;
position: absolute;
cursor: ew-resize;
background: white;
top: 0;
left: 50%;
&:before {
position: absolute;
content: ' ';
width: 1rem;
height: 3rem;
border: .25rem solid white;
top: 50%;
left: 50%;
transform: translate3d(-50%, -50%, 0);
}
}
}
import { Component, ElementRef, HostListener, Input, OnDestroy, OnInit, Renderer2, ViewChild } from '@angular/core';
@Component({
selector: 'app-before-after',
templateUrl: 'before-after.component.html',
styleUrls: ['before-after.component.scss']
})
export class BeforeAfterComponent implements OnInit, OnDestroy {
@Input() beforeImage: string;
@Input() afterImage: string;
isDragging: boolean;
leftPos: number;
posX: number;
moveX: number;
handlerWidth: number;
elementWidth: number;
elementOffset: number;
minLeftPos: number;
maxLeftPos: number;
@ViewChild('before') before: ElementRef;
@ViewChild('handler') handler: ElementRef;
@HostListener('mousemove', ['$event'])
onMouseMove(event: MouseEvent) {
this.onDrag(event);
}
@HostListener('touchmove', ['$event'])
onTouchMove(event: TouchEvent) {
this.onDrag(event);
}
@HostListener('window:resize')
onResize() {
this.computeDimensions();
}
constructor(private el: ElementRef, private renderer: Renderer2) {
document.addEventListener('mouseup', () => this.onDragEnd());
}
ngOnInit() {
this.handler.nativeElement.addEventListener('mousedown', (event: Event) => this.onDragStart(event));
this.handler.nativeElement.addEventListener('touchstart', (event: Event) => this.onDragStart(event));
this.computeDimensions();
}
computeDimensions() {
const beforeImg = this.el.nativeElement.querySelector('img');
this.renderer.setStyle(beforeImg, 'width', `${this.el.nativeElement.clientWidth}px`);
this.elementWidth = this.el.nativeElement.getBoundingClientRect().width;
this.elementOffset = this.el.nativeElement.getBoundingClientRect().left + document.body.scrollLeft;
this.handlerWidth = this.handler.nativeElement.getBoundingClientRect().width;
this.minLeftPos = this.elementOffset + 10;
this.maxLeftPos = this.elementOffset + this.elementWidth - this.handlerWidth - 10;
}
onDragStart(event) {
event.preventDefault();
const startX = (event.pageX) ? event.pageX : event.touches[0].pageX;
this.posX = (this.handler.nativeElement.getBoundingClientRect().left + document.body.scrollLeft + this.handlerWidth) - startX;
this.isDragging = true;
}
onDragEnd() {
this.isDragging = false;
}
onDrag(event: MouseEvent|TouchEvent) {
event.preventDefault();
if (this.isDragging) {
this.moveX = (event['pageX']) ? event['pageX'] : event['touches'][0].pageX;
this.leftPos = (this.moveX + this.posX) - this.handlerWidth;
this.requestDrag();
}
}
drag() {
if (this.leftPos < this.minLeftPos) {
this.leftPos = this.minLeftPos;
} else if (this.leftPos > this.maxLeftPos) {
this.leftPos = this.maxLeftPos;
}
let openRatio = (this.leftPos + (this.handlerWidth / 2)) - this.elementOffset;
openRatio /= this.elementWidth;
const width = `${openRatio * 100}%`;
this.renderer.setStyle(this.handler.nativeElement, 'left', width);
this.renderer.setStyle(this.before.nativeElement, 'width', width);
}
requestDrag() {
window.requestAnimationFrame(this.drag.bind(this));
}
ngOnDestroy() {
document.removeEventListener('mouseup', () => this.onDragEnd());
this.before.nativeElement.removeEventListener('mousedown', (event: Event) => this.onDragStart(event));
this.before.nativeElement.removeEventListener('touchstart', (event: Event) => this.onDragStart(event));
}
}
@dm-grinko
Copy link

Thank you for sharing! I would add default values:

<div class="before" #before style="width: 50%;">
    <img [src]="beforeImage">
</div>
<img class="after" [src]="afterImage">
<span class="handler" #handler style="left: 50%;"></span>

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment