-
-
Save calebergh/65b7f0f8b5d7e5d1e6cb977a4cb907f4 to your computer and use it in GitHub Desktop.
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')); | |
} | |
} |
My honest Kudos!
first of all ,thanks a lot for this great directive
I have only one problem ,when toggling device toolbar on google chrome ,to test the code in responsive and in different screens ,it doesn't draw and events in the directive are not fired ,any idea why is that ?
first of all ,thanks a lot for this great directive I have only one problem ,when toggling device toolbar on google chrome ,to test the code in responsive and in different screens ,it doesn't draw and events in the directive are not fired ,any idea why is that ?
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.
This is a minimal electronic "signature" pad, compatible with desktop and touchscreen devices.
Why?
No libraries or demos I found worked properly with Angular 8+ and touch devices. This aims to be a super-simple, within-your-control, easy to use directive.
This started out as a component, but in order to get proper scoping on the @HostListeners (to just the target canvas), it made more sense as a directive.
I've tested on limited machines / browsers, so if you see something that doesn't work, that's not surprising. (Mac in Chrome and Firefox, iPhone 8+)
Example usage:
Be sure to include this directive in your
app.module.ts
file, in thedeclarations
.In the component template file:
In the component controller:
WARNING:
Attempting to adjust the width / height via CSS has undesirable results. For this to work properly, it appears the
width
andheight
HTML attributes need to be set.