LWC – Capture Signature using LWC

A small component to help capture a signature or just drawing some terrible pictures. The component supports saving to Salesforce File or Attachment.

// SingatureUtils.cls
public with sharing class SignatureUtils {
    @AuraEnabled
    public static void saveSignature(string relatedId, string data, string type){
        //No Id No save
        if(String.isBlank(relatedId)) return;
 
        if(type == 'Attachment'){
            saveAttachment(relatedId, data);
        }
 
        if(type == 'SFFile'){
            saveSFFile(relatedId,data);
        }
    }
 
    //Save file as Attachment
 
    private static void saveAttachment(string relatedId, string data){
        Attachment att = new Attachment();
        att.Name = 'Singed-'+System.now().getTime()+'.png';
        att.Body = EncodingUtil.base64Decode(data);
        att.ContentType = 'image/png';
        att.ParentId = relatedId;
 
        insert att;
 
    }
 
    private static void saveSFFile(string relatedId, string data){
        ContentVersion cv = new ContentVersion();
        cv.PathOnClient = 'Singed-'+System.now().getTime()+'.png';
        cv.Title = 'Singed-'+System.now().getTime()+'.png';
        cv.VersionData = EncodingUtil.base64Decode(data);
 
        insert cv;
 
 
        ContentDocumentLink cdl = new ContentDocumentLink();
        cdl.ContentDocumentId = [select contentDocumentId from ContentVersion where id=:cv.id].contentDocumentId;
        cdl.LinkedEntityId = relatedId;
        cdl.ShareType = 'V';
 
        insert cdl;
 
    }
}
<!--signaturePanel.html-->
<template>
    <article class="slds-card">
        <h1>Signature: {recordId}</h1>
        <div class="slds-align_absolute-center">
            <canvas id="sPanel" style="border:1px solid black;">
                         
            </canvas>
        </div>
        <div>
            <lightning-button variant="brand" label="Save" class="slds-m-left_x-small" onclick={handleSaveSignature}></lightning-button>
            <lightning-button variant="brand" label="Clear" class="slds-m-left_x-small" onclick={handleClear}></lightning-button>
 
        </div>
    </article>
</template>
//signaturePanel.js
import { LightningElement,api } from 'lwc';
import saveSignature from '@salesforce/apex/SignatureUtils.saveSignature';
import {ShowToastEvent} from 'lightning/platformShowToastEvent';
let saveType = 'SFFile'; //'SFFile' 'Attachment'
let sCanvas , context; //canvas and it context 2d
let mDown = false;
let currPos = {x:0,y:0};
let prePos = {x:0,y:0};
export default class SignaturePanel extends LightningElement {
    @api recordId;
 
    constructor(){
        super();
        //mouse Events
        this.template.addEventListener('mousedown',this.handleMousedown.bind(this));
        this.template.addEventListener('mouseup',this.handleMouseup.bind(this));
        this.template.addEventListener('mousemove',this.handleMousemove.bind(this));
        this.template.addEventListener('mouseout',this.handleMouseend.bind(this));
 
        //Touch Events
        //this.template.addEventListener('touchstart',this.handleMousedown.bind(this))
        //this.template.addEventListener('touchend',this.handleMouseup.bind(this));
        //this.template.addEventListener('touchmove',this.handleMousemove.bind(this));
        //this.template.addEventListener('touchcancel',this.handleMouseend.bind(this));
    }
 
     
 
    renderedCallback(){
        sCanvas = this.template.querySelector('canvas');
        context = sCanvas.getContext('2d'); 
    }
 
    handleMousedown(evt){
        evt.preventDefault();
        mDown = true;
        this.getPos(evt);
         
    }
 
    handleSaveSignature(evt){
         
        context.globalCompositeOperation  = "destination-over";
        context.fillStyle  = "#FFF";
        context.fillRect(0,0,sCanvas.width,sCanvas.height);
         
        let imageURL = sCanvas.toDataURL('image/png');
        let imageData = imageURL.replace(/^data:image\/(png|jpg);base64,/, "");
        console.log(this.recordId);
        saveSignature({relatedId:this.recordId,data:imageData,type:saveType})
            .then(result =>{
                this.dispatchEvent(new ShowToastEvent({
                    title:'Signature Captured',
                    message: 'The signature has been saved',
                    variant: 'success'
                }));
                this.handleClear();
                 
            })
            .catch(error =>{
                console.log('error : ', error.body.message);
            })
    }
 
    handleMouseup(evt){
        evt.preventDefault();
        mDown = false;
    }
 
    handleMousemove(evt){
        evt.preventDefault();
        if(mDown){
            this.getPos(evt);
            this.draw();
        }
    }
 
    getPos(evt){
        let cRect = sCanvas.getBoundingClientRect();
        prePos = currPos;
        currPos = {x:(evt.clientX - cRect.left),y:(evt.clientY - cRect.top)};
    }
 
    handleMouseend(evt){
        evt.preventDefault();
        mDown = false;
    }
 
    draw(){
        context.beginPath();
        context.moveTo(prePos.x,prePos.y);
        context.lineCap = 'round';//smooth line
        context.lineWidth = 1.5;
        context.strokeStyle = "#0000FF";//blue
        context.lineTo(currPos.x,currPos.y);
        context.closePath();
        context.stroke();
    }
 
    handleClear(){
        context.clearRect(0,0,sCanvas.width,sCanvas.height);
    }
}

Results:

No Comments Yet.

Leave a comment