Last active
February 29, 2020 08:10
-
-
Save ruffiem/2a62243ffbe1e0b78a6d1afa6b240a99 to your computer and use it in GitHub Desktop.
before-after image comparison in Angular
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<div class="before" #before> | |
<img [src]="beforeImage"> | |
</div> | |
<img class="after" [src]="afterImage"> | |
<span class="handler" #handler></span> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
: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); | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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)); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Thank you for sharing! I would add default values: