Created
February 23, 2020 18:59
-
-
Save calebergh/65b7f0f8b5d7e5d1e6cb977a4cb907f4 to your computer and use it in GitHub Desktop.
Electronic Signature Pad for Angular 8
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 { Directive, ElementRef, EventEmitter, HostListener, Input, OnChanges, OnInit, Output, SimpleChanges } from '@angular/core'; | |
@Directive({ | |
// tslint:disable-next-line:directive-selector | |
selector: '[signature]', | |
}) | |
export class SignatureDirective implements OnInit, OnChanges { | |
@Output() imgSrc = new EventEmitter<string>(); | |
@Input('clear') clearMe; | |
@Input('options') options; | |
private context; | |
private density: number; | |
private isDrawing = false; | |
private sigPadElement; | |
ngOnInit() { | |
this.sigPadElement = this.signaturePad.nativeElement; | |
this.context = this.sigPadElement.getContext('2d'); | |
this.context.strokeStyle = this.options.color || '#3742fa'; | |
this.context.lineWidth = this.options.width || 2; | |
if ( this.options.blur) { | |
this.context.shadowBlur = this.options.blur || 2; | |
this.context.shadowColor = this.options.blurColor || this.context.strokeStyle; | |
} | |
this.context.lineCap = 'round'; | |
this.context.lineJoin = 'round'; | |
this.density = window.devicePixelRatio || 1; | |
} | |
ngOnChanges(changes: SimpleChanges) { | |
if (!this.context) { | |
return; | |
} | |
if (changes.clearMe) { | |
this.clear(); | |
this.save(); | |
} | |
} | |
constructor(private signaturePad: ElementRef) { | |
} | |
@HostListener('touchstart', ['$event']) | |
handleTouchStart(e) { | |
e.preventDefault(); | |
this.isDrawing = true; | |
this.start(e, 'layer'); | |
} | |
@HostListener('touchmove', ['$event']) | |
handleTouchMove(e) { | |
if (!this.isDrawing) { | |
return; | |
} | |
e.preventDefault(); | |
this.draw(e, 'layer'); | |
} | |
@HostListener('document:touchend', ['$event']) | |
@HostListener('document:mouseup', ['$event']) | |
onMouseUp() { | |
if ( this.isDrawing) { | |
this.save(); | |
} | |
this.isDrawing = false; | |
} | |
@HostListener('mousedown', ['$event']) | |
onMouseDown(e) { | |
this.isDrawing = true; | |
this.start(e); | |
} | |
@HostListener('mousemove', ['$event']) | |
onMouseMove(e) { | |
if (!this.isDrawing) { | |
return; | |
} | |
this.draw(e); | |
} | |
private start(e, type: 'client' | 'layer' = 'client') { | |
const coords = this.relativeCoords(e, type); | |
this.context.moveTo(coords.x, coords.y); | |
} | |
private draw(e, type: 'client' | 'layer' = 'client') { | |
const coords = this.relativeCoords(e, type); | |
this.context.lineTo(coords.x, coords.y); | |
this.context.stroke(); | |
} | |
private relativeCoords(event, type: 'client' | 'layer') { | |
const bounds = this.sigPadElement.getBoundingClientRect(); | |
let x = event[ type + 'X' ]; | |
let y = event[ type + 'Y' ]; | |
if ('layer' !== type) { | |
x -= bounds.left; | |
y -= bounds.top; | |
} | |
return { x, y }; | |
} | |
clear() { | |
this.context.clearRect(0, 0, this.sigPadElement.width, this.sigPadElement.height); | |
this.context.beginPath(); | |
} | |
save() { | |
this.imgSrc.emit(this.sigPadElement.toDataURL('image/png')); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
The directive does not watch for window resize events, but certainly could be made to do so. Adding a HostListener for document resize, and triggering some of the "init" code would likely make it work in a responsive manner.